MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | #pragma strict_types |
| 2 | #pragma no_clone |
| 3 | #pragma no_shadow |
| 4 | #pragma no_inherit |
| 5 | //#pragma pedantic |
| 6 | //#pragma range_check |
| 7 | #pragma warn_deprecated |
| 8 | |
| 9 | #include <wizlevels.h> |
| 10 | #include <daemon.h> |
| 11 | #include <events.h> |
| 12 | #include <strings.h> |
| 13 | #include <files.h> |
| 14 | |
| 15 | #define FTPSAVE "/secure/ARCH/ftpd" |
| 16 | |
| 17 | #define MAXLOGSIZE 2000000 |
| 18 | #define SMALLLOGSIZE 200000 |
| 19 | #define LOGTIME 120 |
| 20 | #define MAXBUFFSIZE 2000 |
| 21 | |
| 22 | #define DEBUG(x) if (find_player("zesstra"))\ |
| 23 | tell_object(find_player("zesstra"),\ |
| 24 | "EDBG: "+x+"\n") |
| 25 | |
| 26 | // fuer FTP. |
| 27 | private mapping monitored; |
| 28 | |
| 29 | |
| 30 | #define D_LOGTIME 0 // Logendezeit, <int> |
| 31 | #define D_FLAGS 1 // Flags |
| 32 | #define D_ERSTIE 2 // Name des Ersties. |
| 33 | #define D_IPSTRING 3 |
| 34 | #define D_INDEX 4 // Index fuer D_LOG, naechster freier index. |
| 35 | #define D_LOG 5 // Logdaten, <mixed> (Array) |
| 36 | /* Datenstruktur von D_LOG: |
| 37 | ({ ({<zeit>, <kommando>, <environment>}), .... }) |
| 38 | */ |
| 39 | #define DL_TIME 0 // int |
| 40 | #define DL_CMD 1 // string |
| 41 | #define DL_ENV 2 // string |
| 42 | private nosave mapping ldata = m_allocate(0,D_LOG); |
| 43 | |
| 44 | // Flags: |
| 45 | #define FL_PERMANENT 1 // 'permanent', d.h. nicht nur kurz nach Einloggen |
| 46 | #define FL_SYNC 2 // nicht puffern, synchron auf Platte schreiben. |
| 47 | |
| 48 | // Ja. Macht das bloss nicht nach. |
| 49 | #define P_SECOND "second" |
| 50 | |
| 51 | public int query_bb(); |
| 52 | public void writebb( string msg ); |
| 53 | public varargs void BBWrite(string msg, int catmode); |
| 54 | |
| 55 | public int add( string user, int timeout ); |
| 56 | public int sub( string user ); |
| 57 | public void ftpbb( string user, string msg ); |
| 58 | |
| 59 | private void scan_bb_opfer(); |
| 60 | private void DumpData(string uid, string erstie, string ip, int flags, mixed logdata); |
| 61 | private void ProcessBuffer(string uid); |
| 62 | private void RemoveTemporaryPlayer(string uid); |
| 63 | |
| 64 | public void create() |
| 65 | { |
| 66 | seteuid( getuid(this_object()) ); |
| 67 | restore_object( FTPSAVE ); |
| 68 | scan_bb_opfer(); |
| 69 | EVENTD->RegisterEvent(EVT_LIB_LOGIN, "Eventhandler", this_object()); |
| 70 | EVENTD->RegisterEvent(EVT_LIB_LOGOUT, "Eventhandler", this_object()); |
| 71 | log_file("ARCH/bbmaster.log", strftime("%c: bbmaster wurde geladen.\n")); |
| 72 | } |
| 73 | |
| 74 | // Auf den asynchronen Logout-event zu warten ermoeglicht es theoretisch, 1-2s |
| 75 | // das Log zu umgehen. Andererseits geht das ohnehin nur dann, wenn die |
| 76 | // Logzeit eh abgelaufen ist. |
| 77 | public void Eventhandler(string eid, object trigob, mixed data) { |
| 78 | if (previous_object() == find_object(EVENTD)) { |
| 79 | string uid; |
| 80 | if (objectp(trigob) |
| 81 | && strstr(load_name(trigob),"/std/shells/") == 0 |
| 82 | && !trigob->QueryGuest()) { |
| 83 | // Bei Login und Logout den BBMode einschalten (weil der Loginevent ja |
| 84 | // erst 1-2s nach Einloggen abgearbeitet wird. |
| 85 | trigob->__set_bb(1); |
| 86 | uid=getuid(trigob); |
| 87 | } |
| 88 | else { |
| 89 | // kein Objekt mehr da. Vermutlich hat ein Spieler 'ende' gemacht, aber |
| 90 | // es koennte auch sein, dass jemand mit nem Selbstzerstoerer den Event |
| 91 | // gefakt hat. Aber selbst wenn, viel kann man damit nicht erreichen. |
| 92 | uid = data[E_PLNAME]; |
| 93 | if (!stringp(uid)) return; |
| 94 | // Pruefung auf nicht-Anwesenheit von uid waere noch moeglich, hat aber |
| 95 | // Probleme, wenn ein Spieler sehr schnell wieder einloggt. |
| 96 | } |
| 97 | |
| 98 | if (eid == EVT_LIB_LOGOUT && member(ldata,uid)) { |
| 99 | // Wenn Logout und es gibt Daten im Puffer, koennte man die evtl. |
| 100 | // wegschreiben oder loeschen. |
| 101 | ProcessBuffer(uid); |
| 102 | // auf jeden Fall temporaere Spieler entfernen. (Wichtig!) |
| 103 | RemoveTemporaryPlayer(uid); |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | // schreibt alle Puffer synchron, ohne Callout... Kann laggen. |
| 109 | public int ProcessAllBuffers() { |
| 110 | |
| 111 | if (extern_call() && !ARCH_SECURITY) |
| 112 | return -1; |
| 113 | |
| 114 | foreach(string uid, int logtime, int flags, string erstie, string ip, |
| 115 | int index, mixed data: ldata) { |
| 116 | if (index) { |
| 117 | DumpData(uid, erstie, ip, flags, data); |
| 118 | ldata[uid,D_LOG]=({}); |
| 119 | ldata[uid,D_INDEX]=0; |
| 120 | } |
| 121 | } |
| 122 | return 1; |
| 123 | } |
| 124 | |
| 125 | private void ProcessBuffer(string uid) { |
| 126 | |
| 127 | if (time() <= ldata[uid,D_LOGTIME] |
| 128 | && ldata[uid,D_INDEX]) { |
| 129 | // Daten wegschreiben, wenn Logzeit nicht abgelaufen. Sonst nicht. |
| 130 | call_out(#'DumpData, 2, uid, ldata[uid,D_ERSTIE], ldata[uid,D_IPSTRING], |
| 131 | ldata[uid,D_FLAGS], ldata[uid,D_LOG]); |
| 132 | } |
| 133 | ldata[uid,D_LOG] = ({}); |
| 134 | ldata[uid,D_INDEX] = 0; |
| 135 | } |
| 136 | |
| 137 | private void DumpData(string uid, string erstie, string ip, int flags, mixed logdata) { |
| 138 | string res = sprintf("\n%s%s, IP: %s\n", capitalize(uid), |
| 139 | (stringp(erstie) ? " ("+capitalize(erstie)+")" : ""), |
| 140 | (stringp(ip) ? ip : "Unbekannt")); |
| 141 | logdata-=({0}); |
| 142 | foreach(mixed arr : logdata) { |
| 143 | res+=sprintf("%O: %O [%s]\n", |
| 144 | strftime("%y%m%d-%H%M%S",arr[DL_TIME]), |
| 145 | arr[DL_CMD], arr[DL_ENV] || "<unbekannt>"); |
| 146 | } |
| 147 | |
| 148 | //DEBUG("DumpData: "+res); |
| 149 | if (flags & FL_PERMANENT) |
| 150 | catch(log_file("ARCH/bb."+uid, res, MAXLOGSIZE)); |
| 151 | else if (file_size(LIBLOGDIR"/ARCH/bbmaster") == FSIZE_DIR) |
| 152 | catch(log_file("ARCH/bbmaster/"+uid, res, SMALLLOGSIZE)); |
| 153 | // kein else, in anderen Faellen werden die Daten verworfen. |
| 154 | } |
| 155 | |
| 156 | private void AddTemporaryPlayer(string uid) { |
| 157 | // natuerlich nur, wenn noch nix eingetragen. |
| 158 | if (!member(ldata, uid)) { |
| 159 | object ob = find_player(uid) || find_netdead(uid); |
| 160 | |
| 161 | mixed erstie; |
| 162 | if (ob) |
| 163 | erstie = (string)ob->QueryProp(P_SECOND); |
| 164 | |
| 165 | ldata += ([uid: time() + LOGTIME + random(LOGTIME/2); |
| 166 | 0; |
| 167 | (stringp(erstie) ? erstie : 0); |
| 168 | query_ip_number(ob); |
| 169 | 0; ({}) |
| 170 | ]); |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | private void RemoveTemporaryPlayer(string uid) { |
| 175 | if (!(ldata[uid,D_FLAGS] & FL_PERMANENT)) { |
| 176 | m_delete(ldata, uid); |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | |
| 181 | // Vom Spielererobjekt bei Erschaffung in InitPlayer() gerufen. |
| 182 | public int query_bb() |
| 183 | { |
| 184 | |
| 185 | if (load_name(previous_object())[0..11] != "/std/shells/") |
| 186 | return 0; |
| 187 | |
| 188 | // in jedem Fall wird nun (temporaer) der BB-Modus aktiviert. |
| 189 | if (!previous_object()->QueryGuest()) |
| 190 | previous_object()->__set_bb(1); |
| 191 | |
| 192 | // nur fuer 'permanente' auch 1 zurueckgeben. |
| 193 | return ldata[getuid(previous_object()),D_FLAGS] & FL_PERMANENT; |
| 194 | } |
| 195 | |
| 196 | |
| 197 | |
| 198 | // neue Funktion. Kriegt nur Kommandosstring uebergegen, werden ggf. gepuffert |
| 199 | // und dann weggeschrieben. |
| 200 | public varargs void BBWrite(string msg, int catmode) { |
| 201 | |
| 202 | if ( !this_interactive() || |
| 203 | (extern_call() && |
| 204 | strstr(load_name(previous_object()), "/std/shells/") != 0 ) ) |
| 205 | return; |
| 206 | |
| 207 | string uid = getuid(this_interactive()); |
| 208 | |
| 209 | if (!member(ldata, uid)) |
| 210 | AddTemporaryPlayer(uid); |
| 211 | else if (ldata[uid,D_LOGTIME] < time()) { |
| 212 | // Logzeit abgelaufen. -> ENDE. |
| 213 | if (ldata[uid,D_INDEX]) { |
| 214 | this_interactive()->__set_bb(0); |
| 215 | // es kann vorkommen, dass hier nen ProcessBuffer mit anderer uid |
| 216 | // drinhaengt. Ist aber egal, dann wird der Puffer halt naechstesmal |
| 217 | // geschrieben. |
| 218 | if (find_call_out(#'ProcessBuffer) == -1) |
| 219 | call_out(#'ProcessBuffer, 2, uid); |
| 220 | } |
| 221 | return; |
| 222 | } |
| 223 | |
| 224 | // im synchronen Modus direkt auf Platte schreiben. |
| 225 | //DEBUG("BBWrite: Flags von +"+uid+": "+to_string(ldata[uid,D_FLAGS]) |
| 226 | // +"\n"); |
| 227 | if (ldata[uid,D_FLAGS] & FL_SYNC) { |
| 228 | //DEBUG("BBWrite: Syncmodus\n"); |
| 229 | if (!catmode) { |
| 230 | msg = sprintf("%s: %s [%O]\n", strftime("%y%m%d-%H%M%S"), |
| 231 | msg, environment(this_interactive())); |
| 232 | } |
| 233 | else |
| 234 | msg = msg + "\n"; |
| 235 | log_file( "ARCH/bb."+uid, msg, MAXLOGSIZE ); |
| 236 | return; |
| 237 | } |
| 238 | |
| 239 | // alle anderen werden erstmal gepuffert. |
| 240 | |
| 241 | // wenn catmode und nen Index > 0 wird der Kram an den vorherigen Eintragen |
| 242 | // angehaengt. |
| 243 | int index = ldata[uid,D_INDEX]; |
| 244 | if (catmode && index > 0) { |
| 245 | --index; |
| 246 | ldata[uid,D_LOG][index][DL_CMD] += msg; |
| 247 | } |
| 248 | else { |
| 249 | // Puffer vergroessern? |
| 250 | if (index >= sizeof(ldata[uid,D_LOG])) |
| 251 | ldata[uid,D_LOG]+=allocate(100); |
| 252 | ldata[uid,D_LOG][index] = ({ time(), msg, |
| 253 | object_name(environment(this_interactive())) |
| 254 | }); |
| 255 | ldata[uid,D_INDEX]++; |
| 256 | // es kann vorkommen, dass hier nen ProcessBuffer mit anderer uid |
| 257 | // drinhaengt. Ist aber egal, dann wird der Puffer halt naechstesmal |
| 258 | // geschrieben. |
| 259 | if (index > MAXBUFFSIZE |
| 260 | && find_call_out(#'ProcessBuffer) == -1) |
| 261 | call_out(#'ProcessBuffer, 2, uid); |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | // Alte Funktion, kriegt Strings, teilweise mit Datum, teilweise ohne, |
| 266 | // schrieb frueher nur weg. msg faengt entweder mit einem Datum/Zeit-String |
| 267 | // oder mit einem "->" an. |
| 268 | public void writebb( string msg ) |
| 269 | { |
| 270 | int catmode; |
| 271 | |
| 272 | if ( !this_interactive() || |
| 273 | (extern_call() && |
| 274 | strstr(load_name(previous_object()), "/std/shells/") != 0 ) ) |
| 275 | return; |
| 276 | |
| 277 | // erstmal String bereinigen. |
| 278 | msg = trim(msg,TRIM_RIGHT,"\n"); |
| 279 | if (strstr(msg,"->") == 0) { |
| 280 | catmode=1; |
| 281 | msg= " -> " + msg[2..]; |
| 282 | } |
| 283 | else { |
| 284 | // faengt mit Datumsstring an, erstes Leerzeichen ab dem zehnten Zeichen |
| 285 | // suchen und von dort abschneiden. |
| 286 | msg = msg[strstr(msg," ",10)+1 ..]; |
| 287 | } |
| 288 | |
| 289 | // Dann weitergeben |
| 290 | BBWrite(msg, catmode); |
| 291 | } |
| 292 | |
| 293 | private void scan_bb_opfer() |
| 294 | { |
| 295 | string* lines; |
| 296 | object pl; |
| 297 | string uid; |
| 298 | |
| 299 | // diese user werden 'permanent' ueberwacht, nicht nur direkt nach dem |
| 300 | // Einloggen. |
| 301 | lines = explode( lower_case( read_file("/secure/ARCH/BB_OPFER.dump") |
| 302 | || "" ), "\n" )[2..]; |
| 303 | |
| 304 | foreach(string line : lines) { |
| 305 | if( sizeof(line) && line[0] != '#' ) { |
| 306 | uid=line[0 .. member(line,' ')-1]; |
| 307 | AddTemporaryPlayer(uid); |
| 308 | ldata[uid,D_LOGTIME] = __INT_MAX__; |
| 309 | ldata[uid,D_FLAGS] = FL_PERMANENT; |
| 310 | pl = find_player(uid) || find_netdead(uid); |
| 311 | if (pl) |
| 312 | pl->__set_bb(1); |
| 313 | } |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | // Neuladen ist kein grosses Problem, weil der bbmaster ja automatisch |
| 318 | // neugeladen wird. Nebeneffekt ist lediglich, dass fuer alle laufenden Logs |
| 319 | // die Logzeit wieder von vorne anfaengt. Da das Schreiben der Puffer aber Lag |
| 320 | // verursachen kann, duerfen es nur EM+. |
| 321 | public varargs int remove(int silent) { |
| 322 | |
| 323 | if (!ARCH_SECURITY) |
| 324 | return 0; |
| 325 | |
| 326 | log_file("ARCH/bbmaster.log", strftime("%c: remove() called.\n")); |
| 327 | |
| 328 | // alle Puffer leeren... |
| 329 | // ProcessAllBuffers() wird hierbei _ohne_ Limits aufgerufen! Kann fieses |
| 330 | // Lag erzeugen, aber sonst wurde der Kram evtl. nicht ordentlich |
| 331 | // geschrieben. |
| 332 | limited(#'ProcessAllBuffers); |
| 333 | destruct(this_object()); |
| 334 | return 1; |
| 335 | } |
| 336 | |
| 337 | |
| 338 | // Alles ab hier nur zum Ueberwachen von FTP-Aktivitaeten. |
| 339 | private int player_exists( string user ) |
| 340 | { |
| 341 | if ( !stringp( user ) || sizeof( user ) < 2 ) |
| 342 | return 0; |
| 343 | |
| 344 | return file_size( "/save/" + user[0..0] + "/" + user + ".o" ) > 0; |
| 345 | } |
| 346 | |
| 347 | public int add( string user, int timeout ) |
| 348 | { |
| 349 | if ( !ARCH_SECURITY || process_call() ) |
| 350 | return -1; |
| 351 | |
| 352 | if( !stringp(user) || !player_exists(lower_case(user)) || !intp(timeout) || |
| 353 | !timeout ) |
| 354 | return -2; |
| 355 | |
| 356 | monitored[lower_case(user)] = timeout; |
| 357 | save_object( FTPSAVE ); |
| 358 | |
| 359 | return 1; |
| 360 | } |
| 361 | |
| 362 | |
| 363 | public int sub( string user ) |
| 364 | { |
| 365 | if ( !ARCH_SECURITY || process_call() ) |
| 366 | return -1; |
| 367 | |
| 368 | if( !stringp(user) || !member( monitored, lower_case(user) ) ) |
| 369 | return -2; |
| 370 | |
| 371 | m_delete( monitored, lower_case(user) ); |
| 372 | save_object( FTPSAVE ); |
| 373 | |
| 374 | return 1; |
| 375 | } |
| 376 | |
| 377 | |
| 378 | public void ftpbb( string user, string msg ) |
| 379 | { |
| 380 | if( getuid(previous_object()) != ROOTID ) |
| 381 | return; |
| 382 | |
| 383 | if ( ldata[user,D_FLAGS] & FL_PERMANENT ) |
| 384 | log_file( "ARCH/bb."+user, msg, 2000000 ); |
| 385 | |
| 386 | if ( monitored[user] ){ |
| 387 | if ( monitored[user] > 0 && monitored[user] < time() ) |
| 388 | sub( user ); |
| 389 | else |
| 390 | CHMASTER->send( "FTP", capitalize(user), msg ); |
| 391 | } |
| 392 | } |
| 393 | |