Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/secure/news.c b/secure/news.c
new file mode 100644
index 0000000..751c887
--- /dev/null
+++ b/secure/news.c
@@ -0,0 +1,566 @@
+// 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 (file_size("/"+SAVEPATH+owner[0..0]+"/"+owner+".o")<0) 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("news/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("news/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)});
+}