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