blob: 9677f7ff9027078c695e53360887ca0658e15725 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001#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.
27private 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
42private 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
51public int query_bb();
52public void writebb( string msg );
53public varargs void BBWrite(string msg, int catmode);
54
55public int add( string user, int timeout );
56public int sub( string user );
57public void ftpbb( string user, string msg );
58
59private void scan_bb_opfer();
60private void DumpData(string uid, string erstie, string ip, int flags, mixed logdata);
61private void ProcessBuffer(string uid);
62private void RemoveTemporaryPlayer(string uid);
63
64public 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.
77public 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.
109public 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
125private 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
137private 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
156private 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
174private 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.
182public 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.
200public 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.
268public 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
293private 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+.
321public 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.
339private 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
347public 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
363public 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
378public 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