]> code.citadel.org Git - citadel.git/blob - citadel/rooms.c
* Message reading and other functions which use the fmout() screen
[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
79         for (a = 0; a < 128; ++a)
80                 floorlist[a][0] = 0;
81
82         serv_puts("LFLR");
83         serv_gets(buf);
84         if (buf[0] != '1') {
85                 strcpy(floorlist[0], "Main Floor");
86                 return;
87         }
88         while (serv_gets(buf), strcmp(buf, "000")) {
89                 extract(floorlist[extract_int(buf, 0)], buf, 1);
90         }
91 }
92
93
94 void room_tree_list(struct roomlisting *rp)
95 {
96         static int c = 0;
97         char rmname[ROOMNAMELEN];
98         int f;
99
100         if (rp == NULL) {
101                 c = 1;
102                 return;
103         }
104
105         if (rp->lnext != NULL) {
106                 room_tree_list(rp->lnext);
107         }
108
109         if (sigcaught == 0) {
110                 strcpy(rmname, rp->rlname);
111                 f = rp->rlflags;
112                 if ((c + strlen(rmname) + 4) > screenwidth) {
113
114                         /* line break, check the paginator */
115                         pprintf("\n");
116                         c = 1;
117                 }
118                 if (f & QR_MAILBOX) {
119                         color(BRIGHT_YELLOW);
120                 } else if (f & QR_PRIVATE) {
121                         color(BRIGHT_RED);
122                 } else {
123                         color(DIM_WHITE);
124                 }
125                 pprintf("%s", rmname);
126                 if ((f & QR_DIRECTORY) && (f & QR_NETWORK))
127                         pprintf("}  ");
128                 else if (f & QR_DIRECTORY)
129                         pprintf("]  ");
130                 else if (f & QR_NETWORK)
131                         pprintf(")  ");
132                 else
133                         pprintf(">  ");
134                 c = c + strlen(rmname) + 3;
135         }
136
137         if (rp->rnext != NULL) {
138                 room_tree_list(rp->rnext);
139         }
140
141         free(rp);
142 }
143
144
145 /* 
146  * Room ordering stuff (compare first by floor, then by order)
147  */
148 int rordercmp(struct roomlisting *r1, struct roomlisting *r2)
149 {
150         if ((r1 == NULL) && (r2 == NULL))
151                 return (0);
152         if (r1 == NULL)
153                 return (-1);
154         if (r2 == NULL)
155                 return (1);
156         if (r1->rlfloor < r2->rlfloor)
157                 return (-1);
158         if (r1->rlfloor > r2->rlfloor)
159                 return (1);
160         if (r1->rlorder < r2->rlorder)
161                 return (-1);
162         if (r1->rlorder > r2->rlorder)
163                 return (1);
164         return (0);
165 }
166
167
168 /*
169  * Common code for all room listings
170  */
171 void listrms(char *variety)
172 {
173         char buf[SIZ];
174
175         struct roomlisting *rl = NULL;
176         struct roomlisting *rp;
177         struct roomlisting *rs;
178
179
180         /* Ask the server for a room list */
181         serv_puts(variety);
182         serv_gets(buf);
183         if (buf[0] != '1') {
184                 return;
185         }
186         while (serv_gets(buf), strcmp(buf, "000")) {
187                 rp = malloc(sizeof(struct roomlisting));
188                 extract(rp->rlname, buf, 0);
189                 rp->rlflags = extract_int(buf, 1);
190                 rp->rlfloor = extract_int(buf, 2);
191                 rp->rlorder = extract_int(buf, 3);
192                 rp->lnext = NULL;
193                 rp->rnext = NULL;
194
195                 rs = rl;
196                 if (rl == NULL) {
197                         rl = rp;
198                 } else {
199                         while (rp != NULL) {
200                                 if (rordercmp(rp, rs) < 0) {
201                                         if (rs->lnext == NULL) {
202                                                 rs->lnext = rp;
203                                                 rp = NULL;
204                                         } else {
205                                                 rs = rs->lnext;
206                                         }
207                                 } else {
208                                         if (rs->rnext == NULL) {
209                                                 rs->rnext = rp;
210                                                 rp = NULL;
211                                         } else {
212                                                 rs = rs->rnext;
213                                         }
214                                 }
215                         }
216                 }
217         }
218
219         room_tree_list(NULL);
220         room_tree_list(rl);
221         color(DIM_WHITE);
222 }
223
224
225 void list_other_floors(void)
226 {
227         int a, c;
228
229         c = 1;
230         for (a = 0; a < 128; ++a) {
231                 if ((strlen(floorlist[a]) > 0) && (a != curr_floor)) {
232                         if ((c + strlen(floorlist[a]) + 4) > screenwidth) {
233                                 pprintf("\n");
234                                 c = 1;
235                         }
236                         pprintf("%s:  ", floorlist[a]);
237                         c = c + strlen(floorlist[a]) + 3;
238                 }
239         }
240 }
241
242
243 /*
244  * List known rooms.  kn_floor_mode should be set to 0 for a 'flat' listing,
245  * 1 to list rooms on the current floor, or 1 to list rooms on all floors.
246  */
247 void knrooms(int kn_floor_mode)
248 {
249         char buf[SIZ];
250         int a;
251
252         load_floorlist();
253
254         if (kn_floor_mode == 0) {
255                 color(BRIGHT_CYAN);
256                 pprintf("\n   Rooms with unread messages:\n");
257                 listrms("LKRN");
258                 color(BRIGHT_CYAN);
259                 pprintf("\n\n   No unseen messages in:\n");
260                 listrms("LKRO");
261                 pprintf("\n");
262         }
263
264         if (kn_floor_mode == 1) {
265                 color(BRIGHT_CYAN);
266                 pprintf("\n   Rooms with unread messages on %s:\n",
267                         floorlist[(int) curr_floor]);
268                 snprintf(buf, sizeof buf, "LKRN %d", curr_floor);
269                 listrms(buf);
270                 color(BRIGHT_CYAN);
271                 pprintf("\n\n   Rooms with no new messages on %s:\n",
272                         floorlist[(int) curr_floor]);
273                 snprintf(buf, sizeof buf, "LKRO %d", curr_floor);
274                 listrms(buf);
275                 color(BRIGHT_CYAN);
276                 pprintf("\n\n   Other floors:\n");
277                 list_other_floors();
278                 pprintf("\n");
279         }
280
281         if (kn_floor_mode == 2) {
282                 for (a = 0; a < 128; ++a) {
283                         if (floorlist[a][0] != 0) {
284                                 color(BRIGHT_CYAN);
285                                 pprintf("\n   Rooms on %s:\n",
286                                         floorlist[a]);
287                                 snprintf(buf, sizeof buf, "LKRA %d", a);
288                                 listrms(buf);
289                                 pprintf("\n");
290                         }
291                 }
292         }
293
294         color(DIM_WHITE);
295         IFNEXPERT hit_any_key();
296 }
297
298
299 void listzrooms(void)
300 {                               /* list public forgotten rooms */
301         color(BRIGHT_CYAN);
302         pprintf("\n   Forgotten public rooms:\n");
303         listrms("LZRM");
304         pprintf("\n");
305         color(DIM_WHITE);
306         IFNEXPERT hit_any_key();
307 }
308
309
310 int set_room_attr(int ibuf, char *prompt, unsigned int sbit)
311 {
312         int a;
313
314         a = boolprompt(prompt, (ibuf & sbit));
315         ibuf = (ibuf | sbit);
316         if (!a) {
317                 ibuf = (ibuf ^ sbit);
318         }
319         return (ibuf);
320 }
321
322
323
324 /*
325  * Select a floor (used in several commands)
326  * The supplied argument is the 'default' floor number.
327  * This function returns the selected floor number.
328  */
329 int select_floor(int rfloor)
330 {
331         int a, newfloor;
332         char floorstr[SIZ];
333
334         if (floor_mode == 1) {
335                 if (floorlist[(int) curr_floor][0] == 0) {
336                         load_floorlist();
337                 }
338
339                 do {
340                         newfloor = (-1);
341                         safestrncpy(floorstr, floorlist[rfloor],
342                                     sizeof floorstr);
343                         strprompt("Which floor", floorstr, SIZ);
344                         for (a = 0; a < 128; ++a) {
345                                 if (!strcasecmp
346                                     (floorstr, &floorlist[a][0]))
347                                         newfloor = a;
348                                 if ((newfloor < 0)
349                                     &&
350                                     (!strncasecmp
351                                      (floorstr, &floorlist[a][0],
352                                       strlen(floorstr))))
353                                         newfloor = a;
354                                 if ((newfloor < 0)
355                                     && (pattern(&floorlist[a][0], floorstr)
356                                         >= 0))
357                                         newfloor = a;
358                         }
359                         if (newfloor < 0) {
360                                 scr_printf("\n One of:\n");
361                                 for (a = 0; a < 128; ++a) {
362                                         if (floorlist[a][0] != 0) {
363                                                 scr_printf("%s\n",
364                                                        &floorlist[a][0]);
365                                         }
366                                 }
367                         }
368                 } while (newfloor < 0);
369                 return (newfloor);
370         }
371         return (rfloor);
372 }
373
374
375
376
377 /*
378  * .<A>ide <E>dit room
379  */
380 void editthisroom(void)
381 {
382         char rname[ROOMNAMELEN];
383         char rpass[10];
384         char rdir[15];
385         unsigned rflags;
386         int rbump;
387         char raide[32];
388         char buf[SIZ];
389         int rfloor;
390         int rorder;
391         int expire_mode = 0;
392         int expire_value = 0;
393
394         /* Fetch the existing room config */
395         serv_puts("GETR");
396         serv_gets(buf);
397         if (buf[0] != '2') {
398                 scr_printf("%s\n", &buf[4]);
399                 return;
400         }
401
402         extract(rname, &buf[4], 0);
403         extract(rpass, &buf[4], 1);
404         extract(rdir, &buf[4], 2);
405         rflags = extract_int(&buf[4], 3);
406         rfloor = extract_int(&buf[4], 4);
407         rorder = extract_int(&buf[4], 5);
408         rbump = 0;
409
410         /* Fetch the name of the current room aide */
411         serv_puts("GETA");
412         serv_gets(buf);
413         if (buf[0] == '2') {
414                 safestrncpy(raide, &buf[4], sizeof raide);
415         }
416         else {
417                 strcpy(raide, "");
418         }
419         if (strlen(raide) == 0) {
420                 strcpy(raide, "none");
421         }
422
423         /* Fetch the expire policy (this will silently fail on old servers,
424          * resulting in "default" policy)
425          */
426         serv_puts("GPEX room");
427         serv_gets(buf);
428         if (buf[0] == '2') {
429                 expire_mode = extract_int(&buf[4], 0);
430                 expire_value = extract_int(&buf[4], 1);
431         }
432
433         /* Now interact with the user. */
434         strprompt("Room name", rname, ROOMNAMELEN - 1);
435
436         rfloor = select_floor(rfloor);
437         rflags = set_room_attr(rflags, "Private room", QR_PRIVATE);
438         if (rflags & QR_PRIVATE) {
439                 rflags = set_room_attr(rflags,
440                                        "Accessible by guessing room name",
441                                        QR_GUESSNAME);
442         }
443
444         /* if it's public, clear the privacy classes */
445         if ((rflags & QR_PRIVATE) == 0) {
446                 if (rflags & QR_GUESSNAME) {
447                         rflags = rflags - QR_GUESSNAME;
448                 }
449                 if (rflags & QR_PASSWORDED) {
450                         rflags = rflags - QR_PASSWORDED;
451                 }
452         }
453
454         /* if it's private, choose the privacy classes */
455         if ((rflags & QR_PRIVATE)
456             && ((rflags & QR_GUESSNAME) == 0)) {
457                 rflags = set_room_attr(rflags,
458                                        "Accessible by entering a password",
459                                        QR_PASSWORDED);
460         }
461         if ((rflags & QR_PRIVATE)
462             && ((rflags & QR_PASSWORDED) == QR_PASSWORDED)) {
463                 strprompt("Room password", rpass, 9);
464         }
465
466         if ((rflags & QR_PRIVATE) == QR_PRIVATE) {
467                 rbump =
468                     boolprompt("Cause current users to forget room", 0);
469         }
470
471         rflags =
472             set_room_attr(rflags, "Preferred users only", QR_PREFONLY);
473         rflags = set_room_attr(rflags, "Read-only room", QR_READONLY);
474         rflags = set_room_attr(rflags, "Directory room", QR_DIRECTORY);
475         rflags = set_room_attr(rflags, "Permanent room", QR_PERMANENT);
476         if (rflags & QR_DIRECTORY) {
477                 strprompt("Directory name", rdir, 14);
478                 rflags =
479                     set_room_attr(rflags, "Uploading allowed", QR_UPLOAD);
480                 rflags =
481                     set_room_attr(rflags, "Downloading allowed",
482                                   QR_DOWNLOAD);
483                 rflags =
484                     set_room_attr(rflags, "Visible directory", QR_VISDIR);
485         }
486         rflags = set_room_attr(rflags, "Network shared room", QR_NETWORK);
487         rflags = set_room_attr(rflags,
488                                "Automatically make all messages anonymous",
489                                QR_ANONONLY);
490         if ((rflags & QR_ANONONLY) == 0) {
491                 rflags = set_room_attr(rflags,
492                                        "Ask users whether to make messages anonymous",
493                                        QR_ANONOPT);
494         }
495         rorder = intprompt("Listing order", rorder, 1, 127);
496
497         /* Ask about the room aide */
498         do {
499                 strprompt("Room aide (or 'none')", raide, 29);
500                 if (!strcasecmp(raide, "none")) {
501                         strcpy(raide, "");
502                         strcpy(buf, "200");
503                 } else {
504                         snprintf(buf, sizeof buf, "QUSR %s", raide);
505                         serv_puts(buf);
506                         serv_gets(buf);
507                         if (buf[0] != '2')
508                                 scr_printf("%s\n", &buf[4]);
509                 }
510         } while (buf[0] != '2');
511
512         if (!strcasecmp(raide, "none")) {
513                 strcpy(raide, "");
514         }
515
516
517         /* Angels and demons dancing in my head... */
518         do {
519                 snprintf(buf, sizeof buf, "%d", expire_mode);
520                 strprompt("Message expire policy (? for list)", buf, 1);
521                 if (buf[0] == '?') {
522                         scr_printf("\n"
523                                 "0. Use the default for this floor\n"
524                                 "1. Never automatically expire messages\n"
525                                 "2. Expire by message count\n"
526                                 "3. Expire by message age\n");
527                 }
528         } while ((buf[0] < 48) || (buf[0] > 51));
529         expire_mode = buf[0] - 48;
530
531         /* ...lunatics and monsters underneath my bed */
532         if (expire_mode == 2) {
533                 snprintf(buf, sizeof buf, "%d", expire_value);
534                 strprompt("Keep how many messages online?", buf, 10);
535                 expire_value = atol(buf);
536         }
537
538         if (expire_mode == 3) {
539                 snprintf(buf, sizeof buf, "%d", expire_value);
540                 strprompt("Keep messages for how many days?", buf, 10);
541                 expire_value = atol(buf);
542         }
543
544         /* Give 'em a chance to change their minds */
545         scr_printf("Save changes (y/n)? ");
546
547         if (yesno() == 1) {
548                 snprintf(buf, sizeof buf, "SETA %s", raide);
549                 serv_puts(buf);
550                 serv_gets(buf);
551                 if (buf[0] != '2') {
552                         scr_printf("%s\n", &buf[4]);
553                 }
554
555                 snprintf(buf, sizeof buf, "SPEX room|%d|%d",
556                          expire_mode, expire_value);
557                 serv_puts(buf);
558                 serv_gets(buf);
559
560                 snprintf(buf, sizeof buf, "SETR %s|%s|%s|%d|%d|%d|%d",
561                          rname, rpass, rdir, rflags, rbump, rfloor,
562                          rorder);
563                 serv_puts(buf);
564                 serv_gets(buf);
565                 scr_printf("%s\n", &buf[4]);
566                 if (buf[0] == '2')
567                         dotgoto(rname, 2, 0);
568         }
569 }
570
571
572 /*
573  * un-goto the previous room
574  */
575 void ungoto(void)
576 {
577         char buf[SIZ];
578     if (uglistsize == 0)
579       return;
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 aaa[31], bbb[SIZ];
808
809         /* Because kicking people out of public rooms now sets a LOCKOUT
810          * flag, we need to be able to invite people into public rooms
811          * in order to let them back in again.
812          *        - cough
813          */
814
815         /*
816          * if ((room_flags & QR_PRIVATE)==0) {
817          *         scr_printf("This is not a private room.\n");
818          *         return;
819          * }
820          */
821
822         newprompt("Name of user? ", aaa, 30);
823         if (aaa[0] == 0)
824                 return;
825
826         snprintf(bbb, sizeof bbb, "INVT %s", aaa);
827         serv_puts(bbb);
828         serv_gets(bbb);
829         scr_printf("%s\n", &bbb[4]);
830 }
831
832
833 /*
834  * kick a user out of a room
835  */
836 void kickout(void)
837 {
838         char username[31], cmd[SIZ];
839
840         newprompt("Name of user? ", username, 30);
841         if (strlen(username) == 0) {
842                 return;
843         }
844
845         snprintf(cmd, sizeof cmd, "KICK %s", username);
846         serv_puts(cmd);
847         serv_gets(cmd);
848         scr_printf("%s\n", &cmd[4]);
849 }
850
851
852 /*
853  * aide command: kill the current room
854  */
855 void killroom(void)
856 {
857         char aaa[100];
858
859         serv_puts("KILL 0");
860         serv_gets(aaa);
861         if (aaa[0] != '2') {
862                 scr_printf("%s\n", &aaa[4]);
863                 return;
864         }
865
866         scr_printf("Are you sure you want to kill this room? ");
867         if (yesno() == 0)
868                 return;
869
870         serv_puts("KILL 1");
871         serv_gets(aaa);
872         scr_printf("%s\n", &aaa[4]);
873         if (aaa[0] != '2')
874                 return;
875         dotgoto("_BASEROOM_", 0, 0);
876 }
877
878 void forget(void)
879 {                               /* forget the current room */
880         char cmd[SIZ];
881
882         scr_printf("Are you sure you want to forget this room? ");
883         if (yesno() == 0)
884                 return;
885
886         serv_puts("FORG");
887         serv_gets(cmd);
888         if (cmd[0] != '2') {
889                 scr_printf("%s\n", &cmd[4]);
890                 return;
891         }
892
893         /* now return to the lobby */
894         dotgoto("_BASEROOM_", 0, 0);
895 }
896
897
898 /*
899  * create a new room
900  */
901 void entroom(void)
902 {
903         char cmd[SIZ];
904         char new_room_name[ROOMNAMELEN];
905         int new_room_type;
906         char new_room_pass[10];
907         int new_room_floor;
908         int a, b;
909
910         serv_puts("CRE8 0");
911         serv_gets(cmd);
912
913         if (cmd[0] != '2') {
914                 scr_printf("%s\n", &cmd[4]);
915                 return;
916         }
917
918         newprompt("Name for new room? ", new_room_name, ROOMNAMELEN - 1);
919         if (strlen(new_room_name) == 0) {
920                 return;
921         }
922         for (a = 0; a < strlen(new_room_name); ++a) {
923                 if (new_room_name[a] == '|') {
924                         new_room_name[a] = '_';
925                 }
926         }
927
928         new_room_floor = select_floor((int) curr_floor);
929
930         IFNEXPERT formout("roomaccess");
931         do {
932                 scr_printf("<?>Help\n<1>Public room\n<2>Guess-name room\n"
933                        "<3>Passworded room\n<4>Invitation-only room\n"
934                        "<5>Personal room\n"
935                         "Enter room type: ");
936                 do {
937                         b = inkey();
938                 } while (((b < '1') || (b > '5')) && (b != '?'));
939                 if (b == '?') {
940                         scr_printf("?\n");
941                         formout("roomaccess");
942                 }
943         } while ((b < '1') || (b > '5'));
944         b = b - 48;
945         scr_printf("%d\n", b);
946         new_room_type = b - 1;
947         if (new_room_type == 2) {
948                 newprompt("Enter a room password: ", new_room_pass, 9);
949                 for (a = 0; a < strlen(new_room_pass); ++a)
950                         if (new_room_pass[a] == '|')
951                                 new_room_pass[a] = '_';
952         } else {
953                 strcpy(new_room_pass, "");
954         }
955
956         scr_printf("\042%s\042, a", new_room_name);
957         if (b == 1)
958                 scr_printf(" public room.");
959         if (b == 2)
960                 scr_printf(" guess-name room.");
961         if (b == 3)
962                 scr_printf(" passworded room, password: %s", new_room_pass);
963         if (b == 4)
964                 scr_printf("n invitation-only room.");
965         if (b == 5)
966                 scr_printf(" personal room.");
967         scr_printf("\nInstall it? (y/n) : ");
968         if (yesno() == 0) {
969                 return;
970         }
971
972         snprintf(cmd, sizeof cmd, "CRE8 1|%s|%d|%s|%d", new_room_name,
973                  new_room_type, new_room_pass, new_room_floor);
974         serv_puts(cmd);
975         serv_gets(cmd);
976         if (cmd[0] != '2') {
977                 scr_printf("%s\n", &cmd[4]);
978                 return;
979         }
980
981         /* command succeeded... now GO to the new room! */
982         dotgoto(new_room_name, 0, 0);
983 }
984
985
986
987 void readinfo(void)
988 {                               /* read info file for current room */
989         char buf[SIZ];
990         char raide[64];
991         int r;                  /* IPC response code */
992         char *text = NULL;
993
994         /* Name of currernt room aide */
995         r = CtdlIPCGetRoomAide(buf);
996         if (r / 100 == 2)
997                 safestrncpy(raide, buf, sizeof raide);
998         else
999                 strcpy(raide, "");
1000
1001         if (strlen(raide) > 0)
1002                 scr_printf("Room aide is %s.\n\n", raide);
1003
1004         r = CtdlIPCRoomInfo(&text, buf);
1005         if (r / 100 != 1)
1006                 return;
1007
1008         if (text) {
1009                 fmout(screenwidth, NULL, text, NULL,
1010                       ((userflags & US_PAGINATOR) ? 1 : 0), screenheight, 
1011                       (*raide) ? 2 : 0, 1);
1012                 free(text);
1013         }
1014 }
1015
1016
1017 /*
1018  * <W>ho knows room...
1019  */
1020 void whoknows(void)
1021 {
1022         char buf[SIZ];
1023         serv_puts("WHOK");
1024         serv_gets(buf);
1025         if (buf[0] != '1') {
1026                 pprintf("%s\n", &buf[4]);
1027                 return;
1028         }
1029         while (serv_gets(buf), strncmp(buf, "000", 3)) {
1030                 if (sigcaught == 0)
1031                         pprintf("%s\n", buf);
1032         }
1033 }
1034
1035
1036 void do_edit(char *desc, char *read_cmd, char *check_cmd, char *write_cmd)
1037 {
1038         FILE *fp;
1039         char cmd[SIZ];
1040         int b, cksum, editor_exit;
1041
1042
1043         if (strlen(editor_path) == 0) {
1044                 scr_printf("Do you wish to re-enter %s? ", desc);
1045                 if (yesno() == 0)
1046                         return;
1047         }
1048
1049         fp = fopen(temp, "w");
1050         fclose(fp);
1051
1052         serv_puts(check_cmd);
1053         serv_gets(cmd);
1054         if (cmd[0] != '2') {
1055                 scr_printf("%s\n", &cmd[4]);
1056                 return;
1057         }
1058
1059         if (strlen(editor_path) > 0) {
1060                 serv_puts(read_cmd);
1061                 serv_gets(cmd);
1062                 if (cmd[0] == '1') {
1063                         fp = fopen(temp, "w");
1064                         while (serv_gets(cmd), strcmp(cmd, "000")) {
1065                                 fprintf(fp, "%s\n", cmd);
1066                         }
1067                         fclose(fp);
1068                 }
1069         }
1070
1071         cksum = file_checksum(temp);
1072
1073         if (strlen(editor_path) > 0) {
1074                 char tmp[SIZ];
1075
1076                 snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", desc);
1077                 putenv(tmp);
1078                 editor_pid = fork();
1079                 if (editor_pid == 0) {
1080                         chmod(temp, 0600);
1081                         screen_reset();
1082                         sttybbs(SB_RESTORE);
1083                         execlp(editor_path, editor_path, temp, NULL);
1084                         exit(1);
1085                 }
1086                 if (editor_pid > 0)
1087                         do {
1088                                 editor_exit = 0;
1089                                 b = wait(&editor_exit);
1090                         } while ((b != editor_pid) && (b >= 0));
1091                 editor_pid = (-1);
1092                 scr_printf("Executed %s\n", editor_path);
1093                 sttybbs(0);
1094                 screen_set();
1095         } else {
1096                 scr_printf("Entering %s.  "
1097                         "Press return twice when finished.\n", desc);
1098                 fp = fopen(temp, "r+");
1099                 citedit(fp);
1100                 fclose(fp);
1101         }
1102
1103         if (file_checksum(temp) == cksum) {
1104                 scr_printf("*** Aborted.\n");
1105         }
1106
1107         else {
1108                 serv_puts(write_cmd);
1109                 serv_gets(cmd);
1110                 if (cmd[0] != '4') {
1111                         scr_printf("%s\n", &cmd[4]);
1112                         return;
1113                 }
1114
1115                 fp = fopen(temp, "r");
1116                 while (fgets(cmd, SIZ - 1, fp) != NULL) {
1117                         cmd[strlen(cmd) - 1] = 0;
1118                         serv_puts(cmd);
1119                 }
1120                 fclose(fp);
1121                 serv_puts("000");
1122         }
1123
1124         unlink(temp);
1125 }
1126
1127
1128 void enterinfo(void)
1129 {                               /* edit info file for current room */
1130         do_edit("the Info file for this room", "RINF", "EINF 0", "EINF 1");
1131 }
1132
1133 void enter_bio(void)
1134 {
1135         char cmd[SIZ];
1136         snprintf(cmd, sizeof cmd, "RBIO %s", fullname);
1137         do_edit("your Bio", cmd, "NOOP", "EBIO");
1138 }
1139
1140 /*
1141  * create a new floor
1142  */
1143 void create_floor(void)
1144 {
1145         char buf[SIZ];
1146         char newfloorname[SIZ];
1147
1148         load_floorlist();
1149
1150         serv_puts("CFLR xx|0");
1151         serv_gets(buf);
1152         if (buf[0] != '2') {
1153                 scr_printf("%s\n", &buf[4]);
1154                 return;
1155         }
1156
1157         newprompt("Name for new floor: ", newfloorname, 255);
1158         snprintf(buf, sizeof buf, "CFLR %s|1", newfloorname);
1159         serv_puts(buf);
1160         serv_gets(buf);
1161         if (buf[0] == '2') {
1162                 scr_printf("Floor has been created.\n");
1163         } else {
1164                 scr_printf("%s\n", &buf[4]);
1165         }
1166
1167         load_floorlist();
1168 }
1169
1170 /*
1171  * edit the current floor
1172  */
1173 void edit_floor(void)
1174 {
1175         char buf[SIZ];
1176         int expire_mode = 0;
1177         int expire_value = 0;
1178
1179         load_floorlist();
1180
1181         /* Fetch the expire policy (this will silently fail on old servers,
1182          * resulting in "default" policy)
1183          */
1184         serv_puts("GPEX floor");
1185         serv_gets(buf);
1186         if (buf[0] == '2') {
1187                 expire_mode = extract_int(&buf[4], 0);
1188                 expire_value = extract_int(&buf[4], 1);
1189         }
1190
1191         /* Interact with the user */
1192         strprompt("Floor name", &floorlist[(int) curr_floor][0], 255);
1193
1194         /* Angels and demons dancing in my head... */
1195         do {
1196                 snprintf(buf, sizeof buf, "%d", expire_mode);
1197                 strprompt
1198                     ("Floor default essage expire policy (? for list)",
1199                      buf, 1);
1200                 if (buf[0] == '?') {
1201                         scr_printf("\n"
1202                                 "0. Use the system default\n"
1203                                 "1. Never automatically expire messages\n"
1204                                 "2. Expire by message count\n"
1205                                 "3. Expire by message age\n");
1206                 }
1207         } while ((buf[0] < 48) || (buf[0] > 51));
1208         expire_mode = buf[0] - 48;
1209
1210         /* ...lunatics and monsters underneath my bed */
1211         if (expire_mode == 2) {
1212                 snprintf(buf, sizeof buf, "%d", expire_value);
1213                 strprompt("Keep how many messages online?", buf, 10);
1214                 expire_value = atol(buf);
1215         }
1216
1217         if (expire_mode == 3) {
1218                 snprintf(buf, sizeof buf, "%d", expire_value);
1219                 strprompt("Keep messages for how many days?", buf, 10);
1220                 expire_value = atol(buf);
1221         }
1222
1223         /* Save it */
1224         snprintf(buf, sizeof buf, "SPEX floor|%d|%d",
1225                  expire_mode, expire_value);
1226         serv_puts(buf);
1227         serv_gets(buf);
1228
1229         snprintf(buf, sizeof buf, "EFLR %d|%s", curr_floor,
1230                  &floorlist[(int) curr_floor][0]);
1231         serv_puts(buf);
1232         serv_gets(buf);
1233         scr_printf("%s\n", &buf[4]);
1234         load_floorlist();
1235 }
1236
1237
1238
1239
1240 /*
1241  * kill the current floor 
1242  */
1243 void kill_floor(void)
1244 {
1245         int floornum_to_delete, a;
1246         char buf[SIZ];
1247
1248         load_floorlist();
1249         do {
1250                 floornum_to_delete = (-1);
1251                 scr_printf("(Press return to abort)\n");
1252                 newprompt("Delete which floor? ", buf, 255);
1253                 if (strlen(buf) == 0)
1254                         return;
1255                 for (a = 0; a < 128; ++a)
1256                         if (!strcasecmp(&floorlist[a][0], buf))
1257                                 floornum_to_delete = a;
1258                 if (floornum_to_delete < 0) {
1259                         scr_printf("No such floor.  Select one of:\n");
1260                         for (a = 0; a < 128; ++a)
1261                                 if (floorlist[a][0] != 0)
1262                                         scr_printf("%s\n", &floorlist[a][0]);
1263                 }
1264         } while (floornum_to_delete < 0);
1265         snprintf(buf, sizeof buf, "KFLR %d|1", floornum_to_delete);
1266         serv_puts(buf);
1267         serv_gets(buf);
1268         scr_printf("%s\n", &buf[4]);
1269         load_floorlist();
1270 }