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