* Holy war on strlen: use IsEmptyStr where apropriate.
[citadel.git] / citadel / citadel.c
index 22171d57a5ae8967fbfbf4abb96684410f2c4cae..920f06cc402b1ce1537991109cdfb85f2cf71d39 100644 (file)
 #include "client_passwords.h"
 #include "citadel_decls.h"
 #include "tools.h"
-#include "acconfig.h"
+#include "sysdep.h"
 #ifndef HAVE_SNPRINTF
 #include "snprintf.h"
 #endif
 #include "screen.h"
+#include "citadel_dirs.h"
 
 #include "md5.h"
 
@@ -58,6 +59,8 @@
 #define IFAIDE if (axlevel>=6)
 #define IFNAIDE if (axlevel<6)
 
+int rordercmp(struct ctdlroomlisting *r1, struct ctdlroomlisting *r2);
+
 struct march *march = NULL;
 
 /* globals associated with the client program */
@@ -71,6 +74,7 @@ char fullname[USERNAME_SIZE];
 int screenwidth;
 int screenheight;
 unsigned room_flags;
+unsigned room_flags2;
 char room_name[ROOMNAMELEN];
 char *uglist[UGLISTLEN]; /* size of the ungoto list */
 long uglistlsn[UGLISTLEN]; /* current read position for all the ungoto's. Not going to make any friends with this one. */
@@ -100,7 +104,7 @@ int termn8 = 0;                     /* Set to nonzero to cause a logoff */
 int secure;                    /* Set to nonzero when wire is encrypted */
 int can_do_msg4 = 0;           /* Set to nonzero if the server can handle MSG4 commands */
 
-extern char express_msgs;      /* express messages waiting! */
+extern char instant_msgs;      /* instant messages waiting! */
 extern int rc_ansi_color;      /* ansi color value from citadel.rc */
 extern int next_lazy_cmd;
 
@@ -109,7 +113,7 @@ CtdlIPC *ipc_for_signal_handlers;   /* KLUDGE cover your eyes */
 /*
  * here is our 'clean up gracefully and exit' routine
  */
-void logoff(CtdlIPC *ipc, int code)
+void ctdl_logoff(char *file, int line, CtdlIPC *ipc, int code)
 {
        int lp;
 
@@ -147,7 +151,12 @@ void logoff(CtdlIPC *ipc, int code)
                kill(0 - getpgrp(), SIGKILL);
        }
        color(ORIGINAL_PAIR);   /* Restore the old color settings */
-       sttybbs(SB_RESTORE);    /* return the old terminal settings */
+       stty_ctdl(SB_RESTORE);  /* return the old terminal settings */
+       /* 
+        * uncomment the following if you need to know why Citadel exited
+       printf("*** Exit code %d at %s:%d\n", code, file, line);
+       sleep(2);
+        */
        exit(code);             /* exit with the proper exit code */
 }
 
@@ -169,7 +178,7 @@ void dropcarr(int signum)
  */
 void catch_sigcont(int signum)
 {
-       sttybbs(SB_LAST);
+       stty_ctdl(SB_LAST);
        signal(SIGCONT, catch_sigcont);
 }
 
@@ -201,12 +210,12 @@ void userlist(CtdlIPC *ipc, char *patn)
 {
        char buf[SIZ];
        char fl[SIZ];
-       struct tm *tmbuf;
+       struct tm tmbuf;
        time_t lc;
        int r;                          /* IPC response code */
        char *listing = NULL;
 
-       r = CtdlIPCUserListing(ipc, &listing, buf);
+       r = CtdlIPCUserListing(ipc, patn, &listing, buf);
        if (r / 100 != 1) {
                pprintf("%s\n", buf);
                return;
@@ -214,22 +223,22 @@ void userlist(CtdlIPC *ipc, char *patn)
 
        pprintf("       User Name           Num  L  LastCall  Calls Posts\n");
        pprintf("------------------------- ----- - ---------- ----- -----\n");
-       while (strlen(listing) > 0) {
-               extract_token(buf, listing, 0, '\n');
+       if (listing != NULL) while (!IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
                remove_token(listing, 0, '\n');
 
                if (sigcaught == 0) {
-                   extract(fl, buf, 0);
+                   extract_token(fl, buf, 0, '|', sizeof fl);
                    if (pattern(fl, patn) >= 0) {
                        pprintf("%-25s ", fl);
                        pprintf("%5ld %d ", extract_long(buf, 2),
                               extract_int(buf, 1));
                        lc = extract_long(buf, 3);
-                       tmbuf = (struct tm *) localtime(&lc);
+                       localtime_r(&lc, &tmbuf);
                        pprintf("%02d/%02d/%04d ",
-                              (tmbuf->tm_mon + 1),
-                              tmbuf->tm_mday,
-                              (tmbuf->tm_year + 1900));
+                              (tmbuf.tm_mon + 1),
+                              tmbuf.tm_mday,
+                              (tmbuf.tm_year + 1900));
                        pprintf("%5ld %5ld\n", extract_long(buf, 4), extract_long(buf, 5));
                    }
 
@@ -245,7 +254,7 @@ void userlist(CtdlIPC *ipc, char *patn)
  */
 void load_user_info(char *params)
 {
-       extract(fullname, params, 0);
+       extract_token(fullname, params, 0, '|', sizeof fullname);
        axlevel = extract_int(params, 1);
        timescalled = extract_int(params, 2);
        posted = extract_int(params, 3);
@@ -295,7 +304,7 @@ void remove_march(char *roomname, int floornum)
  * Locate the room on the march list which we most want to go to.  Each room
  * is measured given a "weight" of preference based on various factors.
  */
-char *pop_march(int desired_floor)
+char *pop_march(int desired_floor, struct march *_march)
 {
        static char TheRoom[ROOMNAMELEN];
        int TheFloor = 0;
@@ -305,10 +314,10 @@ char *pop_march(int desired_floor)
        struct march *mptr = NULL;
 
        strcpy(TheRoom, "_BASEROOM_");
-       if (march == NULL)
+       if (_march == NULL)
                return (TheRoom);
 
-       for (mptr = march; mptr != NULL; mptr = mptr->next) {
+       for (mptr = _march; mptr != NULL; mptr = mptr->next) {
                weight = 0;
                if ((strcasecmp(mptr->march_name, "_BASEROOM_")))
                        weight = weight + 10000;
@@ -411,11 +420,10 @@ void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto)
                        }
                }
 
-               if (strlen(bbb) == 0) {
+               if (IsEmptyStr(bbb)) {
                        scr_printf("No room '%s'.\n", towhere);
                        return;
                }
-               room = NULL;
                r = CtdlIPCGotoRoom(ipc, bbb, "", &room, aaa);
        }
        if (r / 100 != 1 && r / 100 != 2) {
@@ -424,6 +432,7 @@ void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto)
        }
        safestrncpy(room_name, room->RRname, ROOMNAMELEN);
        room_flags = room->RRflags;
+       room_flags2 = room->RRflags2;
        from_floor = curr_floor;
        curr_floor = room->RRfloor;
 
@@ -475,12 +484,13 @@ void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto)
                                        newmailcount);
                }
                color(DIM_WHITE);
-               if (strlen(rc_gotmail_cmd) > 0) {
+               if (!IsEmptyStr(rc_gotmail_cmd)) {
                        system(rc_gotmail_cmd);
                }
        }
-       status_line(ipc->ServInfo.humannode, ipc->ServInfo.bbs_city,
+       status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location,
                        room_name, secure, newmailcount);
+       free(room);
 }
 
 /* Goto next room having unread messages.
@@ -523,7 +533,7 @@ void gotonext(CtdlIPC *ipc)
                remove_march(room_name, 0);
        }
        if (march != NULL) {
-               strcpy(next_room, pop_march(curr_floor));
+               strcpy(next_room, pop_march(curr_floor, march));
        } else {
                strcpy(next_room, "_BASEROOM_");
        }
@@ -537,28 +547,33 @@ void gotonext(CtdlIPC *ipc)
 void forget_all_rooms_on(CtdlIPC *ipc, int ffloor)
 {
        char buf[SIZ];
-       struct march *flist, *fptr;
-       struct ctdlipcroom *room;       /* Ignored */
+       struct march *flist = NULL;
+       struct march *fptr = NULL;
+       struct ctdlipcroom *room = NULL;
        int r;                          /* IPC response code */
 
-       scr_printf("Forgetting all rooms on %s...\r", &floorlist[ffloor][0]);
+       scr_printf("Forgetting all rooms on %s...\n", &floorlist[ffloor][0]);
        scr_flush();
        remove_march("_FLOOR_", ffloor);
        r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, ffloor, &flist, buf);
        if (r / 100 != 1) {
-               scr_printf("%-72s\n", buf);
+               scr_printf("Error %d: %s\n", r, buf);
                return;
        }
        while (flist) {
                r = CtdlIPCGotoRoom(ipc, flist->march_name, "", &room, buf);
                if (r / 100 == 2) {
                        r = CtdlIPCForgetRoom(ipc, buf);
+                       if (r / 100 != 2) {
+                               scr_printf("Error %d: %s\n", r, buf);
+                       }
+
                }
                fptr = flist;
                flist = flist->next;
                free(fptr);
        }
-       scr_printf("%-72s\r", "");
+       if (room) free(room);
 }
 
 
@@ -571,15 +586,15 @@ void gf_toroom(CtdlIPC *ipc, char *towhere, int mode)
 
        floor_being_left = curr_floor;
 
-       if (mode == GF_GOTO) {  /* <;G>oto mode */
+       if (mode == GF_GOTO) {          /* <;G>oto mode */
                updatels(ipc);
                dotgoto(ipc, towhere, 1, 0);
        }
-       if (mode == GF_SKIP) {  /* <;S>kip mode */
+       else if (mode == GF_SKIP) {     /* <;S>kip mode */
                dotgoto(ipc, towhere, 1, 0);
                remove_march("_FLOOR_", floor_being_left);
        }
-       if (mode == GF_ZAP) {   /* <;Z>ap mode */
+       else if (mode == GF_ZAP) {      /* <;Z>ap mode */
                dotgoto(ipc, towhere, 1, 0);
                remove_march("_FLOOR_", floor_being_left);
                forget_all_rooms_on(ipc, floor_being_left);
@@ -621,9 +636,10 @@ void gotofloor(CtdlIPC *ipc, char *towhere, int mode)
                return;
        }
        for (mptr = march; mptr != NULL; mptr = mptr->next) {
-               if ((mptr->march_floor) == tofloor)
+               if ((mptr->march_floor) == tofloor) {
                        gf_toroom(ipc, mptr->march_name, mode);
-               return;
+                       return;
+               }
        }
 
        /* Find first known room on the floor */
@@ -634,16 +650,16 @@ void gotofloor(CtdlIPC *ipc, char *towhere, int mode)
        if (r / 100 == 1) {
                struct march *tmp = mptr;
 
-               /* TODO: room order is being ignored? */
+               /*. . . according to room order */
                if (mptr)
-                       strncpy(targ, mptr->march_name, ROOMNAMELEN);
+           strcpy(targ, pop_march(tofloor, mptr));
                while (mptr) {
                        tmp = mptr->next;
                        free(mptr);
                        mptr = tmp;
                }
        }
-       if (strlen(targ) > 0) {
+       if (!IsEmptyStr(targ)) {
                gf_toroom(ipc, targ, mode);
                return;
        }
@@ -655,40 +671,349 @@ void gotofloor(CtdlIPC *ipc, char *towhere, int mode)
        r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, tofloor, &mptr, buf);
        if (r / 100 == 1) {
                struct march *tmp = mptr;
-
-               /* TODO: room order is being ignored? */
+               
+        /*. . . according to room order */
                if (mptr)
-                       strncpy(targ, mptr->march_name, ROOMNAMELEN);
+                       strcpy(targ, pop_march(tofloor, mptr));
                while (mptr) {
                        tmp = mptr->next;
                        free(mptr);
                        mptr = tmp;
                }
        }
-       if (strlen(targ) > 0) {
+       if (!IsEmptyStr(targ)) {
                gf_toroom(ipc, targ, mode);
        } else {
                scr_printf("There are no rooms on '%s'.\n", &floorlist[tofloor][0]);
        }
 }
 
+/*
+ * Indexing mechanism for a room list, called by gotoroomstep()
+ */
+void room_tree_list_query(struct ctdlroomlisting *rp, char *findrmname, int findrmslot, char *rmname, int *rmslot, int *rmtotal)
+{
+       char roomname[ROOMNAMELEN];
+       static int cur_rmslot = 0;
+
+       if (rp == NULL) {
+               cur_rmslot = 0;
+               return;
+       }
+
+       if (rp->lnext != NULL) {
+               room_tree_list_query(rp->lnext, findrmname, findrmslot, rmname, rmslot, rmtotal);
+       }
+
+       if (sigcaught == 0) {
+               strcpy(roomname, rp->rlname);
+
+               if (rmname != NULL) {
+                       if (cur_rmslot == findrmslot) {
+                               strcpy(rmname, roomname);
+                       }
+               }
+               if (rmslot != NULL) {
+                       if (!strcmp(roomname, findrmname)) {
+                               *rmslot = cur_rmslot;
+                       }
+               }
+               cur_rmslot++;
+       }
+
+       if (rp->rnext != NULL) {
+               room_tree_list_query(rp->rnext, findrmname, findrmslot, rmname, rmslot, rmtotal);
+       }
+
+       if ((rmname == NULL) && (rmslot == NULL))
+               free(rp);
+
+       if (rmtotal != NULL) {
+               *rmtotal = cur_rmslot;
+       }
+}
+
+/*
+ * step through rooms on current floor
+ */
+void  gotoroomstep(CtdlIPC *ipc, int direction, int mode)
+{
+       struct march *listing = NULL;
+       struct march *mptr;
+       int r;          /* IPC response code */
+       char buf[SIZ];
+       struct ctdlroomlisting *rl = NULL;
+       struct ctdlroomlisting *rp;
+       struct ctdlroomlisting *rs;
+       int list_it;
+       char rmname[ROOMNAMELEN];
+       int rmslot;
+       int rmtotal;
+
+       /* Ask the server for a room list */
+       r = CtdlIPCKnownRooms(ipc, SubscribedRooms, (-1), &listing, buf);
+       if (r / 100 != 1) {
+               listing = NULL;
+       }
+
+       load_floorlist(ipc);
+
+       for (mptr = listing; mptr != NULL; mptr = mptr->next) {
+               list_it = 1;
+
+               if ( floor_mode 
+                        && (mptr->march_floor != curr_floor))
+                       list_it = 0;
+
+               if (list_it) {
+                       rp = malloc(sizeof(struct ctdlroomlisting));
+                       strncpy(rp->rlname, mptr->march_name, ROOMNAMELEN);
+                       rp->rlflags = mptr->march_flags;
+                       rp->rlfloor = mptr->march_floor;
+                       rp->rlorder = mptr->march_order;
+                       rp->lnext = NULL;
+                       rp->rnext = NULL;
+
+                       rs = rl;
+                       if (rl == NULL) {
+                               rl = rp;
+                       } else {
+                               while (rp != NULL) {
+                                       if (rordercmp(rp, rs) < 0) {
+                                               if (rs->lnext == NULL) {
+                                                       rs->lnext = rp;
+                                                       rp = NULL;
+                                               } else {
+                                                       rs = rs->lnext;
+                                               }
+                                       } else {
+                                               if (rs->rnext == NULL) {
+                                                       rs->rnext = rp;
+                                                       rp = NULL;
+                                               } else {
+                                                       rs = rs->rnext;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /* Find position of current room */
+       room_tree_list_query(NULL, NULL, 0, NULL, NULL, NULL);
+       room_tree_list_query(rl,  room_name, 0, NULL, &rmslot, &rmtotal);
+
+       if (direction == 0) { /* Previous room */
+               /* If we're at the first room, wrap to the last room */
+               if (rmslot == 0) {
+                       rmslot = rmtotal - 1;
+               } else {
+                       rmslot--;
+               }
+       } else {                 /* Next room */
+               /* If we're at the last room, wrap to the first room */
+               if (rmslot == rmtotal - 1) {
+                       rmslot = 0; 
+               } else {
+                       rmslot++;
+               }
+       }
+
+       /* Get name of next/previous room */
+       room_tree_list_query(NULL, NULL, 0, NULL, NULL, NULL);
+       room_tree_list_query(rl,  NULL, rmslot, rmname, NULL, NULL);
+
+       /* Free the tree */
+       room_tree_list_query(rl, NULL, 0, NULL, NULL, NULL);
+
+       if (mode == 0) { /* not skipping */
+           updatels(ipc);
+       } else {
+               if (rc_alt_semantics) {
+               updatelsa(ipc);
+               }
+       }
+
+       /* Free the room list */
+       while (listing) {
+               mptr = listing->next;
+               free(listing);
+               listing = mptr;
+       };
+
+       dotgoto(ipc, rmname, 1, 0);
+}
+
+
+/*
+ * step through floors on system
+ */
+void  gotofloorstep(CtdlIPC *ipc, int direction, int mode)
+{
+       int  tofloor;
+
+       if (floorlist[0][0] == 0)
+               load_floorlist(ipc);
+
+       empty_keep_going:
+
+       if (direction == 0) { /* Previous floor */
+               if (curr_floor) tofloor = curr_floor - 1;
+               else tofloor = 127;
+
+               while (!floorlist[tofloor][0]) tofloor--;
+       } else {                   /* Next floor */
+               if (curr_floor < 127) tofloor = curr_floor + 1;
+               else tofloor = 0;
+
+               while (!floorlist[tofloor][0] && tofloor < 127) tofloor++;
+               if (!floorlist[tofloor][0])     tofloor = 0;
+       }
+       /* ;g works when not in floor mode so . . . */
+       if (!floor_mode) {
+               scr_printf("(%s)\n", floorlist[tofloor] );
+       }
+
+       gotofloor(ipc, floorlist[tofloor], mode);
+       if (curr_floor != tofloor) { /* gotofloor failed */
+            curr_floor = tofloor;
+            goto empty_keep_going;
+       }            
+}
+
+/* 
+ * Display user 'preferences'.
+ */
+extern int rc_prompt_control;
+void read_config(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       char *resp = NULL;
+       int r;                  /* IPC response code */
+    char _fullname[USERNAME_SIZE];
+       long _usernum;
+       int _axlevel, _timescalled, _posted;
+       time_t _lastcall;
+       struct ctdluser *user = NULL;
+
+       /* get misc user info */   
+       r = CtdlIPCGetBio(ipc, fullname, &resp, buf);
+       if (r / 100 != 1) {
+               pprintf("%s\n", buf);
+               return;
+       }
+       extract_token(_fullname, buf, 1, '|', sizeof fullname);
+       _usernum = extract_long(buf, 2);
+       _axlevel = extract_int(buf, 3);
+       _lastcall = extract_long(buf, 4);
+    _timescalled = extract_int(buf, 5);
+       _posted = extract_int(buf, 6);
+       free(resp);
+       resp = NULL;
+   
+       /* get preferences */
+       r = CtdlIPCGetConfig(ipc, &user, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               free(user);
+               return;
+       }
+
+       /* show misc user info */
+       scr_printf("%s\nAccess level: %d (%s)\n"
+                  "User #%ld / %d Calls / %d Posts",
+                  _fullname, _axlevel, axdefs[(int) _axlevel],
+                  _usernum, _timescalled, _posted);
+       if (_lastcall > 0L) {
+               scr_printf(" / Curr login: %s",
+                          asctime(localtime(&_lastcall)));
+       }
+       scr_printf("\n");
+
+       /* show preferences */
+       scr_printf("Your screen width: ");                                     color(BRIGHT_CYAN); scr_printf("%d",   /*user->USscreenwidth*/ screenwidth);          color(DIM_WHITE); 
+       scr_printf(", height: ");                                              color(BRIGHT_CYAN); scr_printf("%d\n", /*user->USscreenheight*/ screenheight);        color(DIM_WHITE);  
+       scr_printf("Are you an experienced Citadel user: ");                   color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_EXPERT) ? "Yes" : "No");     color(DIM_WHITE);
+       scr_printf("Print last old message on New message request: ");         color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_LASTOLD)? "Yes" : "No");     color(DIM_WHITE);
+       scr_printf("Prompt after each message: ");                             color(BRIGHT_CYAN); scr_printf("%s\n", (!(user->flags & US_NOPROMPT))? "Yes" : "No"); color(DIM_WHITE);
+       if ((user->flags & US_NOPROMPT) == 0) {
+       scr_printf("Use 'disappearing' prompts: ");                        color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_DISAPPEAR)? "Yes" : "No");   color(DIM_WHITE);
+       }
+       scr_printf("Pause after each screenful of text: ");                    color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_PAGINATOR)? "Yes" : "No");   color(DIM_WHITE);
+    if (rc_prompt_control == 3 && (user->flags & US_PAGINATOR)) {
+       scr_printf("<N>ext and <S>top work at paginator prompt: ");        color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_PROMPTCTL)? "Yes" : "No");   color(DIM_WHITE);
+       }
+    if (rc_floor_mode == RC_DEFAULT) {
+       scr_printf("View rooms by floor: ");                               color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_FLOORS)? "Yes" : "No");          color(DIM_WHITE);
+       }
+       if (rc_ansi_color == 3) {
+           scr_printf("Enable color support: ");                              color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_COLOR)? "Yes" : "No");       color(DIM_WHITE);
+       }
+       scr_printf("Be unlisted in userlog: ");                                color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_UNLISTED)? "Yes" : "No");    color(DIM_WHITE);
+       if (!IsEmptyStr(editor_paths[0])) {
+       scr_printf("Always enter messages with the full-screen editor: "); color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_EXTEDIT)? "Yes" : "No");     color(DIM_WHITE);
+       }
+       free(user);
+}
+
+/*
+ * Display system statistics.
+ */
+void system_info(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       char *resp = NULL;
+       size_t bytes;
+       int mrtg_users, mrtg_active_users; 
+       char mrtg_server_uptime[40];
+       long mrtg_himessage;
+       int ret;                        /* IPC response code */
+
+       /* get #users, #active & server uptime */
+       ret = CtdlIPCGenericCommand(ipc, "MRTG|users", NULL, 0, &resp, &bytes, buf);
+       mrtg_users = extract_int(resp, 0);
+       remove_token(resp, 0, '\n');
+       mrtg_active_users = extract_int(resp, 0);
+       remove_token(resp, 0, '\n');
+       extract_token(mrtg_server_uptime, resp, 0, '\n', sizeof mrtg_server_uptime);
+    free(resp);
+       resp = NULL;
+
+       /* get high message# */
+       ret = CtdlIPCGenericCommand(ipc, "MRTG|messages", NULL, 0, &resp, &bytes, buf);
+       mrtg_himessage = extract_long(resp, 0);
+       free(resp);
+       resp = NULL;
+
+       /* refresh server info just in case */
+       CtdlIPCServerInfo(ipc, buf);
+
+       scr_printf("You are connected to %s (%s) @%s\n", ipc->ServInfo.nodename, ipc->ServInfo.humannode, ipc->ServInfo.fqdn);
+       scr_printf("running %s with text client v%.2f,\n", ipc->ServInfo.software, (float)REV_LEVEL/100);
+    scr_printf("and located in %s.\n", ipc->ServInfo.site_location);
+    scr_printf("Connected users %d / Active users %d / Highest message #%ld\n", mrtg_users, mrtg_active_users, mrtg_himessage);
+    scr_printf("Server uptime: %s\n", mrtg_server_uptime);
+    scr_printf("Your system administrator is %s.\n", ipc->ServInfo.sysadm);
+    scr_printf("Copyright (C)1987-2007 by the Citadel development team\n");
+}
 
 /*
  * forget all rooms on current floor
  */
 void forget_this_floor(CtdlIPC *ipc)
 {
-
        if (curr_floor == 0) {
                scr_printf("Can't forget this floor.\n");
                return;
        }
-       if (floorlist[0][0] == 0)
+       if (floorlist[0][0] == 0) {
                load_floorlist(ipc);
+       }
        scr_printf("Are you sure you want to forget all rooms on %s? ",
               &floorlist[(int) curr_floor][0]);
-       if (yesno() == 0)
+       if (yesno() == 0) {
                return;
+       }
 
        gf_toroom(ipc, "_BASEROOM_", GF_ZAP);
 }
@@ -751,7 +1076,7 @@ int set_password(CtdlIPC *ipc)
        char pass2[20];
        char buf[SIZ];
 
-       if (strlen(rc_password) > 0) {
+       if (!IsEmptyStr(rc_password)) {
                strcpy(pass1, rc_password);
                strcpy(pass2, rc_password);
        } else {
@@ -790,8 +1115,15 @@ void get_serv_info(CtdlIPC *ipc, char *supplied_hostname)
                 /* Look up the , in the bible if you're confused */
                 (locate_host(ipc, buf), buf), buf);
 
-       /* Tell the server what our preferred content formats are */
-       if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "text/html|text/plain") / 100 )== 2) {
+       /*
+        * Tell the server what our preferred content formats are.
+        *
+        * Originally we preferred HTML over plain text because we can format
+        * it to the reader's screen width, but since our HTML-to-text parser
+        * isn't really all that great, it's probably better to just go with
+        * the plain text when we have it available.
+        */
+       if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "text/plain|text/html") / 100 )== 2) {
                can_do_msg4 = 1;
        }
 }
@@ -837,8 +1169,8 @@ char *SortOnlineUsers(char *listing) {
        /* Copy the list into a fixed-record-size array for sorting */
        for (i=0; i<rows; ++i) {
                memset(buf, 0, SIZ);
-               extract_token(buf, listing, i, '\n');
-               memcpy(&sortbuf[i*SIZ], buf, SIZ);
+               extract_token(buf, listing, i, '\n', sizeof buf);
+               memcpy(&sortbuf[i*SIZ], buf, (size_t)SIZ);
        }
 
        /* Do the sort */
@@ -850,6 +1182,8 @@ char *SortOnlineUsers(char *listing) {
                strcat(retbuf, &sortbuf[i*SIZ]);
                if (i<(rows-1)) strcat(retbuf, "\n");
        }
+    free(listing);
+    free(sortbuf);
        return(retbuf);
 }
 
@@ -878,25 +1212,29 @@ void who_is_online(CtdlIPC *ipc, int longlist)
 
        if (!longlist) {
                color(BRIGHT_WHITE);
-               pprintf("           User Name               Room           Idle        From host\n");
+               pprintf("           User Name               Room          ");
+               if (screenwidth >= 80) pprintf(" Idle        From host");
+               pprintf("\n");
                color(DIM_WHITE);
-               pprintf("   ------------------------- -------------------- ---- ------------------------\n");
+               pprintf("   ------------------------- --------------------");
+               if (screenwidth >= 80) pprintf(" ---- ------------------------");
+               pprintf("\n");
        }
        r = CtdlIPCOnlineUsers(ipc, &listing, &timenow, buf);
        listing = SortOnlineUsers(listing);
        if (r / 100 == 1) {
-               while (strlen(listing) > 0) {
+               while (!IsEmptyStr(listing)) {
                        int isidle = 0;
                        
                        /* Get another line */
-                       extract_token(buf, listing, 0, '\n');
+                       extract_token(buf, listing, 0, '\n', sizeof buf);
                        remove_token(listing, 0, '\n');
 
-                       extract(username, buf, 1);
-                       extract(roomname, buf, 2);
-                       extract(fromhost, buf, 3);
-                       extract(clientsoft, buf, 4);
-                       extract(flags, buf, 7);
+                       extract_token(username, buf, 1, '|', sizeof username);
+                       extract_token(roomname, buf, 2, '|', sizeof roomname);
+                       extract_token(fromhost, buf, 3, '|', sizeof fromhost);
+                       extract_token(clientsoft, buf, 4, '|', sizeof clientsoft);
+                       extract_token(flags, buf, 7, '|', sizeof flags);
 
                        idletime = timenow - extract_long(buf, 5);
                        idlehours = idletime / 3600;
@@ -904,20 +1242,15 @@ void who_is_online(CtdlIPC *ipc, int longlist)
                        idlesecs = (idletime - (idlehours * 3600) - (idlemins * 60));
 
                        if (idletime > rc_idle_threshold) {
-                               /*
-                               while (strlen(roomname) < 20) {
-                                       strcat(roomname, " ");
-                               }
-                               strcpy(&roomname[14], "[idle]");
-                               */
-                               if (skipidle)
+                               if (skipidle) {
                                        isidle = 1;
+                               }
                        }
 
                        if (longlist) {
-                               extract(actual_user, buf, 8);
-                               extract(actual_room, buf, 9);
-                               extract(actual_host, buf, 10);
+                               extract_token(actual_user, buf, 8, '|', sizeof actual_user);
+                               extract_token(actual_room, buf, 9, '|', sizeof actual_room);
+                               extract_token(actual_host, buf, 10, '|', sizeof actual_host);
 
                                pprintf("  Flags: %s\n", flags);
                                pprintf("Session: %d\n", extract_int(buf, 0));
@@ -930,57 +1263,64 @@ void who_is_online(CtdlIPC *ipc, int longlist)
                                        (long) idlemins,
                                        (long) idlesecs);
 
-                               if ( (strlen(actual_user)+strlen(actual_room)+strlen(actual_host)) > 0) {
+                               if ( (!IsEmptyStr(actual_user)&&
+                                     !IsEmptyStr(actual_room)&&
+                                     !IsEmptyStr(actual_host))) {
                                        pprintf("(really ");
-                                       if (strlen(actual_user)>0) pprintf("<%s> ", actual_user);
-                                       if (strlen(actual_room)>0) pprintf("in <%s> ", actual_room);
-                                       if (strlen(actual_host)>0) pprintf("from <%s> ", actual_host);
+                                       if (!IsEmptyStr(actual_user)) pprintf("<%s> ", actual_user);
+                                       if (!IsEmptyStr(actual_room)) pprintf("in <%s> ", actual_room);
+                                       if (!IsEmptyStr(actual_host)) pprintf("from <%s> ", actual_host);
                                        pprintf(")\n");
                                }
                                pprintf("\n");
 
                        } else {
-                   if (isidle == 0) {
-                               if (extract_int(buf, 0) == last_session) {
-                                       pprintf("        ");
-                               } else {
+                               if (isidle == 0) {
+                                       if (extract_int(buf, 0) == last_session) {
+                                               pprintf("        ");
+                                       }
+                                       else {
+                                               color(BRIGHT_MAGENTA);
+                                               pprintf("%-3s", flags);
+                                       }
+                                       last_session = extract_int(buf, 0);
+                                       color(BRIGHT_CYAN);
+                                       pprintf("%-25s ", username);
                                        color(BRIGHT_MAGENTA);
-                                       pprintf("%-3s", flags);
-                               }
-                               last_session = extract_int(buf, 0);
-                               color(BRIGHT_CYAN);
-                               pprintf("%-25s ", username);
-                               color(BRIGHT_MAGENTA);
-                               roomname[20] = 0;
-                               pprintf("%-20s ", roomname);
-                               if (idletime > rc_idle_threshold) {
-                                       /* over 1000d, must be gone fishing */
-                                       if (idlehours > 23999) {
-                                               pprintf("fish");
-                                       /* over 10 days */
-                                       } else if (idlehours > 239) {
-                                               pprintf("%3ldd",
-                                                       idlehours / 24);
-                                       /* over 10 hours */
-                                       } else if (idlehours > 9) {
-                                               pprintf("%1ldd%02ld",
-                                                       idlehours / 24,
-                                                       idlehours % 24);
-                                       /* less than 10 hours */
-                                       } else {
-                                               pprintf("%1ld:%02ld",
-                                                       idlehours, idlemins);
+                                       roomname[20] = 0;
+                                       pprintf("%-20s", roomname);
+
+                                       if (screenwidth >= 80) {
+                                               pprintf(" ");
+                                               if (idletime > rc_idle_threshold) {
+                                                       /* over 1000d, must be gone fishing */
+                                                       if (idlehours > 23999) {
+                                                               pprintf("fish");
+                                                       /* over 10 days */
+                                                       } else if (idlehours > 239) {
+                                                               pprintf("%3ldd", idlehours / 24);
+                                                       /* over 10 hours */
+                                                       } else if (idlehours > 9) {
+                                                               pprintf("%1ldd%02ld",
+                                                                       idlehours / 24,
+                                                                       idlehours % 24);
+                                                       /* less than 10 hours */
+                                                       }
+                                                       else {
+                                                               pprintf("%1ld:%02ld", idlehours, idlemins);
+                                                       }
+                                               }
+                                               else {
+                                                       pprintf("    ");
+                                               }
+                                               pprintf(" ");
+                                               color(BRIGHT_CYAN);
+                                               fromhost[24] = '\0';
+                                               pprintf("%-24s", fromhost);
                                        }
-                               }
-                               else {
-                                       pprintf("    ");
-                               }
-                               pprintf(" ");
-                               color(BRIGHT_CYAN);
-                               fromhost[24] = '\0';
-                               pprintf("%-24s\n", fromhost);
-                               color(DIM_WHITE);
-                       }
+                                       pprintf("\n");
+                                       color(DIM_WHITE);
+                               }
                        }
                }
        }
@@ -1018,7 +1358,7 @@ int main(int argc, char **argv)
 {
        int a, b, mcmd;
        char aaa[100], bbb[100];/* general purpose variables */
-       char argbuf[32];        /* command line buf */
+       char argbuf[64];        /* command line buf */
        char nonce[NONCE_SIZE];
        char *telnet_client_host = NULL;
        char *sptr, *sptr2;     /* USed to extract the nonce */
@@ -1030,6 +1370,15 @@ int main(int argc, char **argv)
        CtdlIPC* ipc;                   /* Our server connection */
        int r;                          /* IPC result code */
 
+       int relh=0;
+       int home=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+    int lp; 
+
+
+       calc_dirs_n_files(relh, home, relhome, ctdldir);
+       
        setIPCDeathHook(screen_delete);
        setIPCErrorPrintf(err_printf);
        setCryptoStatusHook(statusHook);
@@ -1043,10 +1392,10 @@ int main(int argc, char **argv)
                logoff(NULL, 3);
        }
 
-       sttybbs(SB_SAVE);       /* Store the old terminal parameters */
+       stty_ctdl(SB_SAVE);     /* Store the old terminal parameters */
        load_command_set();     /* parse the citadel.rc file */
-       sttybbs(SB_NO_INTR);    /* Install the new ones */
-       signal(SIGHUP, dropcarr);       /* Cleanup gracefully if carrier is dropped */
+       stty_ctdl(SB_NO_INTR);  /* Install the new ones */
+       /* signal(SIGHUP, dropcarr);FIXME */    /* Cleanup gracefully if carrier is dropped */
        signal(SIGPIPE, dropcarr);      /* Cleanup gracefully if local conn. dropped */
        signal(SIGTERM, dropcarr);      /* Cleanup gracefully if terminated */
        signal(SIGCONT, catch_sigcont); /* Catch SIGCONT so we can reset terminal */
@@ -1100,8 +1449,8 @@ int main(int argc, char **argv)
                if (!strcmp(argv[a], "-p")) {
                        struct stat st;
                
-                       if (chdir(BBSDIR) < 0) {
-                               perror("can't change to " BBSDIR);
+                       if (chdir(CTDLDIR) < 0) {
+                               perror("can't change to " CTDLDIR);
                                logoff(NULL, 3);
                        }
 
@@ -1111,7 +1460,7 @@ int main(int argc, char **argv)
                         * guaranteed to have the uid/gid we want.
                         */
                        if (!getuid() || !getgid()) {
-                               if (stat(BBSDIR "/citadel.config", &st) < 0) {
+                               if (stat(file_citadel_config, &st) < 0) {
                                        perror("couldn't stat citadel.config");
                                        logoff(NULL, 3);
                                }
@@ -1131,6 +1480,7 @@ int main(int argc, char **argv)
                        argc = shift(argc, argv, a, 1);
                }
        }
+       
 
        screen_new();
 
@@ -1151,7 +1501,7 @@ int main(int argc, char **argv)
 #endif
        ipc_for_signal_handlers = ipc;  /* KLUDGE cover your eyes */
 
-       CtdlIPC_getline(ipc, aaa);
+       CtdlIPC_chat_recv(ipc, aaa);
        if (aaa[0] != '2') {
                scr_printf("%s\n", &aaa[4]);
                logoff(ipc, atoi(aaa));
@@ -1175,7 +1525,7 @@ int main(int argc, char **argv)
                                {
                                        sptr2++;
                                        *sptr2 = '\0';
-                                       strncpy(nonce, sptr, NONCE_SIZE);
+                                       strncpy(nonce, sptr, (size_t)NONCE_SIZE);
                                }
                }
 
@@ -1192,10 +1542,10 @@ int main(int argc, char **argv)
 
        get_serv_info(ipc, telnet_client_host);
        scr_printf("%-24s\n%s\n%s\n", ipc->ServInfo.software, ipc->ServInfo.humannode,
-                  ipc->ServInfo.bbs_city);
+                  ipc->ServInfo.site_location);
        scr_flush();
 
-       status_line(ipc->ServInfo.humannode, ipc->ServInfo.bbs_city, NULL,
+       status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location, NULL,
                    secure, -1);
 
        screenwidth = 80;       /* default screen dimensions */
@@ -1209,7 +1559,7 @@ int main(int argc, char **argv)
  GSTA: /* See if we have a username and password on disk */
        if (rc_remember_passwords) {
                get_stored_password(hostbuf, portbuf, fullname, password);
-               if (strlen(fullname) > 0) {
+               if (!IsEmptyStr(fullname)) {
                        r = CtdlIPCTryLogin(ipc, fullname, aaa);
                        if (r / 100 == 3) {
                                if (*nonce) {
@@ -1232,7 +1582,7 @@ int main(int argc, char **argv)
        termn8 = 0;
        newnow = 0;
        do {
-               if (strlen(rc_username) > 0) {
+               if (!IsEmptyStr(rc_username)) {
                        strcpy(fullname, rc_username);
                } else {
                        newprompt("Enter your name: ", fullname, 29);
@@ -1244,7 +1594,7 @@ int main(int argc, char **argv)
        } while (
                 (!strcasecmp(fullname, "bbs"))
                 || (!strcasecmp(fullname, "new"))
-                || (strlen(fullname) == 0));
+                || (IsEmptyStr(fullname)));
 
        if (!strcasecmp(fullname, "off")) {
                mcmd = 29;
@@ -1256,7 +1606,7 @@ int main(int argc, char **argv)
                goto NEWUSR;
 
        /* password authentication */
-       if (strlen(rc_password) > 0) {
+       if (!IsEmptyStr(rc_password)) {
                strcpy(password, rc_password);
        } else {
                newprompt("\rPlease enter your password: ", password, -19);
@@ -1276,16 +1626,21 @@ int main(int argc, char **argv)
                goto PWOK;
        }
        scr_printf("<< wrong password >>\n");
-       if (strlen(rc_password) > 0)
+       if (!IsEmptyStr(rc_password))
                logoff(ipc, 2);
        goto GSTA;
 
-NEWUSR:        if (strlen(rc_password) == 0) {
-               scr_printf("'%s' not found.\n"
-                       "Do you want to create a new account with this name? ",
+NEWUSR:        if (IsEmptyStr(rc_password)) {
+               scr_printf("'%s' not found.\n", fullname);
+               scr_printf("Type 'off' if you would like to exit.\n");
+               if (ipc->ServInfo.newuser_disabled == 1) {
+                       goto GSTA;
+               }
+               scr_printf("Do you want to create a new user account called '%s'? ",
                        fullname);
-               if (yesno() == 0)
+               if (yesno() == 0) {
                        goto GSTA;
+               }
        }
 
        r = CtdlIPCCreateUser(ipc, fullname, 1, aaa);
@@ -1330,7 +1685,7 @@ NEWUSR:   if (strlen(rc_password) == 0) {
                        if (b > 1)
                                scr_printf("*** You have %d new private messages in Mail>\n", b);
                        color(DIM_WHITE);
-                       if (strlen(rc_gotmail_cmd) > 0) {
+                       if (!IsEmptyStr(rc_gotmail_cmd)) {
                                system(rc_gotmail_cmd);
                        }
                }
@@ -1347,9 +1702,9 @@ NEWUSR:   if (strlen(rc_password) == 0) {
         * program.  Don't mess with these once they've been set, because we
         * will be unlinking them later on in the program and we don't
         * want to delete something that we didn't create. */
-       snprintf(temp, sizeof temp, tmpnam(NULL));
-       snprintf(temp2, sizeof temp2, tmpnam(NULL));
-       snprintf(tempdir, sizeof tempdir, tmpnam(NULL));
+       CtdlMakeTempFileName(temp, sizeof temp);
+       CtdlMakeTempFileName(temp2, sizeof temp2);
+       CtdlMakeTempFileName(tempdir, sizeof tempdir);
 
        /* Get screen dimensions.  First we go to a default of 80x24.  Then
         * we try to get the user's actual screen dimensions off the server.
@@ -1378,6 +1733,7 @@ NEWUSR:   if (strlen(rc_password) == 0) {
        dotgoto(ipc, "_BASEROOM_", 1, 0);
 
        /* Main loop for the system... user is logged in. */
+    free(uglist[0]);
        uglistsize = 0;
 
        if (newnow == 1)
@@ -1399,23 +1755,16 @@ NEWUSR: if (strlen(rc_password) == 0) {
                                formout(ipc, "help");
                                break;
                        case 4:
-                               entmsg(ipc, 0, 0);
+                               entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 0);
                                break;
                        case 36:
-                               entmsg(ipc, 0, 1);
+                               entmsg(ipc, 0, 1, 0);
                                break;
                        case 46:
-                               entmsg(ipc, 0, 2);
+                               entmsg(ipc, 0, 2, 0);
                                break;
                        case 78:
-                               newprompt("What do you want your username to be? ", aaa, 32);
-                               snprintf(bbb, sizeof bbb, "ENT0 2|0|0|0|%s", aaa);
-                               CtdlIPC_putline(ipc, bbb);
-                               CtdlIPC_getline(ipc, aaa);
-                               if (strncmp("200", aaa, 3))
-                                       scr_printf("\n%s\n", aaa);
-                               else
-                                       entmsg(ipc, 0, 0);
+                               entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 1);
                                break;
                        case 5:                         /* <G>oto */
                                updatels(ipc);
@@ -1437,20 +1786,20 @@ NEWUSR: if (strlen(rc_password) == 0) {
                                dotgoto(ipc, "_MAIL_", 1, 0);
                                break;
                        case 20:
-                               if (strlen(argbuf) > 0) {
+                               if (!IsEmptyStr(argbuf)) {
                                        updatels(ipc);
                                        dotgoto(ipc, argbuf, 0, 0);
                                }
                                break;
                        case 52:
-                               if (strlen(argbuf) > 0) {
+                               if (!IsEmptyStr(argbuf)) {
                                        if (rc_alt_semantics) {
                                                updatelsa(ipc);
                                        }
                                        dotgoto(ipc, argbuf, 0, 0);
                                }
                                break;
-                       case 95: /* what exactly is the numbering scheme supposed to be anyway? */
+                       case 95: /* what exactly is the numbering scheme supposed to be anyway? --Ford, there isn't one. -IO */
                                dotungoto(ipc, argbuf);
                                break;
                        case 10:
@@ -1682,12 +2031,12 @@ NEWUSR: if (strlen(rc_password) == 0) {
                        case 2:
                                if (ipc->isLocal) {
                                        screen_reset();
-                                       sttybbs(SB_RESTORE);
+                                       stty_ctdl(SB_RESTORE);
                                        snprintf(aaa, sizeof aaa, "USERNAME=\042%s\042; export USERNAME;"
                                                 "exec ./subsystem %ld %d %d", fullname,
                                                 usernum, screenwidth, axlevel);
                                        ka_system(aaa);
-                                       sttybbs(SB_NO_INTR);
+                                       stty_ctdl(SB_NO_INTR);
                                        screen_set();
                                } else {
                                        scr_printf("*** Can't run doors when server is not local.\n");
@@ -1773,7 +2122,11 @@ NEWUSR:  if (strlen(rc_password) == 0) {
                                break;
 
                        case 25:
-                               edituser(ipc);
+                               edituser(ipc, 25);
+                               break;
+
+                       case 96:
+                               edituser(ipc, 96);
                                break;
 
                        case 8:
@@ -1815,6 +2168,74 @@ NEWUSR:  if (strlen(rc_password) == 0) {
                                page_user(ipc);
                                break;
 
+            case 110:           /* <+> Next room */
+                                gotoroomstep(ipc, 1, 0);
+                            break;
+
+            case 111:           /* <-> Previous room */
+                 gotoroomstep(ipc, 0, 0);
+                            break;
+
+                       case 112:           /* <>> Next floor */
+                 gotofloorstep(ipc, 1, GF_GOTO);
+                            break;
+
+                       case 113:           /* <<> Previous floor */
+                 gotofloorstep(ipc, 0, GF_GOTO);
+                                break;
+
+            case 116:           /* <.> skip to <+> Next room */
+                 gotoroomstep(ipc, 1, 1);
+                            break;
+
+            case 117:           /* <.> skip to <-> Previous room */
+                 gotoroomstep(ipc, 0, 1);
+                            break;
+
+                       case 118:           /* <.> skip to <>> Next floor */
+                 gotofloorstep(ipc, 1, GF_SKIP);
+                            break;
+
+                       case 119:           /* <.> skip to <<> Previous floor */
+                 gotofloorstep(ipc, 0, GF_SKIP);
+                                break;
+
+                       case 114:           
+                 read_config(ipc);
+                                break;
+
+                       case 115:           
+                 system_info(ipc);
+                                break;
+
+                       case 120:           /* .KAnonymous */
+                        dotknown(ipc, 0, NULL);
+                                break;
+
+                       case 121:           /* .KDirectory */
+                        dotknown(ipc, 1, NULL);
+                                break;
+
+                       case 122:           /* .KMatch */
+                        dotknown(ipc, 2, argbuf);
+                                break;
+
+                       case 123:           /* .KpreferredOnly */
+                        dotknown(ipc, 3, NULL);
+                                break;
+
+                       case 124:           /* .KPrivate */
+                        dotknown(ipc, 4, NULL);
+                                break;
+
+                       case 125:           /* .KRead only */
+                        dotknown(ipc, 5, NULL);
+                                break;
+
+                       case 126:           /* .KShared */
+                        dotknown(ipc, 6, NULL);
+                                break;
+
                        default: /* allow some math on the command */
                                /* commands 100... to 100+MAX_EDITORS-1 will
                                   call the appropriate editor... in other
@@ -1823,7 +2244,7 @@ NEWUSR:   if (strlen(rc_password) == 0) {
                                if (mcmd >= 100 && mcmd < (100+MAX_EDITORS))
                                {
                                        /* entmsg mode >=2 select editor */
-                                       entmsg(ipc, 0, mcmd - 100 + 2);
+                                       entmsg(ipc, 0, mcmd - 100 + 2, 0);
                                        break;
                                }
                        }       /* end switch */
@@ -1842,10 +2263,15 @@ TERMN8: scr_printf("%s logged out.", fullname);
        CtdlIPCLogout(ipc);
        if ((mcmd == 29) || (mcmd == 15)) {
                screen_delete();
-               sttybbs(SB_RESTORE);
+               stty_ctdl(SB_RESTORE);
                formout(ipc, "goodbye");
                logoff(ipc, 0);
        }
+       /* Free the ungoto list */
+       for (lp = 0; lp < uglistsize; lp++) {
+               free(uglist[lp]);
+       }
+    uglistsize = 0;
        goto GSTA;
 
 }      /* end main() */