blob: 3faf4247914d14628869fddad2c91fc09d4947c9 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001/* MorgenGrauen MUDlib
2 /p/daemon/errord.c
3 speichert Fehler und Warnungen
4 Autor: Zesstra
5 $Id: errord.c 9439 2016-01-20 09:48:28Z Zesstra $
6 ggf. Changelog:
7*/
8
9#pragma strict_types,save_types,rtt_checks
10#pragma no_clone
11#pragma no_shadow
12#pragma no_inherit
13#pragma pedantic
14#pragma range_check
15#pragma warn_deprecated
16
17#include <config.h>
18#include <wizlevels.h>
19#include <defines.h>
20#include <debug_info.h>
21#include <commands.h>
22#include <wizlevels.h>
23#include <mail.h>
24#include <tls.h>
25#include <events.h>
26
27inherit "/secure/errord-structs";
28
29#define __NEED_IMPLEMENTATION__
30#include "errord.h"
31#undef __NEED_IMPLEMENTATION__
32
33#define SAVEFILE (__DIR__+"ARCH/errord")
34
35#define TI this_interactive()
36
37private int access_check(string uid,int mode);
38private varargs int set_lock(int issueid, int lock, string note);
39private varargs int set_resolution(int issueid, int resolution, string note);
Bugfix67091cb2022-09-22 15:57:31 +020040public string* print_type(int type);
MG Mud User88f12472016-06-24 23:31:02 +020041
42private int versende_mail(struct fullissue_s fehler);
43
44nosave mapping lasterror; // die letzen 5 jeder Art.
45
46
47/* ******************* Helfer **************************** */
48
49public int getErrorID(string hashkey)
50{
51 int** row=sl_exec("SELECT id from issues WHERE hashkey=?1;",
52 hashkey);
53 //DEBUG(sprintf("getEID: %s: %O\n",hashkey,row));
54 if (pointerp(row))
55 {
56 return row[0][0];
57 }
58 return -1;
59}
60
61// note->id muss auf einen Eintrag in issues verweisen, es erfolgt keine
62// Pruefung.
63int db_add_note(struct note_s note)
64{
65 sl_exec("INSERT INTO notes(issueid,time,user,txt) "
66 "VALUES(?1,?2,?3,?4);",
67 to_array(note)...);
68 return sl_insert_id();
69}
70
71private struct frame_s* db_get_stack(int issueid)
72{
73 mixed rows = sl_exec("SELECT * FROM stacktraces WHERE issueid=?1 "
74 "ORDER BY rowid;", issueid);
75 if (pointerp(rows))
76 {
77 struct frame_s* stack = allocate(sizeof(rows));
78 int i;
79 foreach(mixed row : rows)
80 {
81 stack[i] = to_struct(row, (<frame_s>));
82 ++i;
83 }
84 return stack;
85 }
86 return 0;
87}
88
89private struct note_s* db_get_notes(int issueid)
90{
91 mixed rows = sl_exec("SELECT * FROM notes WHERE issueid=?1 "
92 "ORDER BY rowid;", issueid);
93 if (pointerp(rows))
94 {
95 struct note_s* notes = allocate(sizeof(rows));
96 int i;
97 foreach(mixed row : rows)
98 {
99 notes[i] = to_struct(row, (<note_s>));
100 ++i;
101 }
102 return notes;
103 }
104 return 0;
105}
106
107// einen durch id oder hashkey bezeichneten Eintrag als fullissue_s liefern.
108private struct fullissue_s db_get_issue(int issueid, string hashkey)
109{
110 mixed rows = sl_exec("SELECT * FROM issues WHERE id=?1 OR hashkey=?2;",
111 issueid, hashkey);
112 if (pointerp(rows))
113 {
114 // Einfachster Weg - funktioniert aber nur, solange die Felder in der DB
115 // die gleiche Reihenfolge wie in der struct haben! Entweder immer
116 // sicherstellen oder Ergebnisreihenfolge oben im select festlegen!
117 struct fullissue_s issue = to_struct( rows[0], (<fullissue_s>) );
118 if (issue->type == T_RTERROR)
119 issue->stack = db_get_stack(issue->id);
120 issue->notes = db_get_notes(issue->id);
121 return issue;
122 }
123 return 0;
124}
125
126private struct fullissue_s filter_private(struct fullissue_s issue)
127{
128 //momentan wird F_CLI, also die Spielereingabe vor dem Fehler
129 //ausgefiltert, wenn TI kein EM oder man in process_string() ist.
130
131 //Wenn EM und nicht in process_string() oder die Spielereingabe gar nicht
132 //im Fehlereintrag drinsteht: ungefiltert zurueck
133 if (!issue->command ||
134 (!process_call() && ARCH_SECURITY) )
135 return issue;
136
137 //sonst F_CLI rausfiltern, also Kopie und in der Kopie aendern.
138 issue->command="Bitte EM fragen";
139 return issue;
140}
141
142// setzt oder loescht die Loeschsperre.
143// Prueft, ob <issueid> existiert und aendert den Zustand nur, wenn noetig.
144// Rueckgabe: -1, wenn Issue nicht existiert, -2 wenn bereits resolved, -3
145// wenn keine Aenderung noetig, sonst den neuen Sperrzustand
146int db_set_lock(int issueid, int lockstate, string note)
147{
148 int** rows = sl_exec("SELECT locked,resolved FROM issues WHERE id=?1;",
149 issueid);
150 if (!rows)
151 return -1; // nicht vorhanden.
152
153 if (rows[0][1])
154 return -2; // bereits resolved -> Sperre nicht moeglich.
155
156
157 if (lockstate && !rows[0][0])
158 {
159 // Sperren
160// sl_exec("BEGIN TRANSACTION;");
161 sl_exec("UPDATE issues SET locked=1,locked_by=?2,locked_time=?3,mtime=?3 "
162 "WHERE id=?1;",
163 issueid, getuid(TI), time());
164 db_add_note( (<note_s> id: issueid, time: time(), user: getuid(TI),
165 txt: sprintf("Lock gesetzt: %s",
166 note ? note : "<kein Kommentar>")) );
167// sl_exec("COMMIT;");
168 return 1;
169 }
170 else if (!lockstate && rows[0][0])
171 {
172 // entsperren
173// sl_exec("BEGIN TRANSACTION;");
174 sl_exec("UPDATE issues SET locked=0,locked_by=0,locked_time=0,mtime=?2 "
175 "WHERE id=?1;", issueid, time());
176 db_add_note( (<note_s> id: issueid, time: time(), user: getuid(TI),
177 txt: sprintf("Lock geloescht: %s",
178 note ? note : "<kein Kommentar>")) );
179// sl_exec("COMMIT;");
180 return 0;
181 }
182 // nix aendern.
183 return -3;
184}
185
186// markiert ein Issue als gefixt oder nicht gefixt.
187// Prueft, ob <issueid> existiert und aendert den Zustand nur, wenn noetig.
188// Rueckgabe: -1, wenn Issue nicht existiert, -3 wenn keine Aenderung noetig,
189// sonst den neuen Sperrzustand
190int db_set_resolution(int issueid, int resolved, string note)
191{
192 int** rows = sl_exec("SELECT resolved FROM issues WHERE id=?1;",
193 issueid);
194 if (!rows)
195 return -1; // nicht vorhanden.
196
197 if (resolved && !rows[0][0])
198 {
199 // Als gefixt markieren.
200// sl_exec("BEGIN TRANSACTION;");
201 sl_exec("UPDATE issues SET resolved=1,resolver=?2,mtime=?3 "
202 "WHERE id=?1;",
203 issueid, getuid(TI),time());
204 db_add_note( (<note_s> id: issueid, time: time(), user: getuid(TI),
205 txt: sprintf("Fehler gefixt: %s",
206 note ? note : "<kein Kommentar>")) );
207// sl_exec("COMMIT;");
208 return 1;
209 }
210 else if (!resolved && rows[0][0])
211 {
212 // als nicht gefixt markieren.
213// sl_exec("BEGIN TRANSACTION;");
214 sl_exec("UPDATE issues SET resolved=0,resolver=0,mtime=?2 "
215 "WHERE id=?1;", issueid, time());
216 db_add_note( (<note_s> id: issueid, time: time(), user: getuid(TI),
217 txt: sprintf("Fix zurueckgezogen: %s",
218 note ? note : "<kein Kommentar>")) );
219// sl_exec("COMMIT;");
220 return 0;
221 }
222 // nix aendern.
223 return -3;
224
225}
226
227// Transferiert ein Issue zu einer neuen zustaendigen UID
228// Prueft, ob <issueid> existiert und aendert den Zustand nur, wenn noetig.
229// Rueckgabe: -1, wenn Issue nicht existiert, -3 wenn keine Aenderung noetig,
230// 1, wenn erfolgreich neu zugewiesen
231int db_reassign_issue(int issueid, string newuid, string note)
232{
233 string** rows = sl_exec("SELECT uid FROM issues WHERE id=?1;",
234 issueid);
235 if (!rows)
236 return -1; // nicht vorhanden.
237
238 if (!stringp(newuid))
239 return(-2);
240
241 if (newuid != rows[0][0])
242 {
243// sl_exec("BEGIN TRANSACTION;");
244 sl_exec("UPDATE issues SET uid=?2,mtime=?3 WHERE id=?1;",
245 issueid, newuid,time());
246 db_add_note( (<note_s> id: issueid, time: time(), user: getuid(TI),
247 txt: sprintf("Fehler von %s an %s uebertragen. (%s)",
248 rows[0][0], newuid,
249 note ? note : "<kein Kommentar>")) );
250// sl_exec("COMMIT;");
251 return 1;
252 }
253
254 return -3;
255}
256
257// inkrementiert count und aktualisiert mtime, atime.
258// Ausserdem wird ggf. das Loeschflag genullt - ein erneut aufgetretener
259// Fehler sollte anschliessend nicht mehr geloescht sein. Geloeste
260// (resolved) Eintraege werden NICHT auf ungeloest gesetzt. Vermutlich trat
261// der Fehler in einem alten Objekte auf...
262// Issue muss in der DB existieren.
263int db_countup_issue(int issueid)
264{
265 sl_exec("UPDATE issues SET count=count+1,mtime=?2,atime=?2,deleted=0 WHERE id=?1;",
266 issueid,time());
267 return 1;
268}
269
270// Das Issue wird ggf. ent-loescht und als nicht resvolved markiert.
271// Sind pl und msg != 0, wird eine Notiz angehaengt.
272// aktualisiert mtime, atime.
273// Issue muss in der DB existieren.
274int db_reopen_issue(int issueid, string pl, string msg)
275{
276 int** row=sl_exec("SELECT deleted,resolved from issues WHERE id=?1;",
277 issueid);
278 if (pointerp(row)
279 && (row[0][0] || row[0][1]) )
280 {
281// sl_exec("BEGIN TRANSACTION;");
282 if (pl && msg)
283 {
284 db_add_note( (<note_s> id: issueid,
285 time: time(),
286 user: pl,
287 txt: msg) );
288 }
289 sl_exec("UPDATE issues SET "
290 "deleted=0,resolved=0,resolver=0,mtime=?2,atime=?2 WHERE id=?1;",
291 issueid,time());
292// sl_exec("COMMIT;");
293 }
294 return 1;
295}
296
297int db_insert_issue(struct fullissue_s issue)
298{
299 //DEBUG(sprintf("db_insert: %O\n", issue));
300
301 mixed row=sl_exec("SELECT id from issues WHERE hashkey=?1;",
302 issue->hashkey);
303 //DEBUG(sprintf("insert: %s: %O\n",issue->hashkey,row));
304 if (pointerp(row))
305 {
306 issue->id=row[0][0];
307 return db_countup_issue(issue->id);
308 }
309// sl_exec("BEGIN TRANSACTION;");
310 sl_exec("INSERT INTO issues(hashkey,uid,type,mtime,ctime,atime,count,"
311 "deleted,resolved,locked,locked_by,locked_time,resolver,message,"
312 "loadname,obj,prog,loc,titp,tienv,hbobj,caught,command,verb) "
313 "VALUES(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13,?14,?15,"
314 "?16,?17,?18,?19,?20,?21,?22,?23,?24);",
315 (to_array(issue)[1..24])...);
316 issue->id=sl_insert_id();
317
318 if (pointerp(issue->stack))
319 {
320 foreach(struct frame_s entry : issue->stack)
321 {
322 entry->id = issue->id;
323 sl_exec("INSERT INTO stacktraces(issueid,type,name,prog,obj,loc,ticks) "
324 "VALUES(?1,?2,?3,?4,?5,?6,?7);",
325 to_array(entry)...);
326 }
327 }
328 if (pointerp(issue->notes))
329 {
330 foreach(struct note_s entry : issue->notes)
331 {
332 entry->id = issue->id;
333 sl_exec("INSERT INTO notes(issueid,time,user,txt) "
334 "VALUES(?1,?2,?3,?4);",
335 to_array(entry)...);
336 }
337 }
338// sl_exec("COMMIT;");
339
340 return issue->id;
341}
342
343
Bugfixa75344d2017-06-16 14:04:48 +0200344// loggt einen T_REPORTED_ERR, T_REPORTED_IDEA, T_REPORTED_TYPO,
345// T_REPORTED_MD, T_REPORTED_SYNTAX
MG Mud User88f12472016-06-24 23:31:02 +0200346public string LogReportedError(mapping err)
347{
Zesstra0339e4f2019-11-11 21:38:07 +0100348 //darf nur von Spielershells oder Objekt in /obj/ (z.B. Fehlerteufel oder
349 //vitem_proxy) gerufen werden.
MG Mud User88f12472016-06-24 23:31:02 +0200350 if (extern_call() && !previous_object()
351 || (strstr(load_name(previous_object()),"/std/shells/") == -1
Zesstra0339e4f2019-11-11 21:38:07 +0100352 && strstr(load_name(previous_object()), "/"LIBOBJDIR"/") == -1))
MG Mud User88f12472016-06-24 23:31:02 +0200353 return 0;
354
355 //DEBUG("LogReportedError\n");
356 // DEBUG(sprintf("%O\n",err));
Vanionb4c06532020-03-09 21:54:54 +0100357 string uid = ({string})master()->creator_file(err[F_OBJ]);
MG Mud User88f12472016-06-24 23:31:02 +0200358
359 // default-Typ
360 if (!member(err, F_TYPE)) err[F_TYPE] = T_REPORTED_ERR;
361
362 // div. Keys duerfen nicht gesetzt sein.
363 err -= ([F_STATE, F_READSTAMP, F_CAUGHT, F_STACK, F_CLI, F_VERB,
364 F_LOCK, F_RESOLVER, F_NOTES]);
365
366 // Errormapping in issue-struct umwandeln und befuellen.
367 struct fullissue_s issue = (<fullissue_s>);
368 issue->type = err[F_TYPE];
369 issue->uid = uid;
370 issue->mtime = issue->ctime = issue->atime = time();
371 issue->count=1;
372 issue->loadname = load_name(err[F_OBJ]);
373 issue->message = err[F_MSG];
Arathorn2e77c0a2016-08-27 14:37:47 +0200374 issue->obj = objectp(err[F_OBJ]) ? object_name(err[F_OBJ]) : err[F_OBJ];
Bugfixf6d9f2a2022-08-31 11:42:00 +0200375 issue->loc = err[F_LINE];
MG Mud User88f12472016-06-24 23:31:02 +0200376 // Normalisieren auf fuehrenden / und kein .c
377 if (err[F_PROG]!="unbekannt")
378 issue->prog = load_name(err[F_PROG]);
379 else
380 issue->prog = "unbekannt";
381 issue->titp = getuid(this_interactive() || this_player());
Bugfix6a4979e2021-08-30 21:04:09 +0200382 issue->tienv = object_name(environment(this_interactive() || this_player()));
MG Mud User88f12472016-06-24 23:31:02 +0200383
384 //DEBUG(sprintf("%O\n",issue));
385 issue->hashkey = hash(TLS_HASH_MD5,
386 sprintf("%d%s%s", issue->type, issue->loadname, issue->message));
387
388 // ggf. vorhandenen Fehler suchen - zugegeben: sollte bei von Spielern
389 // gemeldeten Dingen vermutlich nie vorkommen...
390 int oldid = getErrorID(issue->hashkey);
391 if (oldid >= 0)
392 {
393 // ggf. sicherstellen, dass er wieder eroeffnet wird.
394 db_reopen_issue(oldid, "<ErrorD>",
395 "Automatisch wiedereroeffnet wegen erneutem Auftreten.");
396 db_countup_issue(oldid);
397 return issue->hashkey;
398 }
399
400 // sonst fuegen wir einen neuen Eintrag hinzu
401 // Spielergemeldete Bugs werden erstmal vor automatischem Loeschen
402 // geschuetzt, bis ein zustaendiger Magier ihn zur Kenntnis nimmt und
403 // entsperrt.
404 issue->locked = 1;
405 issue->locked_by = getuid(TI || PL);
406 issue->locked_time = time();
407
408 // In DB eintragen.
409 issue->id = db_insert_issue(issue);
410
411 lasterror[issue->type]=issue->id;
412
413 // Event triggern, aber nur eine Teilmenge der Daten direkt mitliefern.
414 EVENTD->TriggerEvent(EVT_LIB_NEW_ERROR,
415 ([F_TYPE: issue->type, F_HASHKEY:issue->hashkey,
416 F_UID:issue->uid, F_ID: issue->id]));
417
418 DEBUG(sprintf("LogReportedError: %s\n",issue->hashkey));
419
420 return issue->hashkey;
421}
422
423//Fehler registrieren
424//Diese Funktion darf nicht mehr als 200k Ticks verbrauchen und wird nur vom
425//Master gerufen!
426public int LogError(string msg,string prg,string curobj,int line,mixed culprit,
427 int caught)
428{
429 //DEBUG(sprintf("LogError: Prog: %O, Obj: %O,\n",prg,curobj));
430
431 //darf nur vom Master gerufen werden
432 if (!extern_call() ||
433 (previous_object() && previous_object() != master()))
434 return 0;
435
436 struct fullissue_s issue = (<fullissue_s>);
437
Zesstraddddbf72021-05-14 16:52:16 +0200438 //UID bestimmen, curobj is 0 for lwobjects, then the program is used.
439 issue->uid=({string})master()->creator_file(curobj || prg);
Zesstra1c432b72021-07-12 21:18:05 +0200440 //DEBUG(sprintf("LogError: UID: %s\n",issue->uid));
MG Mud User88f12472016-06-24 23:31:02 +0200441
442 //Loadname (besser als BP, falls rename_object() benutzt wurde) bestimmen
443 if (!stringp(curobj) || !sizeof(curobj))
444 issue->loadname = curobj = "<Unbekannt>";
445 else
446 {
447 //load_name nimmt Strings und Objects und konstruiert den loadname,
448 //wie er sein sollte, wenn das Objekt nicht mehr existiert.
449 issue->loadname=load_name(curobj);
450 }
451 if (!stringp(issue->loadname))
452 {
453 //hier kommt man rein, falls curobj ein 'kaputter' Name ist,
454 //d.h. load_name() 0 liefert.
455 issue->loadname="<Illegal object name>";
456 }
457
458 // Wenn curobj in /players/ liegt, es einen TI gibt, welcher ein Magier
459 // ist und dieser die Prop P_DONT_LOG_ERRORS gesetzt hat, wird der FEhler
460 // nicht gespeichert.
461 if (this_interactive() && IS_LEARNER(this_interactive())
462 && strstr(issue->loadname,WIZARDDIR)==0
bugfixd94d0932020-04-08 11:27:13 +0200463 && ({int})this_interactive()->QueryProp(P_DONT_LOG_ERRORS))
MG Mud User88f12472016-06-24 23:31:02 +0200464 {
465 return 0;
466 }
467
468 // prg und curobj auf fuehrenden / und ohne .c am Ende normieren.
469 if (stringp(prg))
470 issue->prog = load_name(prg);
471 if (stringp(curobj) && curobj[0]!='/')
472 {
473 curobj="/"+curobj;
474 }
475
476 issue->obj = curobj;
477 issue->loc = line;
478 issue->message = msg;
479 issue->ctime = issue->mtime = issue->atime = time();
480 issue->type = T_RTERROR;
481 issue->count = 1;
482 issue->caught = caught;
483
484 //Hashkey bestimmen: Typ, Name der Blueprint des buggenden Objekts,
485 //Programmname, Zeilennr., Fehlermeldung
486 //TODO: evtl. sha1() statt md5()?
487 issue->hashkey=hash(TLS_HASH_MD5,
488 sprintf("%d%s%s%d%s", T_RTERROR, issue->loadname||"",
489 issue->prog || "", issue->loc,
490 issue->message||"<No error message given.>"));
491 DEBUG(sprintf("LogError: Hashkey: %s", issue->hashkey));
492
493 // ggf. vorhandenen Fehler suchen
494 int oldid = getErrorID(issue->hashkey);
495 if (oldid >= 0)
496 {
497 db_reopen_issue(oldid, "<ErrorD>",
498 "Automatisch wiedereroeffnet wegen erneutem Auftreten.");
499 db_countup_issue(oldid);
500 return oldid;
501 }
502
503 //sonst fuegen wir einen neuen Eintrag hinzu
504 //DEBUG(sprintf("LogError: OBJ: %s, BP: %s",curobj,loadname));
505 // Wenn Fehler im HB, Objektnamen ermitteln
506 if (objectp(culprit))
507 issue->hbobj = object_name(culprit);
508
509 //gibt es einen TI/TP? Name mit erfassen
510 mixed tienv;
511 if(objectp(this_interactive()))
512 {
513 issue->titp=getuid(this_interactive());
514 tienv=environment(this_interactive());
515 }
516 else if (objectp(PL) && query_once_interactive(PL))
517 {
518 issue->titp=getuid(PL);
519 tienv=environment(PL);
520 }
521 else if (objectp(PL))
522 {
523 issue->titp=object_name(PL);
524 tienv=environment(PL);
525 }
526 if (objectp(tienv))
527 issue->tienv=object_name(tienv);
528
529 // Mal schauen, ob der Commandstack auch was fuer uns hat. ;-)
530 mixed cli;
531 if (pointerp(cli=command_stack()) && sizeof(cli))
532 {
533 issue->verb=cli[0][CMD_VERB];
534 issue->command=cli[0][CMD_TEXT];
535 }
536
537 //stacktrace holen
538 mixed stacktrace;
539 if (caught)
540 stacktrace=debug_info(DINFO_TRACE,DIT_ERROR);
541 else
542 stacktrace=debug_info(DINFO_TRACE,DIT_UNCAUGHT_ERROR);
543 // gueltige Stacktraces haben min. 2 Elemente.
544 // (leerer Trace: ({"No trace."}))
545 if (sizeof(stacktrace) > 1)
546 {
547 int i;
548 issue->stack = allocate(sizeof(stacktrace)-1);
549 // erstes Element ist 0 oder HB-Objekt: kein frame, daher ueberspringen
550 foreach(mixed entry : stacktrace[1..])
551 {
552 // frame->id will be set later by db_insert_issue().
553 struct frame_s frame = (<frame_s> type : entry[TRACE_TYPE],
554 name: entry[TRACE_NAME],
555 prog: entry[TRACE_PROGRAM],
556 obj: entry[TRACE_OBJECT],
557 loc: entry[TRACE_LOC],
558 ticks: entry[TRACE_EVALCOST]);
559 issue->stack[i] = frame;
560 ++i;
561 }
562 }
563
564 issue->id = db_insert_issue(issue);
565
566 lasterror[T_RTERROR]=issue->id;
567
568 // Event triggern, aber nur eine Teilmenge der Daten direkt mitliefern.
569 EVENTD->TriggerEvent(EVT_LIB_NEW_ERROR,
570 ([ F_TYPE: T_RTERROR, F_HASHKEY: issue->hashkey, F_UID:
571 issue->uid, F_ID: issue->id ]));
572
573// DEBUG(sprintf("LogError: Fehlereintrag:\n%O\n",
574// errors[uid][hashkey]));
575// DEBUG(sprintf("LogError: Verbrauchte Ticks: %d\n",
576// 200000-get_eval_cost()));
577 return issue->id;
578}
579
580//Warnungen registrieren
581//Diese Funktion darf nicht mehr als 200k Ticks verbrauchen und wird nur vom
582//Master gerufen!
583public int LogWarning(string msg,string prg,string curobj,int line, int in_catch)
584{
585 //DEBUG(sprintf("LogWarning: Prog: %O, Obj: %O,\n",prg,curobj));
586
587 //darf nur vom Master gerufen werden
588 if (!extern_call() ||
589 (previous_object() && previous_object() != master()))
590 return 0;
591
592 struct fullissue_s issue = (<fullissue_s>);
593
Zesstraddddbf72021-05-14 16:52:16 +0200594 //UID bestimmen, curobj is 0 for lwobjects, then the program is used.
595 issue->uid=({string})master()->creator_file(curobj || prg);
MG Mud User88f12472016-06-24 23:31:02 +0200596 //DEBUG(sprintf("LogWarning UID: %s\n",uid));
597
598 //Loadname (besser als BP, falls rename_object() benutzt wurde) bestimmen
599 if (!stringp(curobj) || !sizeof(curobj))
600 issue->loadname = curobj = "<Unbekannt>";
601 else
602 {
603 //load_name nimmt Strings und Objects und konstruiert den loadname,
604 //wie er sein sollte, wenn das Objekt nicht mehr existiert.
605 issue->loadname=load_name(curobj);
606 }
607
608 if (!stringp(issue->loadname))
609 //hier sollte man reinkommen, falls curobj ein 'kaputter' Name ist,
610 //d.h. load_name() 0 liefert.
611 issue->loadname="<Illegal object name>";
612
613 // prg und curobj auf abs. Pfade normalisieren.
614 if (stringp(prg))
615 issue->prog=load_name(prg);
616 if (stringp(curobj) && curobj[0]!='/')
617 {
618 curobj="/"+curobj;
619 }
620
621 //DEBUG(sprintf("LogWarning: OBJ: %s, BP: %s\n",curobj,blue));
622
623 // Wenn curobj in /players/ liegt, es einen TI gibt, welcher ein Magier
624 // ist und dieser die Prop P_DONT_LOG_ERRORS gesetzt hat, wird der FEhler
625 // nicht gespeichert.
626 if (this_interactive() && IS_LEARNER(this_interactive())
627 && strstr(issue->loadname,WIZARDDIR)==0
bugfixd94d0932020-04-08 11:27:13 +0200628 && ({int})this_interactive()->QueryProp(P_DONT_LOG_ERRORS)) {
MG Mud User88f12472016-06-24 23:31:02 +0200629 return 0;
630 }
631
632 //Hashkey bestimmen, Typ, Name der Blueprint des buggenden Objekts, Programm
633 //Zeilennr., Warnungsmeldung
634 issue->hashkey=hash(TLS_HASH_MD5,
635 sprintf("%d%s%s%d%s", T_RTWARN, issue->loadname, issue->prog, line,
636 msg));
637 //DEBUG(sprintf("LogWarning: Hashkey: %s",hashkey));
638
639
640 // ggf. vorhandenen Fehler suchen
641 int oldid = getErrorID(issue->hashkey);
642 if (oldid >= 0)
643 {
644 db_reopen_issue(oldid, "<ErrorD>",
645 "Automatisch wiedereroeffnet wegen erneutem Auftreten.");
646 db_countup_issue(oldid);
647 return oldid;
648 }
649
650 //sonst fuegen wir einen neuen Eintrag hinzu
651 // erstmal vervollstaendigen
652 issue->obj = curobj;
653 issue->message = msg;
654 issue->ctime = issue->mtime = issue->atime = time();
655 issue->loc = line;
656 issue->count = 1;
657 issue->type = T_RTWARN;
658 issue->caught = in_catch;
659
660 //gibt es einen TI/TP? Name mit erfassen
661 mixed tienv;
662 if(objectp(this_interactive()))
663 {
664 issue->titp=getuid(this_interactive());
665 tienv=environment(this_interactive());
666 }
667 else if (objectp(PL) && query_once_interactive(PL))
668 {
669 issue->titp=getuid(PL);
670 tienv=environment(PL);
671 }
672 else if (objectp(PL))
673 {
674 issue->titp=object_name(PL);
675 tienv=environment(PL);
676 }
677 if (objectp(tienv))
678 issue->tienv=object_name(tienv);
679
680 // Mal schauen, ob der Commandstack auch was fuer uns hat. ;-)
681 mixed cli;
682 if (pointerp(cli=command_stack()) && sizeof(cli))
683 {
684 issue->verb=cli[0][CMD_VERB];
685 issue->command=cli[0][CMD_TEXT];
686 }
687
688 issue->id = db_insert_issue(issue);
689
690 lasterror[T_RTWARN]=issue->id;
691 // Event triggern, aber nur eine Teilmenge der Daten direkt mitliefern.
692 EVENTD->TriggerEvent(EVT_LIB_NEW_ERROR,
693 ([F_TYPE: issue->type, F_ID: issue->id,
694 F_UID: issue->uid, F_HASHKEY: issue->hashkey]) );
695
696// DEBUG(sprintf("LogWarning: Warnungseintrag:\n%O\n",
697// warnings[uid][hashkey]));
698// DEBUG(sprintf("LogWarning: Verbrauchte Ticks: %d\n",
699// 200000-get_eval_cost()));
700 return issue->id;
701}
702
703//Warnungen und Fehler beim Kompilieren registrieren
704//Diese Funktion darf nicht mehr als 200k Ticks verbrauchen und wird nur vom
705//Master gerufen!
706public int LogCompileProblem(string file,string msg,int warn) {
707
708 //DEBUG(sprintf("LogCompileProblem: Prog: %O, Obj: %O,\n",file,msg));
709
710 //darf nur vom Master gerufen werden
711 if (!extern_call() ||
712 (previous_object() && previous_object() != master()))
713 return 0;
714
715 struct fullissue_s issue = (<fullissue_s>);
716
717 //UID bestimmen
Vanionb4c06532020-03-09 21:54:54 +0100718 issue->uid=({string})master()->creator_file(file);
MG Mud User88f12472016-06-24 23:31:02 +0200719 //DEBUG(sprintf("LogCompileProblem UID: %s\n",uid));
720
721 // An File a) fuehrenden / anhaengen und b) endendes .c abschneiden. Macht
722 // beides netterweise load_name().
723 issue->loadname = load_name(file);
724 issue->type = warn ? T_CTWARN : T_CTERROR;
725
726 //loggen wir fuer das File ueberhaupt?
727 if (member(BLACKLIST,explode(issue->loadname,"/")[<1])>=0)
728 return 0;
729
730 //Hashkey bestimmen, in diesem Fall einfach, wir koennen die
731 //Fehlermeldunge selber nehmen.
732 issue->hashkey=hash(TLS_HASH_MD5,sprintf(
733 "%d%s%s",issue->type,issue->loadname, msg));
734 //DEBUG(sprintf("LogCompileProblem: Hashkey: %s",hashkey));
735
736 // ggf. vorhandenen Fehler suchen
737 int oldid = getErrorID(issue->hashkey);
738 if (oldid >= 0)
739 {
740 db_reopen_issue(oldid, "<ErrorD>",
741 "Automatisch wiedereroeffnet wegen erneutem Auftreten.");
742 db_countup_issue(oldid);
743 return oldid;
744 }
745
746 // neuen Eintrag
747 issue->message = msg;
748 issue->count = 1;
749 issue->ctime = issue->mtime = issue->atime = time();
750
751 issue->id = db_insert_issue(issue);
752
753 if (warn) lasterror[T_CTWARN]=issue->id;
754 else lasterror[T_CTERROR]=issue->id;
755
756// DEBUG(sprintf("LogCompileProblem: Eintrag:\n%O\n",
757// (warn ? ctwarnings[uid][hashkey] : cterrors[uid][hashkey])));
758// DEBUG(sprintf("LogCompileProblem: Verbrauchte Ticks: %d\n",
759// 200000-get_eval_cost()));
760 return issue->id;
761}
762
763/* **************** Public Interface ****************** */
764
765//Einen bestimmten Fehler nach Hashkey suchen und als fullissue_s inkl. Notes
766//und Stacktrace liefern.
767struct fullissue_s QueryIssueByHash(string hashkey)
768{
769 struct fullissue_s issue = db_get_issue(0, hashkey);
770 if (structp(issue))
771 return filter_private(issue);
772 return 0;
773}
774//Einen bestimmten Fehler nach ID suchen und als fullissue_s inkl. Notes
775//und Stacktrace liefern.
776struct fullissue_s QueryIssueByID(int issueid)
777{
778 struct fullissue_s issue = db_get_issue(issueid, 0);
779 if (structp(issue))
780 return filter_private(issue);
781 return 0;
782}
783
784// den letzten Eintrag den jeweiligen Typ liefern.
785struct fullissue_s QueryLastIssue(int type)
786{
787 if (!member(lasterror,type))
788 return 0;
789 //einfach den kompletten letzten Eintrag zurueckliefern
790 return(QueryIssueByID(lasterror[type]));
791}
792
793// Liefert alle Issues, deren obj,prog oder loadname gleich <file> ist.
794public struct fullissue_s* QueryIssuesByFile(string file, int type)
795{
796 mixed rows = sl_exec("SELECT * FROM issues "
797 "WHERE (loadname=?1 OR prog=?1 OR obj=?1) "
798 "AND deleted=0 AND resolved=0 AND type=?2"
799 "ORDER BY type,mtime;", file, type);
800 if (pointerp(rows))
801 {
802 struct fullissue_s* ilist = allocate(sizeof(rows));
803 int i;
804 foreach(mixed row : rows)
805 {
806 // Einfachster Weg - funktioniert aber nur, solange die Felder in der DB
807 // die gleiche Reihenfolge wie in der struct haben! Entweder immer
808 // sicherstellen oder Ergebnisreihenfolge oben im select festlegen!
809 struct fullissue_s issue = to_struct( row, (<fullissue_s>) );
810 if (issue->type == T_RTERROR)
811 issue->stack = db_get_stack(issue->id);
812 issue->notes = db_get_notes(issue->id);
813 ilist[i] = filter_private(issue);
814 ++i;
815 }
816 return ilist;
817 }
818 return 0;
819}
820
821// Liefert eine Liste von allen IDs, Loadnames, UIDs und Typen fuer die
822// angebenen <type> und <uid>.
823varargs < <int|string>* >* QueryIssueListByFile(string file)
824{
825 mixed rows = sl_exec("SELECT id,loadname,obj,prog,loc FROM issues "
826 "WHERE (loadname=?1 OR prog=?1 OR obj=?1) "
827 "AND deleted=0 AND resolved=0 "
828 "ORDER BY type,mtime;", file);
829 return rows;
830}
831
832// Liefert eine Liste von allen IDs, Loadnames, UIDs und Typen fuer die
833// angebenen <type> und <uid>.
834varargs < <int|string>* >* QueryIssueListByLoadname(string file, int type)
835{
836 mixed rows;
837 if (type && file)
838 {
839 rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
840 "WHERE loadname=?1 AND type=?2 AND deleted=0 "
841 "AND resolved=0 "
842 "ORDER BY type,mtime;", file, type);
843 }
844 else if (type)
845 {
846 rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
847 "WHERE type=?1 AND deleted=0 AND resolved=0 "
848 "ORDER BY type,mtime;", type);
849 }
850 else if (file)
851 {
852 rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
853 "WHERE loadname=?1 AND deleted=0 AND resolved=0 "
854 "ORDER BY type,mtime;", file);
855 }
856 return rows;
857}
858
859
860// Liefert eine Liste von allen IDs, Loadnames, UIDs und Typen fuer die
861// angebenen <type> und <uid>.
862varargs < <int|string>* >* QueryIssueList(int type, string uid)
863{
864 mixed rows;
865 if (type && uid)
866 {
867 rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
868 "WHERE type=?1 AND uid=?2 AND deleted=0 "
869 "AND resolved=0 "
870 "ORDER BY type,rowid;", type,uid);
871 }
872 else if (type)
873 {
874 rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
875 "WHERE type=?1 AND deleted=0 AND resolved=0 "
876 "ORDER BY type,rowid;", type);
877 }
878 else if (uid)
879 {
880 rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
881 "WHERE uid=?1 AND deleted=0 AND resolved=0 "
882 "ORDER BY type,rowid;", uid);
883 }
884 return rows;
885}
886
887varargs string* QueryUIDsForType(int type) {
888 //liefert alle UIDs fuer einen Fehlertyp oder fuer alle Fehlertypen
889 string** rows;
890
891 if (type)
892 {
893 rows = sl_exec("SELECT uid FROM issues "
894 "WHERE type=?1 AND deleted=0 AND resvoled=0;", type);
895 }
896 else
897 rows = sl_exec("SELECT uid FROM issues WHERE deleted=0 "
898 "AND resolved=0;");
899
900 return map(rows, function string (string* item)
901 {return item[0];} );
902}
903
904//Wieviele unterschiedliche Fehler in diesem Typ?
905varargs int QueryUniqueIssueCount(int type, string uid)
906{
907 int** rows;
908
909 if (type && uid)
910 {
911 rows = sl_exec("SELECT count(*) FROM issues "
912 "WHERE type=?1 AND uid=?2 AND deleted=0 AND resolved=0;",
913 type, uid);
914 }
915 else if (type)
916 {
917 rows = sl_exec("SELECT count(*) FROM issues "
918 "WHERE type=?1 AND deleted=0 AND resolved=0;",
919 type);
920 }
921 else if (uid)
922 {
923 rows = sl_exec("SELECT count(*) FROM issues "
924 "WHERE uid=?1 AND deleted=0 AND resolved=0;",
925 uid);
926 }
927 else
928 rows = sl_exec("SELECT count(*) FROM issues "
929 "WHERE deleted=0 AND resolved=0;");
930
931 return rows[0][0];
932}
933
934//Einen bestimmten Fehler loeschen
935varargs int ToggleDeleteError(int issueid, string note)
936{
937 mixed rows = sl_exec("SELECT uid,deleted from issues WHERE id=?1;",
938 issueid);
939 if (!pointerp(rows))
940 return -1;
941
942 if (!access_check(rows[0][0], M_DELETE))
943 //zugriff zum Schreiben nicht gestattet
944 return -10;
945
946// sl_exec("BEGIN TRANSACTION;");
947 if (rows[0][1])
948 {
949 // was deleted -> undelete it
950 sl_exec("UPDATE issues SET deleted=0,mtime=?2 WHERE id=?1;",
951 issueid,time());
952 db_add_note((<note_s> id: issueid, time: time(), user: getuid(TI),
953 txt: sprintf("Loeschmarkierung entfernt. (%s)",
954 note ? note: "<kein Kommentar>")
955 ));
956 }
957 else
958 {
959 // was not deleted -> delete it.
960 sl_exec("UPDATE issues SET deleted=1,mtime=?2 WHERE id=?1;",
961 issueid, time());
962 db_add_note((<note_s> id: issueid, time: time(), user: getuid(TI),
963 txt: sprintf("Loeschmarkierung gesetzt. (%s)",
964 note ? note: "<kein Kommentar>")
965 ));
966 }
967// sl_exec("COMMIT;");
968 return !rows[0][1];
969}
970
971
972// sperrt den Eintrag oder entsperrt ihn.
973// Sperre heisst hier, dass der Fehler vom Expire nicht automatisch geloescht
974// wird.
975varargs int LockIssue(int issueid, string note) {
976 return set_lock(issueid, 1, note);
977}
978
979varargs int UnlockIssue(int issueid, string note) {
980 return set_lock(issueid, 0, note);
981}
982
983// einen Fehler als gefixt markieren
984varargs int ResolveIssue(int issueid, string note) {
985 return set_resolution(issueid, 1, note);
986}
987// einen Fehler als nicht gefixt markieren
988varargs int ReOpenIssue(int issueid, string note) {
989 return set_resolution(issueid, 0, note);
990}
991
992varargs int AddNote(int issueid, string note)
993{
994
995 if (!stringp(note) || !sizeof(note))
996 return(-3);
997
998 // existiert die ID in der DB?
999 struct fullissue_s issue = db_get_issue(issueid,0);
1000 if (!issue)
1001 return -1;
1002
1003 if (!access_check(issue->uid, M_WRITE))
1004 //zugriff zum Schreiben nicht gestattet
1005 return(-10);
1006
1007 return db_add_note((<note_s> id: issueid, time: time(), user: getuid(TI),
1008 txt: note));
1009}
1010
1011//Einen bestimmten Fehler einer anderen UID zuweisen.
1012//Hashkey ist zwar eindeutig, aber Angabe der
1013//der UID ist deutlich schneller. Weglassen des Typs nochmal langsamer. ;-)
1014//Potentiell also sehr teuer, wenn man UID oder UID+Typ weglaesst.
1015varargs int ReassignIssue(int issueid, string newuid, string note)
1016{
1017 struct fullissue_s issue = db_get_issue(issueid,0);
1018 if (!issue)
1019 return -1;
1020
1021 if (!access_check(issue->uid, M_REASSIGN))
1022 //zugriff zum Schreiben nicht gestattet
1023 return(-10);
1024
1025 return db_reassign_issue(issueid, newuid, note);
1026}
1027
Bugfix67091cb2022-09-22 15:57:31 +02001028varargs int ChangeType(int issueid, int newtype, int oldtype, string note)
1029{
1030 struct fullissue_s issue = db_get_issue(issueid, 0);
1031 if(!issue)
1032 return -1;
1033
1034 if (!access_check(issue->uid, M_CHANGE_TYPE))
1035 {
1036 // Zugriff zum Schreiben nicht gestattet
1037 return(-10);
1038 }
1039
1040 sl_exec(
1041 "UPDATE issues SET type=?1,mtime=?2 WHERE id=?3;",
1042 newtype, time(), issueid);
1043 db_add_note(
1044 (<note_s> id: issueid, time: time(), user: getuid(TI),
1045 txt: sprintf("Fehlertyp von %s auf %s geaendert. (%s)",
1046 print_type(oldtype)[0], print_type(newtype)[0], (note || "<kein Kommentar>"))));
1047 return 1;
1048}
1049
MG Mud User88f12472016-06-24 23:31:02 +02001050/* *********** Eher fuer Debug-Zwecke *********************** */
1051/*
1052mixed QueryAll(int type) {
1053 //das koennte ein sehr sehr grosses Mapping sein, ausserdem wird keine
1054 //Kopie zurueckgegeben, daher erstmal nur ich...
1055 if (!this_interactive() ||
1056 member(MAINTAINER,getuid(this_interactive()))<0)
1057 return(-1);
1058 if (process_call()) return(-2);
1059 if (!type) return(-3);
1060 return(errors[type]);
1061}
1062
1063mixed QueryResolved()
1064{
1065 //das koennte ein sehr sehr grosses Mapping sein, ausserdem wird keine
1066 //Kopie zurueckgegeben, daher erstmal nur ich...
1067 if (!this_interactive() ||
1068 member(MAINTAINER,getuid(this_interactive()))<0)
1069 return(-1);
1070 if (process_call()) return(-2);
1071 return(resolved);
1072}
1073*/
1074
1075/* ***************** Internal Stuff ******************** */
1076void create() {
1077 seteuid(getuid(ME));
1078
bugfix4fcca732020-03-23 17:04:14 +01001079 if (sl_open("/"LIBDATADIR"/secure/ARCH/errord.sqlite") != 1)
MG Mud User88f12472016-06-24 23:31:02 +02001080 //if (sl_open("/errord.sqlite") != 1)
1081 {
1082 raise_error("Datenbank konnte nicht geoeffnet werden.\n");
1083 }
1084 sl_exec("PRAGMA foreign_keys = ON; PRAGMA temp_store = 2; ");
1085 //string* res=sl_exec("PRAGMA quick_check(N);");
1086 //if (pointerp(res))
1087 //{
1088 // raise_error("");
1089 //}
1090 //sl_exec("CREATE TABLE issue(id INTEGER,haskey TEXT);");
1091 foreach (string cmd :
1092 explode(read_file("/secure/ARCH/errord.sql.init"),";\n"))
1093 {
1094 if (sizeof(cmd) && cmd != "\n")
1095 {
1096 sl_exec(cmd);
1097 }
1098 }
1099 sl_exec("ANALYZE main;");
1100
1101 lasterror=([]);
1102}
1103
1104string name() {return("<Error-Daemon>");}
1105
1106void save_me(int now) {
1107 if (now)
1108 save_object(SAVEFILE);
1109 else if (find_call_out(#'save_object)==-1) {
1110 call_out(#'save_object, 30, SAVEFILE);
1111 }
1112}
1113
1114varargs int remove(int silent) {
1115 save_me(1);
1116 destruct(ME);
1117 return(1);
1118}
1119
1120
1121// sperrt den Eintrag (lock!=0) oder entsperrt ihn (lock==0).
1122// Sperre heisst hier, dass der Fehler vom Expire nicht automatisch geloescht
1123// wird.
1124// liefert <0 im Fehlerfall, sonst Array mit Lockdaten
1125private varargs int set_lock(int issueid, int lock, string note)
1126{
1127 struct fullissue_s issue = db_get_issue(issueid,0);
1128 if (!issue)
1129 return -1;
1130
1131 if (!access_check(issue->uid, M_WRITE))
1132 //zugriff zum Schreiben nicht gestattet
1133 return(-10);
1134
1135 return db_set_lock(issueid, lock, note);
1136}
1137
1138//markiert einen Fehler als gefixt, mit 'note' als Bemerkung (res!=0) oder
1139//markiert einen Fehler wieder als nicht-gefixt (resolution==0)
1140//liefert < 0 im Fehlerfall, sonst den neuen Zustand.
1141private varargs int set_resolution(int issueid, int resolution, string note)
1142{
1143 struct fullissue_s issue = db_get_issue(issueid,0);
1144 if (!issue)
1145 return -1;
1146
1147 // Fixen duerfen nur zustaendige
1148 if (resolution
1149 && !access_check(issue->uid, M_FIX))
1150 return -10;
1151 // ggf. Fix zurueckziehen darf jeder mit M_WRITE
1152 if (!resolution &&
1153 !access_check(issue->uid, M_WRITE))
1154 return -10;
1155
1156 int res = db_set_resolution(issueid, resolution, note);
1157
1158 if (res == 1)
1159 {
1160 // Fehler jetzt gefixt.
1161 versende_mail(db_get_issue(issueid,0)); // per Mail verschicken
1162 }
1163
1164 return res;
1165}
1166
1167//ist der Zugriff auf uid erlaubt? Geprueft wird TI (wenn kein TI, auch kein
1168//Schreibzugriff)
1169//mode gibt an, ob lesend oder schreibend
1170private int access_check(string uid, int mode) {
1171
1172 if (mode==M_READ)
1173 return LEARNER_SECURITY; //lesen darf jeder Magier
1174
1175 // In process_string() schonmal gar nicht.
1176 if (process_call()) return(0);
1177 // EM+ duerfen alles loeschen.
1178 if (ARCH_SECURITY) return(1);
1179 // eigene UIDs darf man auch vollumfaenglich bearbeiten.
1180 if (secure_euid()==uid) return(1);
1181
1182 // alles andere wird speziell geprueft
1183 switch(mode)
1184 {
1185 // M_WRITE ist zur zeit eigentlich immer nen Append - das duerfen zur
1186 // Zeit alle ab Vollmagier
1187 case M_WRITE:
1188 return LEARNER_SECURITY;
MG Mud User88f12472016-06-24 23:31:02 +02001189 // Loeschen und Fixen duerfen zur Zeit nur Zustaendige.
1190 case M_DELETE:
1191 case M_REASSIGN:
1192 case M_FIX:
Bugfix67091cb2022-09-22 15:57:31 +02001193 case M_CHANGE_TYPE:
MG Mud User88f12472016-06-24 23:31:02 +02001194 // Master nach UIDs fragen, fuer die der jew. Magier
1195 // zustaendig ist.
Vanionb4c06532020-03-09 21:54:54 +01001196 if (member(({string*})master()->QueryUIDsForWizard(secure_euid()),uid) >= 0)
MG Mud User88f12472016-06-24 23:31:02 +02001197 return 1;
1198
1199 break;
1200 }
1201
1202 return(0); //Fall-through, neinRNER_SECURITY
1203}
1204
1205public string format_stacktrace(struct frame_s* stacktrace) {
1206 string *lines;
1207
1208 if (!pointerp(stacktrace) || !sizeof(stacktrace))
1209 return("");
1210
1211 lines=allocate(sizeof(stacktrace));
1212 int i;
1213 foreach(struct frame_s frame: stacktrace)
1214 {
1215 lines[i]=sprintf("Fun: %.20O in Prog: %.40s\n"
1216 " Zeile: %.8d [%.50s]\n"
1217 " Evalcosts: %d",
1218 frame->name,frame->prog,
1219 frame->loc,frame->obj,frame->ticks);
1220 ++i;
1221 }
1222 return(implode(lines,"\n"));
1223}
1224
1225public string format_notes(struct note_s* notes)
1226{
1227 int i;
1228 string text="";
1229 foreach(struct note_s note: notes)
1230 {
1231 text+=sprintf("Notiz %d von %.10s am %.30s\n%s",
1232 ++i,capitalize(note->user),dtime(note->time),
1233 break_string(note->txt, 78," "));
1234 }
1235 return text;
1236}
1237
1238public string format_error_spieler(struct fullissue_s issue)
1239{
1240 string txt;
1241 string *label;
1242
1243 if (!issue)
1244 return 0;
1245
1246 switch(issue->type)
1247 {
1248 case T_RTERROR:
1249 label=({"Laufzeitfehler","Dieser Laufzeitfehler"});
1250 break;
1251 case T_REPORTED_ERR:
1252 label=({"Fehlerhinweis","Dieser Fehlerhinweis"});
1253 break;
1254 case T_REPORTED_TYPO:
1255 label=({"Typo","Dieser Typo"});
1256 break;
1257 case T_REPORTED_IDEA:
1258 label=({"Idee","Diese Idee"});
1259 break;
1260 case T_REPORTED_MD:
1261 label=({"Fehlendes Detail","Dieses fehlende Detail"});
1262 break;
Bugfixa75344d2017-06-16 14:04:48 +02001263 case T_REPORTED_SYNTAX:
1264 label=({"Syntaxproblem","Dieses Syntaxproblem"});
1265 break;
MG Mud User88f12472016-06-24 23:31:02 +02001266 case T_RTWARN:
1267 label=({"Laufzeitwarnung","Diese Laufzeitwarnung"});
1268 break;
1269 case T_CTWARN:
1270 label=({"Ladezeitwarnung","Diese Ladezeitwarnung"});
1271 break;
1272 case T_CTERROR:
1273 label=({"Ladezeitfehler","Dieser Ladezeitfehler"});
1274 break;
1275 default: return 0;
1276 }
1277
1278 txt=sprintf( "\nDaten fuer %s mit ID '%s':\n"
1279 "Zeit: %25s\n",
1280 label[0], issue->hashkey,
1281 dtime(issue->ctime)
1282 );
1283
1284 txt+=sprintf("%s",break_string(issue->message,78,
1285 "Meldung: ",BS_INDENT_ONCE));
1286
1287 if (pointerp(issue->notes))
1288 txt+="Bemerkungen:\n"+format_notes(issue->notes)+"\n";
1289
1290 return txt;
1291}
1292
Bugfix67091cb2022-09-22 15:57:31 +02001293public string* print_type(int type)
1294{
1295 string* res;
1296 switch(type)
1297 {
1298 case T_RTERROR:
1299 res = ({"Laufzeitfehler","Dieser Laufzeitfehler"});
1300 break;
1301 case T_REPORTED_ERR:
1302 res = ({"Fehlerhinweis","Dieser Fehlerhinweis"});
1303 break;
1304 case T_REPORTED_TYPO:
1305 res = ({"Typo","Dieser Typo"});
1306 break;
1307 case T_REPORTED_IDEA:
1308 res = ({"Idee","Diese Idee"});
1309 break;
1310 case T_REPORTED_MD:
1311 res = ({"Fehlendes Detail","Dieses fehlende Detail"});
1312 break;
1313 case T_REPORTED_SYNTAX:
1314 res = ({"Syntaxproblem","Dieses Syntaxproblem"});
1315 break;
1316 case T_RTWARN:
1317 res = ({"Laufzeitwarnung","Diese Laufzeitwarnung"});
1318 break;
1319 case T_CTWARN:
1320 res = ({"Ladezeitwarnung","Diese Ladezeitwarnung"});
1321 break;
1322 case T_CTERROR:
1323 res = ({"Ladezeitfehler","Dieser Ladezeitfehler"});
1324 break;
1325 default:
1326 res = 0;
1327 }
1328 return res;
1329}
1330
MG Mud User88f12472016-06-24 23:31:02 +02001331public string format_error(struct fullissue_s issue, int only_essential)
1332{
1333 string txt;
1334 string *label;
1335
1336 if (!issue)
1337 return 0;
1338
Bugfix67091cb2022-09-22 15:57:31 +02001339 label = print_type(issue->type);
1340 if(!label) return 0;
MG Mud User88f12472016-06-24 23:31:02 +02001341
1342 txt=sprintf( "\nDaten fuer %s mit ID %d:\n"
1343 "Hashkey: %s\n"
1344 "Zeit: %25s (Erstmalig: %25s)\n",
1345 label[0], issue->id, issue->hashkey,
1346 dtime(abs(issue->mtime)),dtime(issue->ctime)
1347 );
1348
1349 if (stringp(issue->prog))
1350 txt += sprintf(
1351 "Programm: %.60s\n"
1352 "Zeile: %.60d\n",
1353 issue->prog, issue->loc
1354 );
1355
1356 if (stringp(issue->obj))
1357 txt+=sprintf("Objekt: %.60s\n",
1358 issue->obj);
1359
1360 txt += sprintf("Loadname: %.60s\n"
1361 "UID: %.60s\n",
1362 issue->loadname, issue->uid);
1363
1364 txt+=sprintf("%s",break_string(issue->message,78,
1365 "Meldung: ",BS_INDENT_ONCE));
1366 if (stringp(issue->hbobj))
1367 txt+=sprintf(
1368 "HB-Obj: %.60s\n",issue->hbobj);
1369
1370 if (stringp(issue->titp)) {
1371 txt+=sprintf(
1372 "TI/TP: %.60s\n",issue->titp);
1373 if (stringp(issue->tienv))
1374 txt+=sprintf(
1375 "Environm.: %.60s\n",issue->tienv);
1376 }
1377
1378 if (!stringp(issue->command) ||
1379 !ARCH_SECURITY || process_call())
1380 {
1381 // Kommandoeingabe ist Privatsphaere und darf nicht von jedem einsehbar
1382 // sein.
1383 // in diesem Fall aber zumindest das Verb ausgeben, so vorhanden
1384 if (issue->verb)
1385 txt+=sprintf(
1386 "Verb: %.60s\n",issue->verb);
1387 }
1388 // !process_call() && ARCH_SECURITY erfuellt...
1389 else if (stringp(issue->command))
1390 txt+=sprintf(
1391 "Befehl: %.60s\n",issue->command);
1392
1393 if (issue->caught)
1394 txt+=label[1]+" trat in einem 'catch()' auf.\n";
1395
1396 if (!only_essential)
1397 {
1398 if (issue->deleted)
1399 txt+=label[1]+" wurde als geloescht markiert.\n";
1400
1401 if (issue->locked)
1402 txt+=break_string(
1403 sprintf("%s wurde von %s am %s vor automatischem Loeschen "
1404 "geschuetzt (locked).\n",
1405 label[1],issue->locked_by, dtime(issue->locked_time)),78);
1406 if (issue->resolved)
1407 txt+=label[1]+" wurde als erledigt markiert.\n";
1408 }
1409
1410 txt+=sprintf("%s trat bisher %d Mal auf.\n",
1411 label[1],issue->count);
1412
1413 if (pointerp(issue->stack))
1414 txt+="Stacktrace:\n"+format_stacktrace(issue->stack)+"\n";
1415
1416 if (pointerp(issue->notes))
1417 txt+="Bemerkungen:\n"+format_notes(issue->notes)+"\n";
1418
1419 return txt;
1420}
1421
1422// letzter Aenderung eines Spieler-/Magiersavefiles. Naeherung fuer letzten
1423// Logout ohne das Savefile einzulesen und P_LAST_LOGOUT zu pruefen.
1424private int recent_lastlogout(string nam, int validtime)
1425{
1426 if (!nam || sizeof(nam)<2) return 0;
1427 return file_time("/"LIBSAVEDIR"/" + nam[0..0] + "/" + nam + ".o") >= validtime;
1428}
1429
1430// Versendet Mail an zustaendigen Magier und ggf. Spieler, der den Eintrag
1431// erstellt hat.
1432// ACHTUNG: loescht issue->command.
1433private int versende_mail(struct fullissue_s issue)
1434{
1435 // Versendet eine mail mit dem gefixten Fehler.
1436 mixed *mail;
1437 string text, *empf;
1438 int res = -1;
1439
1440 mail = allocate(9);
1441 mail[MSG_FROM] = "<Fehler-Daemon>";
1442 mail[MSG_SENDER] = getuid(TI);
1443 mail[MSG_BCC] = 0;
1444 mail[MSG_SUBJECT] = sprintf("Fehler/Warnung in %s behoben", issue->loadname);
1445 mail[MSG_DATE] = dtime(time());
1446
1447 // auch wenn ein EM fixt, sollen die Empfaenger nicht automatisch die
1448 // Spielereingabe erhalten, also command loeschen.
1449 issue->command = 0;
1450
1451 // erstmal eine Mail an zustaendige Magier.
Vanionb4c06532020-03-09 21:54:54 +01001452 empf = ({string*})master()->QueryWizardsForUID(issue->uid);
MG Mud User88f12472016-06-24 23:31:02 +02001453 // lang (180 Tage) nicht eingeloggte Magier ausfiltern
1454 empf = filter(empf, #'recent_lastlogout, time() - 15552000);
1455 if (sizeof(empf))
1456 {
1457
1458 text = break_string(
1459 sprintf(STANDARDMAILTEXT,capitalize(getuid(TI)))
1460 +format_error(issue, 1),78,"",BS_LEAVE_MY_LFS);
1461
1462 mail[MSG_RECIPIENT] = empf[0];
1463 if (sizeof(empf)>1)
1464 mail[MSG_CC] = empf[1..];
1465 else
1466 mail[MSG_CC] = 0;
1467 mail[MSG_BODY]=text;
1468 mail[MSG_ID]=sprintf(MUDNAME": %d.%d",time(),random(__INT_MAX__));
1469
bugfixd94d0932020-04-08 11:27:13 +02001470 if (!sizeof(({string*})"/secure/mailer"->DeliverMail(mail,0)))
MG Mud User88f12472016-06-24 23:31:02 +02001471 res = -1; // an niemanden erfolgreich zugestellt. :-(
1472 else
1473 res = 1;
1474 }
1475
1476 // Bei von Spielern gemeldeten Fehler werden Spieler bei
1477 // Erledigung informiert, wenn deren letzter Logout weniger als 180 Tage her
1478 // ist.
1479 if ( (issue->type &
Bugfixa75344d2017-06-16 14:04:48 +02001480 (T_REPORTED_ERR|T_REPORTED_TYPO|T_REPORTED_IDEA|T_REPORTED_MD|
1481 T_REPORTED_SYNTAX))
MG Mud User88f12472016-06-24 23:31:02 +02001482 && issue->titp
1483 && recent_lastlogout(issue->titp, time() - 15552000) )
1484 {
1485 text = break_string(
1486 sprintf(STANDARDMAILTEXT_ERRORHINT,
1487 capitalize(issue->titp), capitalize(getuid(TI)))
1488 +format_error_spieler(issue), 78,"",BS_LEAVE_MY_LFS);
1489
1490 mail[MSG_ID]=sprintf(MUDNAME": %d.%d",time(),random(__INT_MAX__));
1491 mail[MSG_RECIPIENT] = issue->titp;
1492 mail[MSG_CC] = 0;
Zesstrac5401ce2021-02-13 20:28:16 +01001493 mail[MSG_SUBJECT] = sprintf("Fehler/Idee/Typo wurde von %s bearbeitet",
MG Mud User88f12472016-06-24 23:31:02 +02001494 getuid(TI));
1495 mail[MSG_BODY] = text;
1496
bugfixd94d0932020-04-08 11:27:13 +02001497 if (!sizeof(({string*})"/secure/mailer"->DeliverMail(mail,0)))
MG Mud User88f12472016-06-24 23:31:02 +02001498 res |= -1;
1499 else
1500 res |= 1;
1501 }
1502
1503 return res;
1504}
1505
1506void reset()
1507{
1508 // geloeschte Issues sofort, gefixte 30 Tage nach letzter Aenderung
1509 // loeschen.
1510 sl_exec("DELETE FROM issues WHERE deleted=1;");
1511 sl_exec("DELETE FROM issues WHERE resolved=1 AND mtime<?1;",
1512 time()-30*24*3600);
1513 set_next_reset(3600*24);
1514}
1515
1516// Nicht jeder Magier muss den ErrorD direkt zerstoeren koennen.
1517public string NotifyDestruct(object caller) {
1518 if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
1519 return "Du darfst den Error-Daemon nicht zerstoeren!\n";
1520 }
1521 return 0;
1522}
1523