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