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