]> code.citadel.org Git - citadel.git/blob - citadel/rooms.c
* rooms.c: converted most functions to new IPC code
[citadel.git] / citadel / rooms.c
1 /*
2  * $Id$
3  *
4  * 
5  * Client-side functions which perform room operations
6  *
7  */
8
9 #include "sysdep.h"
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <ctype.h>
15 #include <string.h>
16 #include <signal.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/wait.h>
20 #include <errno.h>
21 #include <stdarg.h>
22 #include "citadel.h"
23 #include "citadel_ipc.h"
24 #include "rooms.h"
25 #include "commands.h"
26 #include "tools.h"
27 #include "messages.h"
28 #ifndef HAVE_SNPRINTF
29 #include "snprintf.h"
30 #endif
31 #include "screen.h"
32
33 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
34
35
36 void sttybbs(int cmd);
37 void hit_any_key(void);
38 int yesno(void);
39 void strprompt(char *prompt, char *str, int len);
40 void newprompt(char *prompt, char *str, int len);
41 void dotgoto(char *towhere, int display_name, int fromungoto);
42 void serv_read(char *buf, int bytes);
43 void formout(char *name);
44 int inkey(void);
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);
49
50 extern unsigned room_flags;
51 extern char room_name[];
52 extern char temp[];
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[];
59 extern int userflags;
60 extern char sigcaught;
61 extern char floor_mode;
62 extern char curr_floor;
63
64
65 extern int ugnum;
66 extern long uglsn;
67 extern char *uglist[];
68 extern long uglistlsn[];
69 extern int uglistsize;
70
71 extern char floorlist[128][SIZ];
72
73
74 void load_floorlist(void)
75 {
76         int a;
77         char buf[SIZ];
78         char *listing = NULL;
79         int r;                  /* IPC response code */
80
81         for (a = 0; a < 128; ++a)
82                 floorlist[a][0] = 0;
83
84         r = CtdlIPCFloorListing(&listing, buf);
85         if (r / 100 != 1) {
86                 strcpy(floorlist[0], "Main Floor");
87                 return;
88         }
89         while (*listing && strlen(listing)) {
90                 extract_token(buf, listing, 0, '\n');
91                 strcpy(listing, &listing[strlen(buf) + 1]);
92                 extract(floorlist[extract_int(buf, 0)], buf, 1);
93         }
94 }
95
96
97 void room_tree_list(struct roomlisting *rp)
98 {
99         static int c = 0;
100         char rmname[ROOMNAMELEN];
101         int f;
102
103         if (rp == NULL) {
104                 c = 1;
105                 return;
106         }
107
108         if (rp->lnext != NULL) {
109                 room_tree_list(rp->lnext);
110         }
111
112         if (sigcaught == 0) {
113                 strcpy(rmname, rp->rlname);
114                 f = rp->rlflags;
115                 if ((c + strlen(rmname) + 4) > screenwidth) {
116
117                         /* line break, check the paginator */
118                         pprintf("\n");
119                         c = 1;
120                 }
121                 if (f & QR_MAILBOX) {
122                         color(BRIGHT_YELLOW);
123                 } else if (f & QR_PRIVATE) {
124                         color(BRIGHT_RED);
125                 } else {
126                         color(DIM_WHITE);
127                 }
128                 pprintf("%s", rmname);
129                 if ((f & QR_DIRECTORY) && (f & QR_NETWORK))
130                         pprintf("}  ");
131                 else if (f & QR_DIRECTORY)
132                         pprintf("]  ");
133                 else if (f & QR_NETWORK)
134                         pprintf(")  ");
135                 else
136                         pprintf(">  ");
137                 c = c + strlen(rmname) + 3;
138         }
139
140         if (rp->rnext != NULL) {
141                 room_tree_list(rp->rnext);
142         }
143
144         free(rp);
145 }
146
147
148 /* 
149  * Room ordering stuff (compare first by floor, then by order)
150  */
151 int rordercmp(struct roomlisting *r1, struct roomlisting *r2)
152 {
153         if ((r1 == NULL) && (r2 == NULL))
154                 return (0);
155         if (r1 == NULL)
156                 return (-1);
157         if (r2 == NULL)
158                 return (1);
159         if (r1->rlfloor < r2->rlfloor)
160                 return (-1);
161         if (r1->rlfloor > r2->rlfloor)
162                 return (1);
163         if (r1->rlorder < r2->rlorder)
164                 return (-1);
165         if (r1->rlorder > r2->rlorder)
166                 return (1);
167         return (0);
168 }
169
170
171 /*
172  * Common code for all room listings
173  */
174 void listrms(char *variety)
175 {
176         char buf[SIZ];
177
178         struct roomlisting *rl = NULL;
179         struct roomlisting *rp;
180         struct roomlisting *rs;
181
182
183         /* Ask the server for a room list */
184         serv_puts(variety);
185         serv_gets(buf);
186         if (buf[0] != '1') {
187                 return;
188         }
189         while (serv_gets(buf), strcmp(buf, "000")) {
190                 rp = malloc(sizeof(struct roomlisting));
191                 extract(rp->rlname, buf, 0);
192                 rp->rlflags = extract_int(buf, 1);
193                 rp->rlfloor = extract_int(buf, 2);
194                 rp->rlorder = extract_int(buf, 3);
195                 rp->lnext = NULL;
196                 rp->rnext = NULL;
197
198                 rs = rl;
199                 if (rl == NULL) {
200                         rl = rp;
201                 } else {
202                         while (rp != NULL) {
203                                 if (rordercmp(rp, rs) < 0) {
204                                         if (rs->lnext == NULL) {
205                                                 rs->lnext = rp;
206                                                 rp = NULL;
207                                         } else {
208                                                 rs = rs->lnext;
209                                         }
210                                 } else {
211                                         if (rs->rnext == NULL) {
212                                                 rs->rnext = rp;
213                                                 rp = NULL;
214                                         } else {
215                                                 rs = rs->rnext;
216                                         }
217                                 }
218                         }
219                 }
220         }
221
222         room_tree_list(NULL);
223         room_tree_list(rl);
224         color(DIM_WHITE);
225 }
226
227
228 void list_other_floors(void)
229 {
230         int a, c;
231
232         c = 1;
233         for (a = 0; a < 128; ++a) {
234                 if ((strlen(floorlist[a]) > 0) && (a != curr_floor)) {
235                         if ((c + strlen(floorlist[a]) + 4) > screenwidth) {
236                                 pprintf("\n");
237                                 c = 1;
238                         }
239                         pprintf("%s:  ", floorlist[a]);
240                         c = c + strlen(floorlist[a]) + 3;
241                 }
242         }
243 }
244
245
246 /*
247  * List known rooms.  kn_floor_mode should be set to 0 for a 'flat' listing,
248  * 1 to list rooms on the current floor, or 1 to list rooms on all floors.
249  */
250 void knrooms(int kn_floor_mode)
251 {
252         char buf[SIZ];
253         int a;
254
255         load_floorlist();
256
257         if (kn_floor_mode == 0) {
258                 color(BRIGHT_CYAN);
259                 pprintf("\n   Rooms with unread messages:\n");
260                 listrms("LKRN");
261                 color(BRIGHT_CYAN);
262                 pprintf("\n\n   No unseen messages in:\n");
263                 listrms("LKRO");
264                 pprintf("\n");
265         }
266
267         if (kn_floor_mode == 1) {
268                 color(BRIGHT_CYAN);
269                 pprintf("\n   Rooms with unread messages on %s:\n",
270                         floorlist[(int) curr_floor]);
271                 snprintf(buf, sizeof buf, "LKRN %d", curr_floor);
272                 listrms(buf);
273                 color(BRIGHT_CYAN);
274                 pprintf("\n\n   Rooms with no new messages on %s:\n",
275                         floorlist[(int) curr_floor]);
276                 snprintf(buf, sizeof buf, "LKRO %d", curr_floor);
277                 listrms(buf);
278                 color(BRIGHT_CYAN);
279                 pprintf("\n\n   Other floors:\n");
280                 list_other_floors();
281                 pprintf("\n");
282         }
283
284         if (kn_floor_mode == 2) {
285                 for (a = 0; a < 128; ++a) {
286                         if (floorlist[a][0] != 0) {
287                                 color(BRIGHT_CYAN);
288                                 pprintf("\n   Rooms on %s:\n",
289                                         floorlist[a]);
290                                 snprintf(buf, sizeof buf, "LKRA %d", a);
291                                 listrms(buf);
292                                 pprintf("\n");
293                         }
294                 }
295         }
296
297         color(DIM_WHITE);
298         IFNEXPERT hit_any_key();
299 }
300
301
302 void listzrooms(void)
303 {                               /* list public forgotten rooms */
304         color(BRIGHT_CYAN);
305         pprintf("\n   Forgotten public rooms:\n");
306         listrms("LZRM");
307         pprintf("\n");
308         color(DIM_WHITE);
309         IFNEXPERT hit_any_key();
310 }
311
312
313 int set_room_attr(int ibuf, char *prompt, unsigned int sbit)
314 {
315         int a;
316
317         a = boolprompt(prompt, (ibuf & sbit));
318         ibuf = (ibuf | sbit);
319         if (!a) {
320                 ibuf = (ibuf ^ sbit);
321         }
322         return (ibuf);
323 }
324
325
326
327 /*
328  * Select a floor (used in several commands)
329  * The supplied argument is the 'default' floor number.
330  * This function returns the selected floor number.
331  */
332 int select_floor(int rfloor)
333 {
334         int a, newfloor;
335         char floorstr[SIZ];
336
337         if (floor_mode == 1) {
338                 if (floorlist[(int) curr_floor][0] == 0) {
339                         load_floorlist();
340                 }
341
342                 do {
343                         newfloor = (-1);
344                         safestrncpy(floorstr, floorlist[rfloor],
345                                     sizeof floorstr);
346                         strprompt("Which floor", floorstr, SIZ);
347                         for (a = 0; a < 128; ++a) {
348                                 if (!strcasecmp
349                                     (floorstr, &floorlist[a][0]))
350                                         newfloor = a;
351                                 if ((newfloor < 0)
352                                     &&
353                                     (!strncasecmp
354                                      (floorstr, &floorlist[a][0],
355                                       strlen(floorstr))))
356                                         newfloor = a;
357                                 if ((newfloor < 0)
358                                     && (pattern(&floorlist[a][0], floorstr)
359                                         >= 0))
360                                         newfloor = a;
361                         }
362                         if (newfloor < 0) {
363                                 scr_printf("\n One of:\n");
364                                 for (a = 0; a < 128; ++a) {
365                                         if (floorlist[a][0] != 0) {
366                                                 scr_printf("%s\n",
367                                                        &floorlist[a][0]);
368                                         }
369                                 }
370                         }
371                 } while (newfloor < 0);
372                 return (newfloor);
373         }
374         return (rfloor);
375 }
376
377
378
379
380 /*
381  * .<A>ide <E>dit room
382  */
383 void editthisroom(void)
384 {
385         char rname[ROOMNAMELEN];
386         char rpass[10];
387         char rdir[15];
388         unsigned rflags;
389         int rbump;
390         char raide[32];
391         char buf[SIZ];
392         int rfloor;
393         int rorder;
394         int expire_mode = 0;
395         int expire_value = 0;
396
397         /* Fetch the existing room config */
398         serv_puts("GETR");
399         serv_gets(buf);
400         if (buf[0] != '2') {
401                 scr_printf("%s\n", &buf[4]);
402                 return;
403         }
404
405         extract(rname, &buf[4], 0);
406         extract(rpass, &buf[4], 1);
407         extract(rdir, &buf[4], 2);
408         rflags = extract_int(&buf[4], 3);
409         rfloor = extract_int(&buf[4], 4);
410         rorder = extract_int(&buf[4], 5);
411         rbump = 0;
412
413         /* Fetch the name of the current room aide */
414         serv_puts("GETA");
415         serv_gets(buf);
416         if (buf[0] == '2') {
417                 safestrncpy(raide, &buf[4], sizeof raide);
418         }
419         else {
420                 strcpy(raide, "");
421         }
422         if (strlen(raide) == 0) {
423                 strcpy(raide, "none");
424         }
425
426         /* Fetch the expire policy (this will silently fail on old servers,
427          * resulting in "default" policy)
428          */
429         serv_puts("GPEX room");
430         serv_gets(buf);
431         if (buf[0] == '2') {
432                 expire_mode = extract_int(&buf[4], 0);
433                 expire_value = extract_int(&buf[4], 1);
434         }
435
436         /* Now interact with the user. */
437         strprompt("Room name", rname, ROOMNAMELEN - 1);
438
439         rfloor = select_floor(rfloor);
440         rflags = set_room_attr(rflags, "Private room", QR_PRIVATE);
441         if (rflags & QR_PRIVATE) {
442                 rflags = set_room_attr(rflags,
443                                        "Accessible by guessing room name",
444                                        QR_GUESSNAME);
445         }
446
447         /* if it's public, clear the privacy classes */
448         if ((rflags & QR_PRIVATE) == 0) {
449                 if (rflags & QR_GUESSNAME) {
450                         rflags = rflags - QR_GUESSNAME;
451                 }
452                 if (rflags & QR_PASSWORDED) {
453                         rflags = rflags - QR_PASSWORDED;
454                 }
455         }
456
457         /* if it's private, choose the privacy classes */
458         if ((rflags & QR_PRIVATE)
459             && ((rflags & QR_GUESSNAME) == 0)) {
460                 rflags = set_room_attr(rflags,
461                                        "Accessible by entering a password",
462                                        QR_PASSWORDED);
463         }
464         if ((rflags & QR_PRIVATE)
465             && ((rflags & QR_PASSWORDED) == QR_PASSWORDED)) {
466                 strprompt("Room password", rpass, 9);
467         }
468
469         if ((rflags & QR_PRIVATE) == QR_PRIVATE) {
470                 rbump =
471                     boolprompt("Cause current users to forget room", 0);
472         }
473
474         rflags =
475             set_room_attr(rflags, "Preferred users only", QR_PREFONLY);
476         rflags = set_room_attr(rflags, "Read-only room", QR_READONLY);
477         rflags = set_room_attr(rflags, "Directory room", QR_DIRECTORY);
478         rflags = set_room_attr(rflags, "Permanent room", QR_PERMANENT);
479         if (rflags & QR_DIRECTORY) {
480                 strprompt("Directory name", rdir, 14);
481                 rflags =
482                     set_room_attr(rflags, "Uploading allowed", QR_UPLOAD);
483                 rflags =
484                     set_room_attr(rflags, "Downloading allowed",
485                                   QR_DOWNLOAD);
486                 rflags =
487                     set_room_attr(rflags, "Visible directory", QR_VISDIR);
488         }
489         rflags = set_room_attr(rflags, "Network shared room", QR_NETWORK);
490         rflags = set_room_attr(rflags,
491                                "Automatically make all messages anonymous",
492                                QR_ANONONLY);
493         if ((rflags & QR_ANONONLY) == 0) {
494                 rflags = set_room_attr(rflags,
495                                        "Ask users whether to make messages anonymous",
496                                        QR_ANONOPT);
497         }
498         rorder = intprompt("Listing order", rorder, 1, 127);
499
500         /* Ask about the room aide */
501         do {
502                 strprompt("Room aide (or 'none')", raide, 29);
503                 if (!strcasecmp(raide, "none")) {
504                         strcpy(raide, "");
505                         break;
506                 } else {
507                         r = CtdlIPCQueryUsername(raide, buf);
508                         if (r / 100 != 2)
509                                 scr_printf("%s\n", buf);
510                 }
511         } while (r / 100 != 2);
512
513         /* FIXME: Duplicate code??? */
514         if (!strcasecmp(raide, "none")) {
515                 strcpy(raide, "");
516         }
517
518         /* Angels and demons dancing in my head... */
519         do {
520                 snprintf(buf, sizeof buf, "%d", expire_mode);
521                 strprompt("Message expire policy (? for list)", buf, 1);
522                 if (buf[0] == '?') {
523                         scr_printf("\n"
524                                 "0. Use the default for this floor\n"
525                                 "1. Never automatically expire messages\n"
526                                 "2. Expire by message count\n"
527                                 "3. Expire by message age\n");
528                 }
529         } while ((buf[0] < 48) || (buf[0] > 51));
530         expire_mode = buf[0] - 48;
531
532         /* ...lunatics and monsters underneath my bed */
533         if (expire_mode == 2) {
534                 snprintf(buf, sizeof buf, "%d", expire_value);
535                 strprompt("Keep how many messages online?", buf, 10);
536                 expire_value = atol(buf);
537         }
538
539         if (expire_mode == 3) {
540                 snprintf(buf, sizeof buf, "%d", expire_value);
541                 strprompt("Keep messages for how many days?", buf, 10);
542                 expire_value = atol(buf);
543         }
544
545         /* Give 'em a chance to change their minds */
546         scr_printf("Save changes (y/n)? ");
547
548         if (yesno() == 1) {
549                 r = CtdlIPCSetRoomAide(raide, buf);
550                 if (r != 2) {
551                         scr_printf("%s\n", buf);
552                 }
553
554                 r = CtdlIPCSetMessageExpirationPolicy(0, expire_mode,
555                                                       expire_value, buf);
556
557                 snprintf(buf, sizeof buf, "SETR %s|%s|%s|%d|%d|%d|%d",
558                          rname, rpass, rdir, rflags, rbump, rfloor,
559                          rorder);
560                 serv_puts(buf);
561                 serv_gets(buf);
562                 scr_printf("%s\n", &buf[4]);
563                 if (buf[0] == '2')
564                         dotgoto(rname, 2, 0);
565         }
566 }
567
568
569 /*
570  * un-goto the previous room
571  */
572 void ungoto(void)
573 {
574         char buf[SIZ];
575         int r;                          /* IPC response code */
576
577         if (uglistsize == 0)
578                 return;
579
580         snprintf(buf, sizeof buf, "GOTO %s", uglist[uglistsize-1]); 
581         serv_puts(buf);
582         serv_gets(buf);
583         if (buf[0] != '2') {
584                 scr_printf("%s\n", &buf[4]);
585                 return;
586         }
587         snprintf(buf, sizeof buf, "SLRP %ld", uglistlsn[uglistsize-1]); 
588         serv_puts(buf);
589         serv_gets(buf);
590         if (buf[0] != '2') {
591                 scr_printf("%s\n", &buf[4]);
592         }
593     safestrncpy (buf, uglist[uglistsize-1], sizeof(buf));
594     uglistsize--;
595     free(uglist[uglistsize]);
596         dotgoto(buf, 0, 1); /* Don't queue ungoto info or we end up in a loop */
597 }
598
599
600 /* Here's the code for simply transferring the file to the client,
601  * for folks who have their own clientware.  It's a lot simpler than
602  * the [XYZ]modem code below...
603  * (This function assumes that a download file is already open on the server)
604  */
605 void download_to_local_disk(char *supplied_filename, long total_bytes)
606 {
607         char buf[SIZ];
608         char dbuf[4096];
609         long transmitted_bytes = 0L;
610         long aa, bb;
611         FILE *savefp;
612         int broken = 0;
613         int packet;
614         char filename[SIZ];
615
616         strcpy(filename, supplied_filename);
617         if (strlen(filename) == 0) {
618                 newprompt("Filename: ", filename, 250);
619         }
620
621         scr_printf("Enter the name of the directory to save '%s'\n"
622                 "to, or press return for the current directory.\n", filename);
623         newprompt("Directory: ", dbuf, sizeof dbuf);
624         if (strlen(dbuf) == 0)
625                 strcpy(dbuf, ".");
626         strcat(dbuf, "/");
627         strcat(dbuf, filename);
628
629         savefp = fopen(dbuf, "w");
630         if (savefp == NULL) {
631                 scr_printf("Cannot open '%s': %s\n", dbuf, strerror(errno));
632                 /* close the download file at the server */
633                 serv_puts("CLOS");
634                 serv_gets(buf);
635                 if (buf[0] != '2') {
636                         scr_printf("%s\n", &buf[4]);
637                 }
638                 return;
639         }
640         progress(0, total_bytes);
641         while ((transmitted_bytes < total_bytes) && (broken == 0)) {
642                 bb = total_bytes - transmitted_bytes;
643                 aa = ((bb < 4096) ? bb : 4096);
644                 snprintf(buf, sizeof buf, "READ %ld|%ld", transmitted_bytes, aa);
645                 serv_puts(buf);
646                 serv_gets(buf);
647                 if (buf[0] != '6') {
648                         scr_printf("%s\n", &buf[4]);
649                         return;
650                 }
651                 packet = extract_int(&buf[4], 0);
652                 serv_read(dbuf, packet);
653                 if (fwrite(dbuf, packet, 1, savefp) < 1)
654                         broken = 1;
655                 transmitted_bytes = transmitted_bytes + (long) packet;
656                 progress(transmitted_bytes, total_bytes);
657         }
658         fclose(savefp);
659         /* close the download file at the server */
660         serv_puts("CLOS");
661         serv_gets(buf);
662         if (buf[0] != '2') {
663                 scr_printf("%s\n", &buf[4]);
664         }
665         return;
666 }
667
668
669 /*
670  * download()  -  download a file or files.  The argument passed to this
671  *                function determines which protocol to use.
672  *  proto - 0 = paginate, 1 = xmodem, 2 = raw, 3 = ymodem, 4 = zmodem, 5 = save
673  */
674 void download(int proto)
675 {
676         char buf[SIZ];
677         char filename[SIZ];
678         char tempname[SIZ];
679         char transmit_cmd[SIZ];
680         long total_bytes = 0L;
681         char dbuf[4096];
682         long transmitted_bytes = 0L;
683         long aa, bb;
684         int packet;
685         FILE *tpipe = NULL;
686         int broken = 0;
687
688         if ((room_flags & QR_DOWNLOAD) == 0) {
689                 scr_printf("*** You cannot download from this room.\n");
690                 return;
691         }
692
693         newprompt("Enter filename: ", filename, 255);
694
695         snprintf(buf, sizeof buf, "OPEN %s", filename);
696         serv_puts(buf);
697         serv_gets(buf);
698         if (buf[0] != '2') {
699                 scr_printf("%s\n", &buf[4]);
700                 return;
701         }
702         total_bytes = extract_long(&buf[4], 0);
703
704         /* Save to local disk, for folks with their own copy of the client */
705         if (proto == 5) {
706                 download_to_local_disk(filename, total_bytes);
707                 return;
708         }
709
710         /* Meta-download for public clients */
711         scr_printf("Fetching file from Citadel server...\n");
712         mkdir(tempdir, 0700);
713         snprintf(tempname, sizeof tempname, "%s/%s", tempdir, filename);
714         tpipe = fopen(tempname, "wb");
715         while ((transmitted_bytes < total_bytes) && (broken == 0)) {
716                 progress(transmitted_bytes, total_bytes);
717                 bb = total_bytes - transmitted_bytes;
718                 aa = ((bb < 4096) ? bb : 4096);
719                 snprintf(buf, sizeof buf, "READ %ld|%ld", transmitted_bytes, aa);
720                 serv_puts(buf);
721                 serv_gets(buf);
722                 if (buf[0] != '6') {
723                         scr_printf("%s\n", &buf[4]);
724                 }
725                 packet = extract_int(&buf[4], 0);
726                 serv_read(dbuf, packet);
727                 if (fwrite(dbuf, packet, 1, tpipe) < 1) {
728                         broken = 1;
729                 }
730                 transmitted_bytes = transmitted_bytes + (long) packet;
731         }
732         fclose(tpipe);
733         progress(transmitted_bytes, total_bytes);
734
735         /* close the download file at the server */
736         serv_puts("CLOS");
737         serv_gets(buf);
738         if (buf[0] != '2') {
739                 scr_printf("%s\n", &buf[4]);
740         }
741
742         if (proto == 0) {
743                 snprintf(transmit_cmd, sizeof transmit_cmd,
744                         "SHELL=/dev/null; export SHELL; TERM=dumb; export TERM; exec more -d <%s",
745                         tempname);
746         }
747         else if (proto == 1)
748                 snprintf(transmit_cmd, sizeof transmit_cmd, "exec sx %s", tempname);
749         else if (proto == 3)
750                 snprintf(transmit_cmd, sizeof transmit_cmd, "exec sb %s", tempname);
751         else if (proto == 4)
752                 snprintf(transmit_cmd, sizeof transmit_cmd, "exec sz %s", tempname);
753         else
754                 snprintf(transmit_cmd, sizeof transmit_cmd, "exec cat %s", tempname);
755
756         screen_reset();
757         sttybbs(SB_RESTORE);
758         system(transmit_cmd);
759         sttybbs(SB_NO_INTR);
760         screen_set();
761
762         /* clean up the temporary directory */
763         nukedir(tempdir);
764         scr_putc(7);
765 }
766
767
768 /*
769  * read directory of this room
770  */
771 void roomdir(void)
772 {
773         char flnm[SIZ];
774         char flsz[32];
775         char comment[SIZ];
776         char buf[SIZ];
777
778         serv_puts("RDIR");
779         serv_gets(buf);
780         if (buf[0] != '1') {
781                 pprintf("%s\n", &buf[4]);
782                 return;
783         }
784
785         extract(comment, &buf[4], 0);
786         extract(flnm, &buf[4], 1);
787         pprintf("\nDirectory of %s on %s\n", flnm, comment);
788         pprintf("-----------------------\n");
789         while (serv_gets(buf), strcmp(buf, "000")) {
790                 extract(flnm, buf, 0);
791                 extract(flsz, buf, 1);
792                 extract(comment, buf, 2);
793                 if (strlen(flnm) <= 14)
794                         pprintf("%-14s %8s %s\n", flnm, flsz, comment);
795                 else
796                         pprintf("%s\n%14s %8s %s\n", flnm, "", flsz,
797                                 comment);
798         }
799 }
800
801
802 /*
803  * add a user to a private room
804  */
805 void invite(void)
806 {
807         char username[USERNAME_SIZE];
808         char buf[SIZ];
809         int r;                          /* IPC response code */
810
811         newprompt("Name of user? ", username, USERNAME_SIZE);
812         if (username[0] == 0)
813                 return;
814
815         r = CtdlIPCInviteUserToRoom(username, buf);
816         scr_printf("%s\n", buf);
817 }
818
819
820 /*
821  * kick a user out of a room
822  */
823 void kickout(void)
824 {
825         char username[USERNAME_SIZE];
826         char buf[SIZ];
827         int r;                          /* IPC response code */
828
829         newprompt("Name of user? ", username, USERNAME_SIZE);
830         if (username[0] == 0)
831                 return;
832
833         r = CtdlIPCKickoutUserFromRoom(username, buf);
834         scr_printf("%s\n", buf);
835 }
836
837
838 /*
839  * aide command: kill the current room
840  */
841 void killroom(void)
842 {
843         char aaa[100];
844         int r;
845
846         r = CtdlIPCDeleteRoom(0, aaa);
847         if (r / 100 != 2) {
848                 scr_printf("%s\n", aaa);
849                 return;
850         }
851
852         scr_printf("Are you sure you want to kill this room? ");
853         if (yesno() == 0)
854                 return;
855
856         r = CtdlIPCDeleteRoom(1, aaa);
857         scr_printf("%s\n", aaa);
858         if (r / 100 != 2)
859                 return;
860         dotgoto("_BASEROOM_", 0, 0);
861 }
862
863 void forget(void)
864 {                               /* forget the current room */
865         char buf[SIZ];
866
867         scr_printf("Are you sure you want to forget this room? ");
868         if (yesno() == 0)
869                 return;
870
871         if (CtdlIPCForgetRoom(buf) / 100 != 2) {
872                 scr_printf("%s\n", buf);
873                 return;
874         }
875
876         /* now return to the lobby */
877         dotgoto("_BASEROOM_", 0, 0);
878 }
879
880
881 /*
882  * create a new room
883  */
884 void entroom(void)
885 {
886         char buf[SIZ];
887         char new_room_name[ROOMNAMELEN];
888         int new_room_type;
889         char new_room_pass[10];
890         int new_room_floor;
891         int a, b;
892         int r;                          /* IPC response code */
893
894         /* Check permission to create room */
895         r = CtdlIPCCreateRoom(0, "", 1, "", 0, buf);
896         if (r / 100 != 2) {
897                 scr_printf("%s\n", buf);
898                 return;
899         }
900
901         newprompt("Name for new room? ", new_room_name, ROOMNAMELEN - 1);
902         if (strlen(new_room_name) == 0) {
903                 return;
904         }
905         for (a = 0; a < strlen(new_room_name); ++a) {
906                 if (new_room_name[a] == '|') {
907                         new_room_name[a] = '_';
908                 }
909         }
910
911         new_room_floor = select_floor((int) curr_floor);
912
913         IFNEXPERT formout("roomaccess");
914         do {
915                 scr_printf("<?>Help\n<1>Public room\n<2>Guess-name room\n"
916                        "<3>Passworded room\n<4>Invitation-only room\n"
917                        "<5>Personal room\n"
918                         "Enter room type: ");
919                 do {
920                         b = inkey();
921                 } while (((b < '1') || (b > '5')) && (b != '?'));
922                 if (b == '?') {
923                         scr_printf("?\n");
924                         formout("roomaccess");
925                 }
926         } while ((b < '1') || (b > '5'));
927         b -= '0';                       /* Portable */
928         scr_printf("%d\n", b);
929         new_room_type = b - 1;
930         if (new_room_type == 2) {
931                 newprompt("Enter a room password: ", new_room_pass, 9);
932                 for (a = 0; a < strlen(new_room_pass); ++a)
933                         if (new_room_pass[a] == '|')
934                                 new_room_pass[a] = '_';
935         } else {
936                 strcpy(new_room_pass, "");
937         }
938
939         scr_printf("\042%s\042, a", new_room_name);
940         if (b == 1)
941                 scr_printf(" public room.");
942         if (b == 2)
943                 scr_printf(" guess-name room.");
944         if (b == 3)
945                 scr_printf(" passworded room, password: %s", new_room_pass);
946         if (b == 4)
947                 scr_printf("n invitation-only room.");
948         if (b == 5)
949                 scr_printf(" personal room.");
950         scr_printf("\nInstall it? (y/n) : ");
951         if (yesno() == 0) {
952                 return;
953         }
954
955         r = CtdlIPCCreateRoom(1, new_room_name, new_room_type,
956                               new_room_pass, new_room_floor, buf);
957         if (r / 100 != 2) {
958                 scr_printf("%s\n", buf);
959                 return;
960         }
961
962         /* command succeeded... now GO to the new room! */
963         dotgoto(new_room_name, 0, 0);
964 }
965
966
967
968 void readinfo(void)
969 {                               /* read info file for current room */
970         char buf[SIZ];
971         char raide[64];
972         int r;                  /* IPC response code */
973         char *text = NULL;
974
975         /* Name of currernt room aide */
976         r = CtdlIPCGetRoomAide(buf);
977         if (r / 100 == 2)
978                 safestrncpy(raide, buf, sizeof raide);
979         else
980                 strcpy(raide, "");
981
982         if (strlen(raide) > 0)
983                 scr_printf("Room aide is %s.\n\n", raide);
984
985         r = CtdlIPCRoomInfo(&text, buf);
986         if (r / 100 != 1)
987                 return;
988
989         if (text) {
990                 fmout(screenwidth, NULL, text, NULL,
991                       ((userflags & US_PAGINATOR) ? 1 : 0), screenheight, 
992                       (*raide) ? 2 : 0, 1);
993                 free(text);
994         }
995 }
996
997
998 /*
999  * <W>ho knows room...
1000  */
1001 void whoknows(void)
1002 {
1003         char buf[SIZ];
1004         serv_puts("WHOK");
1005         serv_gets(buf);
1006         if (buf[0] != '1') {
1007                 pprintf("%s\n", &buf[4]);
1008                 return;
1009         }
1010         while (serv_gets(buf), strncmp(buf, "000", 3)) {
1011                 if (sigcaught == 0)
1012                         pprintf("%s\n", buf);
1013         }
1014 }
1015
1016
1017 void do_edit(char *desc, char *read_cmd, char *check_cmd, char *write_cmd)
1018 {
1019         FILE *fp;
1020         char cmd[SIZ];
1021         int b, cksum, editor_exit;
1022
1023
1024         if (strlen(editor_path) == 0) {
1025                 scr_printf("Do you wish to re-enter %s? ", desc);
1026                 if (yesno() == 0)
1027                         return;
1028         }
1029
1030         fp = fopen(temp, "w");
1031         fclose(fp);
1032
1033         serv_puts(check_cmd);
1034         serv_gets(cmd);
1035         if (cmd[0] != '2') {
1036                 scr_printf("%s\n", &cmd[4]);
1037                 return;
1038         }
1039
1040         if (strlen(editor_path) > 0) {
1041                 serv_puts(read_cmd);
1042                 serv_gets(cmd);
1043                 if (cmd[0] == '1') {
1044                         fp = fopen(temp, "w");
1045                         while (serv_gets(cmd), strcmp(cmd, "000")) {
1046                                 fprintf(fp, "%s\n", cmd);
1047                         }
1048                         fclose(fp);
1049                 }
1050         }
1051
1052         cksum = file_checksum(temp);
1053
1054         if (strlen(editor_path) > 0) {
1055                 char tmp[SIZ];
1056
1057                 snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", desc);
1058                 putenv(tmp);
1059                 editor_pid = fork();
1060                 if (editor_pid == 0) {
1061                         chmod(temp, 0600);
1062                         screen_reset();
1063                         sttybbs(SB_RESTORE);
1064                         execlp(editor_path, editor_path, temp, NULL);
1065                         exit(1);
1066                 }
1067                 if (editor_pid > 0)
1068                         do {
1069                                 editor_exit = 0;
1070                                 b = wait(&editor_exit);
1071                         } while ((b != editor_pid) && (b >= 0));
1072                 editor_pid = (-1);
1073                 scr_printf("Executed %s\n", editor_path);
1074                 sttybbs(0);
1075                 screen_set();
1076         } else {
1077                 scr_printf("Entering %s.  "
1078                         "Press return twice when finished.\n", desc);
1079                 fp = fopen(temp, "r+");
1080                 citedit(fp);
1081                 fclose(fp);
1082         }
1083
1084         if (file_checksum(temp) == cksum) {
1085                 scr_printf("*** Aborted.\n");
1086         }
1087
1088         else {
1089                 serv_puts(write_cmd);
1090                 serv_gets(cmd);
1091                 if (cmd[0] != '4') {
1092                         scr_printf("%s\n", &cmd[4]);
1093                         return;
1094                 }
1095
1096                 fp = fopen(temp, "r");
1097                 while (fgets(cmd, SIZ - 1, fp) != NULL) {
1098                         cmd[strlen(cmd) - 1] = 0;
1099                         serv_puts(cmd);
1100                 }
1101                 fclose(fp);
1102                 serv_puts("000");
1103         }
1104
1105         unlink(temp);
1106 }
1107
1108
1109 void enterinfo(void)
1110 {                               /* edit info file for current room */
1111         do_edit("the Info file for this room", "RINF", "EINF 0", "EINF 1");
1112 }
1113
1114 void enter_bio(void)
1115 {
1116         char cmd[SIZ];
1117         snprintf(cmd, sizeof cmd, "RBIO %s", fullname);
1118         do_edit("your Bio", cmd, "NOOP", "EBIO");
1119 }
1120
1121 /*
1122  * create a new floor
1123  */
1124 void create_floor(void)
1125 {
1126         char buf[SIZ];
1127         char newfloorname[SIZ];
1128         int r;                  /* IPC response code */
1129
1130         load_floorlist();
1131
1132         r = CtdlIPCCreateFloor(0, "", buf);
1133         if (r / 100 != 2) {
1134                 scr_printf("%s\n", buf);
1135                 return;
1136         }
1137
1138         newprompt("Name for new floor: ", newfloorname, 255);
1139         r = CtdlIPCCreateFloor(1, newfloorname, buf);
1140         if (r / 100 == 2) {
1141                 scr_printf("Floor has been created.\n");
1142         } else {
1143                 scr_printf("%s\n", buf);
1144         }
1145
1146         load_floorlist();
1147 }
1148
1149 /*
1150  * edit the current floor
1151  */
1152 void edit_floor(void)
1153 {
1154         char buf[SIZ];
1155         int expire_mode = 0;
1156         int expire_value = 0;
1157
1158         load_floorlist();
1159
1160         /* Fetch the expire policy (this will silently fail on old servers,
1161          * resulting in "default" policy)
1162          */
1163         r = CtdlIPCGetMessageExpirationPolicy(1, buf);
1164         if (r / 100 == 2) {
1165                 expire_mode = extract_int(buf, 0);
1166                 expire_value = extract_int(buf, 1);
1167         }
1168
1169         /* Interact with the user */
1170         strprompt("Floor name", &floorlist[(int) curr_floor][0], 255);
1171
1172         /* Angels and demons dancing in my head... */
1173         do {
1174                 snprintf(buf, sizeof buf, "%d", expire_mode);
1175                 strprompt
1176                     ("Floor default essage expire policy (? for list)",
1177                      buf, 1);
1178                 if (buf[0] == '?') {
1179                         scr_printf("\n"
1180                                 "0. Use the system default\n"
1181                                 "1. Never automatically expire messages\n"
1182                                 "2. Expire by message count\n"
1183                                 "3. Expire by message age\n");
1184                 }
1185         } while ((buf[0] < '0') || (buf[0] > '3'));
1186         expire_mode = buf[0] - '0';
1187
1188         /* ...lunatics and monsters underneath my bed */
1189         if (expire_mode == 2) {
1190                 snprintf(buf, sizeof buf, "%d", expire_value);
1191                 strprompt("Keep how many messages online?", buf, 10);
1192                 expire_value = atol(buf);
1193         }
1194
1195         if (expire_mode == 3) {
1196                 snprintf(buf, sizeof buf, "%d", expire_value);
1197                 strprompt("Keep messages for how many days?", buf, 10);
1198                 expire_value = atol(buf);
1199         }
1200
1201         /* Save it */
1202         r = CtdlIPCSetMessageExpirationPolicy(1, expire_mode,
1203                                               expire_value, buf);
1204         r = CtdlIPCEditFloor(curr_floor, &floorlist[(int)curr_floor][0]);
1205         scr_printf("%s\n", buf);
1206         load_floorlist();
1207 }
1208
1209
1210
1211
1212 /*
1213  * kill the current floor 
1214  */
1215 void kill_floor(void)
1216 {
1217         int floornum_to_delete, a;
1218         char buf[SIZ];
1219
1220         load_floorlist();
1221         do {
1222                 floornum_to_delete = (-1);
1223                 scr_printf("(Press return to abort)\n");
1224                 newprompt("Delete which floor? ", buf, 255);
1225                 if (strlen(buf) == 0)
1226                         return;
1227                 for (a = 0; a < 128; ++a)
1228                         if (!strcasecmp(&floorlist[a][0], buf))
1229                                 floornum_to_delete = a;
1230                 if (floornum_to_delete < 0) {
1231                         scr_printf("No such floor.  Select one of:\n");
1232                         for (a = 0; a < 128; ++a)
1233                                 if (floorlist[a][0] != 0)
1234                                         scr_printf("%s\n", &floorlist[a][0]);
1235                 }
1236         } while (floornum_to_delete < 0);
1237         CtdlIPCDeleteFloor(1, floornum_to_delete, buf);
1238         scr_printf("%s\n", buf);
1239         load_floorlist();
1240 }