Fixed bugs related to last three commits
[citadel.git] / citadel / textclient / citadel.c
1 /*
2  * Main source module for the client program.
3  *
4  * Copyright (c) 1987-2009 by the citadel.org team
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "sysdep.h"
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <string.h>
28
29 #if TIME_WITH_SYS_TIME
30 # include <sys/time.h>
31 # include <time.h>
32 #else
33 # if HAVE_SYS_TIME_H
34 #  include <sys/time.h>
35 # else
36 #  include <time.h>
37 # endif
38 #endif
39
40 #include <limits.h>
41 #include <sys/types.h>
42 #include <sys/wait.h>
43 #include <sys/stat.h>
44 #include <sys/ioctl.h>
45 #include <signal.h>
46 #include <pwd.h>
47 #include <stdarg.h>
48 #include <errno.h>
49 #include <libcitadel.h>
50 #include "citadel.h"
51 #include "citadel_ipc.h"
52 #include "axdefs.h"
53 #include "routines.h"
54 #include "routines2.h"
55 #include "tuiconfig.h"
56 #include "rooms.h"
57 #include "messages.h"
58 #include "commands.h"
59 #include "client_chat.h"
60 #include "client_passwords.h"
61 #include "citadel_decls.h"
62 #include "sysdep.h"
63 #ifndef HAVE_SNPRINTF
64 #include "snprintf.h"
65 #endif
66 #include "screen.h"
67 #include "citadel_dirs.h"
68
69 #include "ecrash.h"
70 #include "md5.h"
71
72 #define IFEXPERT if (userflags&US_EXPERT)
73 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
74 #define IFAIDE if (axlevel>=AxAideU)
75 #define IFNAIDE if (axlevel<AxAideU)
76
77 int rordercmp(struct ctdlroomlisting *r1, struct ctdlroomlisting *r2);
78 march *marchptr = NULL;
79 extern char *moreprompt;
80
81 /* globals associated with the client program */
82 char temp[PATH_MAX];            /* Name of general-purpose temp file */
83 char temp2[PATH_MAX];           /* Name of general-purpose temp file */
84 char tempdir[PATH_MAX];         /* Name of general-purpose temp directory */
85 char editor_paths[MAX_EDITORS][SIZ];    /* paths to external editors */
86 char printcmd[SIZ];             /* print command */
87 int editor_pid = (-1);
88 char fullname[USERNAME_SIZE];
89 int screenwidth;
90 int screenheight;
91 unsigned room_flags;
92 unsigned room_flags2;
93 int entmsg_ok = 0;
94 char room_name[ROOMNAMELEN];
95 char *uglist[UGLISTLEN]; /* size of the ungoto list */
96 long uglistlsn[UGLISTLEN]; /* current read position for all the ungoto's. Not going to make any friends with this one. */
97 int uglistsize = 0;
98 char is_mail = 0;               /* nonzero when we're in a mail room */
99 char axlevel = AxDeleted;               /* access level */
100 char is_room_aide = 0;          /* boolean flag, 1 if room aide */
101 int timescalled;
102 int posted;
103 unsigned userflags;
104 long usernum = 0L;              /* user number */
105 time_t lastcall = 0L;           /* Date/time of previous login */
106 char newnow;
107 long highest_msg_read;          /* used for <A>bandon room cmd */
108 long maxmsgnum;                 /* used for <G>oto */
109 char sigcaught = 0;
110 char have_xterm = 0;            /* are we running on an xterm? */
111 char rc_username[USERNAME_SIZE];
112 char rc_password[32];
113 char hostbuf[SIZ];
114 char portbuf[SIZ];
115 char rc_floor_mode;
116 char floor_mode;
117 char curr_floor = 0;            /* number of current floor */
118 char floorlist[128][SIZ];       /* names of floors */
119 int termn8 = 0;                 /* Set to nonzero to cause a logoff */
120 int secure;                     /* Set to nonzero when wire is encrypted */
121
122 extern char instant_msgs;       /* instant messages waiting! */
123 extern int rc_ansi_color;       /* ansi color value from citadel.rc */
124 extern int next_lazy_cmd;
125
126 CtdlIPC *ipc_for_signal_handlers;       /* KLUDGE cover your eyes */
127 int enable_syslog = 0;
128
129
130 /*
131  * here is our 'clean up gracefully and exit' routine
132  */
133 void ctdl_logoff(char *file, int line, CtdlIPC *ipc, int code)
134 {
135         int lp;
136
137         if (editor_pid > 0) {   /* kill the editor if it's running */
138                 kill(editor_pid, SIGHUP);
139         }
140
141         /* Free the ungoto list */
142         for (lp = 0; lp < uglistsize; lp++) {
143                 free(uglist[lp]);
144         }
145
146 /* Shut down the server connection ... but not if the logoff code is 3,
147  * because that means we're exiting because we already lost the server.
148  */
149         if (code != 3) {
150                 CtdlIPCQuit(ipc);
151         }
152
153 /*
154  * now clean up various things
155  */
156         unlink(temp);
157         unlink(temp2);
158         nukedir(tempdir);
159
160         /* Violently kill off any child processes if Citadel is
161          * the login shell. 
162          */
163         if (getppid() == 1) {
164                 kill(0 - getpgrp(), SIGTERM);
165                 sleep(1);
166                 kill(0 - getpgrp(), SIGKILL);
167         }
168         color(ORIGINAL_PAIR);   /* Restore the old color settings */
169         stty_ctdl(SB_RESTORE);  /* return the old terminal settings */
170         /* 
171          * uncomment the following if you need to know why Citadel exited
172         printf("*** Exit code %d at %s:%d\n", code, file, line);
173         sleep(2);
174          */
175         exit(code);             /* exit with the proper exit code */
176 }
177
178
179
180 /*
181  * signal catching function for hangups...
182  */
183 void dropcarr(int signum)
184 {
185         logoff(NULL, 3);        /* No IPC when server's already gone! */
186 }
187
188
189
190 /*
191  * catch SIGCONT to reset terminal modes when were are put back into the
192  * foreground.
193  */
194 void catch_sigcont(int signum)
195 {
196         stty_ctdl(SB_LAST);
197         signal(SIGCONT, catch_sigcont);
198 }
199
200
201 /* general purpose routines */
202
203 /* display a file */
204 void formout(CtdlIPC *ipc, char *name)
205 {
206         int r;                  /* IPC return code */
207         char buf[SIZ];
208         char *text = NULL;
209
210         r = CtdlIPCSystemMessage(ipc, name, &text, buf);
211         if (r / 100 != 1) {
212                 scr_printf("%s\n", buf);
213                 return;
214         }
215         if (text) {
216                 fmout(screenwidth, NULL, text, NULL, 1);
217                 free(text);
218         }
219 }
220
221
222 void userlist(CtdlIPC *ipc, char *patn)
223 {
224         char buf[SIZ];
225         char fl[SIZ];
226         struct tm tmbuf;
227         time_t lc;
228         int r;                          /* IPC response code */
229         char *listing = NULL;
230
231         r = CtdlIPCUserListing(ipc, patn, &listing, buf);
232         if (r / 100 != 1) {
233                 scr_printf("%s\n", buf);
234                 return;
235         }
236
237         scr_printf("       User Name           Num  L Last Visit Logins Messages\n");
238         scr_printf("------------------------- ----- - ---------- ------ --------\n");
239         if (listing != NULL) while (!IsEmptyStr(listing)) {
240                 extract_token(buf, listing, 0, '\n', sizeof buf);
241                 remove_token(listing, 0, '\n');
242
243                 if (sigcaught == 0) {
244                     extract_token(fl, buf, 0, '|', sizeof fl);
245                     if (pattern(fl, patn) >= 0) {
246                         scr_printf("%-25s ", fl);
247                         scr_printf("%5ld %d ", extract_long(buf, 2),
248                                extract_int(buf, 1));
249                         lc = extract_long(buf, 3);
250                         localtime_r(&lc, &tmbuf);
251                         scr_printf("%02d/%02d/%04d ",
252                                (tmbuf.tm_mon + 1),
253                                tmbuf.tm_mday,
254                                (tmbuf.tm_year + 1900));
255                         scr_printf("%6ld %8ld\n", extract_long(buf, 4), extract_long(buf, 5));
256                     }
257
258                 }
259         }
260         free(listing);
261         scr_printf("\n");
262 }
263
264
265 /*
266  * grab assorted info about the user...
267  */
268 void load_user_info(char *params)
269 {
270         extract_token(fullname, params, 0, '|', sizeof fullname);
271         axlevel = extract_int(params, 1);
272         timescalled = extract_int(params, 2);
273         posted = extract_int(params, 3);
274         userflags = extract_int(params, 4);
275         usernum = extract_long(params, 5);
276         lastcall = extract_long(params, 6);
277 }
278
279
280 /*
281  * Remove a room from the march list.  'floornum' is ignored unless
282  * 'roomname' is set to _FLOOR_, in which case all rooms on the requested
283  * floor will be removed from the march list.
284  */
285 void remove_march(char *roomname, int floornum)
286 {
287         struct march *mptr, *mptr2;
288
289         if (marchptr == NULL)
290                 return;
291
292         if ((!strcasecmp(marchptr->march_name, roomname))
293             || ((!strcasecmp(roomname, "_FLOOR_")) && (marchptr->march_floor == floornum))) {
294                 mptr = marchptr->next;
295                 free(marchptr);
296                 marchptr = mptr;
297                 return;
298         }
299         mptr2 = marchptr;
300         for (mptr = marchptr; mptr != NULL; mptr = mptr->next) {
301
302                 if ((!strcasecmp(mptr->march_name, roomname))
303                     || ((!strcasecmp(roomname, "_FLOOR_"))
304                         && (mptr->march_floor == floornum))) {
305
306                         mptr2->next = mptr->next;
307                         free(mptr);
308                         mptr = mptr2;
309                 } else {
310                         mptr2 = mptr;
311                 }
312         }
313 }
314
315
316 /*
317  * Locate the room on the march list which we most want to go to.  Each room
318  * is measured given a "weight" of preference based on various factors.
319  */
320 char *pop_march(int desired_floor, struct march *_march)
321 {
322         static char TheRoom[ROOMNAMELEN];
323         int TheFloor = 0;
324         int TheOrder = 32767;
325         int TheWeight = 0;
326         int weight;
327         struct march *mptr = NULL;
328
329         strcpy(TheRoom, "_BASEROOM_");
330         if (_march == NULL)
331                 return (TheRoom);
332
333         for (mptr = _march; mptr != NULL; mptr = mptr->next) {
334                 weight = 0;
335                 if ((strcasecmp(mptr->march_name, "_BASEROOM_")))
336                         weight = weight + 10000;
337                 if (mptr->march_floor == desired_floor)
338                         weight = weight + 5000;
339
340                 weight = weight + ((128 - (mptr->march_floor)) * 128);
341                 weight = weight + (128 - (mptr->march_order));
342
343                 if (weight > TheWeight) {
344                         TheWeight = weight;
345                         strcpy(TheRoom, mptr->march_name);
346                         TheFloor = mptr->march_floor;
347                         TheOrder = mptr->march_order;
348                 }
349         }
350         return (TheRoom);
351 }
352
353
354 /*
355  * jump directly to a room
356  */
357 void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto)
358 {
359         char aaa[SIZ], bbb[SIZ];
360         static long ls = 0L;
361         int newmailcount = 0;
362         int partial_match, best_match;
363         char from_floor;
364         int ugpos = uglistsize;
365         int r;                          /* IPC result code */
366         struct ctdlipcroom *room = NULL;
367         int rv = 0;
368
369         /* store ungoto information */
370         if (fromungoto == 0) {
371                 /* sloppy slide them all down, hey it's the client, who cares. :-) */
372                 if (uglistsize >= (UGLISTLEN-1)) {
373                         int lp;
374                         free (uglist[0]);
375                         for (lp = 0; lp < (UGLISTLEN-1); lp++) {
376                                 uglist[lp] = uglist[lp+1];
377                                 uglistlsn[lp] = uglistlsn[lp+1];
378                         }
379                         ugpos--;
380                 } else {
381                         uglistsize++;
382                 }
383         
384                 uglist[ugpos] = malloc(strlen(room_name)+1);
385                 strcpy(uglist[ugpos], room_name);
386                 uglistlsn[ugpos] = ls;
387         }
388       
389         /* first try an exact match */
390         r = CtdlIPCGotoRoom(ipc, towhere, "", &room, aaa);
391         if (r / 10 == 54) {
392                 newprompt("Enter room password: ", bbb, 9);
393                 r = CtdlIPCGotoRoom(ipc, towhere, bbb, &room, aaa);
394                 if (r / 10 == 54) {
395                         scr_printf("Wrong password.\n");
396                         return;
397                 }
398         }       
399
400         /*
401          * If a match is not found, try a partial match.
402          * Partial matches anywhere in the string carry a weight of 1,
403          * left-aligned matches carry a weight of 2.  Pick the room that
404          * has the highest-weighted match.  Do not match on forgotten
405          * rooms.
406          */
407         if (r / 100 != 2) {
408                 struct march *march = NULL;
409
410                 best_match = 0;
411                 strcpy(bbb, "");
412
413                 r = CtdlIPCKnownRooms(ipc, SubscribedRooms, AllFloors, &march, aaa);
414                 if (r / 100 == 1) {
415                         /* Run the roomlist; free the data as we go */
416                         struct march *mp = march;       /* Current */
417
418                         while (mp) {
419                                 partial_match = 0;
420                                 if (pattern(mp->march_name, towhere) >= 0) {
421                                         partial_match = 1;
422                                 }
423                                 if (!strncasecmp(mp->march_name, towhere, strlen(towhere))) {
424                                         partial_match = 2;
425                                 }
426                                 if (partial_match > best_match) {
427                                         strcpy(bbb, mp->march_name);
428                                         best_match = partial_match;
429                                 }
430                                 /* Both pointers are NULL at end of list */
431                                 march = mp->next;
432                                 free(mp);
433                                 mp = march;
434                         }
435                 }
436
437                 if (IsEmptyStr(bbb)) {
438                         scr_printf("No room '%s'.\n", towhere);
439                         return;
440                 }
441                 r = CtdlIPCGotoRoom(ipc, bbb, "", &room, aaa);
442         }
443         if (r / 100 != 1 && r / 100 != 2) {
444                 scr_printf("%s\n", aaa);
445                 return;
446         }
447         safestrncpy(room_name, room->RRname, ROOMNAMELEN);
448         room_flags = room->RRflags;
449         room_flags2 = room->RRflags2;
450         from_floor = curr_floor;
451         curr_floor = room->RRfloor;
452
453         /* Determine, based on the room's default view, whether an <E>nter message
454          * command will be valid here.
455          */
456         switch(room->RRdefaultview) {
457                 case VIEW_BBS:
458                 case VIEW_MAILBOX:
459                 case VIEW_BLOG:
460                                         entmsg_ok = 1;
461                                         break;
462                 default:
463                                         entmsg_ok = 0;
464                                         break;
465         }
466
467         remove_march(room_name, 0);
468         if (!strcasecmp(towhere, "_BASEROOM_"))
469                 remove_march(towhere, 0);
470         if (!room->RRunread)
471                 next_lazy_cmd = 5;      /* Don't read new if no new msgs */
472         if ((from_floor != curr_floor) && (display_name > 0) && (floor_mode == 1)) {
473                 if (floorlist[(int) curr_floor][0] == 0)
474                         load_floorlist(ipc);
475                 scr_printf("(Entering floor: %s)\n", &floorlist[(int) curr_floor][0]);
476         }
477         if (display_name == 1) {
478                 color(BRIGHT_WHITE);
479                 scr_printf("%s ", room_name);
480                 color(DIM_WHITE);
481                 scr_printf("- ");
482         }
483         if (display_name != 2) {
484                 color(BRIGHT_YELLOW);
485                 scr_printf("%d ", room->RRunread);
486                 color(DIM_WHITE);
487                 scr_printf("new of ");
488                 color(BRIGHT_YELLOW);
489                 scr_printf("%d ", room->RRtotal);
490                 color(DIM_WHITE);
491                 scr_printf("messages.\n");
492         }
493         highest_msg_read = room->RRlastread;
494         maxmsgnum = room->RRhighest;
495         is_mail = room->RRismailbox;
496         is_room_aide = room->RRaide;
497         ls = room->RRlastread;
498
499         /* read info file if necessary */
500         if (room->RRinfoupdated > 0)
501                 readinfo(ipc);
502
503         /* check for newly arrived mail if we can */
504         newmailcount = room->RRnewmail;
505         if (newmailcount > 0) {
506                 color(BRIGHT_RED);
507                 if (newmailcount == 1) {
508                         scr_printf("*** A new mail message has arrived.\n");
509                 }
510                 else {
511                         scr_printf("*** %d new mail messages have arrived.\n",
512                                         newmailcount);
513                 }
514                 color(DIM_WHITE);
515                 if (!IsEmptyStr(rc_gotmail_cmd)) {
516                         rv = system(rc_gotmail_cmd);
517                 }
518         }
519         free(room);
520
521         if (screenwidth>5) snprintf(&status_line[1], screenwidth-1, "%s  |  %s  |  %s  |  %s  |  %d new mail  |",
522                 (secure ? "Encrypted" : "Unencrypted"),
523                 ipc->ServInfo.humannode,
524                 ipc->ServInfo.site_location,
525                 room_name,
526                 newmailcount
527         );
528 }
529
530 /* Goto next room having unread messages.
531  * We want to skip over rooms that the user has already been to, and take the
532  * user back to the lobby when done.  The room we end up in is placed in
533  * newroom - which is set to 0 (the lobby) initially.
534  */
535 void gotonext(CtdlIPC *ipc)
536 {
537         char buf[SIZ];
538         struct march *mptr, *mptr2;
539         char next_room[ROOMNAMELEN];
540         int r;                          /* IPC response code */
541
542         /* Check to see if the march-mode list is already allocated.
543          * If it is, pop the first room off the list and go there.
544          */
545         if (marchptr == NULL) {
546                 r = CtdlIPCKnownRooms(ipc, SubscribedRoomsWithNewMessages,
547                                         AllFloors, &marchptr, buf);
548
549 /* add _BASEROOM_ to the end of the march list, so the user will end up
550  * in the system base room (usually the Lobby>) at the end of the loop
551  */
552                 mptr = (struct march *) malloc(sizeof(struct march));
553                 mptr->next = NULL;
554                 mptr->march_order = 0;
555                 mptr->march_floor = 0;
556                 strcpy(mptr->march_name, "_BASEROOM_");
557                 if (marchptr == NULL) {
558                         marchptr = mptr;
559                 } else {
560                         mptr2 = marchptr;
561                         while (mptr2->next != NULL)
562                                 mptr2 = mptr2->next;
563                         mptr2->next = mptr;
564                 }
565 /*
566  * ...and remove the room we're currently in, so a <G>oto doesn't make us
567  * walk around in circles
568  */
569                 remove_march(room_name, 0);
570         }
571         if (marchptr != NULL) {
572                 strcpy(next_room, pop_march(curr_floor, marchptr));
573         } else {
574                 strcpy(next_room, "_BASEROOM_");
575         }
576         remove_march(next_room, 0);
577         dotgoto(ipc, next_room, 1, 0);
578 }
579
580 /*
581  * forget all rooms on a given floor
582  */
583 void forget_all_rooms_on(CtdlIPC *ipc, int ffloor)
584 {
585         char buf[SIZ];
586         struct march *flist = NULL;
587         struct march *fptr = NULL;
588         struct ctdlipcroom *room = NULL;
589         int r;                          /* IPC response code */
590
591         scr_printf("Forgetting all rooms on %s...\n", &floorlist[ffloor][0]);
592         remove_march("_FLOOR_", ffloor);
593         r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, ffloor, &flist, buf);
594         if (r / 100 != 1) {
595                 scr_printf("Error %d: %s\n", r, buf);
596                 return;
597         }
598         while (flist) {
599                 r = CtdlIPCGotoRoom(ipc, flist->march_name, "", &room, buf);
600                 if (r / 100 == 2) {
601                         r = CtdlIPCForgetRoom(ipc, buf);
602                         if (r / 100 != 2) {
603                                 scr_printf("Error %d: %s\n", r, buf);
604                         }
605
606                 }
607                 fptr = flist;
608                 flist = flist->next;
609                 free(fptr);
610         }
611         if (room) free(room);
612 }
613
614
615 /*
616  * routine called by gotofloor() to move to a new room on a new floor
617  */
618 void gf_toroom(CtdlIPC *ipc, char *towhere, int mode)
619 {
620         int floor_being_left;
621
622         floor_being_left = curr_floor;
623
624         if (mode == GF_GOTO) {          /* <;G>oto mode */
625                 updatels(ipc);
626                 dotgoto(ipc, towhere, 1, 0);
627         }
628         else if (mode == GF_SKIP) {     /* <;S>kip mode */
629                 dotgoto(ipc, towhere, 1, 0);
630                 remove_march("_FLOOR_", floor_being_left);
631         }
632         else if (mode == GF_ZAP) {      /* <;Z>ap mode */
633                 dotgoto(ipc, towhere, 1, 0);
634                 remove_march("_FLOOR_", floor_being_left);
635                 forget_all_rooms_on(ipc, floor_being_left);
636         }
637 }
638
639
640 /*
641  * go to a new floor
642  */
643 void gotofloor(CtdlIPC *ipc, char *towhere, int mode)
644 {
645         int a, tofloor;
646         int r;          /* IPC response code */
647         struct march *mptr;
648         char buf[SIZ], targ[SIZ];
649
650         if (floorlist[0][0] == 0)
651                 load_floorlist(ipc);
652         tofloor = (-1);
653         for (a = 0; a < 128; ++a)
654                 if (!strcasecmp(&floorlist[a][0], towhere))
655                         tofloor = a;
656
657         if (tofloor < 0) {
658                 for (a = 0; a < 128; ++a) {
659                         if (!strncasecmp(&floorlist[a][0], towhere, strlen(towhere))) {
660                                 tofloor = a;
661                         }
662                 }
663         }
664         if (tofloor < 0) {
665                 for (a = 0; a < 128; ++a)
666                         if (pattern(towhere, &floorlist[a][0]) > 0)
667                                 tofloor = a;
668         }
669         if (tofloor < 0) {
670                 scr_printf("No floor '%s'.\n", towhere);
671                 return;
672         }
673         for (mptr = marchptr; mptr != NULL; mptr = mptr->next) {
674                 if ((mptr->march_floor) == tofloor) {
675                         gf_toroom(ipc, mptr->march_name, mode);
676                         return;
677                 }
678         }
679
680         /* Find first known room on the floor */
681
682         strcpy(targ, "");
683         mptr = NULL;
684         r = CtdlIPCKnownRooms(ipc, SubscribedRooms, tofloor, &mptr, buf);
685         if (r / 100 == 1) {
686                 struct march *tmp = mptr;
687
688                 /*. . . according to room order */
689                 if (mptr)
690             strcpy(targ, pop_march(tofloor, mptr));
691                 while (mptr) {
692                         tmp = mptr->next;
693                         free(mptr);
694                         mptr = tmp;
695                 }
696         }
697         if (!IsEmptyStr(targ)) {
698                 gf_toroom(ipc, targ, mode);
699                 return;
700         }
701
702         /* No known rooms on the floor; unzap the first one then */
703
704         strcpy(targ, "");
705         mptr = NULL;
706         r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, tofloor, &mptr, buf);
707         if (r / 100 == 1) {
708                 struct march *tmp = mptr;
709                 
710         /*. . . according to room order */
711                 if (mptr)
712                         strcpy(targ, pop_march(tofloor, mptr));
713                 while (mptr) {
714                         tmp = mptr->next;
715                         free(mptr);
716                         mptr = tmp;
717                 }
718         }
719         if (!IsEmptyStr(targ)) {
720                 gf_toroom(ipc, targ, mode);
721         } else {
722                 scr_printf("There are no rooms on '%s'.\n", &floorlist[tofloor][0]);
723         }
724 }
725
726 /*
727  * Indexing mechanism for a room list, called by gotoroomstep()
728  */
729 void room_tree_list_query(struct ctdlroomlisting *rp, char *findrmname, int findrmslot, char *rmname, int *rmslot, int *rmtotal)
730 {
731         char roomname[ROOMNAMELEN];
732         static int cur_rmslot = 0;
733
734         if (rp == NULL) {
735                 cur_rmslot = 0;
736                 return;
737         }
738
739         if (rp->lnext != NULL) {
740                 room_tree_list_query(rp->lnext, findrmname, findrmslot, rmname, rmslot, rmtotal);
741         }
742
743         if (sigcaught == 0) {
744                 strcpy(roomname, rp->rlname);
745
746                 if (rmname != NULL) {
747                         if (cur_rmslot == findrmslot) {
748                                 strcpy(rmname, roomname);
749                         }
750                 }
751                 if (rmslot != NULL) {
752                         if (!strcmp(roomname, findrmname)) {
753                                 *rmslot = cur_rmslot;
754                         }
755                 }
756                 cur_rmslot++;
757         }
758
759         if (rp->rnext != NULL) {
760                 room_tree_list_query(rp->rnext, findrmname, findrmslot, rmname, rmslot, rmtotal);
761         }
762
763         if ((rmname == NULL) && (rmslot == NULL))
764                 free(rp);
765
766         if (rmtotal != NULL) {
767                 *rmtotal = cur_rmslot;
768         }
769 }
770
771 /*
772  * step through rooms on current floor
773  */
774 void  gotoroomstep(CtdlIPC *ipc, int direction, int mode)
775 {
776         struct march *listing = NULL;
777         struct march *mptr;
778         int r;          /* IPC response code */
779         char buf[SIZ];
780         struct ctdlroomlisting *rl = NULL;
781         struct ctdlroomlisting *rp;
782         struct ctdlroomlisting *rs;
783         int list_it;
784         char rmname[ROOMNAMELEN];
785         int rmslot = 0;
786         int rmtotal;
787
788         /* Ask the server for a room list */
789         r = CtdlIPCKnownRooms(ipc, SubscribedRooms, (-1), &listing, buf);
790         if (r / 100 != 1) {
791                 listing = NULL;
792         }
793
794         load_floorlist(ipc);
795
796         for (mptr = listing; mptr != NULL; mptr = mptr->next) {
797                 list_it = 1;
798
799                 if ( floor_mode 
800                          && (mptr->march_floor != curr_floor))
801                         list_it = 0;
802
803                 if (list_it) {
804                         rp = malloc(sizeof(struct ctdlroomlisting));
805                         strncpy(rp->rlname, mptr->march_name, ROOMNAMELEN);
806                         rp->rlflags = mptr->march_flags;
807                         rp->rlfloor = mptr->march_floor;
808                         rp->rlorder = mptr->march_order;
809                         rp->lnext = NULL;
810                         rp->rnext = NULL;
811
812                         rs = rl;
813                         if (rl == NULL) {
814                                 rl = rp;
815                         } else {
816                                 while (rp != NULL) {
817                                         if (rordercmp(rp, rs) < 0) {
818                                                 if (rs->lnext == NULL) {
819                                                         rs->lnext = rp;
820                                                         rp = NULL;
821                                                 } else {
822                                                         rs = rs->lnext;
823                                                 }
824                                         } else {
825                                                 if (rs->rnext == NULL) {
826                                                         rs->rnext = rp;
827                                                         rp = NULL;
828                                                 } else {
829                                                         rs = rs->rnext;
830                                                 }
831                                         }
832                                 }
833                         }
834                 }
835         }
836
837         /* Find position of current room */
838         room_tree_list_query(NULL, NULL, 0, NULL, NULL, NULL);
839         room_tree_list_query(rl,  room_name, 0, NULL, &rmslot, &rmtotal);
840
841         if (direction == 0) { /* Previous room */
842                 /* If we're at the first room, wrap to the last room */
843                 if (rmslot == 0) {
844                         rmslot = rmtotal - 1;
845                 } else {
846                         rmslot--;
847                 }
848         } else {                 /* Next room */
849                 /* If we're at the last room, wrap to the first room */
850                 if (rmslot == rmtotal - 1) {
851                         rmslot = 0; 
852                 } else {
853                         rmslot++;
854                 }
855         }
856
857         /* Get name of next/previous room */
858         room_tree_list_query(NULL, NULL, 0, NULL, NULL, NULL);
859         room_tree_list_query(rl,  NULL, rmslot, rmname, NULL, NULL);
860
861         /* Free the tree */
862         room_tree_list_query(rl, NULL, 0, NULL, NULL, NULL);
863
864         if (mode == 0) { /* not skipping */
865             updatels(ipc);
866         } else {
867                 if (rc_alt_semantics) {
868                 updatelsa(ipc);
869                 }
870         }
871
872         /* Free the room list */
873         while (listing) {
874                 mptr = listing->next;
875                 free(listing);
876                 listing = mptr;
877         };
878
879         dotgoto(ipc, rmname, 1, 0);
880 }
881
882
883 /*
884  * step through floors on system
885  */
886 void  gotofloorstep(CtdlIPC *ipc, int direction, int mode)
887 {
888         int  tofloor;
889
890         if (floorlist[0][0] == 0)
891                 load_floorlist(ipc);
892
893         empty_keep_going:
894
895         if (direction == 0) { /* Previous floor */
896                 if (curr_floor) tofloor = curr_floor - 1;
897                 else tofloor = 127;
898
899                 while (!floorlist[tofloor][0]) tofloor--;
900         } else {                   /* Next floor */
901                 if (curr_floor < 127) tofloor = curr_floor + 1;
902                 else tofloor = 0;
903
904                 while (!floorlist[tofloor][0] && tofloor < 127) tofloor++;
905                 if (!floorlist[tofloor][0])     tofloor = 0;
906         }
907         /* ;g works when not in floor mode so . . . */
908         if (!floor_mode) {
909                 scr_printf("(%s)\n", floorlist[tofloor] );
910         }
911
912         gotofloor(ipc, floorlist[tofloor], mode);
913         if (curr_floor != tofloor) { /* gotofloor failed */
914              curr_floor = tofloor;
915              goto empty_keep_going;
916         }            
917 }
918
919 /* 
920  * Display user 'preferences'.
921  */
922 extern int rc_prompt_control;
923 void read_config(CtdlIPC *ipc)
924 {
925         char buf[SIZ];
926         char *resp = NULL;
927         int r;                  /* IPC response code */
928     char _fullname[USERNAME_SIZE];
929         long _usernum;
930         int _axlevel, _timescalled, _posted;
931         time_t _lastcall;
932         struct ctdluser *user = NULL;
933
934         /* get misc user info */   
935         r = CtdlIPCGetBio(ipc, fullname, &resp, buf);
936         if (r / 100 != 1) {
937                 scr_printf("%s\n", buf);
938                 return;
939         }
940         extract_token(_fullname, buf, 1, '|', sizeof fullname);
941         _usernum = extract_long(buf, 2);
942         _axlevel = extract_int(buf, 3);
943         _lastcall = extract_long(buf, 4);
944     _timescalled = extract_int(buf, 5);
945         _posted = extract_int(buf, 6);
946         free(resp);
947         resp = NULL;
948    
949         /* get preferences */
950         r = CtdlIPCGetConfig(ipc, &user, buf);
951         if (r / 100 != 2) {
952                 scr_printf("%s\n", buf);
953                 free(user);
954                 return;
955         }
956
957         /* show misc user info */
958         scr_printf("%s\nAccess level: %d (%s)\n"
959                    "User #%ld / %d Calls / %d Posts",
960                    _fullname, _axlevel, axdefs[(int) _axlevel],
961                    _usernum, _timescalled, _posted);
962         if (_lastcall > 0L) {
963                 scr_printf(" / Curr login: %s",
964                            asctime(localtime(&_lastcall)));
965         }
966         scr_printf("\n");
967
968         /* show preferences */
969         scr_printf("Are you an experienced Citadel user: ");                   color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_EXPERT) ? "Yes" : "No");     color(DIM_WHITE);
970         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);
971         scr_printf("Prompt after each message: ");                             color(BRIGHT_CYAN); scr_printf("%s\n", (!(user->flags & US_NOPROMPT))? "Yes" : "No"); color(DIM_WHITE);
972         if ((user->flags & US_NOPROMPT) == 0) {
973         scr_printf("Use 'disappearing' prompts: ");                        color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_DISAPPEAR)? "Yes" : "No");   color(DIM_WHITE);
974         }
975         scr_printf("Pause after each screenful of text: ");                    color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_PAGINATOR)? "Yes" : "No");   color(DIM_WHITE);
976     if (rc_prompt_control == 3 && (user->flags & US_PAGINATOR)) {
977         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);
978         }
979     if (rc_floor_mode == RC_DEFAULT) {
980         scr_printf("View rooms by floor: ");                               color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_FLOORS)? "Yes" : "No");          color(DIM_WHITE);
981         }
982         if (rc_ansi_color == 3) {
983             scr_printf("Enable color support: ");                              color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_COLOR)? "Yes" : "No");       color(DIM_WHITE);
984         }
985         scr_printf("Be unlisted in userlog: ");                                color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_UNLISTED)? "Yes" : "No");    color(DIM_WHITE);
986         if (!IsEmptyStr(editor_paths[0])) {
987         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);
988         }
989         free(user);
990 }
991
992 /*
993  * Display system statistics.
994  */
995 void system_info(CtdlIPC *ipc)
996 {
997         char buf[SIZ];
998         char *resp = NULL;
999         size_t bytes;
1000         int mrtg_users, mrtg_active_users; 
1001         char mrtg_server_uptime[40];
1002         long mrtg_himessage;
1003         int ret;                        /* IPC response code */
1004
1005         /* get #users, #active & server uptime */
1006         ret = CtdlIPCGenericCommand(ipc, "MRTG|users", NULL, 0, &resp, &bytes, buf);
1007         mrtg_users = extract_int(resp, 0);
1008         remove_token(resp, 0, '\n');
1009         mrtg_active_users = extract_int(resp, 0);
1010         remove_token(resp, 0, '\n');
1011         extract_token(mrtg_server_uptime, resp, 0, '\n', sizeof mrtg_server_uptime);
1012         free(resp);
1013         resp = NULL;
1014
1015         /* get high message# */
1016         ret = CtdlIPCGenericCommand(ipc, "MRTG|messages", NULL, 0, &resp, &bytes, buf);
1017         mrtg_himessage = extract_long(resp, 0);
1018         free(resp);
1019         resp = NULL;
1020
1021         /* refresh server info just in case */
1022         CtdlIPCServerInfo(ipc, buf);
1023
1024         scr_printf("You are connected to %s (%s) @%s\n", ipc->ServInfo.nodename, ipc->ServInfo.humannode, ipc->ServInfo.fqdn);
1025         scr_printf("running %s with text client v%.2f,\n", ipc->ServInfo.software, (float)REV_LEVEL/100);
1026         scr_printf("server build %s,\n", ipc->ServInfo.svn_revision, (float)REV_LEVEL/100);
1027     scr_printf("and located in %s.\n", ipc->ServInfo.site_location);
1028     scr_printf("Connected users %d / Active users %d / Highest message #%ld\n", mrtg_users, mrtg_active_users, mrtg_himessage);
1029     scr_printf("Server uptime: %s\n", mrtg_server_uptime);
1030     scr_printf("Your system administrator is %s.\n", ipc->ServInfo.sysadm);
1031     scr_printf("Copyright (C)1987-2009 by the Citadel development team\n");
1032 }
1033
1034 /*
1035  * forget all rooms on current floor
1036  */
1037 void forget_this_floor(CtdlIPC *ipc)
1038 {
1039         if (curr_floor == 0) {
1040                 scr_printf("Can't forget this floor.\n");
1041                 return;
1042         }
1043         if (floorlist[0][0] == 0) {
1044                 load_floorlist(ipc);
1045         }
1046         scr_printf("Are you sure you want to forget all rooms on %s? ",
1047                &floorlist[(int) curr_floor][0]);
1048         if (yesno() == 0) {
1049                 return;
1050         }
1051
1052         gf_toroom(ipc, "_BASEROOM_", GF_ZAP);
1053 }
1054
1055
1056 /* 
1057  * Figure out the physical screen dimensions, if we can
1058  * WARNING:  this is now called from a signal handler!
1059  */
1060 void check_screen_dims(void)
1061 {
1062 #ifdef TIOCGWINSZ
1063         struct {
1064                 unsigned short height;  /* rows */
1065                 unsigned short width;   /* columns */
1066                 unsigned short xpixels;
1067                 unsigned short ypixels;         /* pixels */
1068         } xwinsz;
1069
1070         if (have_xterm) {       /* dynamically size screen if on an xterm */
1071                 if (ioctl(0, TIOCGWINSZ, &xwinsz) == 0) {
1072                         if (xwinsz.height)
1073                                 screenheight = (int) xwinsz.height;
1074                         if (xwinsz.width)
1075                                 screenwidth = (int) xwinsz.width;
1076                 }
1077         }
1078 #endif
1079 }
1080
1081
1082 /*
1083  * set floor mode depending on client, server, and user settings
1084  */
1085 void set_floor_mode(CtdlIPC* ipc)
1086 {
1087         if (ipc->ServInfo.ok_floors == 0) {
1088                 floor_mode = 0; /* Don't use floors if the server */
1089         }
1090         /* doesn't support them!          */
1091         else {
1092                 if (rc_floor_mode == RC_NO) {   /* never use floors */
1093                         floor_mode = 0;
1094                 }
1095                 if (rc_floor_mode == RC_YES) {  /* always use floors */
1096                         floor_mode = 1;
1097                 }
1098                 if (rc_floor_mode == RC_DEFAULT) {      /* user choice */
1099                         floor_mode = ((userflags & US_FLOORS) ? 1 : 0);
1100                 }
1101         }
1102 }
1103
1104 /*
1105  * Set or change the user's password
1106  */
1107 int set_password(CtdlIPC *ipc)
1108 {
1109         char pass1[20];
1110         char pass2[20];
1111         char buf[SIZ];
1112
1113         if (!IsEmptyStr(rc_password)) {
1114                 strcpy(pass1, rc_password);
1115                 strcpy(pass2, rc_password);
1116         } else {
1117                 IFNEXPERT formout(ipc, "changepw");
1118                 newprompt("Enter a new password: ", pass1, -19);
1119                 newprompt("Enter it again to confirm: ", pass2, -19);
1120         }
1121         strproc(pass1);
1122         strproc(pass2);
1123         if (!strcasecmp(pass1, pass2)) {
1124                 CtdlIPCChangePassword(ipc, pass1, buf);
1125                 scr_printf("%s\n", buf);
1126                 offer_to_remember_password(ipc, hostbuf, portbuf, fullname, pass1);
1127                 return (0);
1128         } else {
1129                 scr_printf("*** They don't match... try again.\n");
1130                 return (1);
1131         }
1132 }
1133
1134
1135
1136 /*
1137  * get info about the server we've connected to
1138  */
1139 void get_serv_info(CtdlIPC *ipc, char *supplied_hostname)
1140 {
1141         char buf[SIZ];
1142
1143         CtdlIPCServerInfo(ipc, buf);
1144         moreprompt = ipc->ServInfo.moreprompt;
1145
1146         /* be nice and identify ourself to the server */
1147         CtdlIPCIdentifySoftware(ipc, SERVER_TYPE, 0, REV_LEVEL,
1148                  (ipc->isLocal ? "local" : CITADEL),
1149                  (supplied_hostname) ? supplied_hostname : 
1150                  /* Look up the , in the bible if you're confused */
1151                  (locate_host(ipc, buf), buf), buf);
1152
1153         /* Indicate to the server that we prefer to decode Base64 and
1154          * quoted-printable on the client side.
1155          */
1156         if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "dont_decode") / 100 ) != 2) {
1157                 scr_printf("ERROR: Extremely old server; MSG4 framework not supported.\n");
1158                 logoff(ipc, 0);
1159         }
1160
1161         /*
1162          * Tell the server what our preferred content formats are.
1163          *
1164          * Originally we preferred HTML over plain text because we can format
1165          * it to the reader's screen width, but since our HTML-to-text parser
1166          * isn't really all that great, it's probably better to just go with
1167          * the plain text when we have it available.
1168          */
1169         if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "text/plain|text/html") / 100 ) != 2) {
1170                 scr_printf("ERROR: Extremely old server; MSG4 framework not supported.\n");
1171                 logoff(ipc, 0);
1172         }
1173 }
1174
1175
1176
1177 /*
1178  * Record compare function for SortOnlineUsers()
1179  */
1180 int idlecmp(const void *rec1, const void *rec2) {
1181         time_t i1, i2;
1182
1183         i1 = extract_long(rec1, 5);
1184         i2 = extract_long(rec2, 5);
1185
1186         if (i1 < i2) return(1);
1187         if (i1 > i2) return(-1);
1188         return(0);
1189 }
1190
1191
1192 /*
1193  * Sort the list of online users by idle time.
1194  * This function frees the supplied buffer, and returns a new one
1195  * to the caller.  The caller is responsible for freeing the returned buffer.
1196  */
1197 char *SortOnlineUsers(char *listing) {
1198         int rows;
1199         char *sortbuf;
1200         char *retbuf;
1201         char buf[SIZ];
1202         int i;
1203
1204         rows = num_tokens(listing, '\n');
1205         sortbuf = malloc(rows * SIZ);
1206         if (sortbuf == NULL) return(listing);
1207         retbuf = malloc(rows * SIZ);
1208         if (retbuf == NULL) {
1209                 free(sortbuf);
1210                 return(listing);
1211         }
1212
1213         /* Copy the list into a fixed-record-size array for sorting */
1214         for (i=0; i<rows; ++i) {
1215                 memset(buf, 0, SIZ);
1216                 extract_token(buf, listing, i, '\n', sizeof buf);
1217                 memcpy(&sortbuf[i*SIZ], buf, (size_t)SIZ);
1218         }
1219
1220         /* Do the sort */
1221         qsort(sortbuf, rows, SIZ, idlecmp);
1222
1223         /* Copy back to a \n delimited list */
1224         strcpy(retbuf, "");
1225         for (i=0; i<rows; ++i) {
1226                 strcat(retbuf, &sortbuf[i*SIZ]);
1227                 if (i<(rows-1)) strcat(retbuf, "\n");
1228         }
1229     free(listing);
1230     free(sortbuf);
1231         return(retbuf);
1232 }
1233
1234
1235
1236 /*
1237  * Display list of users currently logged on to the server
1238  */
1239 void who_is_online(CtdlIPC *ipc, int longlist)
1240 {
1241         char buf[SIZ], username[SIZ], roomname[SIZ], fromhost[SIZ];
1242         char flags[SIZ];
1243         char actual_user[SIZ], actual_room[SIZ], actual_host[SIZ];
1244         char clientsoft[SIZ];
1245         time_t timenow = 0;
1246         time_t idletime, idlehours, idlemins, idlesecs;
1247         int last_session = (-1);
1248         int skipidle = 0;
1249         char *listing = NULL;
1250         int r;                          /* IPC response code */
1251     
1252         if (longlist == 2) {
1253                 longlist = 0;
1254                 skipidle = 1;
1255         }
1256
1257         if (!longlist) {
1258                 color(BRIGHT_WHITE);
1259                 scr_printf("           User Name               Room          ");
1260                 if (screenwidth >= 80) scr_printf(" Idle        From host");
1261                 scr_printf("\n");
1262                 color(DIM_WHITE);
1263                 scr_printf("   ------------------------- --------------------");
1264                 if (screenwidth >= 80) scr_printf(" ---- ------------------------");
1265                 scr_printf("\n");
1266         }
1267         r = CtdlIPCOnlineUsers(ipc, &listing, &timenow, buf);
1268         listing = SortOnlineUsers(listing);
1269         if (r / 100 == 1) {
1270                 while (!IsEmptyStr(listing)) {
1271                         int isidle = 0;
1272                         
1273                         /* Get another line */
1274                         extract_token(buf, listing, 0, '\n', sizeof buf);
1275                         remove_token(listing, 0, '\n');
1276
1277                         extract_token(username, buf, 1, '|', sizeof username);
1278                         extract_token(roomname, buf, 2, '|', sizeof roomname);
1279                         extract_token(fromhost, buf, 3, '|', sizeof fromhost);
1280                         extract_token(clientsoft, buf, 4, '|', sizeof clientsoft);
1281                         extract_token(flags, buf, 7, '|', sizeof flags);
1282
1283                         idletime = timenow - extract_long(buf, 5);
1284                         idlehours = idletime / 3600;
1285                         idlemins = (idletime - (idlehours * 3600)) / 60;
1286                         idlesecs = (idletime - (idlehours * 3600) - (idlemins * 60));
1287
1288                         if (idletime > rc_idle_threshold) {
1289                                 if (skipidle) {
1290                                         isidle = 1;
1291                                 }
1292                         }
1293
1294                         if (longlist) {
1295                                 extract_token(actual_user, buf, 8, '|', sizeof actual_user);
1296                                 extract_token(actual_room, buf, 9, '|', sizeof actual_room);
1297                                 extract_token(actual_host, buf, 10, '|', sizeof actual_host);
1298
1299                                 scr_printf("  Flags: %s\n", flags);
1300                                 scr_printf("Session: %d\n", extract_int(buf, 0));
1301                                 scr_printf("   Name: %s\n", username);
1302                                 scr_printf("In room: %s\n", roomname);
1303                                 scr_printf("   Host: %s\n", fromhost);
1304                                 scr_printf(" Client: %s\n", clientsoft);
1305                                 scr_printf("   Idle: %ld:%02ld:%02ld\n",
1306                                         (long) idlehours,
1307                                         (long) idlemins,
1308                                         (long) idlesecs);
1309
1310                                 if ( (!IsEmptyStr(actual_user)&&
1311                                       !IsEmptyStr(actual_room)&&
1312                                       !IsEmptyStr(actual_host))) {
1313                                         scr_printf("(really ");
1314                                         if (!IsEmptyStr(actual_user)) scr_printf("<%s> ", actual_user);
1315                                         if (!IsEmptyStr(actual_room)) scr_printf("in <%s> ", actual_room);
1316                                         if (!IsEmptyStr(actual_host)) scr_printf("from <%s> ", actual_host);
1317                                         scr_printf(")\n");
1318                                 }
1319                                 scr_printf("\n");
1320
1321                         } else {
1322                                 if (isidle == 0) {
1323                                         if (extract_int(buf, 0) == last_session) {
1324                                                 scr_printf("        ");
1325                                         }
1326                                         else {
1327                                                 color(BRIGHT_MAGENTA);
1328                                                 scr_printf("%-3s", flags);
1329                                         }
1330                                         last_session = extract_int(buf, 0);
1331                                         color(BRIGHT_CYAN);
1332                                         scr_printf("%-25s ", username);
1333                                         color(BRIGHT_MAGENTA);
1334                                         roomname[20] = 0;
1335                                         scr_printf("%-20s", roomname);
1336
1337                                         if (screenwidth >= 80) {
1338                                                 scr_printf(" ");
1339                                                 if (idletime > rc_idle_threshold) {
1340                                                         /* over 1000d, must be gone fishing */
1341                                                         if (idlehours > 23999) {
1342                                                                 scr_printf("fish");
1343                                                         /* over 10 days */
1344                                                         } else if (idlehours > 239) {
1345                                                                 scr_printf("%3ldd", idlehours / 24);
1346                                                         /* over 10 hours */
1347                                                         } else if (idlehours > 9) {
1348                                                                 scr_printf("%1ldd%02ld",
1349                                                                         idlehours / 24,
1350                                                                         idlehours % 24);
1351                                                         /* less than 10 hours */
1352                                                         }
1353                                                         else {
1354                                                                 scr_printf("%1ld:%02ld", idlehours, idlemins);
1355                                                         }
1356                                                 }
1357                                                 else {
1358                                                         scr_printf("    ");
1359                                                 }
1360                                                 scr_printf(" ");
1361                                                 color(BRIGHT_CYAN);
1362                                                 fromhost[24] = '\0';
1363                                                 scr_printf("%-24s", fromhost);
1364                                         }
1365                                         scr_printf("\n");
1366                                         color(DIM_WHITE);
1367                                 }
1368                         }
1369                 }
1370         }
1371         free(listing);
1372 }
1373
1374 void enternew(CtdlIPC *ipc, char *desc, char *buf, int maxlen)
1375 {
1376         char bbb[128];
1377         snprintf(bbb, sizeof bbb, "Enter in your new %s: ", desc);
1378         newprompt(bbb, buf, maxlen);
1379 }
1380
1381
1382
1383 int shift(int argc, char **argv, int start, int count) {
1384         int i;
1385
1386         for (i=start; i<(argc-count); ++i) {
1387                 argv[i] = argv[i+count];
1388         }
1389         argc = argc - count;
1390         return argc;
1391 }
1392
1393 static void statusHook(char *s) {
1394         scr_printf(s);
1395 }
1396
1397 /*
1398  * main
1399  */
1400 int main(int argc, char **argv)
1401 {
1402         int a, b, mcmd;
1403         char aaa[100], bbb[100];/* general purpose variables */
1404         char argbuf[64];        /* command line buf */
1405         char nonce[NONCE_SIZE];
1406         char *telnet_client_host = NULL;
1407         char *sptr, *sptr2;     /* USed to extract the nonce */
1408         char hexstring[MD5_HEXSTRING_SIZE];
1409         int stored_password = 0;
1410         char password[SIZ];
1411         struct ctdlipcmisc chek;
1412         struct ctdluser *myself = NULL;
1413         CtdlIPC* ipc;                   /* Our server connection */
1414         int r;                          /* IPC result code */
1415         int rv = 0;                     /* fetch but ignore syscall return value to suppress warnings */
1416
1417         int relh=0;
1418         int home=0;
1419         char relhome[PATH_MAX]="";
1420         char ctdldir[PATH_MAX]=CTDLDIR;
1421     int lp; 
1422 #ifdef HAVE_BACKTRACE
1423         eCrashParameters params;
1424 //      eCrashSymbolTable symbol_table;
1425 #endif
1426         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
1427
1428 #ifdef HAVE_BACKTRACE
1429         bzero(&params, sizeof(params));
1430         params.filename = file_pid_paniclog;
1431 //      panic_fd=open(file_pid_paniclog, O_APPEND|O_CREAT|O_DIRECT);
1432         params.filep = fopen(file_pid_paniclog, "a+");
1433         params.debugLevel = ECRASH_DEBUG_VERBOSE;
1434         params.dumpAllThreads = TRUE;
1435         params.useBacktraceSymbols = 1;
1436 ///     BuildSymbolTable(&symbol_table);
1437 //      params.symbolTable = &symbol_table;
1438         params.signals[0]=SIGSEGV;
1439         params.signals[1]=SIGILL;
1440         params.signals[2]=SIGBUS;
1441         params.signals[3]=SIGABRT;
1442
1443         eCrash_Init(&params);
1444 #endif  
1445         setIPCErrorPrintf(scr_printf);
1446         setCryptoStatusHook(statusHook);
1447         
1448         /* Permissions sanity check - don't run citadel setuid/setgid */
1449         if (getuid() != geteuid()) {
1450                 scr_printf("Please do not run citadel setuid!\n");
1451                 logoff(NULL, 3);
1452         } else if (getgid() != getegid()) {
1453                 scr_printf("Please do not run citadel setgid!\n");
1454                 logoff(NULL, 3);
1455         }
1456
1457         stty_ctdl(SB_SAVE);     /* Store the old terminal parameters */
1458         load_command_set();     /* parse the citadel.rc file */
1459         stty_ctdl(SB_NO_INTR);  /* Install the new ones */
1460         /* signal(SIGHUP, dropcarr);FIXME */    /* Cleanup gracefully if carrier is dropped */
1461         signal(SIGPIPE, dropcarr);      /* Cleanup gracefully if local conn. dropped */
1462         signal(SIGTERM, dropcarr);      /* Cleanup gracefully if terminated */
1463         signal(SIGCONT, catch_sigcont); /* Catch SIGCONT so we can reset terminal */
1464 #ifdef SIGWINCH
1465         signal(SIGWINCH, scr_winch);    /* Window resize signal */
1466 #endif
1467
1468 #ifdef HAVE_OPENSSL
1469         arg_encrypt = RC_DEFAULT;
1470 #endif
1471
1472         /* 
1473          * Handle command line options as if we were called like /bin/login
1474          * (i.e. from in.telnetd)
1475          */
1476         for (a=0; a<argc; ++a) {
1477                 if ((argc > a+1) && (!strcmp(argv[a], "-h")) ) {
1478                         telnet_client_host = argv[a+1];
1479                         argc = shift(argc, argv, a, 2);
1480                 }
1481                 if (!strcmp(argv[a], "-x")) {
1482 #ifdef HAVE_OPENSSL
1483                         arg_encrypt = RC_NO;
1484 #endif
1485                         argc = shift(argc, argv, a, 1);
1486                 }
1487                 if (!strcmp(argv[a], "-X")) {
1488 #ifdef HAVE_OPENSSL
1489                         arg_encrypt = RC_YES;
1490                         argc = shift(argc, argv, a, 1);
1491 #else
1492                         fprintf(stderr, "Not compiled with encryption support");
1493                         return 1;
1494 #endif
1495                 }
1496                 if (!strcmp(argv[a], "-p")) {
1497                         struct stat st;
1498                 
1499                         if (chdir(CTDLDIR) < 0) {
1500                                 perror("can't change to " CTDLDIR);
1501                                 logoff(NULL, 3);
1502                         }
1503
1504                         /*
1505                          * Drop privileges if necessary. We stat
1506                          * citadel.config to get the uid/gid since it's
1507                          * guaranteed to have the uid/gid we want.
1508                          */
1509                         if (!getuid() || !getgid()) {
1510                                 if (stat(file_citadel_config, &st) < 0) {
1511                                         perror("couldn't stat citadel.config");
1512                                         logoff(NULL, 3);
1513                                 }
1514                                 if (!getgid() && (setgid(st.st_gid) < 0)) {
1515                                         perror("couldn't change gid");
1516                                         logoff(NULL, 3);
1517                                 }
1518                                 if (!getuid() && (setuid(st.st_uid) < 0)) {
1519                                         perror("couldn't change uid");
1520                                         logoff(NULL, 3);
1521                                 }
1522                                 /*
1523                                   scr_printf("Privileges changed to uid %d gid %d\n",
1524                                   getuid(), getgid());
1525                                 */
1526                         }
1527                         argc = shift(argc, argv, a, 1);
1528                 }
1529         }
1530         
1531
1532         screen_new();
1533
1534 #ifdef __CYGWIN__
1535         newprompt("Connect to (return for local server): ", hostbuf, 64);
1536 #endif
1537
1538         scr_printf("Attaching to server...\n");
1539         ipc = CtdlIPC_new(argc, argv, hostbuf, portbuf);
1540         if (!ipc) {
1541                 error_printf("Can't connect: %s\n", strerror(errno));
1542                 logoff(NULL, 3);
1543         }
1544
1545         CtdlIPC_SetNetworkStatusCallback(ipc, scr_wait_indicator);
1546
1547         if (!(ipc->isLocal)) {
1548                 scr_printf("Connected to %s [%s].\n", ipc->ip_hostname, ipc->ip_address);
1549         }
1550
1551         ipc_for_signal_handlers = ipc;  /* KLUDGE cover your eyes */
1552
1553         CtdlIPC_chat_recv(ipc, aaa);
1554         if (aaa[0] != '2') {
1555                 scr_printf("%s\n", &aaa[4]);
1556                 logoff(ipc, atoi(aaa));
1557         }
1558
1559         /* If there is a [nonce] at the end, put the nonce in <nonce>, else nonce
1560          * is zeroized.
1561          */
1562         
1563         if ((sptr = strchr(aaa, '<')) == NULL)
1564                 {
1565                         nonce[0] = '\0';
1566                 }
1567         else
1568                 {
1569                         if ((sptr2 = strchr(sptr, '>')) == NULL)
1570                                 {
1571                                         nonce[0] = '\0';
1572                                 }
1573                         else
1574                                 {
1575                                         sptr2++;
1576                                         *sptr2 = '\0';
1577                                         strncpy(nonce, sptr, (size_t)NONCE_SIZE);
1578                                 }
1579                 }
1580
1581 #ifdef HAVE_OPENSSL
1582         /* Evaluate encryption preferences */
1583         if (arg_encrypt != RC_NO && rc_encrypt != RC_NO) {
1584                 if (!ipc->isLocal || arg_encrypt == RC_YES || rc_encrypt == RC_YES) {
1585                         secure = (CtdlIPCStartEncryption(ipc, aaa) / 100 == 2) ? 1 : 0;
1586                         if (!secure)
1587                                 error_printf("Can't encrypt: %s\n", aaa);
1588                 }
1589         }
1590 #endif
1591
1592         get_serv_info(ipc, telnet_client_host);
1593         scr_printf("%-24s\n%s\n%s\n", ipc->ServInfo.software, ipc->ServInfo.humannode,
1594                    ipc->ServInfo.site_location);
1595
1596         screenwidth = 80;       /* default screen dimensions */
1597         screenheight = 24;
1598         
1599         scr_printf(" pause    next    stop\n");
1600         scr_printf(" ctrl-s  ctrl-o  ctrl-c\n\n");
1601         formout(ipc, "hello");  /* print the opening greeting */
1602         scr_printf("\n");
1603
1604  GSTA:  /* See if we have a username and password on disk */
1605         if (rc_remember_passwords) {
1606                 get_stored_password(hostbuf, portbuf, fullname, password);
1607                 if (!IsEmptyStr(fullname)) {
1608                         r = CtdlIPCTryLogin(ipc, fullname, aaa);
1609                         if (r / 100 == 3) {
1610                                 if (*nonce) {
1611                                         r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
1612                                 } else {
1613                                         r = CtdlIPCTryPassword(ipc, password, aaa);
1614                                 }
1615                         }
1616
1617                         if (r / 100 == 2) {
1618                                 load_user_info(aaa);
1619                                 stored_password = 1;
1620                                 goto PWOK;
1621                         } else {
1622                                 set_stored_password(hostbuf, portbuf, "", "");
1623                         }
1624                 }
1625         }
1626
1627         termn8 = 0;
1628         newnow = 0;
1629         do {
1630                 if (!IsEmptyStr(rc_username)) {
1631                         strcpy(fullname, rc_username);
1632                 } else {
1633                         newprompt("Enter your name: ", fullname, 29);
1634                 }
1635                 strproc(fullname);
1636                 if (!strcasecmp(fullname, "new")) {     /* just in case */
1637                         scr_printf("Please enter the name you wish to log in with.\n");
1638                 }
1639         } while (
1640                  (!strcasecmp(fullname, "bbs"))
1641                  || (!strcasecmp(fullname, "new"))
1642                  || (IsEmptyStr(fullname)));
1643
1644         if (!strcasecmp(fullname, "off")) {
1645                 mcmd = 29;
1646                 goto TERMN8;
1647         }
1648
1649         /* FIXME this is a stupid way to do guest mode but it's a reasonable test harness FIXME */
1650         if ( (ipc->ServInfo.guest_logins) && (!strcasecmp(fullname, "guest")) ) {
1651                 goto PWOK;
1652         }
1653
1654         /* sign on to the server */
1655         r = CtdlIPCTryLogin(ipc, fullname, aaa);
1656         if (r / 100 != 3)
1657                 goto NEWUSR;
1658
1659         /* password authentication */
1660         if (!IsEmptyStr(rc_password)) {
1661                 strcpy(password, rc_password);
1662         } else {
1663                 newprompt("\rPlease enter your password: ", password, -19);
1664         }
1665
1666         if (*nonce) {
1667                 r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
1668                 if (r / 100 != 2) {
1669                         strproc(password);
1670                         r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
1671                 }
1672         } else {
1673                 r = CtdlIPCTryPassword(ipc, password, aaa);
1674                 if (r / 100 != 2) {
1675                         strproc(password);
1676                         r = CtdlIPCTryPassword(ipc, password, aaa);
1677                 }
1678         }
1679         
1680         if (r / 100 == 2) {
1681                 load_user_info(aaa);
1682                 offer_to_remember_password(ipc, hostbuf, portbuf,
1683                                            fullname, password);
1684                 goto PWOK;
1685         }
1686         scr_printf("<< wrong password >>\n");
1687         if (!IsEmptyStr(rc_password))
1688                 logoff(ipc, 2);
1689         goto GSTA;
1690
1691 NEWUSR: if (IsEmptyStr(rc_password)) {
1692                 scr_printf("'%s' not found.\n", fullname);
1693                 scr_printf("Type 'off' if you would like to exit.\n");
1694                 if (ipc->ServInfo.newuser_disabled == 1) {
1695                         goto GSTA;
1696                 }
1697                 scr_printf("Do you want to create a new user account called '%s'? ",
1698                         fullname);
1699                 if (yesno() == 0) {
1700                         goto GSTA;
1701                 }
1702         }
1703
1704         r = CtdlIPCCreateUser(ipc, fullname, 1, aaa);
1705         if (r / 100 != 2) {
1706                 scr_printf("%s\n", aaa);
1707                 goto GSTA;
1708         }
1709         load_user_info(aaa);
1710
1711         while (set_password(ipc) != 0);
1712         newnow = 1;
1713
1714         enter_config(ipc, 1);
1715
1716  PWOK:
1717         /* Switch color support on or off if we're in user mode */
1718         if (rc_ansi_color == 3) {
1719                 if (userflags & US_COLOR)
1720                         enable_color = 1;
1721                 else
1722                         enable_color = 0;
1723         }
1724
1725         color(BRIGHT_WHITE);
1726         scr_printf("\n%s\nAccess level: %d (%s)\n"
1727                    "User #%ld / Login #%d",
1728                    fullname, axlevel, axdefs[(int) axlevel],
1729                    usernum, timescalled);
1730         if (lastcall > 0L) {
1731                 scr_printf(" / Last login: %s",
1732                            asctime(localtime(&lastcall)));
1733         }
1734         scr_printf("\n");
1735
1736         r = CtdlIPCMiscCheck(ipc, &chek, aaa);
1737         if (r / 100 == 2) {
1738                 b = chek.newmail;
1739                 if (b > 0) {
1740                         color(BRIGHT_RED);
1741                         if (b == 1)
1742                                 scr_printf("*** You have a new private message in Mail>\n");
1743                         if (b > 1)
1744                                 scr_printf("*** You have %d new private messages in Mail>\n", b);
1745                         color(DIM_WHITE);
1746                         if (!IsEmptyStr(rc_gotmail_cmd)) {
1747                                 rv = system(rc_gotmail_cmd);
1748                         }
1749                 }
1750                 if ((axlevel >= AxAideU) && (chek.needvalid > 0)) {
1751                         scr_printf("*** Users need validation\n");
1752                 }
1753                 if (chek.needregis > 0) {
1754                         scr_printf("*** Please register.\n");
1755                         formout(ipc, "register");
1756                         entregis(ipc);
1757                 }
1758         }
1759         /* Make up some temporary filenames for use in various parts of the
1760          * program.  Don't mess with these once they've been set, because we
1761          * will be unlinking them later on in the program and we don't
1762          * want to delete something that we didn't create. */
1763         CtdlMakeTempFileName(temp, sizeof temp);
1764         CtdlMakeTempFileName(temp2, sizeof temp2);
1765         CtdlMakeTempFileName(tempdir, sizeof tempdir);
1766
1767         /* Get screen dimensions.  First we go to a default of 80x24.  Then
1768          * we try to get the user's actual screen dimensions off the server.
1769          * However, if we're running on an xterm, all this stuff is
1770          * irrelevant because we're going to dynamically size the screen
1771          * during the session.
1772          */
1773         screenwidth = 80;
1774         screenheight = 24;
1775         r = CtdlIPCGetConfig(ipc, &myself, aaa);
1776         if (getenv("TERM") != NULL)
1777                 if (!strcmp(getenv("TERM"), "xterm")) {
1778                         have_xterm = 1;
1779                 }
1780         check_screen_dims();
1781
1782         set_floor_mode(ipc);
1783
1784         /* Enter the lobby */
1785         dotgoto(ipc, "_BASEROOM_", 1, 0);
1786
1787         /* Main loop for the system... user is logged in. */
1788     free(uglist[0]);
1789         uglistsize = 0;
1790
1791         if (newnow == 1)
1792                 readmsgs(ipc, LastMessages, ReadForward, 5);
1793         else
1794                 readmsgs(ipc, NewMessages, ReadForward, 0);
1795
1796         /* MAIN COMMAND LOOP */
1797         do {
1798                 mcmd = getcmd(ipc, argbuf);     /* Get keyboard command */
1799
1800 #ifdef TIOCGWINSZ
1801                 check_screen_dims();            /* if xterm, get screen size */
1802 #endif
1803
1804                 if (termn8 == 0)
1805                         switch (mcmd) {
1806                         case 1:
1807                                 formout(ipc, "help");
1808                                 break;
1809                         case 4:
1810                                 entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 0);
1811                                 break;
1812                         case 36:
1813                                 entmsg(ipc, 0, 1, 0);
1814                                 break;
1815                         case 46:
1816                                 entmsg(ipc, 0, 2, 0);
1817                                 break;
1818                         case 78:
1819                                 entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 1);
1820                                 break;
1821                         case 5:                         /* <G>oto */
1822                                 updatels(ipc);
1823                                 gotonext(ipc);
1824                                 break;
1825                         case 47:                        /* <A>bandon */
1826                                 if (!rc_alt_semantics) {
1827                                         updatelsa(ipc);
1828                                 }
1829                                 gotonext(ipc);
1830                                 break;
1831                         case 90:                        /* <.A>bandon */
1832                                 if (!rc_alt_semantics)
1833                                         updatelsa(ipc);
1834                                 dotgoto(ipc, argbuf, 0, 0);
1835                                 break;
1836                         case 58:                        /* <M>ail */
1837                                 updatelsa(ipc);
1838                                 dotgoto(ipc, "_MAIL_", 1, 0);
1839                                 break;
1840                         case 20:
1841                                 if (!IsEmptyStr(argbuf)) {
1842                                         updatels(ipc);
1843                                         dotgoto(ipc, argbuf, 0, 0);
1844                                 }
1845                                 break;
1846                         case 52:
1847                                 if (!IsEmptyStr(argbuf)) {
1848                                         if (rc_alt_semantics) {
1849                                                 updatelsa(ipc);
1850                                         }
1851                                         dotgoto(ipc, argbuf, 0, 0);
1852                                 }
1853                                 break;
1854                         case 95: /* what exactly is the numbering scheme supposed to be anyway? --Ford, there isn't one. -IO */
1855                                 dotungoto(ipc, argbuf);
1856                                 break;
1857                         case 10:
1858                                 readmsgs(ipc, AllMessages, ReadForward, 0);
1859                                 break;
1860                         case 9:
1861                                 readmsgs(ipc, LastMessages, ReadForward, 5);
1862                                 break;
1863                         case 13:
1864                                 readmsgs(ipc, NewMessages, ReadForward, 0);
1865                                 break;
1866                         case 11:
1867                                 readmsgs(ipc, AllMessages, ReadReverse, 0);
1868                                 break;
1869                         case 12:
1870                                 readmsgs(ipc, OldMessages, ReadReverse, 0);
1871                                 break;
1872                         case 71:
1873                                 readmsgs(ipc, LastMessages, ReadForward,
1874                                                 atoi(argbuf));
1875                                 break;
1876                         case 7:
1877                                 forget(ipc);
1878                                 break;
1879                         case 18:
1880                                 subshell();
1881                                 break;
1882                         case 38:
1883                                 updatels(ipc);
1884                                 entroom(ipc);
1885                                 break;
1886                         case 22:
1887                                 killroom(ipc);
1888                                 break;
1889                         case 32:
1890                                 userlist(ipc, argbuf);
1891                                 break;
1892                         case 27:
1893                                 invite(ipc);
1894                                 break;
1895                         case 28:
1896                                 kickout(ipc);
1897                                 break;
1898                         case 23:
1899                                 editthisroom(ipc);
1900                                 break;
1901                         case 14:
1902                                 roomdir(ipc);
1903                                 break;
1904                         case 33:
1905                                 download(ipc, 0);
1906                                 break;
1907                         case 34:
1908                                 download(ipc, 1);
1909                                 break;
1910                         case 31:
1911                                 download(ipc, 2);
1912                                 break;
1913                         case 43:
1914                                 download(ipc, 3);
1915                                 break;
1916                         case 45:
1917                                 download(ipc, 4);
1918                                 break;
1919                         case 55:
1920                                 download(ipc, 5);
1921                                 break;
1922                         case 39:
1923                                 upload(ipc, 0);
1924                                 break;
1925                         case 40:
1926                                 upload(ipc, 1);
1927                                 break;
1928                         case 42:
1929                                 upload(ipc, 2);
1930                                 break;
1931                         case 44:
1932                                 upload(ipc, 3);
1933                                 break;
1934                         case 57:
1935                                 cli_upload(ipc);
1936                                 break;
1937                         case 16:
1938                                 ungoto(ipc);
1939                                 break;
1940                         case 24:
1941                                 whoknows(ipc);
1942                                 break;
1943                         case 26:
1944                                 validate(ipc);
1945                                 break;
1946                         case 29:
1947                         case 30:
1948                                 if (!rc_alt_semantics) {
1949                                         updatels(ipc);
1950                                 }
1951                                 termn8 = 1;
1952                                 break;
1953                         case 48:
1954                                 enterinfo(ipc);
1955                                 break;
1956                         case 49:
1957                                 readinfo(ipc);
1958                                 break;
1959                         case 72:
1960                                 cli_image_upload(ipc, "_userpic_");
1961                                 break;
1962                         case 73:
1963                                 cli_image_upload(ipc, "_roompic_");
1964                                 break;
1965
1966                         case 74:
1967                                 snprintf(aaa, sizeof aaa, "_floorpic_|%d", curr_floor);
1968                                 cli_image_upload(ipc, aaa);
1969                                 break;
1970
1971                         case 75:
1972                                 enternew(ipc, "roomname", aaa, 20);
1973                                 r = CtdlIPCChangeRoomname(ipc, aaa, bbb);
1974                                 if (r / 100 != 2)
1975                                         scr_printf("\n%s\n", bbb);
1976                                 break;
1977                         case 76:
1978                                 enternew(ipc, "hostname", aaa, 25);
1979                                 r = CtdlIPCChangeHostname(ipc, aaa, bbb);
1980                                 if (r / 100 != 2)
1981                                         scr_printf("\n%s\n", bbb);
1982                                 break;
1983                         case 77:
1984                                 enternew(ipc, "username", aaa, 32);
1985                                 r = CtdlIPCChangeUsername(ipc, aaa, bbb);
1986                                 if (r / 100 != 2)
1987                                         scr_printf("\n%s\n", bbb);
1988                                 break;
1989
1990                         case 35:
1991                                 set_password(ipc);
1992                                 break;
1993
1994                         case 21:
1995                                 if (argbuf[0] == 0)
1996                                         strcpy(aaa, "?");
1997                                 display_help(ipc, argbuf);
1998                                 break;
1999
2000                         case 41:
2001                                 formout(ipc, "register");
2002                                 entregis(ipc);
2003                                 break;
2004
2005                         case 15:
2006                                 scr_printf("Are you sure (y/n)? ");
2007                                 if (yesno() == 1) {
2008                                         if (!rc_alt_semantics)
2009                                                 updatels(ipc);
2010                                         a = 0;
2011                                         termn8 = 1;
2012                                 }
2013                                 break;
2014
2015                         case 85:
2016                                 scr_printf("All users will be disconnected!  "
2017                                            "Really terminate the server? ");
2018                                 if (yesno() == 1) {
2019                                         if (!rc_alt_semantics)
2020                                                 updatels(ipc);
2021                                         r = CtdlIPCTerminateServerNow(ipc, aaa);
2022                                         scr_printf("%s\n", aaa);
2023                                         if (r / 100 == 2) {
2024                                                 a = 0;
2025                                                 termn8 = 1;
2026                                         }
2027                                 }
2028                                 break;
2029
2030                         case 86:
2031                                 scr_printf("Do you really want to schedule a "
2032                                            "server shutdown? ");
2033                                 if (yesno() == 1) {
2034                                         r = CtdlIPCTerminateServerScheduled(ipc, 1, aaa);
2035                                         if (r / 100 == 2) {
2036                                                 if (atoi(aaa)) {
2037                                                         scr_printf(
2038                                                                    "The Citadel server will terminate when all users are logged off.\n"
2039                                                                    );
2040                                                 } else {
2041                                                         scr_printf(
2042                                                                    "The Citadel server will not terminate.\n"
2043                                                                    );
2044                                                 }
2045                                         }
2046                                 }
2047                                 break;
2048
2049                         case 87:
2050                                 network_config_management(ipc, "listrecp",
2051                                                           "Message-by-message mailing list recipients");
2052                                 break;
2053
2054                         case 94:
2055                                 network_config_management(ipc, "digestrecp",
2056                                                           "Digest mailing list recipients");
2057                                 break;
2058
2059                         case 89:
2060                                 network_config_management(ipc, "ignet_push_share",
2061                                                           "Nodes with which we share this room");
2062                                 break;
2063
2064                         case 88:
2065                                 do_ignet_configuration(ipc);
2066                                 break;
2067
2068                         case 92:
2069                                 do_filterlist_configuration(ipc);
2070                                 break;
2071
2072                         case 6:
2073                                 if (rc_alt_semantics) {
2074                                         updatelsa(ipc);
2075                                 }
2076                                 gotonext(ipc);
2077                                 break;
2078
2079                         case 3:
2080                                 chatmode(ipc);
2081                                 break;
2082
2083                         case 17:
2084                                 who_is_online(ipc, 0);
2085                                 break;
2086
2087                         case 79:
2088                                 who_is_online(ipc, 1);
2089                                 break;
2090
2091                         case 91:
2092                                 who_is_online(ipc, 2);
2093                                 break;
2094                 
2095                         case 80:
2096                                 do_system_configuration(ipc);
2097                                 break;
2098
2099                         case 82:
2100                                 do_internet_configuration(ipc);
2101                                 break;
2102
2103                         case 83:
2104                                 check_message_base(ipc);
2105                                 break;
2106
2107                         case 84:
2108                                 quiet_mode(ipc);
2109                                 break;
2110
2111                         case 93:
2112                                 stealth_mode(ipc);
2113                                 break;
2114
2115                         case 50:
2116                                 enter_config(ipc, 2);
2117                                 break;
2118
2119                         case 37:
2120                                 enter_config(ipc, 0);
2121                                 set_floor_mode(ipc);
2122                                 break;
2123
2124                         case 59:
2125                                 enter_config(ipc, 3);
2126                                 set_floor_mode(ipc);
2127                                 break;
2128
2129                         case 60:
2130                                 gotofloor(ipc, argbuf, GF_GOTO);
2131                                 break;
2132
2133                         case 61:
2134                                 gotofloor(ipc, argbuf, GF_SKIP);
2135                                 break;
2136
2137                         case 62:
2138                                 forget_this_floor(ipc);
2139                                 break;
2140
2141                         case 63:
2142                                 create_floor(ipc);
2143                                 break;
2144
2145                         case 64:
2146                                 edit_floor(ipc);
2147                                 break;
2148
2149                         case 65:
2150                                 kill_floor(ipc);
2151                                 break;
2152
2153                         case 66:
2154                                 enter_bio(ipc);
2155                                 break;
2156
2157                         case 67:
2158                                 read_bio(ipc);
2159                                 break;
2160
2161                         case 25:
2162                                 edituser(ipc, 25);
2163                                 break;
2164
2165                         case 96:
2166                                 edituser(ipc, 96);
2167                                 break;
2168
2169                         case 8:
2170                                 knrooms(ipc, floor_mode);
2171                                 scr_printf("\n");
2172                                 break;
2173
2174                         case 68:
2175                                 knrooms(ipc, 2);
2176                                 scr_printf("\n");
2177                                 break;
2178
2179                         case 69:
2180                                 misc_server_cmd(ipc, argbuf);
2181                                 break;
2182
2183                         case 70:
2184                                 edit_system_message(ipc, argbuf);
2185                                 break;
2186
2187                         case 19:
2188                                 listzrooms(ipc);
2189                                 scr_printf("\n");
2190                                 break;
2191
2192                         case 51:
2193                                 deletefile(ipc);
2194                                 break;
2195
2196                         case 54:
2197                                 movefile(ipc);
2198                                 break;
2199
2200                         case 56:
2201                                 page_user(ipc);
2202                                 break;
2203
2204             case 110:           /* <+> Next room */
2205                                  gotoroomstep(ipc, 1, 0);
2206                              break;
2207
2208             case 111:           /* <-> Previous room */
2209                  gotoroomstep(ipc, 0, 0);
2210                              break;
2211
2212                         case 112:           /* <>> Next floor */
2213                  gotofloorstep(ipc, 1, GF_GOTO);
2214                              break;
2215
2216                         case 113:           /* <<> Previous floor */
2217                  gotofloorstep(ipc, 0, GF_GOTO);
2218                                  break;
2219
2220             case 116:           /* <.> skip to <+> Next room */
2221                  gotoroomstep(ipc, 1, 1);
2222                              break;
2223
2224             case 117:           /* <.> skip to <-> Previous room */
2225                  gotoroomstep(ipc, 0, 1);
2226                              break;
2227
2228                         case 118:           /* <.> skip to <>> Next floor */
2229                  gotofloorstep(ipc, 1, GF_SKIP);
2230                              break;
2231
2232                         case 119:           /* <.> skip to <<> Previous floor */
2233                  gotofloorstep(ipc, 0, GF_SKIP);
2234                                  break;
2235
2236                         case 114:           
2237                  read_config(ipc);
2238                                  break;
2239
2240                         case 115:           
2241                  system_info(ipc);
2242                                  break;
2243
2244                         case 120:           /* .KAnonymous */
2245                          dotknown(ipc, 0, NULL);
2246                                  break;
2247
2248                         case 121:           /* .KDirectory */
2249                          dotknown(ipc, 1, NULL);
2250                                  break;
2251
2252                         case 122:           /* .KMatch */
2253                          dotknown(ipc, 2, argbuf);
2254                                  break;
2255
2256                         case 123:           /* .KpreferredOnly */
2257                          dotknown(ipc, 3, NULL);
2258                                  break;
2259
2260                         case 124:           /* .KPrivate */
2261                          dotknown(ipc, 4, NULL);
2262                                  break;
2263
2264                         case 125:           /* .KRead only */
2265                          dotknown(ipc, 5, NULL);
2266                                  break;
2267
2268                         case 126:           /* .KShared */
2269                          dotknown(ipc, 6, NULL);
2270                                  break;
2271
2272                         case 127:           /* Configure POP3 aggregation */
2273                                 do_pop3client_configuration(ipc);
2274                                 break;
2275
2276                         case 128:           /* Configure XML/RSS feed retrieval */
2277                                 do_rssclient_configuration(ipc);
2278                                 break;
2279
2280                         default: /* allow some math on the command */
2281                                 /* commands 100... to 100+MAX_EDITORS-1 will
2282                                    call the appropriate editor... in other
2283                                    words, command numbers 100+ will match
2284                                    the citadel.rc keys editor0, editor1, etc.*/
2285                                 if (mcmd >= 100 && mcmd < (100+MAX_EDITORS))
2286                                 {
2287                                         /* entmsg mode >=2 select editor */
2288                                         entmsg(ipc, 0, mcmd - 100 + 2, 0);
2289                                         break;
2290                                 }
2291                         }       /* end switch */
2292         } while (termn8 == 0);
2293
2294 TERMN8: scr_printf("%s logged out.", fullname);
2295         termn8 = 0;
2296         color(ORIGINAL_PAIR);
2297         scr_printf("\n");
2298         while (marchptr != NULL) {
2299                 remove_march(marchptr->march_name, 0);
2300         }
2301         if (mcmd == 30) {
2302                 scr_printf("\n\nType 'off' to disconnect, or next user...\n");
2303         }
2304         CtdlIPCLogout(ipc);
2305         if ((mcmd == 29) || (mcmd == 15)) {
2306                 stty_ctdl(SB_RESTORE);
2307                 formout(ipc, "goodbye");
2308                 logoff(ipc, 0);
2309         }
2310         /* Free the ungoto list */
2311         for (lp = 0; lp < uglistsize; lp++) {
2312                 free(uglist[lp]);
2313         }
2314     uglistsize = 0;
2315         goto GSTA;
2316
2317 }       /* end main() */
2318