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