5 * Client-side functions which perform room operations
17 #include <sys/types.h>
31 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
34 void sttybbs(int cmd);
35 void hit_any_key(void);
37 void strprompt(char *prompt, char *str, int len);
38 void newprompt(char *prompt, char *str, int len);
39 void dotgoto(char *towhere, int display_name);
40 void serv_read(char *buf, int bytes);
41 void formout(char *name);
43 int fmout(int width, FILE * fp, char pagin, int height, int starting_lp,
45 void progress(long int curr, long int cmax);
46 int pattern(char *search, char *patn);
47 int file_checksum(char *filename);
48 int nukedir(char *dirname);
50 extern unsigned room_flags;
51 extern char room_name[];
53 extern char tempdir[];
54 extern int editor_pid;
55 extern char editor_path[];
56 extern int screenwidth;
57 extern int screenheight;
58 extern char fullname[];
60 extern char sigcaught;
61 extern char floor_mode;
62 extern char curr_floor;
69 extern char floorlist[128][SIZ];
72 void load_floorlist(void)
77 for (a = 0; a < 128; ++a)
83 strcpy(floorlist[0], "Main Floor");
86 while (serv_gets(buf), strcmp(buf, "000")) {
87 extract(floorlist[extract_int(buf, 0)], buf, 1);
92 void room_tree_list(struct roomlisting *rp)
95 char rmname[ROOMNAMELEN];
103 if (rp->lnext != NULL) {
104 room_tree_list(rp->lnext);
107 if (sigcaught == 0) {
108 strcpy(rmname, rp->rlname);
110 if ((c + strlen(rmname) + 4) > screenwidth) {
112 /* line break, check the paginator */
116 if (f & QR_MAILBOX) {
117 color(BRIGHT_YELLOW);
118 } else if (f & QR_PRIVATE) {
123 pprintf("%s", rmname);
124 if ((f & QR_DIRECTORY) && (f & QR_NETWORK))
126 else if (f & QR_DIRECTORY)
128 else if (f & QR_NETWORK)
132 c = c + strlen(rmname) + 3;
135 if (rp->rnext != NULL) {
136 room_tree_list(rp->rnext);
144 * Room ordering stuff (compare first by floor, then by order)
146 int rordercmp(struct roomlisting *r1, struct roomlisting *r2)
148 if ((r1 == NULL) && (r2 == NULL))
154 if (r1->rlfloor < r2->rlfloor)
156 if (r1->rlfloor > r2->rlfloor)
158 if (r1->rlorder < r2->rlorder)
160 if (r1->rlorder > r2->rlorder)
167 * Common code for all room listings
169 void listrms(char *variety)
173 struct roomlisting *rl = NULL;
174 struct roomlisting *rp;
175 struct roomlisting *rs;
178 /* Ask the server for a room list */
184 while (serv_gets(buf), strcmp(buf, "000")) {
185 rp = malloc(sizeof(struct roomlisting));
186 extract(rp->rlname, buf, 0);
187 rp->rlflags = extract_int(buf, 1);
188 rp->rlfloor = extract_int(buf, 2);
189 rp->rlorder = extract_int(buf, 3);
198 if (rordercmp(rp, rs) < 0) {
199 if (rs->lnext == NULL) {
206 if (rs->rnext == NULL) {
217 room_tree_list(NULL);
223 void list_other_floors(void)
228 for (a = 0; a < 128; ++a) {
229 if ((strlen(floorlist[a]) > 0) && (a != curr_floor)) {
230 if ((c + strlen(floorlist[a]) + 4) > screenwidth) {
234 pprintf("%s: ", floorlist[a]);
235 c = c + strlen(floorlist[a]) + 3;
242 * List known rooms. kn_floor_mode should be set to 0 for a 'flat' listing,
243 * 1 to list rooms on the current floor, or 1 to list rooms on all floors.
245 void knrooms(int kn_floor_mode)
252 if (kn_floor_mode == 0) {
254 pprintf("\n Rooms with unread messages:\n");
257 pprintf("\n\n No unseen messages in:\n");
262 if (kn_floor_mode == 1) {
264 pprintf("\n Rooms with unread messages on %s:\n",
265 floorlist[(int) curr_floor]);
266 sprintf(buf, "LKRN %d", curr_floor);
269 pprintf("\n\n Rooms with no new messages on %s:\n",
270 floorlist[(int) curr_floor]);
271 sprintf(buf, "LKRO %d", curr_floor);
274 pprintf("\n\n Other floors:\n");
279 if (kn_floor_mode == 2) {
280 for (a = 0; a < 128; ++a) {
281 if (floorlist[a][0] != 0) {
283 pprintf("\n Rooms on %s:\n",
285 sprintf(buf, "LKRA %d", a);
293 IFNEXPERT hit_any_key();
297 void listzrooms(void)
298 { /* list public forgotten rooms */
300 pprintf("\n Forgotten public rooms:\n");
304 IFNEXPERT hit_any_key();
308 int set_room_attr(int ibuf, char *prompt, unsigned int sbit)
312 a = boolprompt(prompt, (ibuf & sbit));
313 ibuf = (ibuf | sbit);
315 ibuf = (ibuf ^ sbit);
323 * Select a floor (used in several commands)
324 * The supplied argument is the 'default' floor number.
325 * This function returns the selected floor number.
327 int select_floor(int rfloor)
332 if (floor_mode == 1) {
333 if (floorlist[(int) curr_floor][0] == 0) {
339 safestrncpy(floorstr, floorlist[rfloor],
341 strprompt("Which floor", floorstr, SIZ);
342 for (a = 0; a < 128; ++a) {
344 (floorstr, &floorlist[a][0]))
349 (floorstr, &floorlist[a][0],
353 && (pattern(&floorlist[a][0], floorstr)
358 printf("\n One of:\n");
359 for (a = 0; a < 128; ++a) {
360 if (floorlist[a][0] != 0) {
366 } while (newfloor < 0);
376 * .<A>ide <E>dit room
378 void editthisroom(void)
380 char rname[ROOMNAMELEN];
390 int expire_value = 0;
392 /* Fetch the existing room config */
396 printf("%s\n", &buf[4]);
400 extract(rname, &buf[4], 0);
401 extract(rpass, &buf[4], 1);
402 extract(rdir, &buf[4], 2);
403 rflags = extract_int(&buf[4], 3);
404 rfloor = extract_int(&buf[4], 4);
405 rorder = extract_int(&buf[4], 5);
408 /* Fetch the name of the current room aide */
412 safestrncpy(raide, &buf[4], sizeof raide);
417 if (strlen(raide) == 0) {
418 strcpy(raide, "none");
421 /* Fetch the expire policy (this will silently fail on old servers,
422 * resulting in "default" policy)
424 serv_puts("GPEX room");
427 expire_mode = extract_int(&buf[4], 0);
428 expire_value = extract_int(&buf[4], 1);
431 /* Now interact with the user. */
432 strprompt("Room name", rname, ROOMNAMELEN - 1);
434 rfloor = select_floor(rfloor);
435 rflags = set_room_attr(rflags, "Private room", QR_PRIVATE);
436 if (rflags & QR_PRIVATE) {
437 rflags = set_room_attr(rflags,
438 "Accessible by guessing room name",
442 /* if it's public, clear the privacy classes */
443 if ((rflags & QR_PRIVATE) == 0) {
444 if (rflags & QR_GUESSNAME) {
445 rflags = rflags - QR_GUESSNAME;
447 if (rflags & QR_PASSWORDED) {
448 rflags = rflags - QR_PASSWORDED;
452 /* if it's private, choose the privacy classes */
453 if ((rflags & QR_PRIVATE)
454 && ((rflags & QR_GUESSNAME) == 0)) {
455 rflags = set_room_attr(rflags,
456 "Accessible by entering a password",
459 if ((rflags & QR_PRIVATE)
460 && ((rflags & QR_PASSWORDED) == QR_PASSWORDED)) {
461 strprompt("Room password", rpass, 9);
464 if ((rflags & QR_PRIVATE) == QR_PRIVATE) {
466 boolprompt("Cause current users to forget room", 0);
470 set_room_attr(rflags, "Preferred users only", QR_PREFONLY);
471 rflags = set_room_attr(rflags, "Read-only room", QR_READONLY);
472 rflags = set_room_attr(rflags, "Directory room", QR_DIRECTORY);
473 rflags = set_room_attr(rflags, "Permanent room", QR_PERMANENT);
474 if (rflags & QR_DIRECTORY) {
475 strprompt("Directory name", rdir, 14);
477 set_room_attr(rflags, "Uploading allowed", QR_UPLOAD);
479 set_room_attr(rflags, "Downloading allowed",
482 set_room_attr(rflags, "Visible directory", QR_VISDIR);
484 rflags = set_room_attr(rflags, "Network shared room", QR_NETWORK);
485 rflags = set_room_attr(rflags,
486 "Automatically make all messages anonymous",
488 if ((rflags & QR_ANONONLY) == 0) {
489 rflags = set_room_attr(rflags,
490 "Ask users whether to make messages anonymous",
493 rorder = intprompt("Listing order", rorder, 1, 127);
495 /* Ask about the room aide */
497 strprompt("Room aide (or 'none')", raide, 29);
498 if (!strcasecmp(raide, "none")) {
502 snprintf(buf, sizeof buf, "QUSR %s", raide);
506 printf("%s\n", &buf[4]);
508 } while (buf[0] != '2');
510 if (!strcasecmp(raide, "none")) {
515 /* Angels and demons dancing in my head... */
517 sprintf(buf, "%d", expire_mode);
518 strprompt("Message expire policy (? for list)", buf, 1);
521 printf("0. Use the default for this floor\n");
522 printf("1. Never automatically expire messages\n");
523 printf("2. Expire by message count\n");
524 printf("3. Expire by message age\n");
526 } while ((buf[0] < 48) || (buf[0] > 51));
527 expire_mode = buf[0] - 48;
529 /* ...lunatics and monsters underneath my bed */
530 if (expire_mode == 2) {
531 sprintf(buf, "%d", expire_value);
532 strprompt("Keep how many messages online?", buf, 10);
533 expire_value = atol(buf);
536 if (expire_mode == 3) {
537 sprintf(buf, "%d", expire_value);
538 strprompt("Keep messages for how many days?", buf, 10);
539 expire_value = atol(buf);
542 /* Give 'em a chance to change their minds */
543 printf("Save changes (y/n)? ");
546 snprintf(buf, sizeof buf, "SETA %s", raide);
550 printf("%s\n", &buf[4]);
553 snprintf(buf, sizeof buf, "SPEX room|%d|%d",
554 expire_mode, expire_value);
558 snprintf(buf, sizeof buf, "SETR %s|%s|%s|%d|%d|%d|%d",
559 rname, rpass, rdir, rflags, rbump, rfloor,
563 printf("%s\n", &buf[4]);
571 * un-goto the previous room
577 if (!strcmp(ugname, ""))
579 snprintf(buf, sizeof buf, "GOTO %s", ugname);
583 printf("%s\n", &buf[4]);
586 sprintf(buf, "SLRP %ld", uglsn);
590 printf("%s\n", &buf[4]);
592 safestrncpy(buf, ugname, sizeof buf);
598 /* Here's the code for simply transferring the file to the client,
599 * for folks who have their own clientware. It's a lot simpler than
600 * the [XYZ]modem code below...
601 * (This function assumes that a download file is already open on the server)
603 void download_to_local_disk(char *supplied_filename, long total_bytes)
607 long transmitted_bytes = 0L;
614 strcpy(filename, supplied_filename);
615 if (strlen(filename) == 0) {
616 newprompt("Filename: ", filename, 250);
619 printf("Enter the name of the directory to save '%s'\n", filename);
620 printf("to, or press return for the current directory.\n");
621 newprompt("Directory: ", dbuf, sizeof dbuf);
622 if (strlen(dbuf) == 0)
625 strcat(dbuf, filename);
627 savefp = fopen(dbuf, "w");
628 if (savefp == NULL) {
629 printf("Cannot open '%s': %s\n", dbuf, strerror(errno));
630 /* close the download file at the server */
634 printf("%s\n", &buf[4]);
638 progress(0, total_bytes);
639 while ((transmitted_bytes < total_bytes) && (broken == 0)) {
640 bb = total_bytes - transmitted_bytes;
641 aa = ((bb < 4096) ? bb : 4096);
642 sprintf(buf, "READ %ld|%ld", transmitted_bytes, aa);
646 printf("%s\n", &buf[4]);
649 packet = extract_int(&buf[4], 0);
650 serv_read(dbuf, packet);
651 if (fwrite(dbuf, packet, 1, savefp) < 1)
653 transmitted_bytes = transmitted_bytes + (long) packet;
654 progress(transmitted_bytes, total_bytes);
657 /* close the download file at the server */
661 printf("%s\n", &buf[4]);
668 * download() - download a file or files. The argument passed to this
669 * function determines which protocol to use.
670 * proto - 0 = paginate, 1 = xmodem, 2 = raw, 3 = ymodem, 4 = zmodem, 5 = save
672 void download(int proto)
677 char transmit_cmd[SIZ];
678 long total_bytes = 0L;
680 long transmitted_bytes = 0L;
686 if ((room_flags & QR_DOWNLOAD) == 0) {
687 printf("*** You cannot download from this room.\n");
691 newprompt("Enter filename: ", filename, 255);
693 snprintf(buf, sizeof buf, "OPEN %s", filename);
697 printf("%s\n", &buf[4]);
700 total_bytes = extract_long(&buf[4], 0);
702 /* Save to local disk, for folks with their own copy of the client */
704 download_to_local_disk(filename, total_bytes);
708 /* Meta-download for public clients */
709 printf("Fetching file from Citadel server...\n");
710 mkdir(tempdir, 0700);
711 snprintf(tempname, sizeof tempname, "%s/%s", tempdir, filename);
712 tpipe = fopen(tempname, "wb");
713 while ((transmitted_bytes < total_bytes) && (broken == 0)) {
714 progress(transmitted_bytes, total_bytes);
715 bb = total_bytes - transmitted_bytes;
716 aa = ((bb < 4096) ? bb : 4096);
717 sprintf(buf, "READ %ld|%ld", transmitted_bytes, aa);
721 printf("%s\n", &buf[4]);
723 packet = extract_int(&buf[4], 0);
724 serv_read(dbuf, packet);
725 if (fwrite(dbuf, packet, 1, tpipe) < 1) {
728 transmitted_bytes = transmitted_bytes + (long) packet;
731 progress(transmitted_bytes, total_bytes);
733 /* close the download file at the server */
737 printf("%s\n", &buf[4]);
741 sprintf(transmit_cmd,
742 "SHELL=/dev/null; export SHELL; TERM=dumb; export TERM; exec more -d <%s",
746 sprintf(transmit_cmd, "exec sx %s", tempname);
748 sprintf(transmit_cmd, "exec sb %s", tempname);
750 sprintf(transmit_cmd, "exec sz %s", tempname);
752 sprintf(transmit_cmd, "exec cat %s", tempname);
755 system(transmit_cmd);
758 /* clean up the temporary directory */
765 * read directory of this room
777 pprintf("%s\n", &buf[4]);
781 extract(comment, &buf[4], 0);
782 extract(flnm, &buf[4], 1);
783 pprintf("\nDirectory of %s on %s\n", flnm, comment);
784 pprintf("-----------------------\n");
785 while (serv_gets(buf), strcmp(buf, "000")) {
786 extract(flnm, buf, 0);
787 extract(flsz, buf, 1);
788 extract(comment, buf, 2);
789 if (strlen(flnm) <= 14)
790 pprintf("%-14s %8s %s\n", flnm, flsz, comment);
792 pprintf("%s\n%14s %8s %s\n", flnm, "", flsz,
799 * add a user to a private room
803 char aaa[31], bbb[SIZ];
805 /* Because kicking people out of public rooms now sets a LOCKOUT
806 * flag, we need to be able to invite people into public rooms
807 * in order to let them back in again.
812 * if ((room_flags & QR_PRIVATE)==0) {
813 * printf("This is not a private room.\n");
818 newprompt("Name of user? ", aaa, 30);
822 snprintf(bbb, sizeof bbb, "INVT %s", aaa);
825 printf("%s\n", &bbb[4]);
830 * kick a user out of a room
834 char username[31], cmd[SIZ];
836 newprompt("Name of user? ", username, 30);
837 if (strlen(username) == 0) {
841 snprintf(cmd, sizeof cmd, "KICK %s", username);
844 printf("%s\n", &cmd[4]);
849 * aide command: kill the current room
858 printf("%s\n", &aaa[4]);
862 printf("Are you sure you want to kill this room? ");
868 printf("%s\n", &aaa[4]);
871 dotgoto("_BASEROOM_", 0);
875 { /* forget the current room */
878 printf("Are you sure you want to forget this room? ");
885 printf("%s\n", &cmd[4]);
889 /* now return to the lobby */
890 dotgoto("_BASEROOM_", 0);
900 char new_room_name[ROOMNAMELEN];
902 char new_room_pass[10];
910 printf("%s\n", &cmd[4]);
914 newprompt("Name for new room? ", new_room_name, ROOMNAMELEN - 1);
915 if (strlen(new_room_name) == 0) {
918 for (a = 0; a < strlen(new_room_name); ++a) {
919 if (new_room_name[a] == '|') {
920 new_room_name[a] = '_';
924 new_room_floor = select_floor((int) curr_floor);
926 IFNEXPERT formout("roomaccess");
928 printf("<?>Help\n<1>Public room\n<2>Guess-name room\n"
929 "<3>Passworded room\n<4>Invitation-only room\n"
930 "<5>Personal room\n");
931 printf("Enter room type: ");
934 } while (((b < '1') || (b > '5')) && (b != '?'));
937 formout("roomaccess");
939 } while ((b < '1') || (b > '5'));
942 new_room_type = b - 1;
943 if (new_room_type == 2) {
944 newprompt("Enter a room password: ", new_room_pass, 9);
945 for (a = 0; a < strlen(new_room_pass); ++a)
946 if (new_room_pass[a] == '|')
947 new_room_pass[a] = '_';
949 strcpy(new_room_pass, "");
952 printf("\042%s\042, a", new_room_name);
954 printf(" public room.");
956 printf(" guess-name room.");
958 printf(" passworded room, password: %s", new_room_pass);
960 printf("n invitation-only room.");
962 printf(" personal room.");
963 printf("\nInstall it? (y/n) : ");
968 snprintf(cmd, sizeof cmd, "CRE8 1|%s|%d|%s|%d", new_room_name,
969 new_room_type, new_room_pass, new_room_floor);
973 printf("%s\n", &cmd[4]);
977 /* command succeeded... now GO to the new room! */
978 dotgoto(new_room_name, 0);
984 { /* read info file for current room */
987 sprintf(cmd, "RINF");
995 fmout(screenwidth, NULL,
996 ((userflags & US_PAGINATOR) ? 1 : 0), screenheight, 0, 1);
1001 * <W>ho knows room...
1008 if (buf[0] != '1') {
1009 pprintf("%s\n", &buf[5]);
1012 while (serv_gets(buf), strncmp(buf, "000", 3)) {
1014 pprintf("%s\n", buf);
1019 void do_edit(char *desc, char *read_cmd, char *check_cmd, char *write_cmd)
1023 int b, cksum, editor_exit;
1026 if (strlen(editor_path) == 0) {
1027 printf("Do you wish to re-enter %s? ", desc);
1032 fp = fopen(temp, "w");
1035 serv_puts(check_cmd);
1037 if (cmd[0] != '2') {
1038 printf("%s\n", &cmd[4]);
1042 if (strlen(editor_path) > 0) {
1043 serv_puts(read_cmd);
1045 if (cmd[0] == '1') {
1046 fp = fopen(temp, "w");
1047 while (serv_gets(cmd), strcmp(cmd, "000")) {
1048 fprintf(fp, "%s\n", cmd);
1054 cksum = file_checksum(temp);
1056 if (strlen(editor_path) > 0) {
1057 editor_pid = fork();
1058 if (editor_pid == 0) {
1060 sttybbs(SB_RESTORE);
1061 execlp(editor_path, editor_path, temp, NULL);
1067 b = wait(&editor_exit);
1068 } while ((b != editor_pid) && (b >= 0));
1070 printf("Executed %s\n", editor_path);
1073 printf("Entering %s. ", desc);
1074 printf("Press return twice when finished.\n");
1075 fp = fopen(temp, "r+");
1080 if (file_checksum(temp) == cksum) {
1081 printf("*** Aborted.\n");
1085 serv_puts(write_cmd);
1087 if (cmd[0] != '4') {
1088 printf("%s\n", &cmd[4]);
1092 fp = fopen(temp, "r");
1093 while (fgets(cmd, SIZ - 1, fp) != NULL) {
1094 cmd[strlen(cmd) - 1] = 0;
1105 void enterinfo(void)
1106 { /* edit info file for current room */
1107 do_edit("the Info file for this room", "RINF", "EINF 0", "EINF 1");
1110 void enter_bio(void)
1113 snprintf(cmd, sizeof cmd, "RBIO %s", fullname);
1114 do_edit("your Bio", cmd, "NOOP", "EBIO");
1118 * create a new floor
1120 void create_floor(void)
1123 char newfloorname[SIZ];
1125 serv_puts("CFLR xx|0");
1127 if (buf[0] != '2') {
1128 printf("%s\n", &buf[4]);
1132 newprompt("Name for new floor: ", newfloorname, 255);
1133 snprintf(buf, sizeof buf, "CFLR %s|1", newfloorname);
1136 if (buf[0] == '2') {
1137 printf("Floor has been created.\n");
1139 printf("%s\n", &buf[4]);
1144 * edit the current floor
1146 void edit_floor(void)
1149 int expire_mode = 0;
1150 int expire_value = 0;
1152 if (floorlist[(int) curr_floor][0] == 0)
1155 /* Fetch the expire policy (this will silently fail on old servers,
1156 * resulting in "default" policy)
1158 serv_puts("GPEX floor");
1160 if (buf[0] == '2') {
1161 expire_mode = extract_int(&buf[4], 0);
1162 expire_value = extract_int(&buf[4], 1);
1165 /* Interact with the user */
1166 strprompt("Floor name", &floorlist[(int) curr_floor][0], 255);
1168 /* Angels and demons dancing in my head... */
1170 sprintf(buf, "%d", expire_mode);
1172 ("Floor default essage expire policy (? for list)",
1174 if (buf[0] == '?') {
1176 printf("0. Use the system default\n");
1177 printf("1. Never automatically expire messages\n");
1178 printf("2. Expire by message count\n");
1179 printf("3. Expire by message age\n");
1181 } while ((buf[0] < 48) || (buf[0] > 51));
1182 expire_mode = buf[0] - 48;
1184 /* ...lunatics and monsters underneath my bed */
1185 if (expire_mode == 2) {
1186 sprintf(buf, "%d", expire_value);
1187 strprompt("Keep how many messages online?", buf, 10);
1188 expire_value = atol(buf);
1191 if (expire_mode == 3) {
1192 sprintf(buf, "%d", expire_value);
1193 strprompt("Keep messages for how many days?", buf, 10);
1194 expire_value = atol(buf);
1198 snprintf(buf, sizeof buf, "SPEX floor|%d|%d",
1199 expire_mode, expire_value);
1203 snprintf(buf, sizeof buf, "EFLR %d|%s", curr_floor,
1204 &floorlist[(int) curr_floor][0]);
1207 printf("%s\n", &buf[4]);
1215 * kill the current floor
1217 void kill_floor(void)
1219 int floornum_to_delete, a;
1222 if (floorlist[(int) curr_floor][0] == 0)
1225 floornum_to_delete = (-1);
1226 printf("(Press return to abort)\n");
1227 newprompt("Delete which floor? ", buf, 255);
1228 if (strlen(buf) == 0)
1230 for (a = 0; a < 128; ++a)
1231 if (!strcasecmp(&floorlist[a][0], buf))
1232 floornum_to_delete = a;
1233 if (floornum_to_delete < 0) {
1234 printf("No such floor. Select one of:\n");
1235 for (a = 0; a < 128; ++a)
1236 if (floorlist[a][0] != 0)
1237 printf("%s\n", &floorlist[a][0]);
1239 } while (floornum_to_delete < 0);
1240 sprintf(buf, "KFLR %d|1", floornum_to_delete);
1243 printf("%s\n", &buf[4]);