]> code.citadel.org Git - citadel.git/blobdiff - citadel/room_ops.c
* added server command line option "-f" to defrag databases on startup
[citadel.git] / citadel / room_ops.c
index 445aa2f61675390f186e9c86aae413db9cd824e9..3bca3a7c211cb8b0aba861f93c1977a089ecc204 100644 (file)
@@ -1,10 +1,16 @@
+/* $Id$ */
+#include "sysdep.h"
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <string.h>
+#ifdef HAVE_PTHREAD_H
 #include <pthread.h>
+#endif
 #include <time.h>
+#include <limits.h>
+#include <errno.h>
 #include "citadel.h"
 #include "server.h"
 #include "database.h"
 #include "msgbase.h"
 #include "serv_chat.h"
 #include "citserver.h"
+#include "control.h"
+#include "tools.h"
 
 /*
- * is_known()  -  returns nonzero if room is in user's known room list
+ * Generic routine for determining user access to rooms
  */
-int is_known(struct quickroom *roombuf, int roomnum, struct usersupp *userbuf)
-{
+int CtdlRoomAccess(struct quickroom *roombuf, struct usersupp *userbuf) {
+       int retval = 0;
+       struct visit vbuf;
 
-       /* for internal programs, always succeed */
-       if (((CC->internal_pgm))&&(roombuf->QRflags & QR_INUSE)) return(1);
+       /* for internal programs, always do everything */
+       if (((CC->internal_pgm))&&(roombuf->QRflags & QR_INUSE)) {
+               return(UA_KNOWN | UA_GOTOALLOWED);
+               }
 
-       /* for regular rooms, check the permissions */
-       if ((roombuf->QRflags & QR_INUSE)
-               && ( (roomnum!=2) || (userbuf->axlevel>=6))
-               && (roombuf->QRgen != (userbuf->forget[roomnum]) )
+       /* For mailbox rooms, only allow access to the owner */
+       if (roombuf->QRflags & QR_MAILBOX) {
+               if (userbuf->usernum != atol(roombuf->QRname)) {
+                       return(retval);
+                       }
+               }
 
-               && (    ((roombuf->QRflags&QR_PREFONLY)==0)
-               ||      ((userbuf->axlevel)>=5)
-               )
+       /* Locate any applicable user/room relationships */
+       CtdlGetRelationship(&vbuf, userbuf, roombuf);
 
-               && (    ((roombuf->QRflags&QR_PRIVATE)==0)
-               ||      ((userbuf->axlevel)>=6)
-               ||      (roombuf->QRgen==(userbuf->generation[roomnum]))
-               )
+       /* Force the properties of the Aide room */
+       if (!strcasecmp(roombuf->QRname, AIDEROOM)) {
+               if (userbuf->axlevel >= 6) {
+                       retval = UA_KNOWN | UA_GOTOALLOWED;
+                       }
+               else {
+                       retval=0;
+                       }
+               goto NEWMSG;
+               }
 
-               ) return(1);
-       else return(0);
-       }
+       /* For mailboxes, we skip all the access stuff (and we've
+        * already checked by this point that the mailbox belongs
+        * to the user)
+        */
+       if (roombuf->QRflags & QR_MAILBOX) {
+               retval = UA_KNOWN | UA_GOTOALLOWED;
+               goto NEWMSG;
+               }
 
+       /* If this is a public room, it's accessible... */
+       if ((roombuf->QRflags & QR_PRIVATE) == 0) {
+               retval = retval | UA_KNOWN | UA_GOTOALLOWED;
+               }
 
-/*
- * has_newmsgs()  -  returns nonzero if room has new messages
- */
-int has_newmsgs(struct quickroom *roombuf, int roomnum, struct usersupp *userbuf)
-{
-       if (roomnum == 1) {
-               return ( (NewMailCount() > 0) ? 1 : 0 );
+       /* If this is a preferred users only room, check access level */
+       if (roombuf->QRflags & QR_PREFONLY) {
+               if (userbuf->axlevel < 5) {
+                       retval = retval & ~UA_KNOWN & ~UA_GOTOALLOWED;
+                       }
                }
-       else {
-               if (roombuf->QRhighest > (userbuf->lastseen[roomnum]) ) {
-                       return(1);
+
+       /* For private rooms, check the generation number matchups */
+       if (roombuf->QRflags & QR_PRIVATE) {
+
+               /* An explicit match means the user belongs in this room */
+               if (vbuf.v_flags & V_ACCESS) {
+                       retval = retval | UA_KNOWN | UA_GOTOALLOWED;
                        }
-               else {
-                       return(0);
+               /* Otherwise, check if this is a guess-name or passworded
+                * room.  If it is, a goto may at least be attempted
+                */
+               else if ((roombuf->QRflags & QR_PRIVATE)
+                    ||(roombuf->QRflags & QR_PASSWORDED)) {
+                       retval = retval & ~UA_KNOWN;
+                       retval = retval | UA_GOTOALLOWED;
                        }
                }
-       }
 
+       /* Check to see if the user has forgotten this room */
+       if (vbuf.v_flags & V_FORGET) {
+               retval = retval & ~UA_KNOWN;
+               retval = retval | UA_ZAPPED;
+               }
+
+       /* If user is explicitly locked out of this room, deny everything */
+       if (vbuf.v_flags & V_LOCKOUT) {
+               retval = retval & ~UA_KNOWN & ~UA_GOTOALLOWED;
+               }
+
+       /* Aides get access to everything */
+       if (userbuf->axlevel >= 6) {
+               retval = retval | UA_KNOWN | UA_GOTOALLOWED;
+               retval = retval & ~UA_ZAPPED;
+               }
+
+NEWMSG:        /* By the way, we also check for the presence of new messages */
+       if ( (roombuf->QRhighest) > (vbuf.v_lastseen) ) {
+               retval = retval | UA_HASNEWMSGS;
+               }
+
+       return(retval);
+       }
 
 /*
- * is_zapped()  -  returns nonzero if room is on forgotten rooms list
+ * Self-checking stuff for a room record read into memory
  */
-int is_zapped(struct quickroom *roombuf, int roomnum, struct usersupp *userbuf)
-{
-       if ((roombuf->QRflags & QR_INUSE)
-               && (roombuf->QRgen == (userbuf->forget[roomnum]) )
-               && ( (roomnum!=2) || ((userbuf->axlevel)>=6))
-               && (    ((roombuf->QRflags&QR_PRIVATE)==0)
-               ||      ((userbuf->axlevel)>=6)
-               ||      (roombuf->QRgen==(userbuf->generation[roomnum]))
-               )
-               ) return(1);
-       else return(0);
+void room_sanity_check(struct quickroom *qrbuf) {
+       /* Mailbox rooms are always on the lowest floor */
+       if (qrbuf->QRflags & QR_MAILBOX) {
+               qrbuf->QRfloor = 0;
+               }
+
+       /* Listing order of 0 is illegal except for base rooms */
+       if (qrbuf->QRorder == 0)
+               if (!is_noneditable(qrbuf))
+                       qrbuf->QRorder = 64;
        }
 
+
 /*
  * getroom()  -  retrieve room data from disk
  */
-void getroom(struct quickroom *qrbuf, int room_num)
+int getroom(struct quickroom *qrbuf, char *room_name)
 {
        struct cdbdata *cdbqr;
+       char lowercase_name[ROOMNAMELEN];
        int a;
 
-       bzero(qrbuf, sizeof(struct quickroom));
-       cdbqr = cdb_fetch(CDB_QUICKROOM, &room_num, sizeof(int));
+       for (a=0; room_name[a] && a < sizeof lowercase_name - 1; ++a) {
+               lowercase_name[a] = tolower(room_name[a]);
+               }
+       lowercase_name[a] = 0;
+
+       memset(qrbuf, 0, sizeof(struct quickroom));
+       cdbqr = cdb_fetch(CDB_QUICKROOM,
+                       lowercase_name, strlen(lowercase_name));
        if (cdbqr != NULL) {
                memcpy(qrbuf, cdbqr->ptr,
                        ( (cdbqr->len > sizeof(struct quickroom)) ?
                        sizeof(struct quickroom) : cdbqr->len) );
                cdb_free(cdbqr);
+
+               room_sanity_check(qrbuf);
+
+               return(0);
                }
        else {
-               if (room_num < 3) {
-                       qrbuf->QRflags = QR_INUSE;
-                       qrbuf->QRgen = 1;
-                       switch(room_num) {
-                               case 0: strcpy(qrbuf->QRname, "Lobby");
-                                       break;
-                               case 1: strcpy(qrbuf->QRname, "Mail");
-                                       break;
-                               case 2: strcpy(qrbuf->QRname, "Aide");
-                                       break;
-                               }
-                       }
+               return(1);
                }
-
-
-       /** FIX **   VILE SLEAZY HACK ALERT!!  
-        * This is a temporary fix until we can track down where room names
-        * are getting corrupted on some systems.
-        */
-       for (a=0; a<20; ++a) if (qrbuf->QRname[a] < 32) qrbuf->QRname[a] = 0;
-       qrbuf->QRname[19] = 0;
        }
 
 /*
  * lgetroom()  -  same as getroom() but locks the record (if supported)
  */
-void lgetroom(struct quickroom *qrbuf, int room_num)
+int lgetroom(struct quickroom *qrbuf, char *room_name)
 {
        begin_critical_section(S_QUICKROOM);
-       getroom(qrbuf,room_num);
+       return(getroom(qrbuf, room_name));
        }
 
 
 /*
  * putroom()  -  store room data on disk
+ *             (if the supplied buffer is NULL, delete the room record)
  */
-void putroom(struct quickroom *qrbuf, int room_num)
+void putroom(struct quickroom *qrbuf, char *room_name)
 {
-       time(&qrbuf->QRmtime);
-       cdb_store(CDB_QUICKROOM, &room_num, sizeof(int),
-               qrbuf, sizeof(struct quickroom));
+       char lowercase_name[ROOMNAMELEN];
+       int a;
+
+       for (a=0; a<=strlen(room_name); ++a) {
+               lowercase_name[a] = tolower(room_name[a]);
+               }
+
+       if (qrbuf == NULL) {
+               cdb_delete(CDB_QUICKROOM,
+                       lowercase_name, strlen(lowercase_name));
+               }
+       else {
+               time(&qrbuf->QRmtime);
+               cdb_store(CDB_QUICKROOM,
+                       lowercase_name, strlen(lowercase_name),
+                       qrbuf, sizeof(struct quickroom));
+               }
        }
 
 
 /*
  * lputroom()  -  same as putroom() but unlocks the record (if supported)
  */
-void lputroom(struct quickroom *qrbuf, int room_num)
+void lputroom(struct quickroom *qrbuf, char *room_name)
 {
 
-       putroom(qrbuf,room_num);
+       putroom(qrbuf, room_name);
        end_critical_section(S_QUICKROOM);
 
        }
 
+/****************************************************************************/
 
 /*
  * getfloor()  -  retrieve floor data from disk
@@ -160,7 +225,7 @@ void getfloor(struct floor *flbuf, int floor_num)
 {
        struct cdbdata *cdbfl;
 
-       bzero(flbuf, sizeof(struct floor));
+       memset(flbuf, 0, sizeof(struct floor));
        cdbfl = cdb_fetch(CDB_FLOORTAB, &floor_num, sizeof(int));
        if (cdbfl != NULL) {
                memcpy(flbuf, cdbfl->ptr,
@@ -211,33 +276,46 @@ void lputfloor(struct floor *flbuf, int floor_num)
        }
 
 
+/* 
+ *  Traverse the room file...
+ */
+void ForEachRoom(void (*CallBack)(struct quickroom *EachRoom)) {
+       struct quickroom qrbuf;
+       struct cdbdata *cdbqr;
+
+       cdb_rewind(CDB_QUICKROOM);
+
+       while(cdbqr = cdb_next_item(CDB_QUICKROOM), cdbqr != NULL) {
+               memset(&qrbuf, 0, sizeof(struct quickroom));
+               memcpy(&qrbuf, cdbqr->ptr,
+                       ( (cdbqr->len > sizeof(struct quickroom)) ?
+                       sizeof(struct quickroom) : cdbqr->len) );
+               cdb_free(cdbqr);
+               room_sanity_check(&qrbuf);
+               if (qrbuf.QRflags & QR_INUSE) (*CallBack)(&qrbuf);
+               }
+       }
+
+
 
 /*
  * get_msglist()  -  retrieve room message pointers
  */
-void get_msglist(int room_num)
-{
+void get_msglist(struct quickroom *whichroom) {
        struct cdbdata *cdbfr;
 
        if (CC->msglist != NULL) {
-               free(CC->msglist);
+               phree(CC->msglist);
                }
        CC->msglist = NULL;
        CC->num_msgs = 0;
 
-       if (room_num != 1) {
-               cdbfr = cdb_fetch(CDB_MSGLISTS, &room_num, sizeof(int));
-               }
-       else {
-               cdbfr = cdb_fetch(CDB_MAILBOXES, &CC->usersupp.usernum,
-                                       sizeof(long));
-               }
-
+       cdbfr = cdb_fetch(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
        if (cdbfr == NULL) {
                return;
                }
 
-       CC->msglist = malloc(cdbfr->len);
+       CC->msglist = mallok(cdbfr->len);
        memcpy(CC->msglist, cdbfr->ptr, cdbfr->len);
        CC->num_msgs = cdbfr->len / sizeof(long);
        cdb_free(cdbfr);
@@ -247,17 +325,81 @@ void get_msglist(int room_num)
 /*
  * put_msglist()  -  retrieve room message pointers
  */
-void put_msglist(int room_num)
-{
+void put_msglist(struct quickroom *whichroom) {
+
+       if (CC->msglist != NULL)
+               cdb_store(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long),
+                       CC->msglist, CC->num_msgs * sizeof(long));
+       }
+
 
-       if (room_num != 1) {
-               cdb_store(CDB_MSGLISTS, &room_num, sizeof(int),
-                       CC->msglist, (CC->num_msgs * sizeof(long)) );
+/*
+ * delete_msglist()  -  delete room message pointers
+ * FIX - this really should check first to make sure there's actually a
+ *       msglist to delete.  As things stand now, calling this function on
+ *       a room which has never been posted in will result in a message
+ *       like "gdbm: illegal data" (no big deal, but could use fixing).
+ */
+void delete_msglist(struct quickroom *whichroom) {
+
+       cdb_delete(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
+       }
+
+
+/* 
+ * Add a message number to a room's message list.  
+ * So, why doesn't this function use the get_msglist() and put_msglist() stuff
+ * defined above?  Because the room the message number is being written to
+ * may not be the current room (as is the case with cmd_move() for example).
+ *
+ * This function returns the highest message number present in the room after
+ * the add operation is performed - which is not necessarily the message
+ * being added.
+ */
+long AddMessageToRoom(struct quickroom *whichroom, long newmsgid) {
+       struct cdbdata *cdbfr;
+       int num_msgs;
+       long *msglist;
+       long highest_msg = 0L;
+
+       lprintf(9, "AddMessageToRoom(%s, %ld)\n", whichroom->QRname, newmsgid); 
+       cdbfr = cdb_fetch(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
+       if (cdbfr == NULL) {
+               msglist = NULL;
+               num_msgs = 0;
                }
        else {
-               cdb_store(CDB_MAILBOXES, &CC->usersupp.usernum, sizeof(long),
-                       CC->msglist, (CC->num_msgs * sizeof(long)) );
+               msglist = mallok(cdbfr->len);
+               if (msglist==NULL)  lprintf(3, "ERROR malloc msglist!\n");
+               num_msgs = cdbfr->len / sizeof(long);
+               memcpy(msglist, cdbfr->ptr, cdbfr->len);
+               cdb_free(cdbfr);
+               }
+       
+       /* Now add the new message */
+       ++num_msgs;
+       msglist = reallok(msglist,
+               (num_msgs * sizeof(long)) );
+
+       if (msglist == NULL) {
+               lprintf(3, "ERROR: can't realloc message list!\n");
                }
+
+       msglist[num_msgs - 1] = newmsgid;
+
+       /* Sort the message list, so all the msgid's are in order */
+       num_msgs = sort_msglist(msglist, num_msgs);
+
+       /* Determine the highest message number */
+       highest_msg = msglist[num_msgs - 1];
+
+       /* Write it back to disk. */
+       cdb_store(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long),
+               msglist, num_msgs * sizeof(long));
+
+       /* And finally, free up the memory we used. */
+       phree(msglist);
+       return(highest_msg);
        }
 
 
@@ -320,19 +462,65 @@ int sort_msglist(long listptrs[], int oldcount)
        return(numitems);
        }
 
+
+/*
+ * Determine whether a given room is non-editable.
+ */
+int is_noneditable(struct quickroom *qrbuf) {
+
+       /* Lobby> and Aide> are non-editable */
+       if (!strcasecmp(qrbuf->QRname, BASEROOM)) return(1);
+       else if (!strcasecmp(qrbuf->QRname, AIDEROOM)) return(1);
+
+       /* Mailbox rooms are also non-editable */
+       else if (qrbuf->QRflags & QR_MAILBOX) return(1);
+
+       /* Everything else is editable */
+       else return(0);
+       }
+
+
+
+/*
+ * Back-back-end for all room listing commands
+ */
+void list_roomname(struct quickroom *qrbuf) {
+       char truncated_roomname[ROOMNAMELEN];
+
+       /* For mailbox rooms, chop off the owner prefix */
+       if (qrbuf->QRflags & QR_MAILBOX) {
+               strcpy(truncated_roomname, qrbuf->QRname);
+               strcpy(truncated_roomname, &truncated_roomname[11]);
+               cprintf("%s", truncated_roomname);
+               }
+       /* For all other rooms, just display the name in its entirety */
+       else {
+               cprintf("%s", qrbuf->QRname);
+               }
+
+       /* ...and now the other parameters */
+       cprintf("|%u|%d|%d\n",
+               qrbuf->QRflags,
+               (int)qrbuf->QRfloor,
+               (int)qrbuf->QRorder);
+       }
 
 
 /* 
  * cmd_lrms()   -  List all accessible rooms, known or forgotten
  */
+void cmd_lrms_backend(struct quickroom *qrbuf) {
+       if ( ((CtdlRoomAccess(qrbuf, &CC->usersupp)
+            & (UA_KNOWN | UA_ZAPPED)))
+       && ((qrbuf->QRfloor == (CC->FloorBeingSearched))
+          ||((CC->FloorBeingSearched)<0)) ) 
+               list_roomname(qrbuf);
+       }
+
 void cmd_lrms(char *argbuf)
 {
-       int a;
-       int target_floor = (-1);
-       struct quickroom qrbuf;
-
-       if (strlen(argbuf)>0) target_floor = extract_int(argbuf,0);
+       CC->FloorBeingSearched = (-1);
+       if (strlen(argbuf)>0) CC->FloorBeingSearched = extract_int(argbuf,0);
 
        if (!(CC->logged_in)) {
                cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
@@ -345,61 +533,65 @@ void cmd_lrms(char *argbuf)
                }
 
        cprintf("%d Accessible rooms:\n",LISTING_FOLLOWS);
-       
-       for (a=0; a<MAXROOMS; ++a) {
-               getroom(&qrbuf,a);
-               if ( ( (is_known(&qrbuf,a,&CC->usersupp))
-                  ||   (is_zapped(&qrbuf,a,&CC->usersupp)) )
-               && ((qrbuf.QRfloor == target_floor)||(target_floor<0)) )
-                       cprintf("%s|%u|%d\n",
-                               qrbuf.QRname,qrbuf.QRflags,qrbuf.QRfloor);
-               }
+
+       ForEachRoom(cmd_lrms_backend);  
        cprintf("000\n");
        }
 
+
+
 /* 
  * cmd_lkra()   -  List all known rooms
  */
+void cmd_lkra_backend(struct quickroom *qrbuf) {
+       if ( ((CtdlRoomAccess(qrbuf, &CC->usersupp)
+            & (UA_KNOWN)))
+       && ((qrbuf->QRfloor == (CC->FloorBeingSearched))
+          ||((CC->FloorBeingSearched)<0)) )
+               list_roomname(qrbuf);
+       }
+
 void cmd_lkra(char *argbuf)
 {
-       int a;
-       struct quickroom qrbuf;
-       int target_floor = (-1);
+       CC->FloorBeingSearched = (-1);
+       if (strlen(argbuf)>0) CC->FloorBeingSearched = extract_int(argbuf,0);
 
-       if (strlen(argbuf)>0) target_floor = extract_int(argbuf,0);
-
-       if ((!(CC->logged_in))&&(!(CC->internal_pgm))) {
+       if (!(CC->logged_in)) {
                cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
                return;
                }
 
-       if (!(CC->internal_pgm)) if (getuser(&CC->usersupp,CC->curr_user)) {
+       if (getuser(&CC->usersupp,CC->curr_user)) {
                cprintf("%d Can't locate user!\n",ERROR+INTERNAL_ERROR);
                return;
                }
 
        cprintf("%d Known rooms:\n",LISTING_FOLLOWS);
-       
-       for (a=0; a<MAXROOMS; ++a) {
-               getroom(&qrbuf,a);
-               if ((is_known(&qrbuf,a,&CC->usersupp))
-                  && ((qrbuf.QRfloor == target_floor)||(target_floor<0)) )
-                       cprintf("%s|%u|%d\n",
-                               qrbuf.QRname,qrbuf.QRflags,qrbuf.QRfloor);
-               }
+
+       ForEachRoom(cmd_lkra_backend);  
        cprintf("000\n");
        }
 
+
+
 /* 
- * cmd_lkrn()   -  List Known Rooms with New messages
+ * cmd_lkrn()   -  List all known rooms with new messages
  */
+void cmd_lkrn_backend(struct quickroom *qrbuf) {
+       int ra;
+
+       ra = CtdlRoomAccess(qrbuf, &CC->usersupp);
+       if ( (ra & UA_KNOWN)
+          && (ra & UA_HASNEWMSGS)
+          && ((qrbuf->QRfloor == (CC->FloorBeingSearched))
+             ||((CC->FloorBeingSearched)<0)) )
+               list_roomname(qrbuf);
+       }
+
 void cmd_lkrn(char *argbuf)
 {
-       int a;
-       struct quickroom qrbuf;
-       int target_floor = (-1);
-
-       if (strlen(argbuf)>0) target_floor = extract_int(argbuf,0);
+       CC->FloorBeingSearched = (-1);
+       if (strlen(argbuf)>0) CC->FloorBeingSearched = extract_int(argbuf,0);
 
        if (!(CC->logged_in)) {
                cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
@@ -407,147 +599,158 @@ void cmd_lkrn(char *argbuf)
                }
 
        if (getuser(&CC->usersupp,CC->curr_user)) {
-               cprintf("%d can't locate user\n",ERROR+INTERNAL_ERROR);
+               cprintf("%d Can't locate user!\n",ERROR+INTERNAL_ERROR);
                return;
                }
 
-       cprintf("%d list of rms w/ new msgs\n",LISTING_FOLLOWS);
-       
-       for (a=0; a<MAXROOMS; ++a) {
-               getroom(&qrbuf,a);
-               if ( ( (is_known(&qrbuf,a,&CC->usersupp))
-                  &&   (has_newmsgs(&qrbuf,a,&CC->usersupp)) )
-                  && ((qrbuf.QRfloor == target_floor)||(target_floor<0)) )
-                       cprintf("%s|%u|%d\n",
-                               qrbuf.QRname,qrbuf.QRflags,qrbuf.QRfloor);
-               }
+       cprintf("%d Rooms w/ new msgs:\n",LISTING_FOLLOWS);
+
+       ForEachRoom(cmd_lkrn_backend);  
        cprintf("000\n");
        }
 
+
+
 /* 
- * cmd_lkro()   -  List Known Rooms with Old (no new) messages
+ * cmd_lkro()   -  List all known rooms
  */
+void cmd_lkro_backend(struct quickroom *qrbuf) {
+       int ra;
+
+       ra = CtdlRoomAccess(qrbuf, &CC->usersupp);
+       if ( (ra & UA_KNOWN)
+          && ((ra & UA_HASNEWMSGS)==0)
+          && ((qrbuf->QRfloor == (CC->FloorBeingSearched))
+             ||((CC->FloorBeingSearched)<0)) )
+               list_roomname(qrbuf);
+       }
+
 void cmd_lkro(char *argbuf)
 {
-       int a;
-       struct quickroom qrbuf;
-       int target_floor = (-1);
-
-       if (strlen(argbuf)>0) target_floor = extract_int(argbuf,0);
+       CC->FloorBeingSearched = (-1);
+       if (strlen(argbuf)>0) CC->FloorBeingSearched = extract_int(argbuf,0);
 
        if (!(CC->logged_in)) {
-               cprintf("%d not logged in\n",ERROR+NOT_LOGGED_IN);
+               cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
                return;
                }
 
        if (getuser(&CC->usersupp,CC->curr_user)) {
-               cprintf("%d can't locate user\n",ERROR+INTERNAL_ERROR);
+               cprintf("%d Can't locate user!\n",ERROR+INTERNAL_ERROR);
                return;
                }
 
-       cprintf("%d list of rms w/o new msgs\n",LISTING_FOLLOWS);
-       
-       for (a=0; a<MAXROOMS; ++a) {
-               getroom(&qrbuf,a);
-               if ( ( (is_known(&qrbuf,a,&CC->usersupp))
-                  &&   (!has_newmsgs(&qrbuf,a,&CC->usersupp)) ) 
-                  && ((qrbuf.QRfloor == target_floor)||(target_floor<0)) ) {
-                       if (!strcmp(qrbuf.QRname,"000")) cprintf(">");
-                       cprintf("%s|%u|%d\n",
-                               qrbuf.QRname,qrbuf.QRflags,qrbuf.QRfloor);
-                       }
-               }
+       cprintf("%d Rooms w/o new msgs:\n",LISTING_FOLLOWS);
+
+       ForEachRoom(cmd_lkro_backend);  
        cprintf("000\n");
        }
 
+
+
 /* 
- * cmd_lzrm()   -  List Zapped RooMs
+ * cmd_lzrm()   -  List all forgotten rooms
  */
+void cmd_lzrm_backend(struct quickroom *qrbuf) {
+       int ra;
+
+       ra = CtdlRoomAccess(qrbuf, &CC->usersupp);
+       if ( (ra & UA_GOTOALLOWED)
+          && (ra & UA_ZAPPED)
+          && ((qrbuf->QRfloor == (CC->FloorBeingSearched))
+             ||((CC->FloorBeingSearched)<0)) )
+               list_roomname(qrbuf);
+       }
+
 void cmd_lzrm(char *argbuf)
 {
-       int a;
-       struct quickroom qrbuf;
-       int target_floor = (-1);
-
-       if (strlen(argbuf)>0) target_floor = extract_int(argbuf,0);
+       CC->FloorBeingSearched = (-1);
+       if (strlen(argbuf)>0) CC->FloorBeingSearched = extract_int(argbuf,0);
 
        if (!(CC->logged_in)) {
-               cprintf("%d not logged in\n",ERROR+NOT_LOGGED_IN);
+               cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
                return;
                }
 
        if (getuser(&CC->usersupp,CC->curr_user)) {
-               cprintf("%d can't locate user\n",ERROR+INTERNAL_ERROR);
+               cprintf("%d Can't locate user!\n",ERROR+INTERNAL_ERROR);
                return;
                }
 
-       cprintf("%d list of forgotten rms\n",LISTING_FOLLOWS);
-       
-       for (a=0; a<MAXROOMS; ++a) {
-               getroom(&qrbuf,a);
-               if ( (is_zapped(&qrbuf,a,&CC->usersupp))
-                  && ((qrbuf.QRfloor == target_floor)||(target_floor<0)) ) {
-                       if (!strcmp(qrbuf.QRname,"000")) cprintf(">");
-                       cprintf("%s|%u|%d\n",
-                               qrbuf.QRname,qrbuf.QRflags,qrbuf.QRfloor);
-                       }
-               }
+       cprintf("%d Zapped rooms:\n",LISTING_FOLLOWS);
+
+       ForEachRoom(cmd_lzrm_backend);  
        cprintf("000\n");
        }
 
 
 
-void usergoto(int where, int display_result)
+void usergoto(char *where, int display_result)
 {
-       int a,b,c;
+       int a;
+       int new_messages = 0;
+       int total_messages = 0;
        int info = 0;
        int rmailflag;
        int raideflag;
        int newmailcount = 0;
+       struct visit vbuf;
+       char truncated_roomname[ROOMNAMELEN];
+
+       strcpy(CC->quickroom.QRname, where);
+       getroom(&CC->quickroom, where);
 
-       CC->curr_rm=where;
-       getroom(&CC->quickroom,CC->curr_rm);
        lgetuser(&CC->usersupp,CC->curr_user);
-       CC->usersupp.forget[CC->curr_rm]=(-1);
-       CC->usersupp.generation[CC->curr_rm]=CC->quickroom.QRgen;
+       CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
+
+       /* Know the room ... but not if it's the page log room */
+       if (strcasecmp(where, config.c_logpages)) {
+               vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
+               vbuf.v_flags = vbuf.v_flags | V_ACCESS;
+               }
+
+       CtdlSetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
        lputuser(&CC->usersupp,CC->curr_user);
 
        /* check for new mail */
        newmailcount = NewMailCount();
 
        /* set info to 1 if the user needs to read the room's info file */
-       if (CC->quickroom.QRinfo > CC->usersupp.lastseen[CC->curr_rm]) info = 1;
+       if (CC->quickroom.QRinfo > vbuf.v_lastseen) info = 1;
 
-       b=0; c=0;
        get_mm();
-       get_msglist(CC->curr_rm);
+       get_msglist(&CC->quickroom);
        for (a=0; a<CC->num_msgs; ++a) {
                if (MessageFromList(a)>0L) {
-                       ++b;
-                       if (MessageFromList(a)
-                          > CC->usersupp.lastseen[CC->curr_rm]) ++c;
+                       ++total_messages;
+                       if (MessageFromList(a) > vbuf.v_lastseen) {
+                               ++new_messages;
+                               }
                        }
                }
 
-
-       if (CC->curr_rm == 1) rmailflag = 1;
+       if (CC->quickroom.QRflags & QR_MAILBOX) rmailflag = 1;
        else rmailflag = 0;
 
        if ( (CC->quickroom.QRroomaide == CC->usersupp.usernum)
           || (CC->usersupp.axlevel>=6) )  raideflag = 1;
        else raideflag = 0;
 
+       strcpy(truncated_roomname, CC->quickroom.QRname);
+       if (CC->quickroom.QRflags & QR_MAILBOX) {
+               strcpy(truncated_roomname, &truncated_roomname[11]);
+               }
+
        if (display_result) cprintf("%d%c%s|%d|%d|%d|%d|%ld|%ld|%d|%d|%d|%d\n",
                OK,check_express(),
-               CC->quickroom.QRname,c,b,info,CC->quickroom.QRflags,
-               CC->quickroom.QRhighest,CC->usersupp.lastseen[CC->curr_rm],
+               truncated_roomname,
+               new_messages, total_messages,
+               info,CC->quickroom.QRflags,
+               CC->quickroom.QRhighest,
+               vbuf.v_lastseen,
                rmailflag,raideflag,newmailcount,CC->quickroom.QRfloor);
-       if (CC->quickroom.QRflags & QR_PRIVATE) {
-               set_wtmpsupp("<private room>");
-               }
-       else {
-               set_wtmpsupp(CC->quickroom.QRname);
-               }
+
+       set_wtmpsupp_to_current_room();
        }
 
 
@@ -557,9 +760,12 @@ void usergoto(int where, int display_result)
 void cmd_goto(char *gargs)
 {
        struct quickroom QRscratch;
-       int a,c;
-       int ok;
-       char bbb[20],towhere[32],password[20];
+       int c;
+       int ok = 0;
+       int ra;
+       char augmented_roomname[256];
+       char towhere[256];
+       char password[256];
 
        if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
                cprintf("%d not logged in\n",ERROR+NOT_LOGGED_IN);
@@ -569,68 +775,66 @@ void cmd_goto(char *gargs)
        extract(towhere,gargs,0);
        extract(password,gargs,1);
 
-       c=0;
-       getuser(&CC->usersupp,CC->curr_user);
-       for (a=0; a<MAXROOMS; ++a) {
-               getroom(&QRscratch,a);
-               if ((a==0)&&(!strcasecmp(towhere,"_BASEROOM_"))) {
-                       strncpy(towhere,QRscratch.QRname,31);
-                       }
-               if ((a==1)&&(!strcasecmp(towhere,"_MAIL_"))) {
-                       strncpy(towhere,QRscratch.QRname,31);
-                       }
-               if ((!strcasecmp(QRscratch.QRname,config.c_twitroom))
-                  &&(!strcasecmp(towhere,"_BITBUCKET_"))) {
-                       strncpy(towhere,QRscratch.QRname,31);
-                       }
-               strcpy(bbb,QRscratch.QRname);
-               ok = 0;
+       getuser(&CC->usersupp, CC->curr_user);
+
+       if (!strcasecmp(towhere, "_BASEROOM_"))
+               strcpy(towhere, BASEROOM);
+
+       if (!strcasecmp(towhere, "_MAIL_"))
+               strcpy(towhere, MAILROOM);
+
+       if (!strcasecmp(towhere, "_BITBUCKET_"))
+               strcpy(towhere, config.c_twitroom);
+
+
+       /* First try a regular match */
+       c = getroom(&QRscratch, towhere);
+
+       /* Then try a mailbox name match */
+       if (c != 0) {
+               MailboxName(augmented_roomname, &CC->usersupp, towhere);
+               c = getroom(&QRscratch, augmented_roomname);
+               if (c == 0) strcpy(towhere, augmented_roomname);
+               }
+
+       /* And if the room was found... */
+       if (c == 0) {
 
                /* let internal programs go directly to any room */
-               if (((CC->internal_pgm))&&(!strcasecmp(bbb,towhere))) {
-                       usergoto(a,1);
+               if (CC->internal_pgm) {
+                       usergoto(towhere, 1);
                        return;
                        }
 
-               /* normal clients have to pass through security */
-               if ( 
-                       (strcasecmp(bbb,towhere)==0)
-                       &&      ((QRscratch.QRflags&QR_INUSE)!=0)
-
-                       && (    ((QRscratch.QRflags&QR_PREFONLY)==0)
-                       ||      (CC->usersupp.axlevel>=5)
-                       )
-
-                       && (    (a!=2) || (CC->usersupp.axlevel>=6) )
-
-                       && (    ((QRscratch.QRflags&QR_PRIVATE)==0)
-                       || (QRscratch.QRflags&QR_GUESSNAME)
-                       || (CC->usersupp.axlevel>=6)
-                       || (QRscratch.QRflags&QR_PASSWORDED)
-                       ||      (QRscratch.QRgen==CC->usersupp.generation[a])
-                       )
-       
-                       ) ok = 1;
+               /* See if there is an existing user/room relationship */
+               ra = CtdlRoomAccess(&QRscratch, &CC->usersupp);
 
+               /* normal clients have to pass through security */
+               if (ra & UA_GOTOALLOWED) ok = 1;
 
                if (ok==1) {
-
                        if (  (QRscratch.QRflags&QR_PASSWORDED) &&
-                               (CC->usersupp.axlevel<6) &&
-                               (QRscratch.QRgen!=CC->usersupp.generation[a]) &&
+                               ((ra & UA_KNOWN) == 0) &&
                                (strcasecmp(QRscratch.QRpasswd,password))
                                ) {
                                        cprintf("%d wrong or missing passwd\n",
                                                ERROR+PASSWORD_REQUIRED);
                                        return;
                                        }
-
-                       usergoto(a,1);
-                       return;
+                       else if ( (QRscratch.QRflags&QR_PRIVATE) &&
+                                 ((QRscratch.QRflags&QR_PASSWORDED)==0) &&
+                                 ((QRscratch.QRflags&QR_GUESSNAME)==0) &&
+                                 ((ra & UA_KNOWN) == 0) ) {
+                                       goto NOPE;
+                               }
+                       else {
+                               usergoto(towhere, 1);
+                               return;
+                               }
                        }
-
                }
-       cprintf("%d room '%s' not found\n",ERROR+ROOM_NOT_FOUND,towhere);
+
+NOPE:  cprintf("%d room '%s' not found\n",ERROR+ROOM_NOT_FOUND,towhere);
        }
 
 
@@ -653,25 +857,13 @@ void cmd_whok(void) {
        cprintf("%d Who knows room:\n",LISTING_FOLLOWS);
        cdb_rewind(CDB_USERSUPP);
        while(cdbus = cdb_next_item(CDB_USERSUPP), cdbus != NULL) {
-               bzero(&temp, sizeof(struct usersupp));
+               memset(&temp, 0, sizeof(struct usersupp));
                memcpy(&temp, cdbus->ptr, cdbus->len);
                cdb_free(cdbus);
-               if ((CC->quickroom.QRflags & QR_INUSE)
-                       && ( (CC->curr_rm!=2) || (temp.axlevel>=6) )
-                       && (CC->quickroom.QRgen != (temp.forget[CC->curr_rm]) )
-
-                       && (    ((CC->quickroom.QRflags&QR_PREFONLY)==0)
-                       ||      (temp.axlevel>=5)
-                       )
 
-                       && (    ((CC->quickroom.QRflags&QR_PRIVATE)==0)
-                       ||      (temp.axlevel>=6)
-                       ||      (CC->quickroom.QRgen==(temp.generation[CC->curr_rm]))
-                       )
-
-                       && (strncmp(temp.fullname,"000",3))
-
-               ) cprintf("%s\n",temp.fullname);
+               if ( (CC->quickroom.QRflags & QR_INUSE)
+                       && (CtdlRoomAccess(&CC->quickroom, &temp) & UA_KNOWN)
+                  ) cprintf("%s\n",temp.fullname);
                }
        cprintf("000\n");
        }
@@ -692,8 +884,8 @@ void cmd_rdir(void) {
                return;
                }
 
-       getroom(&CC->quickroom,CC->curr_rm);
-       getuser(&CC->usersupp,CC->curr_user);
+       getroom(&CC->quickroom, CC->quickroom.QRname);
+       getuser(&CC->usersupp, CC->curr_user);
 
        if ((CC->quickroom.QRflags & QR_DIRECTORY) == 0) {
                cprintf("%d not here.\n",ERROR+NOT_HERE);
@@ -760,19 +952,20 @@ void cmd_getr(void) {
                return;
                }
 
-       if (CC->curr_rm < 3) {
+       if (is_noneditable(&CC->quickroom)) {
                cprintf("%d Can't edit this room.\n",ERROR+NOT_HERE);
                return;
                }
 
-       getroom(&CC->quickroom,CC->curr_rm);
-       cprintf("%d%c%s|%s|%s|%d|%d\n",
+       getroom(&CC->quickroom, CC->quickroom.QRname);
+       cprintf("%d%c%s|%s|%s|%d|%d|%d\n",
                OK,check_express(),
                CC->quickroom.QRname,
                ((CC->quickroom.QRflags & QR_PASSWORDED) ? CC->quickroom.QRpasswd : ""),
                ((CC->quickroom.QRflags & QR_DIRECTORY) ? CC->quickroom.QRdirname : ""),
                CC->quickroom.QRflags,
-               (int)CC->quickroom.QRfloor);
+               (int)CC->quickroom.QRfloor,
+               (int)CC->quickroom.QRorder);
        }
 
 
@@ -782,7 +975,9 @@ void cmd_getr(void) {
 void cmd_setr(char *args) {
        char buf[256];
        struct floor flbuf;
+       char old_name[ROOMNAMELEN];
        int old_floor;
+       int new_order = 0;
 
        if (!(CC->logged_in)) {
                cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
@@ -795,7 +990,7 @@ void cmd_setr(char *args) {
                return;
                }
 
-       if (CC->curr_rm < 3) {
+       if (is_noneditable(&CC->quickroom)) {
                cprintf("%d Can't edit this room.\n",ERROR+NOT_HERE);
                return;
                }
@@ -809,14 +1004,23 @@ void cmd_setr(char *args) {
                        }
                }
 
-       lgetroom(&CC->quickroom,CC->curr_rm);
-       extract(buf,args,0); buf[20]=0;
-       strncpy(CC->quickroom.QRname,buf,19);
+       if (num_parms(args)>=7) {
+               new_order = extract_int(args, 6);
+               if (new_order < 1) new_order = 1;
+               if (new_order > 127) new_order = 127;
+               }
+
+       lgetroom(&CC->quickroom, CC->quickroom.QRname);
+       strcpy(old_name, CC->quickroom.QRname);
+       extract(buf,args,0); buf[ROOMNAMELEN]=0;
+       strncpy(CC->quickroom.QRname,buf,ROOMNAMELEN-1);
        extract(buf,args,1); buf[10]=0;
        strncpy(CC->quickroom.QRpasswd,buf,9);
        extract(buf,args,2); buf[15]=0;
        strncpy(CC->quickroom.QRdirname,buf,19);
        CC->quickroom.QRflags = ( extract_int(args,3) | QR_INUSE);
+       if (num_parms(args)>=7)
+               CC->quickroom.QRorder = (char)new_order;
 
        /* Clean up a client boo-boo: if the client set the room to
         * guess-name or passworded, ensure that the private flag is
@@ -826,10 +1030,11 @@ void cmd_setr(char *args) {
           ||(CC->quickroom.QRflags & QR_PASSWORDED))
                CC->quickroom.QRflags |= QR_PRIVATE;
 
-       /* Kick everyone out if the client requested it */
+       /* Kick everyone out if the client requested it (by changing the
+        * room's generation number)
+        */
        if (extract_int(args,4)) {
-               ++CC->quickroom.QRgen;
-               if (CC->quickroom.QRgen==100) CC->quickroom.QRgen=1;
+               time(&CC->quickroom.QRgen);
                }
 
        old_floor = CC->quickroom.QRfloor;
@@ -837,7 +1042,15 @@ void cmd_setr(char *args) {
                CC->quickroom.QRfloor = extract_int(args,5);
                }
 
-       lputroom(&CC->quickroom,CC->curr_rm);
+       /* Write the room record back to disk */
+       lputroom(&CC->quickroom, CC->quickroom.QRname);
+
+       /* If the room name changed, then there are now two room records,
+        * so we have to delete the old one.
+        */
+       if (strcasecmp(CC->quickroom.QRname, old_name)) {
+               putroom(NULL, old_name);
+               }
 
        /* adjust the floor reference counts */
        lgetfloor(&flbuf,old_floor);
@@ -873,8 +1086,8 @@ void cmd_geta(void) {
                return;
                }
 
-       if (CC->curr_rm < 0) {
-               cprintf("%d No current room.\n",ERROR);
+       if (is_noneditable(&CC->quickroom)) {
+               cprintf("%d Can't edit this room.\n",ERROR+NOT_HERE);
                return;
                }
 
@@ -908,11 +1121,6 @@ void cmd_seta(char *new_ra)
                return;
                }
 
-       if (CC->curr_rm < 3) {
-               cprintf("%d Can't edit this room.\n",ERROR+NOT_HERE);
-               return;
-               }
-
        if (getuser(&usbuf,new_ra)!=0) {
                newu = (-1L);
                }
@@ -920,13 +1128,13 @@ void cmd_seta(char *new_ra)
                newu = usbuf.usernum;
                }
 
-       lgetroom(&CC->quickroom,CC->curr_rm);
+       lgetroom(&CC->quickroom, CC->quickroom.QRname);
        post_notice = 0;
        if (CC->quickroom.QRroomaide != newu) {
                post_notice = 1;
                }
        CC->quickroom.QRroomaide = newu;
-       lputroom(&CC->quickroom,CC->curr_rm);
+       lputroom(&CC->quickroom, CC->quickroom.QRname);
 
        /*
         * We have to post the change notice _after_ writing changes to 
@@ -940,16 +1148,22 @@ void cmd_seta(char *new_ra)
        cprintf("%d Ok\n",OK);
        }
 
+/*
+ * Generate an associated file name for a room
+ */
+void assoc_file_name(char *buf, struct quickroom *qrbuf, char *prefix) {
+       sprintf(buf, "./%s/%ld", prefix, qrbuf->QRnumber);
+       }
 
 /* 
  * retrieve info file for this room
  */
 void cmd_rinf(void) {
-       char filename[64];
+       char filename[128];
        char buf[256];
        FILE *info_fp;
        
-       sprintf(filename,"./info/%d",CC->curr_rm);
+       assoc_file_name(filename, &CC->quickroom, "info");
        info_fp = fopen(filename,"r");
 
        if (info_fp==NULL) {
@@ -967,15 +1181,58 @@ void cmd_rinf(void) {
        }
 
 /*
- * aide command: kill the current room
+ * Back end processing to delete a room and everything associated with it
  */
-void cmd_kill(char *argbuf)
-{
+void delete_room(struct quickroom *qrbuf) {
+       struct floor flbuf;
+       long MsgToDelete;
        char aaa[100];
        int a;
+
+       lprintf(9, "Deleting room <%s>\n", qrbuf->QRname);
+
+       /* Delete the info file */
+       assoc_file_name(aaa, qrbuf, "info");
+       unlink(aaa);
+
+       /* Delete the image file */
+       assoc_file_name(aaa, qrbuf, "images");
+       unlink(aaa);
+
+       /* first flag the room record as not in use */
+       lgetroom(qrbuf, qrbuf->QRname);
+       qrbuf->QRflags=0;
+
+       /* then delete the messages in the room */
+       get_msglist(qrbuf);
+       if (CC->num_msgs > 0) for (a=0; a < CC->num_msgs; ++a) {
+               MsgToDelete = MessageFromList(a);
+               cdb_delete(CDB_MSGMAIN, &MsgToDelete, sizeof(long));
+               }
+       put_msglist(qrbuf);
+       phree(CC->msglist);
+       CC->msglist = NULL;
+       CC->num_msgs = 0;
+       delete_msglist(qrbuf);
+       lputroom(qrbuf, qrbuf->QRname);
+
+       /* then decrement the reference count for the floor */
+       lgetfloor(&flbuf,(int)(qrbuf->QRfloor));
+       flbuf.f_ref_count = flbuf.f_ref_count - 1;
+       lputfloor(&flbuf,(int)(qrbuf->QRfloor));
+
+       /* Delete the room record from the database! */
+       putroom(NULL, qrbuf->QRname);
+       }
+
+
+/*
+ * aide command: kill the current room
+ */
+void cmd_kill(char *argbuf) {
+       char aaa[100];
+       char deleted_room_name[ROOMNAMELEN];
        int kill_ok;
-       struct floor flbuf;
-       long MsgToDelete;
        
        kill_ok = extract_int(argbuf,0);
 
@@ -990,41 +1247,21 @@ void cmd_kill(char *argbuf)
                return;
                }
 
-       if (CC->curr_rm < 3) {
-               cprintf("%d Can't kill this room.\n",ERROR+NOT_HERE);
+       if (is_noneditable(&CC->quickroom)) {
+               cprintf("%d Can't edit this room.\n",ERROR+NOT_HERE);
                return;
                }
 
        if (kill_ok) {
-
-               /* first flag the room record as not in use */
-               lgetroom(&CC->quickroom,CC->curr_rm);
-               CC->quickroom.QRflags=0;
-
-               /* then delete the messages in the room */
-               get_msglist(CC->curr_rm);
-               if (CC->num_msgs > 0) for (a=0; a < CC->num_msgs; ++a) {
-                       MsgToDelete = MessageFromList(a);
-                       cdb_delete(CDB_MSGMAIN, &MsgToDelete, sizeof(long));
-                       }
-               put_msglist(CC->curr_rm);
-               free(CC->msglist);
-               CC->num_msgs = 0;
-               cdb_delete(CDB_MSGLISTS, &CC->curr_rm, sizeof(int));
-
-               lputroom(&CC->quickroom,CC->curr_rm);
-
-
-               /* then decrement the reference count for the floor */
-               lgetfloor(&flbuf,(int)CC->quickroom.QRfloor);
-               flbuf.f_ref_count = flbuf.f_ref_count - 1;
-               lputfloor(&flbuf,(int)CC->quickroom.QRfloor);
+               strcpy(deleted_room_name, CC->quickroom.QRname);
+               delete_room(&CC->quickroom);    /* Do the dirty work */
+               usergoto(BASEROOM, 0);          /* Return to the Lobby */
 
                /* tell the world what we did */
-               sprintf(aaa,"%s> killed by %s",CC->quickroom.QRname,CC->curr_user);
+               sprintf(aaa,"%s> killed by %s",
+                       deleted_room_name, CC->curr_user);
                aide_message(aaa);
-               CC->curr_rm=(-1);
-               cprintf("%d '%s' deleted.\n",OK,CC->quickroom.QRname);
+               cprintf("%d '%s' deleted.\n", OK, deleted_room_name);
                }
        else {
                cprintf("%d ok to delete.\n",OK);
@@ -1033,49 +1270,48 @@ void cmd_kill(char *argbuf)
 
 
 /*
- * Find a free slot to create a new room in, or return -1 for error.
- * search_dir is the direction to search in.  1 causes this function to
- * return the first available slot, -1 gets the last available slot.
+ * Internal code to create a new room (returns room flags)
+ *
+ * Room types: 0=public, 1=guessname, 2=passworded, 3=inv-only, 4=mailbox
  */
-int get_free_room_slot(int search_dir)
-{
-       int a,st;
-       struct quickroom qrbuf;
-
-       st = ((search_dir>0) ? 3 : (MAXROOMS-1));
-
-       for (a=st; ((a<MAXROOMS)&&(a>=3)); a=a+search_dir) {
-               getroom(&qrbuf,a);
-               if ((qrbuf.QRflags & QR_INUSE)==0) return(a);
-               }
-       return(-1);
-       }
-
+unsigned create_room(char *new_room_name,
+                       int new_room_type,
+                       char *new_room_pass,
+                       int new_room_floor) {
 
-/*
- * internal code to create a new room (returns room flags)
- */
-unsigned create_room(int free_slot, char *new_room_name, int new_room_type, char *new_room_pass, int new_room_floor)
-{
        struct quickroom qrbuf;
        struct floor flbuf;
+       struct visit vbuf;
 
-       lgetroom(&qrbuf,free_slot);
-       strncpy(qrbuf.QRname,new_room_name,19);
+       if (getroom(&qrbuf, new_room_name)==0) return(0); /* already exists */
+
+       memset(&qrbuf, 0, sizeof(struct quickroom));
+       strncpy(qrbuf.QRname,new_room_name,ROOMNAMELEN);
        strncpy(qrbuf.QRpasswd,new_room_pass,9);
        qrbuf.QRflags = QR_INUSE;
+       qrbuf.QRnumber = get_new_room_number();
        if (new_room_type > 0) qrbuf.QRflags=(qrbuf.QRflags|QR_PRIVATE);
        if (new_room_type == 1) qrbuf.QRflags=(qrbuf.QRflags|QR_GUESSNAME);
        if (new_room_type == 2) qrbuf.QRflags=(qrbuf.QRflags|QR_PASSWORDED);
-       qrbuf.QRroomaide = (-1L);
-       if ((new_room_type > 0)&&(CREATAIDE==1))
+       if (new_room_type == 4) qrbuf.QRflags=(qrbuf.QRflags|QR_MAILBOX);
+
+       /* If the room is private, and the system administrator has elected
+        * to automatically grant room aide privileges, do so now; otherwise,
+        * set the room aide to undefined.
+        */
+       if ( (qrbuf.QRflags & QR_PRIVATE) && (CREATAIDE==1) ) {
                qrbuf.QRroomaide=CC->usersupp.usernum;
-       qrbuf.QRhighest = 0L;
-       ++qrbuf.QRgen; if (qrbuf.QRgen>=126) qrbuf.QRgen=10;
+               }
+       else {
+               qrbuf.QRroomaide = (-1L);
+               }
+
+       qrbuf.QRhighest = 0L;   /* No messages in this room yet */
+       time(&qrbuf.QRgen);     /* Use a timestamp as the generation number */
        qrbuf.QRfloor = new_room_floor;
 
        /* save what we just did... */
-       lputroom(&qrbuf,free_slot);
+       putroom(&qrbuf, qrbuf.QRname);
 
        /* bump the reference count on whatever floor the room is on */
        lgetfloor(&flbuf,(int)qrbuf.QRfloor);
@@ -1084,8 +1320,10 @@ unsigned create_room(int free_slot, char *new_room_name, int new_room_type, char
 
        /* be sure not to kick the creator out of the room! */
        lgetuser(&CC->usersupp,CC->curr_user);
-       CC->usersupp.generation[free_slot] = qrbuf.QRgen;
-       CC->usersupp.forget[free_slot] = (-1);
+       CtdlGetRelationship(&vbuf, &CC->usersupp, &qrbuf);
+       vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
+       vbuf.v_flags = vbuf.v_flags | V_ACCESS;
+       CtdlSetRelationship(&vbuf, &CC->usersupp, &qrbuf);
        lputuser(&CC->usersupp,CC->curr_user);
 
        /* resume our happy day */
@@ -1099,8 +1337,6 @@ unsigned create_room(int free_slot, char *new_room_name, int new_room_type, char
 void cmd_cre8(char *args)
 {
        int cre8_ok;
-       int free_slot;
-       int a;
        char new_room_name[256];
        int new_room_type;
        char new_room_pass[256];
@@ -1112,7 +1348,7 @@ void cmd_cre8(char *args)
 
        cre8_ok = extract_int(args,0);
        extract(new_room_name,args,1);
-       new_room_name[19] = 0;
+       new_room_name[ROOMNAMELEN-1] = 0;
        new_room_type = extract_int(args,2);
        extract(new_room_pass,args,3);
        new_room_pass[9] = 0;
@@ -1123,6 +1359,12 @@ void cmd_cre8(char *args)
                return;
                }
 
+       if (!strcasecmp(new_room_name, MAILROOM)) {
+               cprintf("%d '%s' already exists.\n",
+                       ERROR+ALREADY_EXISTS, new_room_name);
+               return;
+               }
+
        if (num_parms(args)>=5) {
                getfloor(&flbuf,extract_int(args,4));
                if ((flbuf.f_flags & F_INUSE) == 0) {
@@ -1140,40 +1382,35 @@ void cmd_cre8(char *args)
                return;
                }
 
-       if (CC->usersupp.axlevel<3) {
+       if (CC->usersupp.axlevel < config.c_createax) {
                cprintf("%d You need higher access to create rooms.\n",
                        ERROR+HIGHER_ACCESS_REQUIRED);
                return;
                }
 
-       free_slot = get_free_room_slot(1);
-       if (free_slot<0) {
-               cprintf("%d There is no space available for a new room.\n",
-                       ERROR);
+       if ((strlen(new_room_name)==0) && (cre8_ok==0)) {
+               cprintf("%d Ok to create rooms.\n", OK);
                return;
                }
 
-       if (cre8_ok==0) {
-               cprintf("%d ok to create...\n",OK);
+       /* Check to make sure the requested room name doesn't already exist */
+       if (getroom(&qrbuf, new_room_name)==0) {
+               cprintf("%d '%s' already exists.\n",
+                       ERROR+ALREADY_EXISTS, qrbuf.QRname);
                return;
                }
 
-       for (a=0; a<MAXROOMS; ++a) {
-               getroom(&qrbuf,a);
-               if ( (!strcasecmp(qrbuf.QRname,new_room_name))
-                  && (qrbuf.QRflags & QR_INUSE) ) {
-                       cprintf("%d '%s' already exists.\n",
-                               ERROR,qrbuf.QRname);
-                       return;
-                       }
-               }
-
        if ((new_room_type < 0) || (new_room_type > 3)) {
                cprintf("%d Invalid room type.\n",ERROR);
                return;
                }
 
-       newflags = create_room(free_slot,new_room_name,
+       if (cre8_ok == 0) {
+               cprintf("%d OK to create '%s'\n", OK, new_room_name);
+               return;
+               }
+
+       newflags = create_room(new_room_name,
                        new_room_type,new_room_pass,new_room_floor);
 
        /* post a message in Aide> describing the new room */
@@ -1188,11 +1425,6 @@ void cmd_cre8(char *args)
                }
        aide_message(aaa); 
 
-       sprintf(aaa,"./info/%d",free_slot);     /* delete old info file */
-       unlink(aaa);    
-       sprintf(aaa,"./images/room.%d.gif",free_slot);  /* and picture */
-       unlink(aaa);    
-
        cprintf("%d '%s' has been created.\n",OK,qrbuf.QRname);
        }
 
@@ -1201,7 +1433,7 @@ void cmd_cre8(char *args)
 void cmd_einf(char *ok)
 {      /* enter info file for current room */
        FILE *fp;
-       char infofilename[32];
+       char infofilename[256];
        char buf[256];
 
        if (!(CC->logged_in)) {
@@ -1220,11 +1452,18 @@ void cmd_einf(char *ok)
                return;
                }
 
-       cprintf("%d Send info...\n",SEND_LISTING);
+       assoc_file_name(infofilename, &CC->quickroom, "info");
+       lprintf(9, "opening\n");
+       fp = fopen(infofilename, "w");
+       lprintf(9, "checking\n");
+       if (fp == NULL) {
+               cprintf("%d Cannot open %s: %s\n",
+                       ERROR+INTERNAL_ERROR, infofilename, strerror(errno));
+               return;
+               }
 
-       sprintf(infofilename,"./info/%d",CC->curr_rm);
+       cprintf("%d Send info...\n", SEND_LISTING);
 
-       fp=fopen(infofilename,"w");
        do {
                client_gets(buf);
                if (strcmp(buf,"000")) fprintf(fp,"%s\n",buf);
@@ -1232,9 +1471,9 @@ void cmd_einf(char *ok)
        fclose(fp);
 
        /* now update the room index so people will see our new info */
-       lgetroom(&CC->quickroom,CC->curr_rm);   /* lock so no one steps on us */
+       lgetroom(&CC->quickroom,CC->quickroom.QRname); /* lock so no one steps on us */
        CC->quickroom.QRinfo = CC->quickroom.QRhighest + 1L;
-       lputroom(&CC->quickroom,CC->curr_rm);
+       lputroom(&CC->quickroom,CC->quickroom.QRname);
        }