blob: 751c88770c30c26e0906b039b92d203558a407be [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
134 if (file_size("/"+SAVEPATH+owner[0..0]+"/"+owner+".o")<0) return -3;
135
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{
154 int num;
155
156 if (!name) return 0;
157
158 if (!security(name) || process_call()) return -1; // Darf nicht
159
160 if (!mappingp(grouplist) || !member(grouplist,name))
161 return -2; // -2 no such group
162
163 catch(rm(NEWSPATH+grouplist[name,G_SAVEFILE]+".o");publish);
164 m_delete(grouplist,name);
165
166 save_group_list();
167
168 return 1;
169}
170
171int SetGroup(string name,int dlevel,int wlevel,int rlevel,int maxmessages,int expire)
172{
173 mixed *group;
174
175 if (!member(grouplist,name)) return -2;
176 if (grouplist[name,G_OWNER]!=user_euid() &&
177 (!security(name) || process_call())) return -1;
178
179 grouplist[name,G_DLEVEL]=dlevel;
180 grouplist[name,G_WLEVEL]=wlevel;
181 grouplist[name,G_RLEVEL]=rlevel;
182 grouplist[name,G_MAX_MSG]=maxmessages;
183 grouplist[name,G_EXPIRE]=expire;
184
185 save_group_list();
186 return 1;
187}
188
189int AddAllowed(string name,mixed deleters,mixed writers,mixed readers)
190{
191 mixed *group;
192
193 if (!member(grouplist,name)) return -2;
194
195 if ( grouplist[name,G_OWNER]!=user_euid() &&
196 (!security(name) || process_call()) && user_euid() != ROOTID )
197 return -1;
198
199 if (stringp(deleters)) deleters=({deleters});
200 if (stringp(writers)) writers=({writers});
201 if (stringp(readers)) readers=({readers});
202
203 if (!deleters) deleters=({});
204 if (!writers) writers=({});
205 if (!readers) readers=({});
206
207 grouplist[name,G_DELETERS]+=deleters;
208 grouplist[name,G_WRITERS]+=writers;
209 grouplist[name,G_READERS]+=readers;
210
211 save_group_list();
212 return 1;
213}
214
215int RemoveAllowed(string name,mixed deleters,mixed writers,mixed readers)
216{
217 mixed *group;
218
219 if (!member(grouplist,name)) return -2;
220
221 if (grouplist[name,G_OWNER]!=user_euid() &&
222 (!security(name) || process_call()) && user_euid() != ROOTID )
223 return -1;
224
225 if (stringp(deleters)) deleters=({deleters});
226 if (stringp(writers)) writers=({writers});
227 if (stringp(readers)) readers=({readers});
228
229 if (!deleters) deleters=({});
230 if (!writers) writers=({});
231 if (!readers) readers=({});
232
233 grouplist[name,G_DELETERS]-=deleters;
234 grouplist[name,G_WRITERS]-=writers;
235 grouplist[name,G_READERS]-=readers;
236
237 save_group_list();
238 return 1;
239}
240
241static string user_euid()
242{
243 if (previous_object()) {
244 if (geteuid(previous_object())==ROOTID) return ROOTID;
245 if (geteuid(previous_object())=="p.daemon") return "p.daemon";
246 if (load_name(previous_object())=="/obj/mpa") return geteuid(RPL);
247 }
248 return secure_euid();
249}
250
251#define F_DELETE 0
252#define F_READ 1
253#define F_WRITE 2
254#define F_ADMIN 3
255#define F_KEEPNAME 4
256
257private int security( string name )
258{
259 if ( grouplist[name,G_DLEVEL] >= ARCH_LVL
260 || grouplist[name,G_WLEVEL] >= ARCH_LVL
261 || grouplist[name,G_RLEVEL] >= ARCH_LVL )
262 return ARCH_SECURITY;
263 else
264 return ELDER_SECURITY;
265}
266
267static int allowed(string name, int mode)
268{
269 string euid;
270 mixed g_level, g_mode;
271
272 if (process_call()) return 0;
273
274 euid=user_euid();
275
276 if (euid==ROOTID) return 1;
277
278 switch(mode) {
279 case F_KEEPNAME: return (euid=="p.daemon");
280 case F_WRITE: if (euid=="p.daemon") return 1;
281 g_level=G_WLEVEL; g_mode=G_WRITERS; break;
282 case F_ADMIN: if (!(security(name)||grouplist[name,G_OWNER]==euid))
283 return 0;
284 g_level=G_DLEVEL; g_mode=G_DELETERS; break;
285 case F_DELETE: if (euid=="p.daemon") return 1;
286 g_level=G_DLEVEL; g_mode=G_DELETERS; break;
287 case F_READ: g_level=G_RLEVEL; g_mode=G_READERS; break;
288 default: return 0;
289 }
290
291 if (grouplist[name,G_OWNER] != euid && !ARCH_SECURITY &&
292 grouplist[name,g_level] > query_wiz_level(euid) &&
293 member(grouplist[name, g_mode], euid)==-1)
294 return 0; // No such group for the requestor :)
295 return 1;
296}
297
298int WriteNote(mixed message,mixed keepname)
299{
300 mixed *group;
301 int uidok,tmp;
302 string name;
303
304 if (!pointerp(message) || sizeof(message)!=6) return 0;
305
306 if (!pointerp(group=load_group(name=message[M_BOARD]))) return -2;
307
308 if (!allowed(name, F_WRITE)) return -1;
309
310 if (sizeof(group)>=grouplist[name,G_MAX_MSG]) return -3;
311
312 if (!keepname || !allowed(name, F_KEEPNAME))
313 message[M_WRITER]=capitalize(geteuid(this_interactive()||previous_object()));
314
315 if (lasttime>=time()) lasttime++;
316 else lasttime=time();
317 message[M_TIME]=lasttime;
318 message[M_ID]=MUDNAME+":"+lasttime;
319 group+=({message});
320 grouplist[name,WTIME]=lasttime;
321 save_group(name,group);
322 save_group_list();
323 return 1;
324}
325
326int RemoveNote(string name, int note)
327{
328 int num;
329 mixed *group;
330
331 if ((note<0) && (name=="dwnews"))
332 {
333 group=({});
334 grouplist[name,WTIME]=0;
335 save_group(name,group);
336 save_group_list();
337 return 1;
338 }
339
340 if (note<0) return 0;
341
342 if (!pointerp(group=load_group(name))) return -2;
343
344 int count=sizeof(group);
345 if (count<=note)
346 return -3;
347
348 if (!allowed(name, F_DELETE) &&
349 lower_case(group[note][M_WRITER])!=user_euid()) return -1;
350
351 if (count==1)
352 group=({});
353 else if (!note)
354 group = group[1..];
355 else if (note == count-1)
356 group = group[0..<2];
357 else
358 group=group[0..note-1]+group[note+1..];
359
360 if (sizeof(group))
361 grouplist[name,WTIME]=group[<1][M_TIME];
362 else
363 grouplist[name,WTIME]=0;
364 save_group(name,group);
365 save_group_list();
366 return 1;
367}
368
369mixed GetNotes(string name)
370{
371 mixed *group;
372
373 if (!pointerp(group=load_group(name))) return -2;
374 if (!allowed(name, F_READ)) return -2;
375 return(deep_copy(group)); // COPY it
376}
377
378static void dump_file(string filename,mixed news)
379{
380 int i;
381
382 for (i=0;i<sizeof(news);i++)
383 write_file(filename,news[i][M_TITLE]+" ("+news[i][M_WRITER]+", "+
384 dtime(news[i][M_TIME])[5..26]+"):\n"+
385 news[i][M_MESSAGE]+"\n-----------------------------------------------------------------------------\n\n\n\n");
386}
387
388protected varargs void expire(string grp,int etime)
389// etime ist anfangs in Tagen und bezeichnet das max. Alter, was Artikel in
390// der Gruppe haben duerfen.
391{
392 mixed *group;
393
394 if (!pointerp(group=load_group(grp))) return;
395 if (etime)
396 {
397 if (etime>0)
398 etime=etime*60*60*24;
399 }
400 else
401 etime=grouplist[grp,G_EXPIRE];
402 if (etime<=0)
403 return;
404
405 int to_expire=time()-etime;
406 int size=sizeof(group);
407 if (!size) return;
408
409 int first_to_keep = size; // ja, ist noch eins zu hoch
410 // solange runterlaufen bis man ein element findet, welches geloescht werden
411 // soll. first_to_keep bleibt dann eins hoeher als das.
412 while ( first_to_keep && group[first_to_keep-1][M_TIME]>to_expire)
413 --first_to_keep;
414 // first_to_keep kann jetzt auf eins hinter dem letzten Element zeigen (==
415 // size). Das wird unten beruecksichtigt.
416
417 if (!first_to_keep) // alle behalten?
418 return;
419 // Zu loeschende Artikel wegschreiben.
420 dump_file("news/OLD."+grp,group[0..first_to_keep-1]);
421 // dann loeschen
422 if (first_to_keep == size) // alle wegwerfen?
423 group=({});
424 else
425 group=group[first_to_keep..size-1];
426
427 save_group(grp,group);
428}
429
430void dump_group(string grp)
431{
432 int to_expire,size,last;
433 mixed *group;
434
435 if (!ARCH_SECURITY || process_call()) return;
436 if (!pointerp(group=load_group(grp))) return;
437 size=sizeof(group);
438 last=size;
439 if (!last) return;
440 dump_file("news/DUMP."+grp,group[0..last-1]);
441}
442
443protected void expire_all(string *keys) {
444 // neuen call_out fuer den Rest setzen
445 if (sizeof(keys) > 1)
446 call_out(#'expire_all,15,keys[1..]);
447 // und erste Gruppe expiren
448 expire(keys[0]);
449}
450
451void reset() {
452 // naechstes Expire und damit Reset in einem tag
453 set_next_reset(86400);
454 // alte call_outs ggf. entfernen.
455 while(remove_call_out(#'expire_all)>=0);
456 // gruppenliste holen und callout auf expire_all starten
457 if (sizeof(grouplist)) {
458 call_out(#'expire_all,10,m_indices(grouplist));
459 }
460}
461
462static void save_group(string grp,mixed group)
463{
464 saveload=group; // Do NOT save the accessed-Info
465 cache[grp] = group;
466 save_object(NEWSPATH+grouplist[grp,G_SAVEFILE]);
467 saveload=0;
468}
469
470static void save_group_list()
471{
472 saveload=grouplist;
473 save_object(NEWSPATH+"GroupList");
474 saveload=0;
475}
476
477static mixed load_group(string name)
478{
479 int num;
480 mixed *ret;
481
482 if(!member(grouplist,name)) return -1;
483
484 if (member(cache, name)) {
485 ret = cache[name];
486 }
487 else {
488 restore_object(NEWSPATH+grouplist[name,G_SAVEFILE]);
489 if (!pointerp(saveload))
490 saveload=({});
491 ret=saveload;
492 cache[name] = saveload;
493 saveload=0;
494 }
495 return ret;
496}
497
498mixed GetGroups()
499{
500 mixed *returnlist;
501 int i,group,slevel;
502 string seuid;
503
504 returnlist=sort_array(m_indices(grouplist),#'>); //');
505 if (ARCH_SECURITY && !process_call())
506 return returnlist;
507
508 seuid = user_euid();
509 slevel = secure_level();
510
511 for (i=sizeof(returnlist)-1;i>=0;i--)
512 if (!(grouplist[returnlist[i],G_RLEVEL]<=slevel ||
513 grouplist[returnlist[i],G_OWNER]==seuid ||
514 member(grouplist[returnlist[i],G_READERS], seuid)!=-1))
515 returnlist=returnlist[0..i-1]+returnlist[i+1..];
516 return returnlist;
517}
518
519int AskAllowedWrite(string n)
520{
521 mixed *group;
522
523 if (!member(grouplist,n)) return -2;
524 if (!pointerp(group=load_group(n))) return -2;
525
526 if (grouplist[n,G_OWNER] != user_euid() &&
527 !ARCH_SECURITY &&
528 grouplist[n,G_WLEVEL]>secure_level() &&
529 member(grouplist[n,G_WRITERS],user_euid())==-1)
530 return -1;
531
532 if (sizeof(group)>=grouplist[n,G_MAX_MSG]) return -3;
533 return 1;
534}
535
536// Wichtig ...
537
538int query_prevent_shadow()
539{
540 return 1;
541}
542
543mixed GetNewsTime(string boardname)
544
545{
546 int i, ltime, j;
547 mixed *keys;
548
549 if (!boardname)
550 {
551 ltime=-1;
552 for (i=sizeof(keys=m_indices(grouplist))-1;i>=0;i--)
553 if (ltime<(j=grouplist[keys[i],WTIME])) ltime=j;
554 return ltime;
555 }
556 if (!member(grouplist,boardname)) return -1;
557 return grouplist[boardname,WTIME];
558}
559
560mixed* GetGroup(string name)
561{
562 if (process_call()) return 0;
563 if (extern_call() && !allowed(name, F_ADMIN)) return 0;
564#define gl(x) grouplist[name,x]
565 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)});
566}