MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | // 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 | |
| 101 | private int security( string name ); |
| 102 | |
| 103 | mixed saveload; // Diese Variable ist als einzige nicht nosave ist und dient |
| 104 | // Uebertragen von Daten in/aus savefiles |
| 105 | |
| 106 | nosave mapping grouplist; // Groups und ihre save-Files, zudem LastTime |
| 107 | nosave int lasttime; // Um zu vermeiden, dass 2 Notes identische Uhrzeit==id |
| 108 | // haben, wird die jeweils letzte Zeit gespeichert. |
| 109 | |
| 110 | nosave mapping cache = ([]); // cache fuer die Gruppeninhalte |
| 111 | |
| 112 | void 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 | |
| 122 | int 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 | |
| 152 | int 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 | |
| 171 | int 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 | |
| 189 | int 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 | |
| 215 | int 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 | |
| 241 | static 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 | |
| 257 | private 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 | |
| 267 | static 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 | |
| 298 | int 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 | |
| 326 | int 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 | |
| 369 | mixed 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 | |
| 378 | static 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 | |
| 388 | protected 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 | |
| 430 | void 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 | |
| 443 | protected 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 | |
| 451 | void 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 | |
| 462 | static 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 | |
| 470 | static void save_group_list() |
| 471 | { |
| 472 | saveload=grouplist; |
| 473 | save_object(NEWSPATH+"GroupList"); |
| 474 | saveload=0; |
| 475 | } |
| 476 | |
| 477 | static 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 | |
| 498 | mixed 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 | |
| 519 | int 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 | |
| 538 | int query_prevent_shadow() |
| 539 | { |
| 540 | return 1; |
| 541 | } |
| 542 | |
| 543 | mixed 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 | |
| 560 | mixed* 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 | } |