blob: c587dcb95ebc3e61db6b0a1ca58ecbab629446de [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// 18.Dez 1996 - Loco@Morgengrauen
2// 8. Feb 1995 - Jof@MorgenGrauen
3// 5. Juli 1992 - Jof@MorgenGrauen
4// 6. Juli 1992 - Jof@MorgenGrauen
5// Clear-Groups-Mechanismus eingebaut. Die 2 Konstanten TIME_TO_CLEAR und
6// MIN_TOUCHED muessen def. sein. Alle TIME_TO_CLEAR Sekunden werden die
7// Newsgroups, die seit dem letzten Clear seltener als MIN_TOUCHED Sekunden
8// beruehrt wurden, aus dem Cache genommen
9
10// 1. Februar 1995 - Jof@MorgenGrauen
11// Rewrite (Mappings benutzen, Accessinfos im Speicher halten)
12
13
14// Datenstrukturen:
15
16// Newsgroups: grouplist ist ein Mapping mit einem Eintrag pro Newsgroup.
17// Die Keys sind die Gruppennamen, Daten:
18// 0 Zeit des letzen Artikels
19// 1 Besitzer der Gruppe
20// 2 Savefille-Name
21// 3 Expire-Zeit
22// 4 Array mit Loesch-Berechtigten
23// 5 Array mit Scheib-Berechtigten
24// 6 Array mit Leseberechtigten
25// 7 Mindest-Level, um Artikel zu loeschen
26// 8 Mindest-Level, um Artikel zu posten
27// 9 Mindest-Level, um Artikel zu lesen
28// 10 Max. Anzahl Nachrichten in der Gruppe
29
30// Die Nachrichten selber stehen in einem Array.
31
32// Eine nachricht ist ein Array mit den folgenden Elementen:
33// 0 (*) string writer;
34// 1 (*) string id; Mudname+":"+time()+":"+group zur Zeit
35// 2 (*) string time;
36// 3 string title;
37// 4 string message;
38
39// Die mit (*) gekennzeichneten Eintraege setzt der Daemon selber
40
41// Funktionen:
42// Returnvalues: 0 = Parameter error
43// 1 = Ok.
44// -1 = no permission
45// -2 = no such group/group already existing
46//
47// Diese Returnvalues stehen fuer alle Funktionen
48
49// AddGroup(groupname, owner, savefile);
50// Funktion klar, kann nur von Erzmagiern aufgerufen werden
51// -3 = no owner, -4 = savefile already in use
52//
53// RemoveGroup(groupname);
54// Ebenfalls nur fuer Erzmagier
55//
56// SetGroup(groupname, dlevel, wlevel, rlevel, maxmessages, expire);
57// Erzmagier und der Groupowner koennen die nutzen. Legt Level, ab dem je-
58// mand aus der Gruppe loeschen kann (dlevel), in die Gruppe schreiben
59// kann (wlevel), die Gruppe lesen kann (rlevel) und die max. Anzahl Nach-
60// richten in der Gruppe fest.
61//
62// AddAllowed(groupname, deleters, writers, readers);
63// Erzmagier/Owner koennen Arrays mit Namen von fuer die Gruppe loesch/
64// schreib/lese-Berechtigten fuer die Gruppe angeben.
65//
66// RemoveAllowed(groupname, deleters, writers, readers); analog
67//
68// WriteNote(message); klar
69// -3 = Max number of msgs exceeded
70//
71// RemoveNote(boardname, notenummer); notenummer>=0;
72// -3 = No such note
73//
74// GetNotes(boardname); gibt einen Array mit den Notes zurueck.
75//
76// AskAllowedWrite(); return wie bei WriteNote, stellt fest, ob ein Player
77// eine Note aufhaengen darf oder nicht
78//
79// GetNewsTime([boardname]); gibt zurueck, wann am entsprechenden Brett zum
80// letzten Mal eine Note befestigt wurde. Falls kein boardname angegeben
81// wird, liefert GetNewsTime() den Zeitpunkt, zu dem ueberhaupt eine neue
82// Note aufgehaengt wurde.
83//
84#pragma strict_types
85#pragma no_clone
86#pragma no_shadow
87#pragma no_inherit
88#pragma verbose_errors
89#pragma combine_strings
90//#pragma pedantic
91//#pragma range_check
92#pragma warn_deprecated
93
94#include "/secure/wizlevels.h"
95#include <defines.h>
96#include <config.h>
97#include <news.h>
98
99#define WTIME 0
100
101private int security( string name );
102
103mixed saveload; // Diese Variable ist als einzige nicht nosave ist und dient
104 // Uebertragen von Daten in/aus savefiles
105
106nosave mapping grouplist; // Groups und ihre save-Files, zudem LastTime
107nosave int lasttime; // Um zu vermeiden, dass 2 Notes identische Uhrzeit==id
108 // haben, wird die jeweils letzte Zeit gespeichert.
109
110nosave mapping cache = ([]); // cache fuer die Gruppeninhalte
111
112void create() {
113 seteuid(getuid(this_object()));
114 if (!restore_object(NEWSPATH+"GroupList"))
115 grouplist=m_allocate(0,G_MESSAGES);
116 else
117 grouplist=saveload;
118 // ersten reset sobald wie moeglich. ;-)
119 set_next_reset(1);
120}
121
122int AddGroup(string name, string owner)
123{
124 mixed *group;
125 string savefile, *savefilea;
126 int i;
127
128 if (!name || !owner) return 0;
129
130 if (!ARCH_SECURITY || process_call()) return -1; // Darf nicht
131
132 if (member(grouplist, name)) return -2; // Gibt es schon
133
bugfixd94d0932020-04-08 11:27:13 +0200134 if (!({int})master()->find_userinfo(owner)) return -3;
MG Mud User88f12472016-06-24 23:31:02 +0200135
136 savefilea = old_explode(name,".");
137 savefile = implode(savefilea,"/");
138 if (file_size(NEWSPATH+savefile+".o")>=0) return -4;
139
140 // Notwendige Directories anlegen
141 for (i = 0; i < sizeof(savefilea)-1; i++) {
142 mkdir(NEWSPATH+implode(savefilea[0..i],"/"));
143 }
144
145 group=({});
146 grouplist+=([name:0;owner;savefile;-1;({});({});({});20;0;0;80]);
147 save_group_list();
148 save_group(name,group);
149 return 1;
150}
151
152int RemoveGroup(string name)
153{
MG Mud User88f12472016-06-24 23:31:02 +0200154 if (!name) return 0;
155
156 if (!security(name) || process_call()) return -1; // Darf nicht
157
158 if (!mappingp(grouplist) || !member(grouplist,name))
159 return -2; // -2 no such group
160
161 catch(rm(NEWSPATH+grouplist[name,G_SAVEFILE]+".o");publish);
162 m_delete(grouplist,name);
163
164 save_group_list();
165
166 return 1;
167}
168
169int SetGroup(string name,int dlevel,int wlevel,int rlevel,int maxmessages,int expire)
170{
MG Mud User88f12472016-06-24 23:31:02 +0200171 if (!member(grouplist,name)) return -2;
172 if (grouplist[name,G_OWNER]!=user_euid() &&
173 (!security(name) || process_call())) return -1;
174
175 grouplist[name,G_DLEVEL]=dlevel;
176 grouplist[name,G_WLEVEL]=wlevel;
177 grouplist[name,G_RLEVEL]=rlevel;
178 grouplist[name,G_MAX_MSG]=maxmessages;
179 grouplist[name,G_EXPIRE]=expire;
180
181 save_group_list();
182 return 1;
183}
184
185int AddAllowed(string name,mixed deleters,mixed writers,mixed readers)
186{
MG Mud User88f12472016-06-24 23:31:02 +0200187 if (!member(grouplist,name)) return -2;
188
189 if ( grouplist[name,G_OWNER]!=user_euid() &&
190 (!security(name) || process_call()) && user_euid() != ROOTID )
191 return -1;
192
193 if (stringp(deleters)) deleters=({deleters});
194 if (stringp(writers)) writers=({writers});
195 if (stringp(readers)) readers=({readers});
196
197 if (!deleters) deleters=({});
198 if (!writers) writers=({});
199 if (!readers) readers=({});
200
201 grouplist[name,G_DELETERS]+=deleters;
202 grouplist[name,G_WRITERS]+=writers;
203 grouplist[name,G_READERS]+=readers;
204
205 save_group_list();
206 return 1;
207}
208
209int RemoveAllowed(string name,mixed deleters,mixed writers,mixed readers)
210{
MG Mud User88f12472016-06-24 23:31:02 +0200211 if (!member(grouplist,name)) return -2;
212
213 if (grouplist[name,G_OWNER]!=user_euid() &&
214 (!security(name) || process_call()) && user_euid() != ROOTID )
215 return -1;
216
217 if (stringp(deleters)) deleters=({deleters});
218 if (stringp(writers)) writers=({writers});
219 if (stringp(readers)) readers=({readers});
220
221 if (!deleters) deleters=({});
222 if (!writers) writers=({});
223 if (!readers) readers=({});
224
225 grouplist[name,G_DELETERS]-=deleters;
226 grouplist[name,G_WRITERS]-=writers;
227 grouplist[name,G_READERS]-=readers;
228
229 save_group_list();
230 return 1;
231}
232
233static string user_euid()
234{
235 if (previous_object()) {
236 if (geteuid(previous_object())==ROOTID) return ROOTID;
237 if (geteuid(previous_object())=="p.daemon") return "p.daemon";
238 if (load_name(previous_object())=="/obj/mpa") return geteuid(RPL);
239 }
240 return secure_euid();
241}
242
243#define F_DELETE 0
244#define F_READ 1
245#define F_WRITE 2
246#define F_ADMIN 3
247#define F_KEEPNAME 4
248
249private int security( string name )
250{
251 if ( grouplist[name,G_DLEVEL] >= ARCH_LVL
252 || grouplist[name,G_WLEVEL] >= ARCH_LVL
253 || grouplist[name,G_RLEVEL] >= ARCH_LVL )
254 return ARCH_SECURITY;
255 else
256 return ELDER_SECURITY;
257}
258
259static int allowed(string name, int mode)
260{
261 string euid;
262 mixed g_level, g_mode;
263
264 if (process_call()) return 0;
265
266 euid=user_euid();
267
268 if (euid==ROOTID) return 1;
269
270 switch(mode) {
271 case F_KEEPNAME: return (euid=="p.daemon");
272 case F_WRITE: if (euid=="p.daemon") return 1;
273 g_level=G_WLEVEL; g_mode=G_WRITERS; break;
274 case F_ADMIN: if (!(security(name)||grouplist[name,G_OWNER]==euid))
275 return 0;
276 g_level=G_DLEVEL; g_mode=G_DELETERS; break;
277 case F_DELETE: if (euid=="p.daemon") return 1;
278 g_level=G_DLEVEL; g_mode=G_DELETERS; break;
279 case F_READ: g_level=G_RLEVEL; g_mode=G_READERS; break;
280 default: return 0;
281 }
282
283 if (grouplist[name,G_OWNER] != euid && !ARCH_SECURITY &&
284 grouplist[name,g_level] > query_wiz_level(euid) &&
285 member(grouplist[name, g_mode], euid)==-1)
286 return 0; // No such group for the requestor :)
287 return 1;
288}
289
290int WriteNote(mixed message,mixed keepname)
291{
Zesstrabe894b42019-11-27 19:56:32 +0100292 mixed group;
MG Mud User88f12472016-06-24 23:31:02 +0200293 string name;
294
295 if (!pointerp(message) || sizeof(message)!=6) return 0;
296
297 if (!pointerp(group=load_group(name=message[M_BOARD]))) return -2;
298
299 if (!allowed(name, F_WRITE)) return -1;
300
301 if (sizeof(group)>=grouplist[name,G_MAX_MSG]) return -3;
302
303 if (!keepname || !allowed(name, F_KEEPNAME))
304 message[M_WRITER]=capitalize(geteuid(this_interactive()||previous_object()));
305
306 if (lasttime>=time()) lasttime++;
307 else lasttime=time();
308 message[M_TIME]=lasttime;
309 message[M_ID]=MUDNAME+":"+lasttime;
310 group+=({message});
311 grouplist[name,WTIME]=lasttime;
312 save_group(name,group);
313 save_group_list();
314 return 1;
315}
316
317int RemoveNote(string name, int note)
318{
Zesstrabe894b42019-11-27 19:56:32 +0100319 mixed group;
MG Mud User88f12472016-06-24 23:31:02 +0200320
321 if ((note<0) && (name=="dwnews"))
322 {
323 group=({});
324 grouplist[name,WTIME]=0;
325 save_group(name,group);
326 save_group_list();
327 return 1;
328 }
329
330 if (note<0) return 0;
331
332 if (!pointerp(group=load_group(name))) return -2;
333
334 int count=sizeof(group);
335 if (count<=note)
336 return -3;
337
338 if (!allowed(name, F_DELETE) &&
339 lower_case(group[note][M_WRITER])!=user_euid()) return -1;
340
341 if (count==1)
342 group=({});
343 else if (!note)
344 group = group[1..];
345 else if (note == count-1)
346 group = group[0..<2];
347 else
348 group=group[0..note-1]+group[note+1..];
349
350 if (sizeof(group))
351 grouplist[name,WTIME]=group[<1][M_TIME];
352 else
353 grouplist[name,WTIME]=0;
354 save_group(name,group);
355 save_group_list();
356 return 1;
357}
358
359mixed GetNotes(string name)
360{
Zesstrabe894b42019-11-27 19:56:32 +0100361 mixed group;
MG Mud User88f12472016-06-24 23:31:02 +0200362
363 if (!pointerp(group=load_group(name))) return -2;
364 if (!allowed(name, F_READ)) return -2;
365 return(deep_copy(group)); // COPY it
366}
367
368static void dump_file(string filename,mixed news)
369{
370 int i;
371
372 for (i=0;i<sizeof(news);i++)
373 write_file(filename,news[i][M_TITLE]+" ("+news[i][M_WRITER]+", "+
374 dtime(news[i][M_TIME])[5..26]+"):\n"+
375 news[i][M_MESSAGE]+"\n-----------------------------------------------------------------------------\n\n\n\n");
376}
377
378protected varargs void expire(string grp,int etime)
379// etime ist anfangs in Tagen und bezeichnet das max. Alter, was Artikel in
380// der Gruppe haben duerfen.
381{
Zesstrabe894b42019-11-27 19:56:32 +0100382 mixed group;
MG Mud User88f12472016-06-24 23:31:02 +0200383
384 if (!pointerp(group=load_group(grp))) return;
385 if (etime)
386 {
387 if (etime>0)
388 etime=etime*60*60*24;
389 }
390 else
391 etime=grouplist[grp,G_EXPIRE];
392 if (etime<=0)
393 return;
394
395 int to_expire=time()-etime;
396 int size=sizeof(group);
397 if (!size) return;
398
399 int first_to_keep = size; // ja, ist noch eins zu hoch
400 // solange runterlaufen bis man ein element findet, welches geloescht werden
401 // soll. first_to_keep bleibt dann eins hoeher als das.
402 while ( first_to_keep && group[first_to_keep-1][M_TIME]>to_expire)
403 --first_to_keep;
404 // first_to_keep kann jetzt auf eins hinter dem letzten Element zeigen (==
405 // size). Das wird unten beruecksichtigt.
406
407 if (!first_to_keep) // alle behalten?
408 return;
409 // Zu loeschende Artikel wegschreiben.
Zesstra85e539c2017-02-14 21:42:13 +0100410 dump_file(NEWSPATH"OLD."+grp,group[0..first_to_keep-1]);
MG Mud User88f12472016-06-24 23:31:02 +0200411 // dann loeschen
412 if (first_to_keep == size) // alle wegwerfen?
413 group=({});
414 else
415 group=group[first_to_keep..size-1];
416
417 save_group(grp,group);
418}
419
420void dump_group(string grp)
421{
Arathornb3051452021-05-13 21:13:03 +0200422 int size,last;
Zesstrabe894b42019-11-27 19:56:32 +0100423 mixed group;
MG Mud User88f12472016-06-24 23:31:02 +0200424
425 if (!ARCH_SECURITY || process_call()) return;
426 if (!pointerp(group=load_group(grp))) return;
427 size=sizeof(group);
428 last=size;
429 if (!last) return;
Zesstra85e539c2017-02-14 21:42:13 +0100430 dump_file(NEWSPATH"DUMP."+grp,group[0..last-1]);
MG Mud User88f12472016-06-24 23:31:02 +0200431}
432
433protected void expire_all(string *keys) {
434 // neuen call_out fuer den Rest setzen
435 if (sizeof(keys) > 1)
436 call_out(#'expire_all,15,keys[1..]);
437 // und erste Gruppe expiren
438 expire(keys[0]);
439}
440
441void reset() {
442 // naechstes Expire und damit Reset in einem tag
443 set_next_reset(86400);
444 // alte call_outs ggf. entfernen.
445 while(remove_call_out(#'expire_all)>=0);
446 // gruppenliste holen und callout auf expire_all starten
447 if (sizeof(grouplist)) {
448 call_out(#'expire_all,10,m_indices(grouplist));
449 }
450}
451
452static void save_group(string grp,mixed group)
453{
454 saveload=group; // Do NOT save the accessed-Info
455 cache[grp] = group;
456 save_object(NEWSPATH+grouplist[grp,G_SAVEFILE]);
457 saveload=0;
458}
459
460static void save_group_list()
461{
462 saveload=grouplist;
463 save_object(NEWSPATH+"GroupList");
464 saveload=0;
465}
466
467static mixed load_group(string name)
468{
MG Mud User88f12472016-06-24 23:31:02 +0200469 mixed *ret;
470
471 if(!member(grouplist,name)) return -1;
472
473 if (member(cache, name)) {
474 ret = cache[name];
475 }
476 else {
477 restore_object(NEWSPATH+grouplist[name,G_SAVEFILE]);
478 if (!pointerp(saveload))
479 saveload=({});
480 ret=saveload;
481 cache[name] = saveload;
482 saveload=0;
483 }
484 return ret;
485}
486
487mixed GetGroups()
488{
489 mixed *returnlist;
Arathornb3051452021-05-13 21:13:03 +0200490 int i,slevel;
MG Mud User88f12472016-06-24 23:31:02 +0200491 string seuid;
492
493 returnlist=sort_array(m_indices(grouplist),#'>); //');
494 if (ARCH_SECURITY && !process_call())
495 return returnlist;
496
497 seuid = user_euid();
498 slevel = secure_level();
499
500 for (i=sizeof(returnlist)-1;i>=0;i--)
501 if (!(grouplist[returnlist[i],G_RLEVEL]<=slevel ||
502 grouplist[returnlist[i],G_OWNER]==seuid ||
503 member(grouplist[returnlist[i],G_READERS], seuid)!=-1))
504 returnlist=returnlist[0..i-1]+returnlist[i+1..];
505 return returnlist;
506}
507
508int AskAllowedWrite(string n)
509{
Zesstrabe894b42019-11-27 19:56:32 +0100510 mixed group;
MG Mud User88f12472016-06-24 23:31:02 +0200511
512 if (!member(grouplist,n)) return -2;
513 if (!pointerp(group=load_group(n))) return -2;
514
515 if (grouplist[n,G_OWNER] != user_euid() &&
516 !ARCH_SECURITY &&
517 grouplist[n,G_WLEVEL]>secure_level() &&
518 member(grouplist[n,G_WRITERS],user_euid())==-1)
519 return -1;
520
521 if (sizeof(group)>=grouplist[n,G_MAX_MSG]) return -3;
522 return 1;
523}
524
525// Wichtig ...
526
527int query_prevent_shadow()
528{
529 return 1;
530}
531
532mixed GetNewsTime(string boardname)
533
534{
535 int i, ltime, j;
536 mixed *keys;
537
538 if (!boardname)
539 {
540 ltime=-1;
541 for (i=sizeof(keys=m_indices(grouplist))-1;i>=0;i--)
542 if (ltime<(j=grouplist[keys[i],WTIME])) ltime=j;
543 return ltime;
544 }
545 if (!member(grouplist,boardname)) return -1;
546 return grouplist[boardname,WTIME];
547}
548
549mixed* GetGroup(string name)
550{
551 if (process_call()) return 0;
552 if (extern_call() && !allowed(name, F_ADMIN)) return 0;
553#define gl(x) grouplist[name,x]
554 return ({name,gl(1),gl(2),gl(3),gl(4),gl(5),gl(6),gl(7),gl(8),gl(9),gl(10),load_group(name)});
555}
Zesstra646fd3b2020-12-02 00:22:12 +0100556
557public varargs void AllGroups(int details) {
558 if (!ARCH_SECURITY || process_call()) return; // Darf nicht
559
560 string* gnames = sort_array(m_indices(grouplist), #'>);
561
562 printf("%-25s %-11s %12s %3s %3s %3s %3s\n",
563 "Name","Owner","Expire","Max","Rea","Wri","Del");
564 printf("-"*78 + "\n");
565 foreach (string g : gnames)
566 {
567 printf("%-25s %-11s %12s %3d %3d %3d %3d\n",
568 g, grouplist[g,1], time2string("%dd %02h:%02m", grouplist[g,3]),
569 grouplist[g,10], grouplist[g,9], grouplist[g,8], grouplist[g,7]);
570 if (details) {
571 if (sizeof(grouplist[g,6])) // readers
572 printf(break_string(implode(grouplist[g,6], " "), 78, " Rea: ",
573 BS_INDENT_ONCE));
574 if (sizeof(grouplist[g,5])) // writers
575 printf(break_string(implode(grouplist[g,5], " "), 78, " Wri: ",
576 BS_INDENT_ONCE));
577 if (sizeof(grouplist[g,4])) // deleters
578 printf(break_string(implode(grouplist[g,5], " "), 78, " Del: ",
579 BS_INDENT_ONCE));
580 }
581 }
582}