Back to index

citadel  8.12
citadel.c
Go to the documentation of this file.
00001 /*
00002  * Main source module for the client program.
00003  *
00004  * Copyright (c) 1987-2012 by the citadel.org team
00005  *
00006  *  This program is open source software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License version 3.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  */
00014 
00015 #include "sysdep.h"
00016 #include <stdlib.h>
00017 #include <unistd.h>
00018 #include <fcntl.h>
00019 #include <stdio.h>
00020 #include <ctype.h>
00021 #include <string.h>
00022 
00023 #if TIME_WITH_SYS_TIME
00024 # include <sys/time.h>
00025 # include <time.h>
00026 #else
00027 # if HAVE_SYS_TIME_H
00028 #  include <sys/time.h>
00029 # else
00030 #  include <time.h>
00031 # endif
00032 #endif
00033 
00034 #include <limits.h>
00035 #include <sys/types.h>
00036 #include <sys/wait.h>
00037 #include <sys/stat.h>
00038 #include <sys/ioctl.h>
00039 #include <signal.h>
00040 #include <pwd.h>
00041 #include <stdarg.h>
00042 #include <errno.h>
00043 #include <libcitadel.h>
00044 #include "citadel.h"
00045 #include "citadel_ipc.h"
00046 #include "axdefs.h"
00047 #include "routines.h"
00048 #include "routines2.h"
00049 #include "tuiconfig.h"
00050 #include "rooms.h"
00051 #include "messages.h"
00052 #include "commands.h"
00053 #include "client_chat.h"
00054 #include "client_passwords.h"
00055 #include "citadel_decls.h"
00056 #include "sysdep.h"
00057 #ifndef HAVE_SNPRINTF
00058 #include "snprintf.h"
00059 #endif
00060 #include "screen.h"
00061 #include "citadel_dirs.h"
00062 
00063 #include "ecrash.h"
00064 #include "md5.h"
00065 
00066 #define IFEXPERT if (userflags&US_EXPERT)
00067 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
00068 #define IFAIDE if (axlevel>=AxAideU)
00069 #define IFNAIDE if (axlevel<AxAideU)
00070 
00071 int rordercmp(struct ctdlroomlisting *r1, struct ctdlroomlisting *r2);
00072 march *marchptr = NULL;
00073 extern char *moreprompt;
00074 
00075 /* globals associated with the client program */
00076 char temp[PATH_MAX];        /* Name of general-purpose temp file */
00077 char temp2[PATH_MAX];              /* Name of general-purpose temp file */
00078 char tempdir[PATH_MAX];            /* Name of general-purpose temp directory */
00079 char printcmd[SIZ];         /* print command */
00080 int editor_pid = (-1);
00081 char fullname[USERNAME_SIZE];
00082 unsigned room_flags;
00083 unsigned room_flags2;
00084 int entmsg_ok = 0;
00085 char room_name[ROOMNAMELEN];
00086 char *uglist[UGLISTLEN]; /* size of the ungoto list */
00087 long uglistlsn[UGLISTLEN]; /* current read position for all the ungoto's. Not going to make any friends with this one. */
00088 int uglistsize = 0;
00089 char is_mail = 0;           /* nonzero when we're in a mail room */
00090 char axlevel = AxDeleted;          /* access level */
00091 char is_room_aide = 0;             /* boolean flag, 1 if room aide */
00092 int timescalled;
00093 int posted;
00094 unsigned userflags;
00095 long usernum = 0L;          /* user number */
00096 time_t lastcall = 0L;              /* Date/time of previous login */
00097 char newnow;
00098 long highest_msg_read;             /* used for <A>bandon room cmd */
00099 long maxmsgnum;                    /* used for <G>oto */
00100 char sigcaught = 0;
00101 char rc_username[USERNAME_SIZE];
00102 char rc_password[32];
00103 char hostbuf[SIZ];
00104 char portbuf[SIZ];
00105 char rc_floor_mode;
00106 char floor_mode;
00107 char curr_floor = 0;        /* number of current floor */
00108 char floorlist[128][SIZ];   /* names of floors */
00109 int termn8 = 0;                    /* Set to nonzero to cause a logoff */
00110 int secure;                 /* Set to nonzero when wire is encrypted */
00111 
00112 extern char instant_msgs;   /* instant messages waiting! */
00113 extern int rc_ansi_color;   /* ansi color value from citadel.rc */
00114 extern int next_lazy_cmd;
00115 
00116 CtdlIPC *ipc_for_signal_handlers;  /* KLUDGE cover your eyes */
00117 int enable_syslog = 0;
00118 
00119 
00120 /*
00121  * here is our 'clean up gracefully and exit' routine
00122  */
00123 void ctdl_logoff(char *file, int line, CtdlIPC *ipc, int code)
00124 {
00125        int lp;
00126 
00127        if (editor_pid > 0) {       /* kill the editor if it's running */
00128               kill(editor_pid, SIGHUP);
00129        }
00130 
00131        /* Free the ungoto list */
00132        for (lp = 0; lp < uglistsize; lp++) {
00133               free(uglist[lp]);
00134        }
00135 
00136 /* Shut down the server connection ... but not if the logoff code is 3,
00137  * because that means we're exiting because we already lost the server.
00138  */
00139        if (code != 3) {
00140               CtdlIPCQuit(ipc);
00141        }
00142 
00143 /*
00144  * now clean up various things
00145  */
00146        unlink(temp);
00147        unlink(temp2);
00148        nukedir(tempdir);
00149 
00150        /* Violently kill off any child processes if Citadel is
00151         * the login shell. 
00152         */
00153        if (getppid() == 1) {
00154               kill(0 - getpgrp(), SIGTERM);
00155               sleep(1);
00156               kill(0 - getpgrp(), SIGKILL);
00157        }
00158        color(ORIGINAL_PAIR);       /* Restore the old color settings */
00159        stty_ctdl(SB_RESTORE);      /* return the old terminal settings */
00160        /* 
00161         * uncomment the following if you need to know why Citadel exited
00162        printf("*** Exit code %d at %s:%d\n", code, file, line);
00163        sleep(2);
00164         */
00165        exit(code);          /* exit with the proper exit code */
00166 }
00167 
00168 
00169 
00170 /*
00171  * signal catching function for hangups...
00172  */
00173 void dropcarr(int signum)
00174 {
00175        logoff(NULL, 3);     /* No IPC when server's already gone! */
00176 }
00177 
00178 
00179 
00180 /*
00181  * catch SIGCONT to reset terminal modes when were are put back into the
00182  * foreground.
00183  */
00184 void catch_sigcont(int signum)
00185 {
00186        stty_ctdl(SB_LAST);
00187        signal(SIGCONT, catch_sigcont);
00188 }
00189 
00190 
00191 /* general purpose routines */
00192 
00193 /* display a file */
00194 void formout(CtdlIPC *ipc, char *name)
00195 {
00196        int r;               /* IPC return code */
00197        char buf[SIZ];
00198        char *text = NULL;
00199 
00200        r = CtdlIPCSystemMessage(ipc, name, &text, buf);
00201        if (r / 100 != 1) {
00202               scr_printf("%s\n", buf);
00203               return;
00204        }
00205        if (text) {
00206               fmout(screenwidth, NULL, text, NULL, 1);
00207               free(text);
00208        }
00209 }
00210 
00211 
00212 void userlist(CtdlIPC *ipc, char *patn)
00213 {
00214        char buf[SIZ];
00215        char fl[SIZ];
00216        struct tm tmbuf;
00217        time_t lc;
00218        int r;                      /* IPC response code */
00219        char *listing = NULL;
00220 
00221        r = CtdlIPCUserListing(ipc, patn, &listing, buf);
00222        if (r / 100 != 1) {
00223               scr_printf("%s\n", buf);
00224               return;
00225        }
00226 
00227        scr_printf("       User Name           Num  L Last Visit Logins Messages\n");
00228        scr_printf("------------------------- ----- - ---------- ------ --------\n");
00229        if (listing != NULL) while (!IsEmptyStr(listing)) {
00230               extract_token(buf, listing, 0, '\n', sizeof buf);
00231               remove_token(listing, 0, '\n');
00232 
00233               if (sigcaught == 0) {
00234                   extract_token(fl, buf, 0, '|', sizeof fl);
00235                   if (pattern(fl, patn) >= 0) {
00236                      scr_printf("%-25s ", fl);
00237                      scr_printf("%5ld %d ", extract_long(buf, 2),
00238                             extract_int(buf, 1));
00239                      lc = extract_long(buf, 3);
00240                      localtime_r(&lc, &tmbuf);
00241                      scr_printf("%02d/%02d/%04d ",
00242                             (tmbuf.tm_mon + 1),
00243                             tmbuf.tm_mday,
00244                             (tmbuf.tm_year + 1900));
00245                      scr_printf("%6ld %8ld\n", extract_long(buf, 4), extract_long(buf, 5));
00246                   }
00247 
00248               }
00249        }
00250        free(listing);
00251        scr_printf("\n");
00252 }
00253 
00254 
00255 /*
00256  * grab assorted info about the user...
00257  */
00258 void load_user_info(char *params)
00259 {
00260        extract_token(fullname, params, 0, '|', sizeof fullname);
00261        axlevel = extract_int(params, 1);
00262        timescalled = extract_int(params, 2);
00263        posted = extract_int(params, 3);
00264        userflags = extract_int(params, 4);
00265        usernum = extract_long(params, 5);
00266        lastcall = extract_long(params, 6);
00267 }
00268 
00269 
00270 /*
00271  * Remove a room from the march list.  'floornum' is ignored unless
00272  * 'roomname' is set to _FLOOR_, in which case all rooms on the requested
00273  * floor will be removed from the march list.
00274  */
00275 void remove_march(char *roomname, int floornum)
00276 {
00277        struct march *mptr, *mptr2;
00278 
00279        if (marchptr == NULL)
00280               return;
00281 
00282        if ((!strcasecmp(marchptr->march_name, roomname))
00283            || ((!strcasecmp(roomname, "_FLOOR_")) && (marchptr->march_floor == floornum))) {
00284               mptr = marchptr->next;
00285               free(marchptr);
00286               marchptr = mptr;
00287               return;
00288        }
00289        mptr2 = marchptr;
00290        for (mptr = marchptr; mptr != NULL; mptr = mptr->next) {
00291 
00292               if ((!strcasecmp(mptr->march_name, roomname))
00293                   || ((!strcasecmp(roomname, "_FLOOR_"))
00294                      && (mptr->march_floor == floornum))) {
00295 
00296                      mptr2->next = mptr->next;
00297                      free(mptr);
00298                      mptr = mptr2;
00299               } else {
00300                      mptr2 = mptr;
00301               }
00302        }
00303 }
00304 
00305 
00306 /*
00307  * Locate the room on the march list which we most want to go to.  Each room
00308  * is measured given a "weight" of preference based on various factors.
00309  */
00310 char *pop_march(int desired_floor, struct march *_march)
00311 {
00312        static char TheRoom[ROOMNAMELEN];
00313        int TheWeight = 0;
00314        int weight;
00315        struct march *mptr = NULL;
00316 
00317        strcpy(TheRoom, "_BASEROOM_");
00318        if (_march == NULL)
00319               return (TheRoom);
00320 
00321        for (mptr = _march; mptr != NULL; mptr = mptr->next) {
00322               weight = 0;
00323               if ((strcasecmp(mptr->march_name, "_BASEROOM_")))
00324                      weight = weight + 10000;
00325               if (mptr->march_floor == desired_floor)
00326                      weight = weight + 5000;
00327 
00328               weight = weight + ((128 - (mptr->march_floor)) * 128);
00329               weight = weight + (128 - (mptr->march_order));
00330 
00331               if (weight > TheWeight) {
00332                      TheWeight = weight;
00333                      strcpy(TheRoom, mptr->march_name);
00334               }
00335        }
00336        return (TheRoom);
00337 }
00338 
00339 
00340 /*
00341  * jump directly to a room
00342  */
00343 void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto)
00344 {
00345        char aaa[SIZ], bbb[SIZ];
00346        static long ls = 0L;
00347        int newmailcount = 0;
00348        int partial_match, best_match;
00349        char from_floor;
00350        int ugpos = uglistsize;
00351        int r;                      /* IPC result code */
00352        struct ctdlipcroom *room = NULL;
00353        int rv = 0;
00354 
00355        /* store ungoto information */
00356        if (fromungoto == 0) {
00357               /* sloppy slide them all down, hey it's the client, who cares. :-) */
00358               if (uglistsize >= (UGLISTLEN-1)) {
00359                      int lp;
00360                      free (uglist[0]);
00361                      for (lp = 0; lp < (UGLISTLEN-1); lp++) {
00362                             uglist[lp] = uglist[lp+1];
00363                             uglistlsn[lp] = uglistlsn[lp+1];
00364                      }
00365                      ugpos--;
00366               } else {
00367                      uglistsize++;
00368               }
00369         
00370               uglist[ugpos] = malloc(strlen(room_name)+1);
00371               strcpy(uglist[ugpos], room_name);
00372               uglistlsn[ugpos] = ls;
00373        }
00374       
00375        /* first try an exact match */
00376        r = CtdlIPCGotoRoom(ipc, towhere, "", &room, aaa);
00377        if (r / 10 == 54) {
00378               newprompt("Enter room password: ", bbb, 9);
00379               r = CtdlIPCGotoRoom(ipc, towhere, bbb, &room, aaa);
00380               if (r / 10 == 54) {
00381                      scr_printf("Wrong password.\n");
00382                      return;
00383               }
00384        }      
00385 
00386        /*
00387         * If a match is not found, try a partial match.
00388         * Partial matches anywhere in the string carry a weight of 1,
00389         * left-aligned matches carry a weight of 2.  Pick the room that
00390         * has the highest-weighted match.  Do not match on forgotten
00391         * rooms.
00392         */
00393        if (r / 100 != 2) {
00394               struct march *march = NULL;
00395 
00396               best_match = 0;
00397               strcpy(bbb, "");
00398 
00399               r = CtdlIPCKnownRooms(ipc, SubscribedRooms, AllFloors, &march, aaa);
00400               if (r / 100 == 1) {
00401                      /* Run the roomlist; free the data as we go */
00402                      struct march *mp = march;   /* Current */
00403 
00404                      while (mp) {
00405                             partial_match = 0;
00406                             if (pattern(mp->march_name, towhere) >= 0) {
00407                                    partial_match = 1;
00408                             }
00409                             if (!strncasecmp(mp->march_name, towhere, strlen(towhere))) {
00410                                    partial_match = 2;
00411                             }
00412                             if (partial_match > best_match) {
00413                                    strcpy(bbb, mp->march_name);
00414                                    best_match = partial_match;
00415                             }
00416                             /* Both pointers are NULL at end of list */
00417                             march = mp->next;
00418                             free(mp);
00419                             mp = march;
00420                      }
00421               }
00422 
00423               if (IsEmptyStr(bbb)) {
00424                      scr_printf("No room '%s'.\n", towhere);
00425                      return;
00426               }
00427               r = CtdlIPCGotoRoom(ipc, bbb, "", &room, aaa);
00428        }
00429        if (r / 100 != 1 && r / 100 != 2) {
00430               scr_printf("%s\n", aaa);
00431               return;
00432        }
00433        safestrncpy(room_name, room->RRname, ROOMNAMELEN);
00434        room_flags = room->RRflags;
00435        room_flags2 = room->RRflags2;
00436        from_floor = curr_floor;
00437        curr_floor = room->RRfloor;
00438 
00439        /* Determine, based on the room's default view, whether an <E>nter message
00440         * command will be valid here.
00441         */
00442        switch(room->RRdefaultview) {
00443               case VIEW_BBS:
00444               case VIEW_MAILBOX:
00445               case VIEW_BLOG:
00446                                    entmsg_ok = 1;
00447                                    break;
00448               default:
00449                                    entmsg_ok = 0;
00450                                    break;
00451        }
00452 
00453        remove_march(room_name, 0);
00454        if (!strcasecmp(towhere, "_BASEROOM_"))
00455               remove_march(towhere, 0);
00456        if (!room->RRunread)
00457               next_lazy_cmd = 5;   /* Don't read new if no new msgs */
00458        if ((from_floor != curr_floor) && (display_name > 0) && (floor_mode == 1)) {
00459               if (floorlist[(int) curr_floor][0] == 0)
00460                      load_floorlist(ipc);
00461               scr_printf("(Entering floor: %s)\n", &floorlist[(int) curr_floor][0]);
00462        }
00463        if (display_name == 1) {
00464               color(BRIGHT_WHITE);
00465               scr_printf("%s ", room_name);
00466               color(DIM_WHITE);
00467               scr_printf("- ");
00468        }
00469        if (display_name != 2) {
00470               color(BRIGHT_YELLOW);
00471               scr_printf("%d ", room->RRunread);
00472               color(DIM_WHITE);
00473               scr_printf("new of ");
00474               color(BRIGHT_YELLOW);
00475               scr_printf("%d ", room->RRtotal);
00476               color(DIM_WHITE);
00477               scr_printf("messages.\n");
00478        }
00479        highest_msg_read = room->RRlastread;
00480        maxmsgnum = room->RRhighest;
00481        is_mail = room->RRismailbox;
00482        is_room_aide = room->RRaide;
00483        ls = room->RRlastread;
00484 
00485        /* read info file if necessary */
00486        if (room->RRinfoupdated > 0)
00487               readinfo(ipc);
00488 
00489        /* check for newly arrived mail if we can */
00490        newmailcount = room->RRnewmail;
00491        if (newmailcount > 0) {
00492               color(BRIGHT_RED);
00493               if (newmailcount == 1) {
00494                      scr_printf("*** A new mail message has arrived.\n");
00495               }
00496               else {
00497                      scr_printf("*** %d new mail messages have arrived.\n",
00498                                    newmailcount);
00499               }
00500               color(DIM_WHITE);
00501               if (!IsEmptyStr(rc_gotmail_cmd)) {
00502                      rv = system(rc_gotmail_cmd);
00503                      if (rv) 
00504                             scr_printf("*** failed to check for mail calling %s Reason %d.\n",
00505                                       rc_gotmail_cmd, rv);
00506               }
00507        }
00508        free(room);
00509 
00510        if (screenwidth>5) snprintf(&status_line[1], screenwidth-1, "%s  |  %s  |  %s  |  %s  |  %d new mail  |",
00511               (secure ? "Encrypted" : "Unencrypted"),
00512               ipc->ServInfo.humannode,
00513               ipc->ServInfo.site_location,
00514               room_name,
00515               newmailcount
00516        );
00517 }
00518 
00519 /* Goto next room having unread messages.
00520  * We want to skip over rooms that the user has already been to, and take the
00521  * user back to the lobby when done.  The room we end up in is placed in
00522  * newroom - which is set to 0 (the lobby) initially.
00523  */
00524 void gotonext(CtdlIPC *ipc)
00525 {
00526        char buf[SIZ];
00527        struct march *mptr, *mptr2;
00528        char next_room[ROOMNAMELEN];
00529 
00530        /* Check to see if the march-mode list is already allocated.
00531         * If it is, pop the first room off the list and go there.
00532         */
00533        if (marchptr == NULL) {
00534               CtdlIPCKnownRooms(ipc, SubscribedRoomsWithNewMessages,
00535                               AllFloors, &marchptr, buf);
00536 
00537 /* add _BASEROOM_ to the end of the march list, so the user will end up
00538  * in the system base room (usually the Lobby>) at the end of the loop
00539  */
00540               mptr = (struct march *) malloc(sizeof(struct march));
00541               mptr->next = NULL;
00542               mptr->march_order = 0;
00543               mptr->march_floor = 0;
00544               strcpy(mptr->march_name, "_BASEROOM_");
00545               if (marchptr == NULL) {
00546                      marchptr = mptr;
00547               } else {
00548                      mptr2 = marchptr;
00549                      while (mptr2->next != NULL)
00550                             mptr2 = mptr2->next;
00551                      mptr2->next = mptr;
00552               }
00553 /*
00554  * ...and remove the room we're currently in, so a <G>oto doesn't make us
00555  * walk around in circles
00556  */
00557               remove_march(room_name, 0);
00558        }
00559        if (marchptr != NULL) {
00560               strcpy(next_room, pop_march(curr_floor, marchptr));
00561        } else {
00562               strcpy(next_room, "_BASEROOM_");
00563        }
00564        remove_march(next_room, 0);
00565        dotgoto(ipc, next_room, 1, 0);
00566 }
00567 
00568 /*
00569  * forget all rooms on a given floor
00570  */
00571 void forget_all_rooms_on(CtdlIPC *ipc, int ffloor)
00572 {
00573        char buf[SIZ];
00574        struct march *flist = NULL;
00575        struct march *fptr = NULL;
00576        struct ctdlipcroom *room = NULL;
00577        int r;                      /* IPC response code */
00578 
00579        scr_printf("Forgetting all rooms on %s...\n", &floorlist[ffloor][0]);
00580        remove_march("_FLOOR_", ffloor);
00581        r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, ffloor, &flist, buf);
00582        if (r / 100 != 1) {
00583               scr_printf("Error %d: %s\n", r, buf);
00584               return;
00585        }
00586        while (flist) {
00587               r = CtdlIPCGotoRoom(ipc, flist->march_name, "", &room, buf);
00588               if (r / 100 == 2) {
00589                      r = CtdlIPCForgetRoom(ipc, buf);
00590                      if (r / 100 != 2) {
00591                             scr_printf("Error %d: %s\n", r, buf);
00592                      }
00593 
00594               }
00595               fptr = flist;
00596               flist = flist->next;
00597               free(fptr);
00598        }
00599        if (room) free(room);
00600 }
00601 
00602 
00603 /*
00604  * routine called by gotofloor() to move to a new room on a new floor
00605  */
00606 void gf_toroom(CtdlIPC *ipc, char *towhere, int mode)
00607 {
00608        int floor_being_left;
00609 
00610        floor_being_left = curr_floor;
00611 
00612        if (mode == GF_GOTO) {             /* <;G>oto mode */
00613               updatels(ipc);
00614               dotgoto(ipc, towhere, 1, 0);
00615        }
00616        else if (mode == GF_SKIP) { /* <;S>kip mode */
00617               dotgoto(ipc, towhere, 1, 0);
00618               remove_march("_FLOOR_", floor_being_left);
00619        }
00620        else if (mode == GF_ZAP) {  /* <;Z>ap mode */
00621               dotgoto(ipc, towhere, 1, 0);
00622               remove_march("_FLOOR_", floor_being_left);
00623               forget_all_rooms_on(ipc, floor_being_left);
00624        }
00625 }
00626 
00627 
00628 /*
00629  * go to a new floor
00630  */
00631 void gotofloor(CtdlIPC *ipc, char *towhere, int mode)
00632 {
00633        int a, tofloor;
00634        int r;        /* IPC response code */
00635        struct march *mptr;
00636        char buf[SIZ], targ[SIZ];
00637 
00638        if (floorlist[0][0] == 0)
00639               load_floorlist(ipc);
00640        tofloor = (-1);
00641        for (a = 0; a < 128; ++a)
00642               if (!strcasecmp(&floorlist[a][0], towhere))
00643                      tofloor = a;
00644 
00645        if (tofloor < 0) {
00646               for (a = 0; a < 128; ++a) {
00647                      if (!strncasecmp(&floorlist[a][0], towhere, strlen(towhere))) {
00648                             tofloor = a;
00649                      }
00650               }
00651        }
00652        if (tofloor < 0) {
00653               for (a = 0; a < 128; ++a)
00654                      if (pattern(towhere, &floorlist[a][0]) > 0)
00655                             tofloor = a;
00656        }
00657        if (tofloor < 0) {
00658               scr_printf("No floor '%s'.\n", towhere);
00659               return;
00660        }
00661        for (mptr = marchptr; mptr != NULL; mptr = mptr->next) {
00662               if ((mptr->march_floor) == tofloor) {
00663                      gf_toroom(ipc, mptr->march_name, mode);
00664                      return;
00665               }
00666        }
00667 
00668        /* Find first known room on the floor */
00669 
00670        strcpy(targ, "");
00671        mptr = NULL;
00672        r = CtdlIPCKnownRooms(ipc, SubscribedRooms, tofloor, &mptr, buf);
00673        if (r / 100 == 1) {
00674               struct march *tmp = mptr;
00675 
00676               /*. . . according to room order */
00677               if (mptr)
00678            strcpy(targ, pop_march(tofloor, mptr));
00679               while (mptr) {
00680                      tmp = mptr->next;
00681                      free(mptr);
00682                      mptr = tmp;
00683               }
00684        }
00685        if (!IsEmptyStr(targ)) {
00686               gf_toroom(ipc, targ, mode);
00687               return;
00688        }
00689 
00690        /* No known rooms on the floor; unzap the first one then */
00691 
00692        strcpy(targ, "");
00693        mptr = NULL;
00694        r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, tofloor, &mptr, buf);
00695        if (r / 100 == 1) {
00696               struct march *tmp = mptr;
00697               
00698         /*. . . according to room order */
00699               if (mptr)
00700                      strcpy(targ, pop_march(tofloor, mptr));
00701               while (mptr) {
00702                      tmp = mptr->next;
00703                      free(mptr);
00704                      mptr = tmp;
00705               }
00706        }
00707        if (!IsEmptyStr(targ)) {
00708               gf_toroom(ipc, targ, mode);
00709        } else {
00710               scr_printf("There are no rooms on '%s'.\n", &floorlist[tofloor][0]);
00711        }
00712 }
00713 
00714 /*
00715  * Indexing mechanism for a room list, called by gotoroomstep()
00716  */
00717 void room_tree_list_query(struct ctdlroomlisting *rp, char *findrmname, int findrmslot, char *rmname, int *rmslot, int *rmtotal)
00718 {
00719        char roomname[ROOMNAMELEN];
00720        static int cur_rmslot = 0;
00721 
00722        if (rp == NULL) {
00723               cur_rmslot = 0;
00724               return;
00725        }
00726 
00727        if (rp->lnext != NULL) {
00728               room_tree_list_query(rp->lnext, findrmname, findrmslot, rmname, rmslot, rmtotal);
00729        }
00730 
00731        if (sigcaught == 0) {
00732               strcpy(roomname, rp->rlname);
00733 
00734               if (rmname != NULL) {
00735                      if (cur_rmslot == findrmslot) {
00736                             strcpy(rmname, roomname);
00737                      }
00738               }
00739               if (rmslot != NULL) {
00740                      if (!strcmp(roomname, findrmname)) {
00741                             *rmslot = cur_rmslot;
00742                      }
00743               }
00744               cur_rmslot++;
00745        }
00746 
00747        if (rp->rnext != NULL) {
00748               room_tree_list_query(rp->rnext, findrmname, findrmslot, rmname, rmslot, rmtotal);
00749        }
00750 
00751        if ((rmname == NULL) && (rmslot == NULL))
00752               free(rp);
00753 
00754        if (rmtotal != NULL) {
00755               *rmtotal = cur_rmslot;
00756        }
00757 }
00758 
00759 /*
00760  * step through rooms on current floor
00761  */
00762 void  gotoroomstep(CtdlIPC *ipc, int direction, int mode)
00763 {
00764        struct march *listing = NULL;
00765        struct march *mptr;
00766        int r;        /* IPC response code */
00767        char buf[SIZ];
00768        struct ctdlroomlisting *rl = NULL;
00769        struct ctdlroomlisting *rp;
00770        struct ctdlroomlisting *rs;
00771        int list_it;
00772        char rmname[ROOMNAMELEN];
00773        int rmslot = 0;
00774        int rmtotal;
00775 
00776        /* Ask the server for a room list */
00777        r = CtdlIPCKnownRooms(ipc, SubscribedRooms, (-1), &listing, buf);
00778        if (r / 100 != 1) {
00779               listing = NULL;
00780        }
00781 
00782        load_floorlist(ipc);
00783 
00784        for (mptr = listing; mptr != NULL; mptr = mptr->next) {
00785               list_it = 1;
00786 
00787               if ( floor_mode 
00788                       && (mptr->march_floor != curr_floor))
00789                      list_it = 0;
00790 
00791               if (list_it) {
00792                      rp = malloc(sizeof(struct ctdlroomlisting));
00793                      strncpy(rp->rlname, mptr->march_name, ROOMNAMELEN);
00794                      rp->rlflags = mptr->march_flags;
00795                      rp->rlfloor = mptr->march_floor;
00796                      rp->rlorder = mptr->march_order;
00797                      rp->lnext = NULL;
00798                      rp->rnext = NULL;
00799 
00800                      rs = rl;
00801                      if (rl == NULL) {
00802                             rl = rp;
00803                      } else {
00804                             while (rp != NULL) {
00805                                    if (rordercmp(rp, rs) < 0) {
00806                                           if (rs->lnext == NULL) {
00807                                                  rs->lnext = rp;
00808                                                  rp = NULL;
00809                                           } else {
00810                                                  rs = rs->lnext;
00811                                           }
00812                                    } else {
00813                                           if (rs->rnext == NULL) {
00814                                                  rs->rnext = rp;
00815                                                  rp = NULL;
00816                                           } else {
00817                                                  rs = rs->rnext;
00818                                           }
00819                                    }
00820                             }
00821                      }
00822               }
00823        }
00824 
00825        /* Find position of current room */
00826        room_tree_list_query(NULL, NULL, 0, NULL, NULL, NULL);
00827        room_tree_list_query(rl,  room_name, 0, NULL, &rmslot, &rmtotal);
00828 
00829        if (direction == 0) { /* Previous room */
00830               /* If we're at the first room, wrap to the last room */
00831               if (rmslot == 0) {
00832                      rmslot = rmtotal - 1;
00833               } else {
00834                      rmslot--;
00835               }
00836        } else {              /* Next room */
00837               /* If we're at the last room, wrap to the first room */
00838               if (rmslot == rmtotal - 1) {
00839                      rmslot = 0; 
00840               } else {
00841                      rmslot++;
00842               }
00843        }
00844 
00845        /* Get name of next/previous room */
00846        room_tree_list_query(NULL, NULL, 0, NULL, NULL, NULL);
00847        room_tree_list_query(rl,  NULL, rmslot, rmname, NULL, NULL);
00848 
00849        /* Free the tree */
00850        room_tree_list_query(rl, NULL, 0, NULL, NULL, NULL);
00851 
00852        if (mode == 0) { /* not skipping */
00853            updatels(ipc);
00854        }
00855 
00856        /* Free the room list */
00857        while (listing) {
00858               mptr = listing->next;
00859               free(listing);
00860               listing = mptr;
00861        };
00862 
00863        dotgoto(ipc, rmname, 1, 0);
00864 }
00865 
00866 
00867 /*
00868  * step through floors on system
00869  */
00870 void  gotofloorstep(CtdlIPC *ipc, int direction, int mode)
00871 {
00872        int  tofloor;
00873 
00874        if (floorlist[0][0] == 0)
00875               load_floorlist(ipc);
00876 
00877        empty_keep_going:
00878 
00879        if (direction == 0) { /* Previous floor */
00880               if (curr_floor)      tofloor = curr_floor - 1;
00881               else tofloor = 127;
00882 
00883               while (!floorlist[tofloor][0]) tofloor--;
00884        } else {                /* Next floor */
00885               if (curr_floor < 127) tofloor = curr_floor + 1;
00886               else tofloor = 0;
00887 
00888               while (!floorlist[tofloor][0] && tofloor < 127)  tofloor++;
00889               if (!floorlist[tofloor][0]) tofloor = 0;
00890        }
00891        /* ;g works when not in floor mode so . . . */
00892        if (!floor_mode) {
00893               scr_printf("(%s)\n", floorlist[tofloor] );
00894        }
00895 
00896        gotofloor(ipc, floorlist[tofloor], mode);
00897        if (curr_floor != tofloor) { /* gotofloor failed */
00898             curr_floor = tofloor;
00899             goto empty_keep_going;
00900        }           
00901 }
00902 
00903 /* 
00904  * Display user 'preferences'.
00905  */
00906 extern int rc_prompt_control;
00907 void read_config(CtdlIPC *ipc)
00908 {
00909        char buf[SIZ];
00910        char *resp = NULL;
00911        int r;               /* IPC response code */
00912     char _fullname[USERNAME_SIZE];
00913        long _usernum;
00914        int _axlevel, _timescalled, _posted;
00915        time_t _lastcall;
00916        struct ctdluser *user = NULL;
00917 
00918        /* get misc user info */   
00919        r = CtdlIPCGetBio(ipc, fullname, &resp, buf);
00920        if (r / 100 != 1) {
00921               scr_printf("%s\n", buf);
00922               return;
00923        }
00924        extract_token(_fullname, buf, 1, '|', sizeof fullname);
00925        _usernum = extract_long(buf, 2);
00926        _axlevel = extract_int(buf, 3);
00927        _lastcall = extract_long(buf, 4);
00928     _timescalled = extract_int(buf, 5);
00929        _posted = extract_int(buf, 6);
00930        free(resp);
00931        resp = NULL;
00932    
00933        /* get preferences */
00934        r = CtdlIPCGetConfig(ipc, &user, buf);
00935        if (r / 100 != 2) {
00936               scr_printf("%s\n", buf);
00937               free(user);
00938               return;
00939        }
00940 
00941        /* show misc user info */
00942        scr_printf("%s\nAccess level: %d (%s)\n"
00943                  "User #%ld / %d Calls / %d Posts",
00944                  _fullname, _axlevel, axdefs[(int) _axlevel],
00945                  _usernum, _timescalled, _posted);
00946        if (_lastcall > 0L) {
00947               scr_printf(" / Curr login: %s",
00948                         asctime(localtime(&_lastcall)));
00949        }
00950        scr_printf("\n");
00951 
00952        /* show preferences */
00953        scr_printf("Are you an experienced Citadel user: ");                   color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_EXPERT) ? "Yes" : "No");     color(DIM_WHITE);
00954        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);
00955        scr_printf("Prompt after each message: ");                             color(BRIGHT_CYAN); scr_printf("%s\n", (!(user->flags & US_NOPROMPT))? "Yes" : "No"); color(DIM_WHITE);
00956        if ((user->flags & US_NOPROMPT) == 0) {
00957        scr_printf("Use 'disappearing' prompts: ");                        color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_DISAPPEAR)? "Yes" : "No");   color(DIM_WHITE);
00958        }
00959        scr_printf("Pause after each screenful of text: ");                    color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_PAGINATOR)? "Yes" : "No");   color(DIM_WHITE);
00960     if (rc_prompt_control == 3 && (user->flags & US_PAGINATOR)) {
00961        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);
00962        }
00963     if (rc_floor_mode == RC_DEFAULT) {
00964        scr_printf("View rooms by floor: ");                               color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_FLOORS)? "Yes" : "No");            color(DIM_WHITE);
00965        }
00966        if (rc_ansi_color == 3)     {
00967            scr_printf("Enable color support: ");                              color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_COLOR)? "Yes" : "No");         color(DIM_WHITE);
00968        }
00969        scr_printf("Be unlisted in userlog: ");                                color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_UNLISTED)? "Yes" : "No");    color(DIM_WHITE);
00970        if (!IsEmptyStr(editor_path)) {
00971        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);
00972        }
00973        free(user);
00974 }
00975 
00976 /*
00977  * Display system statistics.
00978  */
00979 void system_info(CtdlIPC *ipc)
00980 {
00981        char buf[SIZ];
00982        char *resp = NULL;
00983        size_t bytes;
00984        int mrtg_users, mrtg_active_users; 
00985        char mrtg_server_uptime[40];
00986        long mrtg_himessage;
00987 
00988        /* get #users, #active & server uptime */
00989        CtdlIPCGenericCommand(ipc, "MRTG|users", NULL, 0, &resp, &bytes, buf);
00990        mrtg_users = extract_int(resp, 0);
00991        remove_token(resp, 0, '\n');
00992        mrtg_active_users = extract_int(resp, 0);
00993        remove_token(resp, 0, '\n');
00994        extract_token(mrtg_server_uptime, resp, 0, '\n', sizeof mrtg_server_uptime);
00995        free(resp);
00996        resp = NULL;
00997 
00998        /* get high message# */
00999        CtdlIPCGenericCommand(ipc, "MRTG|messages", NULL, 0, &resp, &bytes, buf);
01000        mrtg_himessage = extract_long(resp, 0);
01001        free(resp);
01002        resp = NULL;
01003 
01004        /* refresh server info just in case */
01005        CtdlIPCServerInfo(ipc, buf);
01006 
01007        scr_printf("You are connected to %s (%s) @%s\n", ipc->ServInfo.nodename, ipc->ServInfo.humannode, ipc->ServInfo.fqdn);
01008        scr_printf("running %s with text client v%.2f,\n", ipc->ServInfo.software, (float)REV_LEVEL/100);
01009        scr_printf("server build %s,\n", ipc->ServInfo.svn_revision, (float)REV_LEVEL/100);
01010     scr_printf("and located in %s.\n", ipc->ServInfo.site_location);
01011     scr_printf("Connected users %d / Active users %d / Highest message #%ld\n", mrtg_users, mrtg_active_users, mrtg_himessage);
01012     scr_printf("Server uptime: %s\n", mrtg_server_uptime);
01013     scr_printf("Your system administrator is %s.\n", ipc->ServInfo.sysadm);
01014     scr_printf("Copyright (C)1987-2009 by the Citadel development team\n");
01015 }
01016 
01017 /*
01018  * forget all rooms on current floor
01019  */
01020 void forget_this_floor(CtdlIPC *ipc)
01021 {
01022        if (curr_floor == 0) {
01023               scr_printf("Can't forget this floor.\n");
01024               return;
01025        }
01026        if (floorlist[0][0] == 0) {
01027               load_floorlist(ipc);
01028        }
01029        scr_printf("Are you sure you want to forget all rooms on %s? ",
01030               &floorlist[(int) curr_floor][0]);
01031        if (yesno() == 0) {
01032               return;
01033        }
01034 
01035        gf_toroom(ipc, "_BASEROOM_", GF_ZAP);
01036 }
01037 
01038 
01039 /*
01040  * set floor mode depending on client, server, and user settings
01041  */
01042 void set_floor_mode(CtdlIPC* ipc)
01043 {
01044        if (ipc->ServInfo.ok_floors == 0) {
01045               floor_mode = 0;      /* Don't use floors if the server */
01046        }
01047        /* doesn't support them!          */
01048        else {
01049               if (rc_floor_mode == RC_NO) {      /* never use floors */
01050                      floor_mode = 0;
01051               }
01052               if (rc_floor_mode == RC_YES) {     /* always use floors */
01053                      floor_mode = 1;
01054               }
01055               if (rc_floor_mode == RC_DEFAULT) { /* user choice */
01056                      floor_mode = ((userflags & US_FLOORS) ? 1 : 0);
01057               }
01058        }
01059 }
01060 
01061 /*
01062  * Set or change the user's password
01063  */
01064 int set_password(CtdlIPC *ipc)
01065 {
01066        char pass1[20];
01067        char pass2[20];
01068        char buf[SIZ];
01069 
01070        if (!IsEmptyStr(rc_password)) {
01071               strcpy(pass1, rc_password);
01072               strcpy(pass2, rc_password);
01073        } else {
01074               IFNEXPERT formout(ipc, "changepw");
01075               newprompt("Enter a new password: ", pass1, -19);
01076               newprompt("Enter it again to confirm: ", pass2, -19);
01077        }
01078        strproc(pass1);
01079        strproc(pass2);
01080        if (!strcasecmp(pass1, pass2)) {
01081               CtdlIPCChangePassword(ipc, pass1, buf);
01082               scr_printf("%s\n", buf);
01083               offer_to_remember_password(ipc, hostbuf, portbuf, fullname, pass1);
01084               return (0);
01085        } else {
01086               scr_printf("*** They don't match... try again.\n");
01087               return (1);
01088        }
01089 }
01090 
01091 
01092 
01093 /*
01094  * get info about the server we've connected to
01095  */
01096 void get_serv_info(CtdlIPC *ipc, char *supplied_hostname)
01097 {
01098        char buf[SIZ];
01099 
01100        CtdlIPCServerInfo(ipc, buf);
01101        moreprompt = ipc->ServInfo.moreprompt;
01102 
01103        /* be nice and identify ourself to the server */
01104        CtdlIPCIdentifySoftware(ipc, SERVER_TYPE, 0, REV_LEVEL,
01105                (ipc->isLocal ? "local" : CITADEL),
01106                (supplied_hostname) ? supplied_hostname : 
01107                /* Look up the , in the bible if you're confused */
01108                (locate_host(ipc, buf), buf), buf);
01109 
01110        /* Indicate to the server that we prefer to decode Base64 and
01111         * quoted-printable on the client side.
01112         */
01113        if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "dont_decode") / 100 ) != 2) {
01114               scr_printf("ERROR: Extremely old server; MSG4 framework not supported.\n");
01115               logoff(ipc, 0);
01116        }
01117 
01118        /*
01119         * Tell the server what our preferred content formats are.
01120         *
01121         * Originally we preferred HTML over plain text because we can format
01122         * it to the reader's screen width, but since our HTML-to-text parser
01123         * isn't really all that great, it's probably better to just go with
01124         * the plain text when we have it available.
01125         */
01126        if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "text/plain|text/html") / 100 ) != 2) {
01127               scr_printf("ERROR: Extremely old server; MSG4 framework not supported.\n");
01128               logoff(ipc, 0);
01129        }
01130 }
01131 
01132 
01133 
01134 /*
01135  * Session username compare function for SortOnlineUsers()
01136  */
01137 int rwho_username_cmp(const void *rec1, const void *rec2) {
01138        char *u1, *u2;
01139 
01140        u1 = strchr(rec1, '|');
01141        u2 = strchr(rec2, '|');
01142 
01143        return strcasecmp( (u1?++u1:"") , (u2?++u2:"") );
01144 }
01145 
01146 
01147 /*
01148  * Idle time compare function for SortOnlineUsers()
01149  */
01150 int idlecmp(const void *rec1, const void *rec2) {
01151        time_t i1, i2;
01152 
01153        i1 = extract_long(rec1, 5);
01154        i2 = extract_long(rec2, 5);
01155 
01156        if (i1 < i2) return(1);
01157        if (i1 > i2) return(-1);
01158        return(0);
01159 }
01160 
01161 
01162 /*
01163  * Sort the list of online users by idle time.
01164  * This function frees the supplied buffer, and returns a new one
01165  * to the caller.  The caller is responsible for freeing the returned buffer.
01166  *
01167  * If 'condense' is nonzero, multiple sessions for the same user will be
01168  * combined into one for brevity.
01169  */
01170 char *SortOnlineUsers(char *listing, int condense) {
01171        int rows;
01172        char *sortbuf;
01173        char *retbuf;
01174        char buf[SIZ];
01175        int i;
01176 
01177        rows = num_tokens(listing, '\n');
01178        sortbuf = malloc(rows * SIZ);
01179        if (sortbuf == NULL) return(listing);
01180        retbuf = malloc(rows * SIZ);
01181        if (retbuf == NULL) {
01182               free(sortbuf);
01183               return(listing);
01184        }
01185 
01186        /* Copy the list into a fixed-record-size array for sorting */
01187        for (i=0; i<rows; ++i) {
01188               memset(buf, 0, SIZ);
01189               extract_token(buf, listing, i, '\n', sizeof buf);
01190               memcpy(&sortbuf[i*SIZ], buf, (size_t)SIZ);
01191        }
01192 
01193        /* Sort by idle time */
01194        qsort(sortbuf, rows, SIZ, idlecmp);
01195 
01196        /* Combine multiple sessions for the same user */
01197        if (condense) {
01198               qsort(sortbuf, rows, SIZ, rwho_username_cmp);
01199               if (rows > 1) for (i=1; i<rows; ++i) if (i>0) {
01200                      char u1[USERNAME_SIZE];
01201                      char u2[USERNAME_SIZE];
01202                      extract_token(u1, &sortbuf[(i-1)*SIZ], 1, '|', sizeof u1);
01203                      extract_token(u2, &sortbuf[i*SIZ], 1, '|', sizeof u2);
01204                      if (!strcasecmp(u1, u2)) {
01205                             memcpy(&sortbuf[i*SIZ], &sortbuf[(i+1)*SIZ], (rows-i-1)*SIZ);
01206                             --rows;
01207                             --i;
01208                      }
01209               }
01210 
01211               qsort(sortbuf, rows, SIZ, idlecmp);       /* idle sort again */
01212        }
01213 
01214        /* Copy back to a \n delimited list */
01215        strcpy(retbuf, "");
01216        for (i=0; i<rows; ++i) {
01217               if (!IsEmptyStr(&sortbuf[i*SIZ])) {
01218                      strcat(retbuf, &sortbuf[i*SIZ]);
01219                      if (i<(rows-1)) strcat(retbuf, "\n");
01220               }
01221        }
01222        free(listing);
01223        free(sortbuf);
01224        return(retbuf);
01225 }
01226 
01227 
01228 
01229 /*
01230  * Display list of users currently logged on to the server
01231  */
01232 void who_is_online(CtdlIPC *ipc, int longlist)
01233 {
01234        char buf[SIZ], username[SIZ], roomname[SIZ], fromhost[SIZ];
01235        char flags[SIZ];
01236        char actual_user[SIZ], actual_room[SIZ], actual_host[SIZ];
01237        char clientsoft[SIZ];
01238        time_t timenow = 0;
01239        time_t idletime, idlehours, idlemins, idlesecs;
01240        int last_session = (-1);
01241        int skipidle = 0;
01242        char *listing = NULL;
01243        int r;                      /* IPC response code */
01244     
01245        if (longlist == 2) {
01246               longlist = 0;
01247               skipidle = 1;
01248        }
01249 
01250        if (!longlist) {
01251               color(BRIGHT_WHITE);
01252               scr_printf("           User Name               Room          ");
01253               if (screenwidth >= 80) scr_printf(" Idle        From host");
01254               scr_printf("\n");
01255               color(DIM_WHITE);
01256               scr_printf("   ------------------------- --------------------");
01257               if (screenwidth >= 80) scr_printf(" ---- ------------------------");
01258               scr_printf("\n");
01259        }
01260        r = CtdlIPCOnlineUsers(ipc, &listing, &timenow, buf);
01261        listing = SortOnlineUsers(listing, (!longlist));
01262        if (r / 100 == 1) {
01263               while (!IsEmptyStr(listing)) {
01264                      int isidle = 0;
01265                      
01266                      /* Get another line */
01267                      extract_token(buf, listing, 0, '\n', sizeof buf);
01268                      remove_token(listing, 0, '\n');
01269 
01270                      extract_token(username, buf, 1, '|', sizeof username);
01271                      extract_token(roomname, buf, 2, '|', sizeof roomname);
01272                      extract_token(fromhost, buf, 3, '|', sizeof fromhost);
01273                      extract_token(clientsoft, buf, 4, '|', sizeof clientsoft);
01274                      extract_token(flags, buf, 7, '|', sizeof flags);
01275 
01276                      idletime = timenow - extract_long(buf, 5);
01277                      idlehours = idletime / 3600;
01278                      idlemins = (idletime - (idlehours * 3600)) / 60;
01279                      idlesecs = (idletime - (idlehours * 3600) - (idlemins * 60));
01280 
01281                      if (idletime > rc_idle_threshold) {
01282                             if (skipidle) {
01283                                    isidle = 1;
01284                             }
01285                      }
01286 
01287                      if (longlist) {
01288                             extract_token(actual_user, buf, 8, '|', sizeof actual_user);
01289                             extract_token(actual_room, buf, 9, '|', sizeof actual_room);
01290                             extract_token(actual_host, buf, 10, '|', sizeof actual_host);
01291 
01292                             scr_printf("  Flags: %s\n", flags);
01293                             scr_printf("Session: %d\n", extract_int(buf, 0));
01294                             scr_printf("   Name: %s\n", username);
01295                             scr_printf("In room: %s\n", roomname);
01296                             scr_printf("   Host: %s\n", fromhost);
01297                             scr_printf(" Client: %s\n", clientsoft);
01298                             scr_printf("   Idle: %ld:%02ld:%02ld\n",
01299                                    (long) idlehours,
01300                                    (long) idlemins,
01301                                    (long) idlesecs);
01302 
01303                             if ( (!IsEmptyStr(actual_user)&&
01304                                   !IsEmptyStr(actual_room)&&
01305                                   !IsEmptyStr(actual_host))) {
01306                                    scr_printf("(really ");
01307                                    if (!IsEmptyStr(actual_user)) scr_printf("<%s> ", actual_user);
01308                                    if (!IsEmptyStr(actual_room)) scr_printf("in <%s> ", actual_room);
01309                                    if (!IsEmptyStr(actual_host)) scr_printf("from <%s> ", actual_host);
01310                                    scr_printf(")\n");
01311                             }
01312                             scr_printf("\n");
01313 
01314                      } else {
01315                             if (isidle == 0) {
01316                                    if (extract_int(buf, 0) == last_session) {
01317                                           scr_printf("        ");
01318                                    }
01319                                    else {
01320                                           color(BRIGHT_MAGENTA);
01321                                           scr_printf("%-3s", flags);
01322                                    }
01323                                    last_session = extract_int(buf, 0);
01324                                    color(BRIGHT_CYAN);
01325                                    scr_printf("%-25s ", username);
01326                                    color(BRIGHT_MAGENTA);
01327                                    roomname[20] = 0;
01328                                    scr_printf("%-20s", roomname);
01329 
01330                                    if (screenwidth >= 80) {
01331                                           scr_printf(" ");
01332                                           if (idletime > rc_idle_threshold) {
01333                                                  /* over 1000d, must be gone fishing */
01334                                                  if (idlehours > 23999) {
01335                                                         scr_printf("fish");
01336                                                  /* over 10 days */
01337                                                  } else if (idlehours > 239) {
01338                                                         scr_printf("%3ldd", idlehours / 24);
01339                                                  /* over 10 hours */
01340                                                  } else if (idlehours > 9) {
01341                                                         scr_printf("%1ldd%02ld",
01342                                                                idlehours / 24,
01343                                                                idlehours % 24);
01344                                                  /* less than 10 hours */
01345                                                  }
01346                                                  else {
01347                                                         scr_printf("%1ld:%02ld", idlehours, idlemins);
01348                                                  }
01349                                           }
01350                                           else {
01351                                                  scr_printf("    ");
01352                                           }
01353                                           scr_printf(" ");
01354                                           color(BRIGHT_CYAN);
01355                                           fromhost[24] = '\0';
01356                                           scr_printf("%-24s", fromhost);
01357                                    }
01358                                    scr_printf("\n");
01359                                    color(DIM_WHITE);
01360                             }
01361                      }
01362               }
01363        }
01364        free(listing);
01365 }
01366 
01367 void enternew(CtdlIPC *ipc, char *desc, char *buf, int maxlen)
01368 {
01369        char bbb[128];
01370        snprintf(bbb, sizeof bbb, "Enter in your new %s: ", desc);
01371        newprompt(bbb, buf, maxlen);
01372 }
01373 
01374 
01375 
01376 int shift(int argc, char **argv, int start, int count) {
01377        int i;
01378 
01379        for (i=start; i<(argc-count); ++i) {
01380               argv[i] = argv[i+count];
01381        }
01382        argc = argc - count;
01383        return argc;
01384 }
01385 
01386 static void statusHook(char *s) {
01387        scr_printf(s);
01388 }
01389 
01390 /*
01391  * main
01392  */
01393 int main(int argc, char **argv)
01394 {
01395        int a, b, mcmd;
01396        char aaa[100], bbb[100];/* general purpose variables */
01397        char argbuf[64];     /* command line buf */
01398        char nonce[NONCE_SIZE];
01399        char *telnet_client_host = NULL;
01400        char *sptr, *sptr2;  /* USed to extract the nonce */
01401        char hexstring[MD5_HEXSTRING_SIZE];
01402        char password[SIZ];
01403        struct ctdlipcmisc chek;
01404        struct ctdluser *myself = NULL;
01405        CtdlIPC* ipc;               /* Our server connection */
01406        int r;                      /* IPC result code */
01407        int rv = 0;                 /* fetch but ignore syscall return value to suppress warnings */
01408 
01409        int relh=0;
01410        int home=0;
01411        char relhome[PATH_MAX]="";
01412        char ctdldir[PATH_MAX]=CTDLDIR;
01413     int lp; 
01414 #ifdef HAVE_BACKTRACE
01415        eCrashParameters params;
01416 //     eCrashSymbolTable symbol_table;
01417 #endif
01418        calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
01419 
01420 #ifdef HAVE_BACKTRACE
01421        bzero(&params, sizeof(params));
01422        params.filename = file_pid_paniclog;
01423 //     panic_fd=open(file_pid_paniclog, O_APPEND|O_CREAT|O_DIRECT);
01424        params.filep = fopen(file_pid_paniclog, "a+");
01425        params.debugLevel = ECRASH_DEBUG_VERBOSE;
01426        params.dumpAllThreads = TRUE;
01427        params.useBacktraceSymbols = 1;
01429 //     params.symbolTable = &symbol_table;
01430        params.signals[0]=SIGSEGV;
01431        params.signals[1]=SIGILL;
01432        params.signals[2]=SIGBUS;
01433        params.signals[3]=SIGABRT;
01434 
01435        eCrash_Init(&params);
01436 #endif 
01437        setIPCErrorPrintf(scr_printf);
01438        setCryptoStatusHook(statusHook);
01439        
01440        /* Permissions sanity check - don't run citadel setuid/setgid */
01441        if (getuid() != geteuid()) {
01442               scr_printf("Please do not run citadel setuid!\n");
01443               logoff(NULL, 3);
01444        } else if (getgid() != getegid()) {
01445               scr_printf("Please do not run citadel setgid!\n");
01446               logoff(NULL, 3);
01447        }
01448 
01449        stty_ctdl(SB_SAVE);  /* Store the old terminal parameters */
01450        load_command_set();  /* parse the citadel.rc file */
01451        stty_ctdl(SB_NO_INTR);      /* Install the new ones */
01452        /* signal(SIGHUP, dropcarr);FIXME */      /* Cleanup gracefully if carrier is dropped */
01453        signal(SIGPIPE, dropcarr);  /* Cleanup gracefully if local conn. dropped */
01454        signal(SIGTERM, dropcarr);  /* Cleanup gracefully if terminated */
01455        signal(SIGCONT, catch_sigcont);    /* Catch SIGCONT so we can reset terminal */
01456 #ifdef SIGWINCH
01457        signal(SIGWINCH, scr_winch);       /* Window resize signal */
01458 #endif
01459 
01460 #ifdef HAVE_OPENSSL
01461        arg_encrypt = RC_DEFAULT;
01462 #endif
01463 
01464        /* 
01465         * Handle command line options as if we were called like /bin/login
01466         * (i.e. from in.telnetd)
01467         */
01468        for (a=0; a<argc; ++a) {
01469               if ((argc > a+1) && (!strcmp(argv[a], "-h")) ) {
01470                      telnet_client_host = argv[a+1];
01471                      argc = shift(argc, argv, a, 2);
01472               }
01473               if (!strcmp(argv[a], "-x")) {
01474 #ifdef HAVE_OPENSSL
01475                      arg_encrypt = RC_NO;
01476 #endif
01477                      argc = shift(argc, argv, a, 1);
01478               }
01479               if (!strcmp(argv[a], "-X")) {
01480 #ifdef HAVE_OPENSSL
01481                      arg_encrypt = RC_YES;
01482                      argc = shift(argc, argv, a, 1);
01483 #else
01484                      fprintf(stderr, "Not compiled with encryption support");
01485                      return 1;
01486 #endif
01487               }
01488               if (!strcmp(argv[a], "-p")) {
01489                      struct stat st;
01490               
01491                      if (chdir(CTDLDIR) < 0) {
01492                             perror("can't change to " CTDLDIR);
01493                             logoff(NULL, 3);
01494                      }
01495 
01496                      /*
01497                       * Drop privileges if necessary. We stat
01498                       * citadel.config to get the uid/gid since it's
01499                       * guaranteed to have the uid/gid we want.
01500                       */
01501                      if (!getuid() || !getgid()) {
01502                             if (stat(file_citadel_config, &st) < 0) {
01503                                    perror("couldn't stat citadel.config");
01504                                    logoff(NULL, 3);
01505                             }
01506                             if (!getgid() && (setgid(st.st_gid) < 0)) {
01507                                    perror("couldn't change gid");
01508                                    logoff(NULL, 3);
01509                             }
01510                             if (!getuid() && (setuid(st.st_uid) < 0)) {
01511                                    perror("couldn't change uid");
01512                                    logoff(NULL, 3);
01513                             }
01514                             /*
01515                               scr_printf("Privileges changed to uid %d gid %d\n",
01516                               getuid(), getgid());
01517                             */
01518                      }
01519                      argc = shift(argc, argv, a, 1);
01520               }
01521        }
01522        
01523 
01524        screen_new();
01525        /* Get screen dimensions.  First we go to a default of 80x24.
01526         * Then attempt to read the actual screen size from the terminal.
01527         */
01528        check_screen_dims();
01529 
01530 
01531 #ifdef __CYGWIN__
01532        newprompt("Connect to (return for local server): ", hostbuf, 64);
01533 #endif
01534 
01535        scr_printf("Attaching to server...\n");
01536        ipc = CtdlIPC_new(argc, argv, hostbuf, portbuf);
01537        if (!ipc) {
01538               error_printf("Can't connect: %s\n", strerror(errno));
01539               logoff(NULL, 3);
01540        }
01541 
01542        CtdlIPC_SetNetworkStatusCallback(ipc, scr_wait_indicator);
01543 
01544        if (!(ipc->isLocal)) {
01545               scr_printf("Connected to %s [%s].\n", ipc->ip_hostname, ipc->ip_address);
01546        }
01547 
01548        ipc_for_signal_handlers = ipc;     /* KLUDGE cover your eyes */
01549 
01550        CtdlIPC_chat_recv(ipc, aaa);
01551        if (aaa[0] != '2') {
01552               scr_printf("%s\n", &aaa[4]);
01553               logoff(ipc, atoi(aaa));
01554        }
01555 
01556        /* If there is a [nonce] at the end, put the nonce in <nonce>, else nonce
01557         * is zeroized.
01558         */
01559        
01560        if ((sptr = strchr(aaa, '<')) == NULL)
01561               {
01562                      nonce[0] = '\0';
01563               }
01564        else
01565               {
01566                      if ((sptr2 = strchr(sptr, '>')) == NULL)
01567                             {
01568                                    nonce[0] = '\0';
01569                             }
01570                      else
01571                             {
01572                                    sptr2++;
01573                                    *sptr2 = '\0';
01574                                    strncpy(nonce, sptr, (size_t)NONCE_SIZE);
01575                             }
01576               }
01577 
01578 #ifdef HAVE_OPENSSL
01579        /* Evaluate encryption preferences */
01580        if (arg_encrypt != RC_NO && rc_encrypt != RC_NO) {
01581               if (!ipc->isLocal || arg_encrypt == RC_YES || rc_encrypt == RC_YES) {
01582                      secure = (CtdlIPCStartEncryption(ipc, aaa) / 100 == 2) ? 1 : 0;
01583                      if (!secure)
01584                             error_printf("Can't encrypt: %s\n", aaa);
01585               }
01586        }
01587 #endif
01588 
01589        get_serv_info(ipc, telnet_client_host);
01590        scr_printf("%-24s\n%s\n%s\n", ipc->ServInfo.software, ipc->ServInfo.humannode,
01591                  ipc->ServInfo.site_location);
01592 
01593        scr_printf(" pause    next    stop\n");
01594        scr_printf(" ctrl-s  ctrl-o  ctrl-c\n\n");
01595        formout(ipc, "hello");      /* print the opening greeting */
01596        scr_printf("\n");
01597 
01598  GSTA: /* See if we have a username and password on disk */
01599        if (rc_remember_passwords) {
01600               get_stored_password(hostbuf, portbuf, fullname, password);
01601               if (!IsEmptyStr(fullname)) {
01602                      r = CtdlIPCTryLogin(ipc, fullname, aaa);
01603                      if (r / 100 == 3) {
01604                             if (*nonce) {
01605                                    r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
01606                             } else {
01607                                    r = CtdlIPCTryPassword(ipc, password, aaa);
01608                             }
01609                      }
01610 
01611                      if (r / 100 == 2) {
01612                             load_user_info(aaa);
01613                             goto PWOK;
01614                      } else {
01615                             set_stored_password(hostbuf, portbuf, "", "");
01616                      }
01617               }
01618        }
01619 
01620        termn8 = 0;
01621        newnow = 0;
01622        do {
01623               if (!IsEmptyStr(rc_username)) {
01624                      strcpy(fullname, rc_username);
01625               } else {
01626                      newprompt("Enter your name: ", fullname, 29);
01627               }
01628               strproc(fullname);
01629               if (!strcasecmp(fullname, "new")) {       /* just in case */
01630                      scr_printf("Please enter the name you wish to log in with.\n");
01631               }
01632        } while (
01633                (!strcasecmp(fullname, "bbs"))
01634                || (!strcasecmp(fullname, "new"))
01635                || (IsEmptyStr(fullname)));
01636 
01637        if (!strcasecmp(fullname, "off")) {
01638               mcmd = 29;
01639               goto TERMN8;
01640        }
01641 
01642        /* FIXME this is a stupid way to do guest mode but it's a reasonable test harness FIXME */
01643        if ( (ipc->ServInfo.guest_logins) && (!strcasecmp(fullname, "guest")) ) {
01644               goto PWOK;
01645        }
01646 
01647        /* sign on to the server */
01648        r = CtdlIPCTryLogin(ipc, fullname, aaa);
01649        if (r / 100 != 3)
01650               goto NEWUSR;
01651 
01652        /* password authentication */
01653        if (!IsEmptyStr(rc_password)) {
01654               strcpy(password, rc_password);
01655        } else {
01656               newprompt("\rPlease enter your password: ", password, -(SIZ-1));
01657        }
01658 
01659        if (*nonce) {
01660               r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
01661               if (r / 100 != 2) {
01662                      strproc(password);
01663                      r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
01664               }
01665        } else {
01666               r = CtdlIPCTryPassword(ipc, password, aaa);
01667               if (r / 100 != 2) {
01668                      strproc(password);
01669                      r = CtdlIPCTryPassword(ipc, password, aaa);
01670               }
01671        }
01672        
01673        if (r / 100 == 2) {
01674               load_user_info(aaa);
01675               offer_to_remember_password(ipc, hostbuf, portbuf,
01676                                       fullname, password);
01677               goto PWOK;
01678        }
01679        scr_printf("<< wrong password >>\n");
01680        if (!IsEmptyStr(rc_password))
01681               logoff(ipc, 2);
01682        goto GSTA;
01683 
01684 NEWUSR:       if (IsEmptyStr(rc_password)) {
01685               scr_printf("'%s' not found.\n", fullname);
01686               scr_printf("Type 'off' if you would like to exit.\n");
01687               if (ipc->ServInfo.newuser_disabled == 1) {
01688                      goto GSTA;
01689               }
01690               scr_printf("Do you want to create a new user account called '%s'? ",
01691                      fullname);
01692               if (yesno() == 0) {
01693                      goto GSTA;
01694               }
01695        }
01696 
01697        r = CtdlIPCCreateUser(ipc, fullname, 1, aaa);
01698        if (r / 100 != 2) {
01699               scr_printf("%s\n", aaa);
01700               goto GSTA;
01701        }
01702        load_user_info(aaa);
01703 
01704        while (set_password(ipc) != 0);
01705        newnow = 1;
01706 
01707        enter_config(ipc, 1);
01708 
01709  PWOK:
01710        /* Switch color support on or off if we're in user mode */
01711        if (rc_ansi_color == 3) {
01712               if (userflags & US_COLOR)
01713                      enable_color = 1;
01714               else
01715                      enable_color = 0;
01716        }
01717 
01718        color(BRIGHT_WHITE);
01719        scr_printf("\n%s\nAccess level: %d (%s)\n"
01720                  "User #%ld / Login #%d",
01721                  fullname, axlevel, axdefs[(int) axlevel],
01722                  usernum, timescalled);
01723        if (lastcall > 0L) {
01724               scr_printf(" / Last login: %s",
01725                         asctime(localtime(&lastcall)));
01726        }
01727        scr_printf("\n");
01728 
01729        r = CtdlIPCMiscCheck(ipc, &chek, aaa);
01730        if (r / 100 == 2) {
01731               b = chek.newmail;
01732               if (b > 0) {
01733                      color(BRIGHT_RED);
01734                      if (b == 1)
01735                             scr_printf("*** You have a new private message in Mail>\n");
01736                      if (b > 1)
01737                             scr_printf("*** You have %d new private messages in Mail>\n", b);
01738                      color(DIM_WHITE);
01739                      if (!IsEmptyStr(rc_gotmail_cmd)) {
01740                             rv = system(rc_gotmail_cmd);
01741                             if (rv)
01742                                    scr_printf("*** failed to check for mail calling %s Reason %d.\n",
01743                                              rc_gotmail_cmd, rv);
01744 
01745                      }
01746               }
01747               if ((axlevel >= AxAideU) && (chek.needvalid > 0)) {
01748                      scr_printf("*** Users need validation\n");
01749               }
01750               if (chek.needregis > 0) {
01751                      scr_printf("*** Please register.\n");
01752                      formout(ipc, "register");
01753                      entregis(ipc);
01754               }
01755        }
01756        /* Make up some temporary filenames for use in various parts of the
01757         * program.  Don't mess with these once they've been set, because we
01758         * will be unlinking them later on in the program and we don't
01759         * want to delete something that we didn't create. */
01760        CtdlMakeTempFileName(temp, sizeof temp);
01761        CtdlMakeTempFileName(temp2, sizeof temp2);
01762        CtdlMakeTempFileName(tempdir, sizeof tempdir);
01763 
01764        r = CtdlIPCGetConfig(ipc, &myself, aaa);
01765        set_floor_mode(ipc);
01766 
01767        /* Enter the lobby */
01768        dotgoto(ipc, "_BASEROOM_", 1, 0);
01769 
01770        /* Main loop for the system... user is logged in. */
01771     free(uglist[0]);
01772        uglistsize = 0;
01773 
01774        if (newnow == 1)
01775               readmsgs(ipc, LastMessages, ReadForward, 5);
01776        else
01777               readmsgs(ipc, NewMessages, ReadForward, 0);
01778 
01779        /* MAIN COMMAND LOOP */
01780        do {
01781               mcmd = getcmd(ipc, argbuf); /* Get keyboard command */
01782 
01783 #ifdef TIOCGWINSZ
01784               check_screen_dims();        /* get screen size */
01785 #endif
01786 
01787               if (termn8 == 0)
01788                      switch (mcmd) {
01789                      case 1:
01790                             formout(ipc, "help");
01791                             break;
01792                      case 4:
01793                             entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 0);
01794                             break;
01795                      case 36:
01796                             entmsg(ipc, 0, 1, 0);
01797                             break;
01798                      case 46:
01799                             entmsg(ipc, 0, 2, 0);
01800                             break;
01801                      case 78:
01802                             entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 1);
01803                             break;
01804                      case 5:                            /* <G>oto */
01805                             updatels(ipc);
01806                             gotonext(ipc);
01807                             break;
01808                      case 47:                    /* <A>bandon */
01809                             gotonext(ipc);
01810                             break;
01811                      case 90:                    /* <.A>bandon */
01812                             dotgoto(ipc, argbuf, 0, 0);
01813                             break;
01814                      case 58:                    /* <M>ail */
01815                             updatelsa(ipc);
01816                             dotgoto(ipc, "_MAIL_", 1, 0);
01817                             break;
01818                      case 20:
01819                             if (!IsEmptyStr(argbuf)) {
01820                                    updatels(ipc);
01821                                    dotgoto(ipc, argbuf, 0, 0);
01822                             }
01823                             break;
01824                      case 52:
01825                             if (!IsEmptyStr(argbuf)) {
01826                                    dotgoto(ipc, argbuf, 0, 0);
01827                             }
01828                             break;
01829                      case 95: /* what exactly is the numbering scheme supposed to be anyway? --Ford, there isn't one. -IO */
01830                             dotungoto(ipc, argbuf);
01831                             break;
01832                      case 10:
01833                             readmsgs(ipc, AllMessages, ReadForward, 0);
01834                             break;
01835                      case 9:
01836                             readmsgs(ipc, LastMessages, ReadForward, 5);
01837                             break;
01838                      case 13:
01839                             readmsgs(ipc, NewMessages, ReadForward, 0);
01840                             break;
01841                      case 11:
01842                             readmsgs(ipc, AllMessages, ReadReverse, 0);
01843                             break;
01844                      case 12:
01845                             readmsgs(ipc, OldMessages, ReadReverse, 0);
01846                             break;
01847                      case 71:
01848                             readmsgs(ipc, LastMessages, ReadForward,
01849                                           atoi(argbuf));
01850                             break;
01851                      case 7:
01852                             forget(ipc);
01853                             break;
01854                      case 18:
01855                             subshell();
01856                             break;
01857                      case 38:
01858                             updatels(ipc);
01859                             entroom(ipc);
01860                             break;
01861                      case 22:
01862                             killroom(ipc);
01863                             break;
01864                      case 32:
01865                             userlist(ipc, argbuf);
01866                             break;
01867                      case 27:
01868                             invite(ipc);
01869                             break;
01870                      case 28:
01871                             kickout(ipc);
01872                             break;
01873                      case 23:
01874                             editthisroom(ipc);
01875                             break;
01876                      case 14:
01877                             roomdir(ipc);
01878                             break;
01879                      case 33:
01880                             download(ipc, 0);
01881                             break;
01882                      case 34:
01883                             download(ipc, 1);
01884                             break;
01885                      case 31:
01886                             download(ipc, 2);
01887                             break;
01888                      case 43:
01889                             download(ipc, 3);
01890                             break;
01891                      case 45:
01892                             download(ipc, 4);
01893                             break;
01894                      case 55:
01895                             download(ipc, 5);
01896                             break;
01897                      case 39:
01898                             upload(ipc, 0);
01899                             break;
01900                      case 40:
01901                             upload(ipc, 1);
01902                             break;
01903                      case 42:
01904                             upload(ipc, 2);
01905                             break;
01906                      case 44:
01907                             upload(ipc, 3);
01908                             break;
01909                      case 57:
01910                             cli_upload(ipc);
01911                             break;
01912                      case 16:
01913                             ungoto(ipc);
01914                             break;
01915                      case 24:
01916                             whoknows(ipc);
01917                             break;
01918                      case 26:
01919                             validate(ipc);
01920                             break;
01921                      case 29:
01922                      case 30:
01923                             updatels(ipc);
01924                             termn8 = 1;
01925                             break;
01926                      case 48:
01927                             enterinfo(ipc);
01928                             break;
01929                      case 49:
01930                             readinfo(ipc);
01931                             break;
01932                      case 72:
01933                             cli_image_upload(ipc, "_userpic_");
01934                             break;
01935                      case 73:
01936                             cli_image_upload(ipc, "_roompic_");
01937                             break;
01938 
01939                      case 74:
01940                             snprintf(aaa, sizeof aaa, "_floorpic_|%d", curr_floor);
01941                             cli_image_upload(ipc, aaa);
01942                             break;
01943 
01944                      case 75:
01945                             enternew(ipc, "roomname", aaa, 20);
01946                             r = CtdlIPCChangeRoomname(ipc, aaa, bbb);
01947                             if (r / 100 != 2)
01948                                    scr_printf("\n%s\n", bbb);
01949                             break;
01950                      case 76:
01951                             enternew(ipc, "hostname", aaa, 25);
01952                             r = CtdlIPCChangeHostname(ipc, aaa, bbb);
01953                             if (r / 100 != 2)
01954                                    scr_printf("\n%s\n", bbb);
01955                             break;
01956                      case 77:
01957                             enternew(ipc, "username", aaa, 32);
01958                             r = CtdlIPCChangeUsername(ipc, aaa, bbb);
01959                             if (r / 100 != 2)
01960                                    scr_printf("\n%s\n", bbb);
01961                             break;
01962 
01963                      case 35:
01964                             set_password(ipc);
01965                             break;
01966 
01967                      case 21:
01968                             if (argbuf[0] == 0)
01969                                    strcpy(aaa, "?");
01970                             display_help(ipc, argbuf);
01971                             break;
01972 
01973                      case 41:
01974                             formout(ipc, "register");
01975                             entregis(ipc);
01976                             break;
01977 
01978                      case 15:
01979                             scr_printf("Are you sure (y/n)? ");
01980                             if (yesno() == 1) {
01981                                    updatels(ipc);
01982                                    a = 0;
01983                                    termn8 = 1;
01984                             }
01985                             break;
01986 
01987                      case 85:
01988                             scr_printf("All users will be disconnected!  "
01989                                       "Really terminate the server? ");
01990                             if (yesno() == 1) {
01991                                    updatels(ipc);
01992                                    r = CtdlIPCTerminateServerNow(ipc, aaa);
01993                                    scr_printf("%s\n", aaa);
01994                                    if (r / 100 == 2) {
01995                                           a = 0;
01996                                           termn8 = 1;
01997                                    }
01998                             }
01999                             break;
02000 
02001                      case 86:
02002                             scr_printf("Do you really want to schedule a "
02003                                       "server shutdown? ");
02004                             if (yesno() == 1) {
02005                                    r = CtdlIPCTerminateServerScheduled(ipc, 1, aaa);
02006                                    if (r / 100 == 2) {
02007                                           if (atoi(aaa)) {
02008                                                  scr_printf(
02009                                                            "The Citadel server will terminate when all users are logged off.\n"
02010                                                            );
02011                                           } else {
02012                                                  scr_printf(
02013                                                            "The Citadel server will not terminate.\n"
02014                                                            );
02015                                           }
02016                                    }
02017                             }
02018                             break;
02019 
02020                      case 87:
02021                             network_config_management(ipc, "listrecp",
02022                                                    "Message-by-message mailing list recipients");
02023                             break;
02024 
02025                      case 94:
02026                             network_config_management(ipc, "digestrecp",
02027                                                    "Digest mailing list recipients");
02028                             break;
02029 
02030                      case 89:
02031                             network_config_management(ipc, "ignet_push_share",
02032                                                    "Nodes with which we share this room");
02033                             break;
02034 
02035                      case 88:
02036                             do_ignet_configuration(ipc);
02037                             break;
02038 
02039                      case 92:
02040                             do_filterlist_configuration(ipc);
02041                             break;
02042 
02043                      case 6:
02044                             gotonext(ipc);
02045                             break;
02046 
02047                      case 3:
02048                             chatmode(ipc);
02049                             break;
02050 
02051                      case 17:
02052                             who_is_online(ipc, 0);
02053                             break;
02054 
02055                      case 79:
02056                             who_is_online(ipc, 1);
02057                             break;
02058 
02059                      case 91:
02060                             who_is_online(ipc, 2);
02061                             break;
02062                 
02063                      case 80:
02064                             do_system_configuration(ipc);
02065                             break;
02066 
02067                      case 82:
02068                             do_internet_configuration(ipc);
02069                             break;
02070 
02071                      case 84:
02072                             quiet_mode(ipc);
02073                             break;
02074 
02075                      case 93:
02076                             stealth_mode(ipc);
02077                             break;
02078 
02079                      case 50:
02080                             enter_config(ipc, 2);
02081                             break;
02082 
02083                      case 37:
02084                             enter_config(ipc, 0);
02085                             set_floor_mode(ipc);
02086                             break;
02087 
02088                      case 59:
02089                             enter_config(ipc, 3);
02090                             set_floor_mode(ipc);
02091                             break;
02092 
02093                      case 60:
02094                             gotofloor(ipc, argbuf, GF_GOTO);
02095                             break;
02096 
02097                      case 61:
02098                             gotofloor(ipc, argbuf, GF_SKIP);
02099                             break;
02100 
02101                      case 62:
02102                             forget_this_floor(ipc);
02103                             break;
02104 
02105                      case 63:
02106                             create_floor(ipc);
02107                             break;
02108 
02109                      case 64:
02110                             edit_floor(ipc);
02111                             break;
02112 
02113                      case 65:
02114                             kill_floor(ipc);
02115                             break;
02116 
02117                      case 66:
02118                             enter_bio(ipc);
02119                             break;
02120 
02121                      case 67:
02122                             read_bio(ipc);
02123                             break;
02124 
02125                      case 25:
02126                             edituser(ipc, 25);
02127                             break;
02128 
02129                      case 96:
02130                             edituser(ipc, 96);
02131                             break;
02132 
02133                      case 8:
02134                             knrooms(ipc, floor_mode);
02135                             scr_printf("\n");
02136                             break;
02137 
02138                      case 68:
02139                             knrooms(ipc, 2);
02140                             scr_printf("\n");
02141                             break;
02142 
02143                      case 69:
02144                             misc_server_cmd(ipc, argbuf);
02145                             break;
02146 
02147                      case 70:
02148                             edit_system_message(ipc, argbuf);
02149                             break;
02150 
02151                      case 19:
02152                             listzrooms(ipc);
02153                             scr_printf("\n");
02154                             break;
02155 
02156                      case 51:
02157                             deletefile(ipc);
02158                             break;
02159 
02160                      case 54:
02161                             movefile(ipc);
02162                             break;
02163 
02164                      case 56:
02165                             page_user(ipc);
02166                             break;
02167 
02168             case 110:           /* <+> Next room */
02169                              gotoroomstep(ipc, 1, 0);
02170                           break;
02171 
02172             case 111:           /* <-> Previous room */
02173                  gotoroomstep(ipc, 0, 0);
02174                           break;
02175 
02176                      case 112:           /* <>> Next floor */
02177                  gotofloorstep(ipc, 1, GF_GOTO);
02178                           break;
02179 
02180                      case 113:           /* <<> Previous floor */
02181                  gotofloorstep(ipc, 0, GF_GOTO);
02182                              break;
02183 
02184             case 116:           /* <.> skip to <+> Next room */
02185                  gotoroomstep(ipc, 1, 1);
02186                           break;
02187 
02188             case 117:           /* <.> skip to <-> Previous room */
02189                  gotoroomstep(ipc, 0, 1);
02190                           break;
02191 
02192                      case 118:           /* <.> skip to <>> Next floor */
02193                  gotofloorstep(ipc, 1, GF_SKIP);
02194                           break;
02195 
02196                      case 119:           /* <.> skip to <<> Previous floor */
02197                  gotofloorstep(ipc, 0, GF_SKIP);
02198                              break;
02199 
02200                      case 114:           
02201                  read_config(ipc);
02202                              break;
02203 
02204                      case 115:           
02205                  system_info(ipc);
02206                              break;
02207 
02208                      case 120:           /* .KAnonymous */
02209                       dotknown(ipc, 0, NULL);
02210                              break;
02211 
02212                      case 121:           /* .KDirectory */
02213                       dotknown(ipc, 1, NULL);
02214                              break;
02215 
02216                      case 122:           /* .KMatch */
02217                       dotknown(ipc, 2, argbuf);
02218                              break;
02219 
02220                      case 123:           /* .KpreferredOnly */
02221                       dotknown(ipc, 3, NULL);
02222                              break;
02223 
02224                      case 124:           /* .KPrivate */
02225                       dotknown(ipc, 4, NULL);
02226                              break;
02227 
02228                      case 125:           /* .KRead only */
02229                       dotknown(ipc, 5, NULL);
02230                              break;
02231 
02232                      case 126:           /* .KShared */
02233                       dotknown(ipc, 6, NULL);
02234                              break;
02235 
02236                      case 127:           /* Configure POP3 aggregation */
02237                             do_pop3client_configuration(ipc);
02238                             break;
02239 
02240                      case 128:           /* Configure XML/RSS feed retrieval */
02241                             do_rssclient_configuration(ipc);
02242                             break;
02243 
02244                      default:
02245                             break;
02246                      }      /* end switch */
02247        } while (termn8 == 0);
02248 
02249 TERMN8:       scr_printf("%s logged out.", fullname);
02250        termn8 = 0;
02251        color(ORIGINAL_PAIR);
02252        scr_printf("\n");
02253        while (marchptr != NULL) {
02254               remove_march(marchptr->march_name, 0);
02255        }
02256        if (mcmd == 30) {
02257               scr_printf("\n\nType 'off' to disconnect, or next user...\n");
02258        }
02259        CtdlIPCLogout(ipc);
02260        if ((mcmd == 29) || (mcmd == 15)) {
02261               stty_ctdl(SB_RESTORE);
02262               formout(ipc, "goodbye");
02263               logoff(ipc, 0);
02264        }
02265        /* Free the ungoto list */
02266        for (lp = 0; lp < uglistsize; lp++) {
02267               free(uglist[lp]);
02268        }
02269     uglistsize = 0;
02270        goto GSTA;
02271 
02272 }      /* end main() */
02273