blob: 83a73f5484cf6a61ddde4ed0f400d4fc4611563c [file] [log] [blame]
// 18.Dez 1996 - Loco@Morgengrauen
// 8. Feb 1995 - Jof@MorgenGrauen
// 5. Juli 1992 - Jof@MorgenGrauen
// 6. Juli 1992 - Jof@MorgenGrauen
// Clear-Groups-Mechanismus eingebaut. Die 2 Konstanten TIME_TO_CLEAR und
// MIN_TOUCHED muessen def. sein. Alle TIME_TO_CLEAR Sekunden werden die
// Newsgroups, die seit dem letzten Clear seltener als MIN_TOUCHED Sekunden
// beruehrt wurden, aus dem Cache genommen
// 1. Februar 1995 - Jof@MorgenGrauen
// Rewrite (Mappings benutzen, Accessinfos im Speicher halten)
// Datenstrukturen:
// Newsgroups: grouplist ist ein Mapping mit einem Eintrag pro Newsgroup.
// Die Keys sind die Gruppennamen, Daten:
// 0 Zeit des letzen Artikels
// 1 Besitzer der Gruppe
// 2 Savefille-Name
// 3 Expire-Zeit
// 4 Array mit Loesch-Berechtigten
// 5 Array mit Scheib-Berechtigten
// 6 Array mit Leseberechtigten
// 7 Mindest-Level, um Artikel zu loeschen
// 8 Mindest-Level, um Artikel zu posten
// 9 Mindest-Level, um Artikel zu lesen
// 10 Max. Anzahl Nachrichten in der Gruppe
// Die Nachrichten selber stehen in einem Array.
// Eine nachricht ist ein Array mit den folgenden Elementen:
// 0 (*) string writer;
// 1 (*) string id; Mudname+":"+time()+":"+group zur Zeit
// 2 (*) string time;
// 3 string title;
// 4 string message;
// Die mit (*) gekennzeichneten Eintraege setzt der Daemon selber
// Funktionen:
// Returnvalues: 0 = Parameter error
// 1 = Ok.
// -1 = no permission
// -2 = no such group/group already existing
//
// Diese Returnvalues stehen fuer alle Funktionen
// AddGroup(groupname, owner, savefile);
// Funktion klar, kann nur von Erzmagiern aufgerufen werden
// -3 = no owner, -4 = savefile already in use
//
// RemoveGroup(groupname);
// Ebenfalls nur fuer Erzmagier
//
// SetGroup(groupname, dlevel, wlevel, rlevel, maxmessages, expire);
// Erzmagier und der Groupowner koennen die nutzen. Legt Level, ab dem je-
// mand aus der Gruppe loeschen kann (dlevel), in die Gruppe schreiben
// kann (wlevel), die Gruppe lesen kann (rlevel) und die max. Anzahl Nach-
// richten in der Gruppe fest.
//
// AddAllowed(groupname, deleters, writers, readers);
// Erzmagier/Owner koennen Arrays mit Namen von fuer die Gruppe loesch/
// schreib/lese-Berechtigten fuer die Gruppe angeben.
//
// RemoveAllowed(groupname, deleters, writers, readers); analog
//
// WriteNote(message); klar
// -3 = Max number of msgs exceeded
//
// RemoveNote(boardname, notenummer); notenummer>=0;
// -3 = No such note
//
// GetNotes(boardname); gibt einen Array mit den Notes zurueck.
//
// AskAllowedWrite(); return wie bei WriteNote, stellt fest, ob ein Player
// eine Note aufhaengen darf oder nicht
//
// GetNewsTime([boardname]); gibt zurueck, wann am entsprechenden Brett zum
// letzten Mal eine Note befestigt wurde. Falls kein boardname angegeben
// wird, liefert GetNewsTime() den Zeitpunkt, zu dem ueberhaupt eine neue
// Note aufgehaengt wurde.
//
#pragma strict_types
#pragma no_clone
#pragma no_shadow
#pragma no_inherit
#pragma verbose_errors
#pragma combine_strings
//#pragma pedantic
//#pragma range_check
#pragma warn_deprecated
#include "/secure/wizlevels.h"
#include <defines.h>
#include <config.h>
#include <news.h>
#define WTIME 0
private int security( string name );
mixed saveload; // Diese Variable ist als einzige nicht nosave ist und dient
// Uebertragen von Daten in/aus savefiles
nosave mapping grouplist; // Groups und ihre save-Files, zudem LastTime
nosave int lasttime; // Um zu vermeiden, dass 2 Notes identische Uhrzeit==id
// haben, wird die jeweils letzte Zeit gespeichert.
nosave mapping cache = ([]); // cache fuer die Gruppeninhalte
void create() {
seteuid(getuid(this_object()));
if (!restore_object(NEWSPATH+"GroupList"))
grouplist=m_allocate(0,G_MESSAGES);
else
grouplist=saveload;
// ersten reset sobald wie moeglich. ;-)
set_next_reset(1);
}
int AddGroup(string name, string owner)
{
mixed *group;
string savefile, *savefilea;
int i;
if (!name || !owner) return 0;
if (!ARCH_SECURITY || process_call()) return -1; // Darf nicht
if (member(grouplist, name)) return -2; // Gibt es schon
if (!({int})master()->find_userinfo(owner)) return -3;
savefilea = old_explode(name,".");
savefile = implode(savefilea,"/");
if (file_size(NEWSPATH+savefile+".o")>=0) return -4;
// Notwendige Directories anlegen
for (i = 0; i < sizeof(savefilea)-1; i++) {
mkdir(NEWSPATH+implode(savefilea[0..i],"/"));
}
group=({});
grouplist+=([name:0;owner;savefile;-1;({});({});({});20;0;0;80]);
save_group_list();
save_group(name,group);
return 1;
}
int RemoveGroup(string name)
{
int num;
if (!name) return 0;
if (!security(name) || process_call()) return -1; // Darf nicht
if (!mappingp(grouplist) || !member(grouplist,name))
return -2; // -2 no such group
catch(rm(NEWSPATH+grouplist[name,G_SAVEFILE]+".o");publish);
m_delete(grouplist,name);
save_group_list();
return 1;
}
int SetGroup(string name,int dlevel,int wlevel,int rlevel,int maxmessages,int expire)
{
mixed *group;
if (!member(grouplist,name)) return -2;
if (grouplist[name,G_OWNER]!=user_euid() &&
(!security(name) || process_call())) return -1;
grouplist[name,G_DLEVEL]=dlevel;
grouplist[name,G_WLEVEL]=wlevel;
grouplist[name,G_RLEVEL]=rlevel;
grouplist[name,G_MAX_MSG]=maxmessages;
grouplist[name,G_EXPIRE]=expire;
save_group_list();
return 1;
}
int AddAllowed(string name,mixed deleters,mixed writers,mixed readers)
{
mixed *group;
if (!member(grouplist,name)) return -2;
if ( grouplist[name,G_OWNER]!=user_euid() &&
(!security(name) || process_call()) && user_euid() != ROOTID )
return -1;
if (stringp(deleters)) deleters=({deleters});
if (stringp(writers)) writers=({writers});
if (stringp(readers)) readers=({readers});
if (!deleters) deleters=({});
if (!writers) writers=({});
if (!readers) readers=({});
grouplist[name,G_DELETERS]+=deleters;
grouplist[name,G_WRITERS]+=writers;
grouplist[name,G_READERS]+=readers;
save_group_list();
return 1;
}
int RemoveAllowed(string name,mixed deleters,mixed writers,mixed readers)
{
mixed *group;
if (!member(grouplist,name)) return -2;
if (grouplist[name,G_OWNER]!=user_euid() &&
(!security(name) || process_call()) && user_euid() != ROOTID )
return -1;
if (stringp(deleters)) deleters=({deleters});
if (stringp(writers)) writers=({writers});
if (stringp(readers)) readers=({readers});
if (!deleters) deleters=({});
if (!writers) writers=({});
if (!readers) readers=({});
grouplist[name,G_DELETERS]-=deleters;
grouplist[name,G_WRITERS]-=writers;
grouplist[name,G_READERS]-=readers;
save_group_list();
return 1;
}
static string user_euid()
{
if (previous_object()) {
if (geteuid(previous_object())==ROOTID) return ROOTID;
if (geteuid(previous_object())=="p.daemon") return "p.daemon";
if (load_name(previous_object())=="/obj/mpa") return geteuid(RPL);
}
return secure_euid();
}
#define F_DELETE 0
#define F_READ 1
#define F_WRITE 2
#define F_ADMIN 3
#define F_KEEPNAME 4
private int security( string name )
{
if ( grouplist[name,G_DLEVEL] >= ARCH_LVL
|| grouplist[name,G_WLEVEL] >= ARCH_LVL
|| grouplist[name,G_RLEVEL] >= ARCH_LVL )
return ARCH_SECURITY;
else
return ELDER_SECURITY;
}
static int allowed(string name, int mode)
{
string euid;
mixed g_level, g_mode;
if (process_call()) return 0;
euid=user_euid();
if (euid==ROOTID) return 1;
switch(mode) {
case F_KEEPNAME: return (euid=="p.daemon");
case F_WRITE: if (euid=="p.daemon") return 1;
g_level=G_WLEVEL; g_mode=G_WRITERS; break;
case F_ADMIN: if (!(security(name)||grouplist[name,G_OWNER]==euid))
return 0;
g_level=G_DLEVEL; g_mode=G_DELETERS; break;
case F_DELETE: if (euid=="p.daemon") return 1;
g_level=G_DLEVEL; g_mode=G_DELETERS; break;
case F_READ: g_level=G_RLEVEL; g_mode=G_READERS; break;
default: return 0;
}
if (grouplist[name,G_OWNER] != euid && !ARCH_SECURITY &&
grouplist[name,g_level] > query_wiz_level(euid) &&
member(grouplist[name, g_mode], euid)==-1)
return 0; // No such group for the requestor :)
return 1;
}
int WriteNote(mixed message,mixed keepname)
{
mixed group;
int uidok,tmp;
string name;
if (!pointerp(message) || sizeof(message)!=6) return 0;
if (!pointerp(group=load_group(name=message[M_BOARD]))) return -2;
if (!allowed(name, F_WRITE)) return -1;
if (sizeof(group)>=grouplist[name,G_MAX_MSG]) return -3;
if (!keepname || !allowed(name, F_KEEPNAME))
message[M_WRITER]=capitalize(geteuid(this_interactive()||previous_object()));
if (lasttime>=time()) lasttime++;
else lasttime=time();
message[M_TIME]=lasttime;
message[M_ID]=MUDNAME+":"+lasttime;
group+=({message});
grouplist[name,WTIME]=lasttime;
save_group(name,group);
save_group_list();
return 1;
}
int RemoveNote(string name, int note)
{
int num;
mixed group;
if ((note<0) && (name=="dwnews"))
{
group=({});
grouplist[name,WTIME]=0;
save_group(name,group);
save_group_list();
return 1;
}
if (note<0) return 0;
if (!pointerp(group=load_group(name))) return -2;
int count=sizeof(group);
if (count<=note)
return -3;
if (!allowed(name, F_DELETE) &&
lower_case(group[note][M_WRITER])!=user_euid()) return -1;
if (count==1)
group=({});
else if (!note)
group = group[1..];
else if (note == count-1)
group = group[0..<2];
else
group=group[0..note-1]+group[note+1..];
if (sizeof(group))
grouplist[name,WTIME]=group[<1][M_TIME];
else
grouplist[name,WTIME]=0;
save_group(name,group);
save_group_list();
return 1;
}
mixed GetNotes(string name)
{
mixed group;
if (!pointerp(group=load_group(name))) return -2;
if (!allowed(name, F_READ)) return -2;
return(deep_copy(group)); // COPY it
}
static void dump_file(string filename,mixed news)
{
int i;
for (i=0;i<sizeof(news);i++)
write_file(filename,news[i][M_TITLE]+" ("+news[i][M_WRITER]+", "+
dtime(news[i][M_TIME])[5..26]+"):\n"+
news[i][M_MESSAGE]+"\n-----------------------------------------------------------------------------\n\n\n\n");
}
protected varargs void expire(string grp,int etime)
// etime ist anfangs in Tagen und bezeichnet das max. Alter, was Artikel in
// der Gruppe haben duerfen.
{
mixed group;
if (!pointerp(group=load_group(grp))) return;
if (etime)
{
if (etime>0)
etime=etime*60*60*24;
}
else
etime=grouplist[grp,G_EXPIRE];
if (etime<=0)
return;
int to_expire=time()-etime;
int size=sizeof(group);
if (!size) return;
int first_to_keep = size; // ja, ist noch eins zu hoch
// solange runterlaufen bis man ein element findet, welches geloescht werden
// soll. first_to_keep bleibt dann eins hoeher als das.
while ( first_to_keep && group[first_to_keep-1][M_TIME]>to_expire)
--first_to_keep;
// first_to_keep kann jetzt auf eins hinter dem letzten Element zeigen (==
// size). Das wird unten beruecksichtigt.
if (!first_to_keep) // alle behalten?
return;
// Zu loeschende Artikel wegschreiben.
dump_file(NEWSPATH"OLD."+grp,group[0..first_to_keep-1]);
// dann loeschen
if (first_to_keep == size) // alle wegwerfen?
group=({});
else
group=group[first_to_keep..size-1];
save_group(grp,group);
}
void dump_group(string grp)
{
int to_expire,size,last;
mixed group;
if (!ARCH_SECURITY || process_call()) return;
if (!pointerp(group=load_group(grp))) return;
size=sizeof(group);
last=size;
if (!last) return;
dump_file(NEWSPATH"DUMP."+grp,group[0..last-1]);
}
protected void expire_all(string *keys) {
// neuen call_out fuer den Rest setzen
if (sizeof(keys) > 1)
call_out(#'expire_all,15,keys[1..]);
// und erste Gruppe expiren
expire(keys[0]);
}
void reset() {
// naechstes Expire und damit Reset in einem tag
set_next_reset(86400);
// alte call_outs ggf. entfernen.
while(remove_call_out(#'expire_all)>=0);
// gruppenliste holen und callout auf expire_all starten
if (sizeof(grouplist)) {
call_out(#'expire_all,10,m_indices(grouplist));
}
}
static void save_group(string grp,mixed group)
{
saveload=group; // Do NOT save the accessed-Info
cache[grp] = group;
save_object(NEWSPATH+grouplist[grp,G_SAVEFILE]);
saveload=0;
}
static void save_group_list()
{
saveload=grouplist;
save_object(NEWSPATH+"GroupList");
saveload=0;
}
static mixed load_group(string name)
{
int num;
mixed *ret;
if(!member(grouplist,name)) return -1;
if (member(cache, name)) {
ret = cache[name];
}
else {
restore_object(NEWSPATH+grouplist[name,G_SAVEFILE]);
if (!pointerp(saveload))
saveload=({});
ret=saveload;
cache[name] = saveload;
saveload=0;
}
return ret;
}
mixed GetGroups()
{
mixed *returnlist;
int i,group,slevel;
string seuid;
returnlist=sort_array(m_indices(grouplist),#'>); //');
if (ARCH_SECURITY && !process_call())
return returnlist;
seuid = user_euid();
slevel = secure_level();
for (i=sizeof(returnlist)-1;i>=0;i--)
if (!(grouplist[returnlist[i],G_RLEVEL]<=slevel ||
grouplist[returnlist[i],G_OWNER]==seuid ||
member(grouplist[returnlist[i],G_READERS], seuid)!=-1))
returnlist=returnlist[0..i-1]+returnlist[i+1..];
return returnlist;
}
int AskAllowedWrite(string n)
{
mixed group;
if (!member(grouplist,n)) return -2;
if (!pointerp(group=load_group(n))) return -2;
if (grouplist[n,G_OWNER] != user_euid() &&
!ARCH_SECURITY &&
grouplist[n,G_WLEVEL]>secure_level() &&
member(grouplist[n,G_WRITERS],user_euid())==-1)
return -1;
if (sizeof(group)>=grouplist[n,G_MAX_MSG]) return -3;
return 1;
}
// Wichtig ...
int query_prevent_shadow()
{
return 1;
}
mixed GetNewsTime(string boardname)
{
int i, ltime, j;
mixed *keys;
if (!boardname)
{
ltime=-1;
for (i=sizeof(keys=m_indices(grouplist))-1;i>=0;i--)
if (ltime<(j=grouplist[keys[i],WTIME])) ltime=j;
return ltime;
}
if (!member(grouplist,boardname)) return -1;
return grouplist[boardname,WTIME];
}
mixed* GetGroup(string name)
{
if (process_call()) return 0;
if (extern_call() && !allowed(name, F_ADMIN)) return 0;
#define gl(x) grouplist[name,x]
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)});
}