4 * citadel.c - Main source file.
16 #include <sys/types.h>
19 #include <sys/ioctl.h>
27 #include "serv_info.h"
29 #include "routines2.h"
34 #include "client_chat.h"
35 #include "citadel_decls.h"
48 #define IFEXPERT if (userflags&US_EXPERT)
49 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
50 #define IFAIDE if (axlevel>=6)
51 #define IFNAIDE if (axlevel<6)
53 struct march *march = NULL;
55 /* globals associated with the client program */
56 char temp[16]; /* Name of general temp file */
57 char temp2[16]; /* Name of general temp file */
58 char tempdir[16]; /* Name of general temp dir */
59 char editor_path[256]; /* path to external editor */
60 char printcmd[256]; /* print command */
61 int editor_pid = (-1);
64 struct CtdlServInfo serv_info; /* Info on the server connected */
68 char room_name[ROOMNAMELEN];
69 char ugname[ROOMNAMELEN];
70 long uglsn; /* holds <u>ngoto info */
71 char is_mail = 0; /* nonzero when we're in a mail room */
72 char axlevel = 0; /* access level */
73 char is_room_aide = 0; /* boolean flag, 1 if room aide */
77 long usernum = 0L; /* user number */
79 long highest_msg_read; /* used for <A>bandon room cmd */
80 long maxmsgnum; /* used for <G>oto */
82 char have_xterm = 0; /* are we running on an xterm? */
87 char curr_floor = 0; /* number of current floor */
88 char floorlist[128][256]; /* names of floors */
89 char express_msgs = 0; /* express messages waiting! */
91 jmp_buf jmp_reconnect; /* for server reconnects */
95 void sigpipehandler(int nothing)
97 longjmp(jmp_reconnect, nothing);
101 * here is our 'clean up gracefully and exit' routine
103 void logoff(int code)
105 if (editor_pid > 0) { /* kill the editor if it's running */
106 kill(editor_pid, SIGHUP);
108 /* shut down the server... but not if the logoff code is 3, because
109 * that means we're exiting because we already lost the server
115 * now clean up various things
122 /* Violently kill off any child processes if Citadel is
125 if (getppid() == 1) {
126 kill(0 - getpgrp(), SIGTERM);
128 kill(0 - getpgrp(), SIGKILL);
130 sttybbs(SB_RESTORE); /* return the old terminal settings */
131 exit(code); /* exit with the proper exit code */
137 * We handle "next" and "stop" much differently than in earlier versions.
138 * The signal catching routine simply sets a flag and returns.
140 void sighandler(int which_sig)
142 signal(SIGINT, SIG_IGN);
143 signal(SIGQUIT, SIG_IGN);
144 sigcaught = which_sig;
150 * signal catching function for hangups...
152 void dropcarr(int signum)
160 * catch SIGCONT to reset terminal modes when were are put back into the
163 void catch_sigcont(int signum)
166 signal(SIGCONT, catch_sigcont);
171 /* general purpose routines */
173 void formout(char *name)
174 { /* display a file */
176 snprintf(cmd, sizeof cmd, "MESG %s", name);
180 printf("%s\n", &cmd[4]);
183 fmout(screenwidth, NULL,
184 ((userflags & US_PAGINATOR) ? 1 : 0),
200 printf("%s\n", &buf[4]);
204 sttybbs(SB_YES_INTR);
205 printf(" User Name Num L LastCall Calls Posts\n");
206 printf("------------------------- ----- - ---------- ----- -----\n");
207 while (serv_gets(buf), strcmp(buf, "000")) {
208 if (sigcaught == 0) {
210 printf("%-25s ", fl);
211 printf("%5ld %d ", extract_long(buf, 2),
212 extract_int(buf, 1));
213 lc = extract_long(buf, 3);
214 tmbuf = (struct tm *) localtime(&lc);
215 printf("%02d/%02d/%04d ",
218 (tmbuf->tm_year + 1900));
219 printf("%5ld %5ld\n", extract_long(buf, 4), extract_long(buf, 5));
222 linecount = checkpagin(linecount,
223 ((userflags & US_PAGINATOR) ? 1 : 0),
234 * grab assorted info about the user...
236 void load_user_info(char *params)
238 extract(fullname, params, 0);
239 axlevel = extract_int(params, 1);
240 timescalled = extract_int(params, 2);
241 posted = extract_int(params, 3);
242 userflags = extract_int(params, 4);
243 usernum = extract_long(params, 5);
248 * Remove a room from the march list. 'floornum' is ignored unless
249 * 'roomname' is set to _FLOOR_, in which case all rooms on the requested
250 * floor will be removed from the march list.
252 void remove_march(char *roomname, int floornum)
254 struct march *mptr, *mptr2;
259 if ((!strucmp(march->march_name, roomname))
260 || ((!strucmp(roomname, "_FLOOR_")) && (march->march_floor == floornum))) {
267 for (mptr = march; mptr != NULL; mptr = mptr->next) {
269 if ((!strucmp(mptr->march_name, roomname))
270 || ((!strucmp(roomname, "_FLOOR_"))
271 && (mptr->march_floor == floornum))) {
273 mptr2->next = mptr->next;
284 * Locate the room on the march list which we most want to go to. Each room
285 * is measured given a "weight" of preference based on various factors.
287 char *pop_march(int desired_floor)
289 static char TheRoom[ROOMNAMELEN];
291 int TheOrder = 32767;
294 struct march *mptr = NULL;
296 strcpy(TheRoom, "_BASEROOM_");
300 for (mptr = march; mptr != NULL; mptr = mptr->next) {
302 if ((strcasecmp(mptr->march_name, "_BASEROOM_")))
303 weight = weight + 10000;
304 if (mptr->march_floor == desired_floor)
305 weight = weight + 5000;
307 weight = weight + ((128 - (mptr->march_floor)) * 128);
308 weight = weight + (128 - (mptr->march_order));
310 if (weight > TheWeight) {
312 strcpy(TheRoom, mptr->march_name);
313 TheFloor = mptr->march_floor;
314 TheOrder = mptr->march_order;
322 * jump directly to a room
324 void dotgoto(char *towhere, int display_name)
326 char aaa[256], bbb[256], psearch[256];
329 static int oldmailcount = (-1);
330 int partial_match, best_match;
333 /* store ungoto information */
334 strcpy(ugname, room_name);
337 /* first try an exact match */
338 snprintf(aaa, sizeof aaa, "GOTO %s", towhere);
343 if (!strncmp(aaa, "54", 2)) {
344 newprompt("Enter room password: ", bbb, 9);
345 snprintf(aaa, sizeof aaa, "GOTO %s|%s", towhere, bbb);
351 if (!strncmp(aaa, "54", 2)) {
352 printf("Wrong password.\n");
356 * If a match is not found, try a partial match.
357 * Partial matches anywhere in the string carry a weight of 1,
358 * left-aligned matches carry a weight of 2. Pick the room that
359 * has the highest-weighted match.
367 while (serv_gets(aaa), strcmp(aaa, "000")) {
368 extract(psearch, aaa, 0);
370 if (pattern(psearch, towhere) >= 0) {
373 if (!struncmp(towhere, psearch, strlen(towhere))) {
376 if (partial_match > best_match) {
377 strcpy(bbb, psearch);
378 best_match = partial_match;
381 if (strlen(bbb) == 0) {
382 printf("No room '%s'.\n", towhere);
385 snprintf(aaa, sizeof aaa, "GOTO %s", bbb);
395 extract(room_name, &aaa[4], 0);
396 room_flags = extract_int(&aaa[4], 4);
397 from_floor = curr_floor;
398 curr_floor = extract_int(&aaa[4], 10);
400 remove_march(room_name, 0);
401 if (!strucmp(towhere, "_BASEROOM_"))
402 remove_march(towhere, 0);
403 if ((from_floor != curr_floor) && (display_name > 0) && (floor_mode == 1)) {
404 if (floorlist[(int) curr_floor][0] == 0)
406 printf("(Entering floor: %s)\n", &floorlist[(int) curr_floor][0]);
408 if (display_name == 1) {
410 printf("%s ", room_name);
414 if (display_name != 2) {
415 color(BRIGHT_YELLOW);
416 printf("%d ", extract_int(&aaa[4], 1));
419 color(BRIGHT_YELLOW);
420 printf("%d ", extract_int(&aaa[4], 2));
422 printf("messages.\n");
424 highest_msg_read = extract_int(&aaa[4], 6);
425 maxmsgnum = extract_int(&aaa[4], 5);
426 is_mail = (char) extract_int(&aaa[4], 7);
427 is_room_aide = (char) extract_int(&aaa[4], 8);
428 ls = extract_long(&aaa[4], 6);
430 /* read info file if necessary */
431 if (extract_int(&aaa[4], 3) > 0)
434 /* check for newly arrived mail if we can */
435 if (num_parms(&aaa[4]) >= 10) {
436 newmailcount = extract_int(&aaa[4], 9);
437 if ((oldmailcount >= 0) && (newmailcount > oldmailcount)) {
439 printf("*** You have new mail\n");
442 oldmailcount = newmailcount;
446 /* Goto next room having unread messages.
447 * We want to skip over rooms that the user has already been to, and take the
448 * user back to the lobby when done. The room we end up in is placed in
449 * newroom - which is set to 0 (the lobby) initially.
454 struct march *mptr, *mptr2;
457 /* Check to see if the march-mode list is already allocated.
458 * If it is, pop the first room off the list and go there.
464 while (serv_gets(buf), strcmp(buf, "000")) {
465 mptr = (struct march *) malloc(sizeof(struct march));
467 extract(mptr->march_name, buf, 0);
468 mptr->march_floor = (char) (extract_int(buf, 2) & 0x7F);
469 mptr->march_order = (char) (extract_int(buf, 3) & 0x7F);
474 while (mptr2->next != NULL)
479 /* add _BASEROOM_ to the end of the march list, so the user will end up
480 * in the system base room (usually the Lobby>) at the end of the loop
482 mptr = (struct march *) malloc(sizeof(struct march));
484 strcpy(mptr->march_name, "_BASEROOM_");
489 while (mptr2->next != NULL)
494 * ...and remove the room we're currently in, so a <G>oto doesn't make us
495 * walk around in circles
497 remove_march(room_name, 0);
500 strcpy(next_room, pop_march(curr_floor));
502 strcpy(next_room, "_BASEROOM_");
504 remove_march(next_room, 0);
505 dotgoto(next_room, 1);
509 * forget all rooms on a given floor
511 void forget_all_rooms_on(int ffloor)
514 struct march *flist, *fptr;
516 printf("Forgetting all rooms on %s...\r", &floorlist[ffloor][0]);
518 snprintf(buf, sizeof buf, "LKRA %d", ffloor);
522 printf("%-72s\n", &buf[4]);
526 while (serv_gets(buf), strcmp(buf, "000")) {
527 fptr = (struct march *) malloc(sizeof(struct march));
530 extract(fptr->march_name, buf, 0);
532 while (flist != NULL) {
533 snprintf(buf, sizeof buf, "GOTO %s", flist->march_name);
544 printf("%-72s\r", "");
549 * routine called by gotofloor() to move to a new room on a new floor
551 void gf_toroom(char *towhere, int mode)
553 int floor_being_left;
555 floor_being_left = curr_floor;
557 if (mode == GF_GOTO) { /* <;G>oto mode */
561 if (mode == GF_SKIP) { /* <;S>kip mode */
563 remove_march("_FLOOR_", floor_being_left);
565 if (mode == GF_ZAP) { /* <;Z>ap mode */
567 remove_march("_FLOOR_", floor_being_left);
568 forget_all_rooms_on(floor_being_left);
576 void gotofloor(char *towhere, int mode)
580 char buf[256], targ[256];
582 if (floorlist[0][0] == 0)
585 for (a = 0; a < 128; ++a)
586 if (!strucmp(&floorlist[a][0], towhere))
590 for (a = 0; a < 128; ++a) {
591 if (!struncmp(&floorlist[a][0], towhere, strlen(towhere))) {
597 for (a = 0; a < 128; ++a)
598 if (pattern(towhere, &floorlist[a][0]) > 0)
602 printf("No floor '%s'.\n", towhere);
605 for (mptr = march; mptr != NULL; mptr = mptr->next) {
606 if ((mptr->march_floor) == tofloor)
607 gf_toroom(mptr->march_name, mode);
612 snprintf(buf, sizeof buf, "LKRA %d", tofloor);
616 while (serv_gets(buf), strcmp(buf, "000")) {
617 if ((extract_int(buf, 2) == tofloor) && (strlen(targ) == 0))
618 extract(targ, buf, 0);
620 if (strlen(targ) > 0) {
621 gf_toroom(targ, mode);
623 printf("There are no rooms on '%s'.\n", &floorlist[tofloor][0]);
629 * forget all rooms on current floor
631 void forget_this_floor(void)
634 if (curr_floor == 0) {
635 printf("Can't forget this floor.\n");
638 if (floorlist[0][0] == 0)
640 printf("Are you sure you want to forget all rooms on %s? ",
641 &floorlist[(int) curr_floor][0]);
645 gf_toroom("_BASEROOM_", GF_ZAP);
650 * Figure out the physical screen dimensions, if we can
652 void check_screen_dims(void)
656 unsigned short height; /* rows */
657 unsigned short width; /* columns */
658 unsigned short xpixels;
659 unsigned short ypixels; /* pixels */
662 if (have_xterm) { /* dynamically size screen if on an xterm */
663 if (ioctl(0, TIOCGWINSZ, &xwinsz) == 0) {
665 screenheight = (int) xwinsz.height;
667 screenwidth = (int) xwinsz.width;
675 * set floor mode depending on client, server, and user settings
677 void set_floor_mode(void)
679 if (serv_info.serv_ok_floors == 0) {
680 floor_mode = 0; /* Don't use floors if the server */
682 /* doesn't support them! */
684 if (rc_floor_mode == RC_NO) { /* never use floors */
687 if (rc_floor_mode == RC_YES) { /* always use floors */
690 if (rc_floor_mode == RC_DEFAULT) { /* user choice */
691 floor_mode = ((userflags & US_FLOORS) ? 1 : 0);
697 * Set or change the user's password
699 int set_password(void)
705 if (strlen(rc_password) > 0) {
706 strcpy(pass1, rc_password);
707 strcpy(pass2, rc_password);
709 IFNEXPERT formout("changepw");
710 newprompt("Enter a new password: ", pass1, -19);
711 newprompt("Enter it again to confirm: ", pass2, -19);
713 if (!strucmp(pass1, pass2)) {
714 snprintf(buf, sizeof buf, "SETP %s", pass1);
717 printf("%s\n", &buf[4]);
718 strcpy(re_password, pass1);
721 printf("*** They don't match... try again.\n");
729 * get info about the server we've connected to
731 void get_serv_info(void)
735 CtdlInternalGetServInfo(&serv_info);
737 /* be nice and identify ourself to the server */
738 snprintf(buf, sizeof buf, "IDEN %d|%d|%d|%s|",
739 SERVER_TYPE, 0, REV_LEVEL,
740 (server_is_local ? "local" : CITADEL));
741 locate_host(&buf[strlen(buf)]); /* append to the end */
743 serv_gets(buf); /* we don't care about the result code */
751 * Display list of users currently logged on to the server
753 void who_is_online(int longlist)
755 char buf[128], username[128], roomname[128], fromhost[128],
757 char tbuf[128], clientsoft[128];
759 time_t idletime, idlehours, idlemins, idlesecs;
760 int last_session = (-1);
765 if (tbuf[0] == '2') {
766 timenow = extract_long(&tbuf[4], 0);
772 printf("FLG ### User Name Room From host\n");
774 printf("--- --- ------------------------- -------------------- ------------------------\n");
779 while (serv_gets(buf), strcmp(buf, "000")) {
780 extract(username, buf, 1);
781 extract(roomname, buf, 2);
782 extract(fromhost, buf, 3);
783 extract(clientsoft, buf, 4);
784 extract(flags, buf, 7);
787 idletime = timenow - extract_long(buf, 5);
788 idlehours = idletime / 3600;
789 idlemins = (idletime - (idlehours * 3600)) / 60;
790 idlesecs = (idletime - (idlehours * 3600) - (idlemins * 60));
791 printf("\nFlags: %-3s Sess# %-3d Name: %-25s Room: %s\n",
792 flags, extract_int(buf, 0), username, roomname);
793 printf("from <%s> using <%s>, idle %ld:%02ld:%02ld\n",
794 fromhost, clientsoft,
795 (long) idlehours, (long) idlemins, (long) idlesecs);
798 if (extract_int(buf, 0) == last_session) {
801 color(BRIGHT_MAGENTA);
802 printf("%-3s ", flags);
804 printf("%-3d ", extract_int(buf, 0));
806 last_session = extract_int(buf, 0);
808 printf("%-25s ", username);
809 color(BRIGHT_MAGENTA);
810 printf("%-20s ", roomname);
812 printf("%-24s\n", fromhost);
819 void enternew(char *desc, char *buf, int maxlen)
822 snprintf(bbb, sizeof bbb, "Enter in your new %s: ", desc);
823 newprompt(bbb, buf, maxlen);
829 int main(int argc, char **argv)
832 char aaa[100], bbb[100], eee[100]; /* general purpose variables */
833 char argbuf[32]; /* command line buf */
834 volatile int termn8 = 0;
837 sttybbs(SB_SAVE); /* Store the old terminal parameters */
838 load_command_set(); /* parse the citadel.rc file */
839 sttybbs(SB_NO_INTR); /* Install the new ones */
840 signal(SIGINT, SIG_IGN);
841 signal(SIGQUIT, SIG_IGN);
842 signal(SIGHUP, dropcarr); /* Cleanup gracefully if carrier is dropped */
843 signal(SIGTERM, dropcarr); /* Cleanup gracefully if terminated */
844 signal(SIGCONT, catch_sigcont); /* Catch SIGCONT so we can reset terminal */
846 printf("Attaching to server...\r");
848 attach_to_server(argc, argv);
854 printf("%s\n", &aaa[4]);
863 printf("%-22s\n%s\n%s\n", serv_info.serv_software, serv_info.serv_humannode,
864 serv_info.serv_bbs_city);
865 screenwidth = 80; /* default screen dimensions */
868 printf(" pause next stop\n");
869 printf(" ctrl-s ctrl-o ctrl-c\n\n");
870 formout("hello"); /* print the opening greeting */
876 if (strlen(rc_username) > 0) {
877 strcpy(fullname, rc_username);
879 newprompt("Enter your name: ", fullname, 29);
882 if (!strucmp(fullname, "new")) { /* just in case */
883 printf("Please enter the name you wish to log in with.\n");
886 (!strucmp(fullname, "bbs"))
887 || (!strucmp(fullname, "new"))
888 || (strlen(fullname) == 0));
890 if (!strucmp(fullname, "off")) {
894 /* sign on to the server */
895 strcpy(re_username, fullname);
896 snprintf(aaa, sizeof aaa, "USER %s", fullname);
902 /* password authentication */
903 if (strlen(rc_password) > 0) {
904 strcpy(eee, rc_password);
906 newprompt("\rPlease enter your password: ", eee, -19);
909 snprintf(aaa, sizeof aaa, "PASS %s", eee);
913 strcpy(re_password, eee);
914 load_user_info(&aaa[4]);
917 printf("<< wrong password >>\n");
918 if (strlen(rc_password) > 0)
922 NEWUSR:if (strlen(rc_password) == 0) {
923 printf("No record. Enter as new user? ");
927 snprintf(aaa, sizeof aaa, "NEWU %s", fullname);
934 load_user_info(&aaa[4]);
936 while (set_password() != 0);;
942 PWOK:printf("%s\nAccess level: %d (%s)\nUser #%ld / Call #%d\n",
943 fullname, axlevel, axdefs[(int) axlevel],
944 usernum, timescalled);
949 b = extract_int(&aaa[4], 0);
953 printf("*** You have a new private message in Mail>\n");
955 printf("*** You have %d new private messages in Mail>\n", b);
958 if ((axlevel >= 6) && (extract_int(&aaa[4], 2) > 0)) {
959 printf("*** Users need validation\n");
961 if (extract_int(&aaa[4], 1) > 0) {
962 printf("*** Please register.\n");
967 /* Make up some temporary filenames for use in various parts of the
968 * program. Don't mess with these once they've been set, because we
969 * will be unlinking them later on in the program and we don't
970 * want to delete something that we didn't create. */
971 snprintf(temp, sizeof temp, "/tmp/citA%d", getpid());
972 snprintf(temp2, sizeof temp2, "/tmp/citB%d", getpid());
973 snprintf(tempdir, sizeof tempdir, "/tmp/citC%d", getpid());
975 /* Get screen dimensions. First we go to a default of 80x24. Then
976 * we try to get the user's actual screen dimensions off the server.
977 * However, if we're running on an xterm, all this stuff is
978 * irrelevant because we're going to dynamically size the screen
979 * during the session.
986 screenwidth = extract_int(&aaa[4], 0);
987 screenheight = extract_int(&aaa[4], 1);
989 if (getenv("TERM") != NULL)
990 if (!strcmp(getenv("TERM"), "xterm")) {
1000 /* Enter the lobby */
1001 dotgoto("_BASEROOM_", 1);
1003 /* Main loop for the system... user is logged in. */
1012 do { /* MAIN LOOP OF PROGRAM */
1014 /* Reconnect to the server if the connection was broken */
1015 if (setjmp(jmp_reconnect)) {
1016 printf("\rServer connection broken; reconnecting...\r");
1018 attach_to_server(argc, argv);
1022 if (aaa[0] != '2') {
1023 printf("%s\n", &aaa[4]);
1027 sprintf(aaa, "USER %s", re_username);
1030 if (aaa[0] != '3') {
1031 printf("%s\n", &aaa[4]);
1034 sprintf(aaa, "PASS %s", re_password);
1037 if (aaa[0] != '2') {
1038 printf("%s\n", &aaa[4]);
1041 load_user_info(&aaa[4]);
1042 sprintf(aaa, "GOTO %s", room_name);
1046 signal(SIGPIPE, sigpipehandler);
1048 signal(SIGINT, SIG_IGN);
1049 signal(SIGQUIT, SIG_IGN);
1050 mcmd = getcmd(argbuf);
1053 check_screen_dims();
1071 newprompt("What do you want your username to be? ", aaa, 32);
1072 snprintf(bbb, sizeof bbb, "ENT0 2|0|0|0|%s", aaa);
1075 if (strncmp("200", aaa, 3))
1076 printf("\n%s\n", aaa);
1090 dotgoto("_MAIL_", 1);
1107 readmsgs(0, (-1), 0);
1110 readmsgs(2, (-1), 0);
1113 readmsgs(3, 1, atoi(argbuf));
1200 cli_image_upload("_userpic_");
1203 cli_image_upload("_roompic_");
1207 snprintf(aaa, sizeof aaa, "_floorpic_|%d", curr_floor);
1208 cli_image_upload(aaa);
1212 enternew("roomname", aaa, 20);
1213 snprintf(bbb, sizeof bbb, "RCHG %s", aaa);
1216 if (strncmp("200", aaa, 3))
1217 printf("\n%s\n", aaa);
1220 enternew("hostname", aaa, 25);
1221 snprintf(bbb, sizeof bbb, "HCHG %s", aaa);
1224 if (strncmp("200", aaa, 3))
1225 printf("\n%s\n", aaa);
1228 enternew("username", aaa, 32);
1229 snprintf(bbb, sizeof bbb, "UCHG %s", aaa);
1232 if (strncmp("200", aaa, 3))
1233 printf("\n%s\n", aaa);
1243 display_help(argbuf);
1247 formout("register");
1252 printf("Are you sure (y/n)? ");
1269 if (server_is_local) {
1270 sttybbs(SB_RESTORE);
1271 snprintf(aaa, sizeof aaa, "USERNAME=\042%s\042; export USERNAME;"
1272 "exec ./subsystem %ld %d %d", fullname,
1273 usernum, screenwidth, axlevel);
1275 sttybbs(SB_NO_INTR);
1277 printf("*** Can't run doors when server is not local.\n");
1290 do_system_configuration();
1308 gotofloor(argbuf, GF_GOTO);
1312 gotofloor(argbuf, GF_SKIP);
1316 forget_this_floor();
1344 knrooms(floor_mode);
1354 misc_server_cmd(argbuf);
1358 edit_system_message(argbuf);
1383 } while (termn8 == 0);
1385 TERMN8:printf("%s logged out.\n", fullname);
1386 while (march != NULL)
1387 remove_march(march->march_name, 0);
1389 printf("\n\nType 'off' to hang up, or next user...\n");
1390 snprintf(aaa, sizeof aaa, "LOUT");
1393 if ((mcmd == 29) || (mcmd == 15)) {