Tue Aug 18 00:42:33 EDT 1998 Nathan Bryant <bryant@cs.usm.maine.edu>
[citadel.git] / citadel / rooms.c
1 /* Citadel/UX room-oriented routines */
2
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <ctype.h>
8 #include <string.h>
9 #include <signal.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <sys/wait.h>
13 #include <errno.h>
14 #include "citadel.h"
15 #include "rooms.h"
16
17 #define IFEXPERT if (userflags&US_EXPERT)
18 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
19 #define IFAIDE if (axlevel>=6)
20 #define IFNAIDE if (axlevel<6)
21
22
23 long finduser(int file, char *name);
24 void sttybbs(int cmd);
25 void extract(char *dest, char *source, int parmnum);
26 int extract_int(char *source, int parmnum);
27 void hit_any_key(void);
28 int yesno(void);
29 int yesno_d(int d);
30 void strprompt(char *prompt, char *str, int len);
31 void newprompt(char *prompt, char *str, int len);
32 int struncmp(char *lstr, char *rstr, int len);
33 void dotgoto(char *towhere, int display_name);
34 long extract_long(char *source, int parmnum);
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 void listrms(char *variety)
86 {
87         char buf[256];
88         char rmname[32];
89         int f,c;
90
91         serv_puts(variety);
92         serv_gets(buf);
93         if (buf[0]!='1') return;
94         c = 1;
95         sigcaught = 0;
96         sttybbs(SB_YES_INTR);
97         while (serv_gets(buf), strcmp(buf,"000")) if (sigcaught==0) {
98                 extract(rmname,buf,0);
99                 if ((c + strlen(rmname) + 4) > screenwidth) {
100                         printf("\n");
101                         c = 1;
102                         }
103                 f = extract_int(buf,1);
104                 if (f & QR_PRIVATE) {
105                         color(1);
106                         }
107                 else {
108                         color(2);
109                         }
110                 printf("%s",rmname);
111                 if ((f & QR_DIRECTORY) && (f & QR_NETWORK)) printf("}  ");
112                 else if (f & QR_DIRECTORY) printf("]  ");
113                 else if (f & QR_NETWORK) printf(")  ");
114                 else printf(">  ");
115                 c = c + strlen(rmname) + 3;
116                 }
117         color(7);
118         sttybbs(SB_NO_INTR);
119         }
120
121
122 void list_other_floors(void) {
123         int a,c;
124
125         c = 1;
126         for (a=0; a<128; ++a) if ((strlen(floorlist[a])>0)&&(a!=curr_floor)) {
127                 if ((c + strlen(floorlist[a]) + 4) > screenwidth) {
128                         printf("\n");
129                         c = 1;
130                         }
131                 printf("%s:  ",floorlist[a]);
132                 c = c + strlen(floorlist[a]) + 3;
133                 }
134         }
135
136
137 /*
138  * List known rooms.  kn_floor_mode should be set to 0 for a 'flat' listing,
139  * 1 to list rooms on the current floor, or 1 to list rooms on all floors.
140  */
141 void knrooms(int kn_floor_mode)
142 {
143         char buf[256];
144         int a;
145
146         load_floorlist();
147
148         if (kn_floor_mode == 0) {
149                 color(3);
150                 printf("\n   Rooms with unread messages:\n");
151                 listrms("LKRN");
152                 color(3);
153                 printf("\n\n   No unseen messages in:\n");
154                 listrms("LKRO");
155                 printf("\n");
156                 }
157
158         if (kn_floor_mode == 1) {
159                 color(3);
160                 printf("\n   Rooms with unread messages on %s:\n",
161                         floorlist[(int)curr_floor]);
162                 sprintf(buf,"LKRN %d",curr_floor);
163                 listrms(buf);
164                 color(3);
165                 printf("\n\n   Rooms with no new messages on %s:\n",
166                         floorlist[(int)curr_floor]);
167                 sprintf(buf,"LKRO %d",curr_floor);
168                 listrms(buf);
169                 color(3);
170                 printf("\n\n   Other floors:\n");
171                 list_other_floors();
172                 printf("\n");
173                 }
174
175         if (kn_floor_mode == 2) {
176                 for (a=0; a<128; ++a) if (floorlist[a][0]!=0) {
177                         color(3);
178                         printf("\n   Rooms on %s:\n",floorlist[a]);
179                         sprintf(buf,"LKRA %d",a);
180                         listrms(buf);
181                         printf("\n");
182                         }
183                 }
184         
185         color(7);
186         IFNEXPERT hit_any_key();
187         }
188
189
190 void listzrooms(void) {         /* list public forgotten rooms */
191         color(3);
192         printf("\n   Forgotten public rooms:\n");
193         listrms("LZRM");
194         printf("\n");
195         color(7);
196         IFNEXPERT hit_any_key();
197         }
198
199
200 int set_room_attr(int ibuf, char *prompt, unsigned int sbit)
201 {
202         int a;
203
204         printf("%s [%s]? ",prompt,((ibuf&sbit) ? "Yes":"No"));
205         a=yesno_d(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                         strcpy(floorstr,&floorlist[rfloor][0]);
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[20];
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
268         serv_puts("GETR");
269         serv_gets(buf);
270         if (buf[0]!='2') {
271                 printf("%s\n",&buf[4]);
272                 return;
273                 }
274
275         extract(rname,&buf[4],0);
276         extract(rpass,&buf[4],1);
277         extract(rdir, &buf[4],2);
278         rflags = extract_int(&buf[4],3);
279         rfloor = extract_int(&buf[4],4);
280         rbump = 0;
281
282         serv_puts("GETA");
283         serv_gets(buf);
284         if (buf[0]=='2') strcpy(raide,&buf[4]);
285         else strcpy(raide,"");
286         if (strlen(raide)==0) strcpy(raide,"none");
287
288         strprompt("Room name",rname,19);
289
290         rfloor = select_floor(rfloor);
291         rflags = set_room_attr(rflags,"Private room",QR_PRIVATE);
292         if (rflags & QR_PRIVATE)
293                 rflags = set_room_attr(rflags,
294                         "Accessible by guessing room name",QR_GUESSNAME);
295
296         /* if it's public, clear the privacy classes */
297         if ((rflags & QR_PRIVATE)==0) {
298                 if (rflags & QR_GUESSNAME)  rflags = rflags - QR_GUESSNAME;
299                 if (rflags & QR_PASSWORDED) rflags = rflags - QR_PASSWORDED;
300                 }
301
302         /* if it's private, choose the privacy classes */
303         if ( (rflags & QR_PRIVATE)
304            && ( (rflags & QR_GUESSNAME) == 0) ) {
305                 rflags = set_room_attr(rflags,
306                         "Accessible by entering a password",QR_PASSWORDED);
307                 }
308         if ( (rflags & QR_PRIVATE)
309            && ((rflags&QR_PASSWORDED)==QR_PASSWORDED) ) {
310                 strprompt("Room password",rpass,9);
311                 }
312
313         if ((rflags&QR_PRIVATE)==QR_PRIVATE) {
314                 printf("Cause current users to forget room [No] ? ");
315                 if (yesno_d(0)==1) rbump = 1;
316                 }
317
318         rflags = set_room_attr(rflags,"Preferred users only",QR_PREFONLY);
319         rflags = set_room_attr(rflags,"Read-only room",QR_READONLY);
320         rflags = set_room_attr(rflags,"Directory room",QR_DIRECTORY);
321         rflags = set_room_attr(rflags,"Permanent room",QR_PERMANENT);
322         if (rflags & QR_DIRECTORY) {
323                 strprompt("Directory name",rdir,14);
324                 rflags = set_room_attr(rflags,"Uploading allowed",QR_UPLOAD);
325                 rflags = set_room_attr(rflags,"Downloading allowed",
326                                                                 QR_DOWNLOAD);
327                 rflags = set_room_attr(rflags,"Visible directory",QR_VISDIR);
328                 }
329         rflags = set_room_attr(rflags,"Network shared room",QR_NETWORK);
330         rflags = set_room_attr(rflags,
331                 "Automatically make all messages anonymous",QR_ANONONLY);
332         if ( (rflags & QR_ANONONLY) == 0) {
333                 rflags = set_room_attr(rflags,
334                         "Ask users whether to make messages anonymous",
335                         QR_ANON2);
336                 }
337
338         do {
339                 strprompt("Room aide (or 'none')",raide,29);
340                 if (!strucmp(raide,"none")) {
341                         strcpy(raide,"");
342                         strcpy(buf,"200");
343                         }
344                 else {
345                         sprintf(buf,"QUSR %s",raide);
346                         serv_puts(buf);
347                         serv_gets(buf);
348                         if (buf[0]!='2') printf("%s\n",&buf[4]);
349                         }
350                 } while(buf[0]!='2');
351
352         if (!strucmp(raide,"none")) strcpy(raide,"");
353
354         printf("Save changes (y/n)? ");
355         if (yesno()==1) {
356                 sprintf(buf,"SETR %s|%s|%s|%d|%d|%d",
357                         rname,rpass,rdir,rflags,rbump,rfloor);
358                 serv_puts(buf);
359                 serv_gets(buf);
360                 printf("%s\n",&buf[4]);
361                 sprintf(buf,"SETA %s",raide);
362                 serv_puts(buf);
363                 serv_gets(buf);
364                 if (buf[0]=='2') dotgoto(rname,2);
365                 }
366         }
367
368
369 /*
370  * un-goto the previous room
371  */
372 void ungoto(void) { 
373         char buf[256];
374         
375         if (!strcmp(ugname,"")) return;
376         sprintf(buf,"GOTO %s",ugname);
377         serv_puts(buf);
378         serv_gets(buf);
379         if (buf[0]!='2') {
380                 printf("%s\n",&buf[4]);
381                 return;
382                 }
383         sprintf(buf,"SLRP %ld",uglsn);
384         serv_puts(buf);
385         serv_gets(buf);
386         if (buf[0]!='2') printf("%s\n",&buf[4]);
387         strcpy(buf,ugname);
388         strcpy(ugname,"");
389         dotgoto(buf,0);
390         }
391
392
393 /*
394  * download()  -  download a file or files.  The argument passed to this
395  *                function determines which protocol to use.
396  */
397 void download(int proto)
398 {
399
400 /*
401   - 0 = paginate, 1 = xmodem, 2 = raw, 3 = ymodem, 4 = zmodem, 5 = save
402 */
403
404
405         char buf[256];
406         char dbuf[4096];
407         char filename[256];
408         long total_bytes = 0L;
409         long transmitted_bytes = 0L;
410         long aa,bb;
411         int a,b;
412         int packet;
413         FILE *tpipe = NULL;
414         FILE *savefp = NULL;
415         int proto_pid;
416         int broken = 0;
417
418         if ((room_flags & QR_DOWNLOAD) == 0) {
419                 printf("*** You cannot download from this room.\n");
420                 return;
421                 }
422         
423         newprompt("Enter filename: ",filename,255);
424
425         sprintf(buf,"OPEN %s",filename);
426         serv_puts(buf);
427         serv_gets(buf);
428         if (buf[0]!='2') {
429                 printf("%s\n",&buf[4]);
430                 return;
431                 }
432         total_bytes = extract_long(&buf[4],0);
433
434
435         /* Here's the code for simply transferring the file to the client,
436          * for folks who have their own clientware.  It's a lot simpler than
437          * the [XYZ]modem code below...
438          */
439         if (proto == 5) {
440                 printf("Enter the name of the directory to save '%s'\n",
441                         filename);
442                 printf("to, or press return for the current directory.\n");
443                 newprompt("Directory: ",dbuf,256);
444                 if (strlen(dbuf)==0) strcpy(dbuf,".");
445                 strcat(dbuf,"/");
446                 strcat(dbuf,filename);
447                 
448                 savefp = fopen(dbuf,"w");
449                 if (savefp == NULL) {
450                         printf("Cannot open '%s': %s\n",dbuf,strerror(errno));
451                         /* close the download file at the server */
452                         serv_puts("CLOS");
453                         serv_gets(buf);
454                         if (buf[0]!='2') {
455                                 printf("%s\n",&buf[4]);
456                                 }
457                         return;
458                         }
459                 progress(0,total_bytes);
460                 while ( (transmitted_bytes < total_bytes) && (broken == 0) ) {
461                         bb = total_bytes - transmitted_bytes;
462                         aa = ((bb < 4096) ? bb : 4096);
463                         sprintf(buf,"READ %ld|%ld",transmitted_bytes,aa);
464                         serv_puts(buf);
465                         serv_gets(buf);
466                         if (buf[0]!='6') {
467                                 printf("%s\n",&buf[4]);
468                                 return;
469                                 }
470                         packet = extract_int(&buf[4],0);
471                         serv_read(dbuf,packet);
472                         if (fwrite(dbuf,packet,1,savefp) < 1) broken = 1;
473                         transmitted_bytes = transmitted_bytes + (long)packet;
474                         progress(transmitted_bytes,total_bytes);
475                         }
476                 fclose(savefp);
477                 /* close the download file at the server */
478                 serv_puts("CLOS");
479                 serv_gets(buf);
480                 if (buf[0]!='2') {
481                         printf("%s\n",&buf[4]);
482                         }
483                 return;
484                 }
485
486
487         mkdir(tempdir,0700);
488 #ifdef USE_MKFIFO
489         sprintf(buf,"mkfifo %s/%s",tempdir,filename);
490 #else
491         sprintf(buf,"mknod %s/%s p",tempdir,filename);
492 #endif
493         system(buf);
494
495         /* We do the remainder of this function as a separate process in
496          * order to allow recovery if the transfer is aborted.  If the
497          * file transfer program aborts, the first child process receives a
498          * "broken pipe" signal and aborts.  We *should* be able to catch
499          * this condition with signal(), but it doesn't seem to work on all
500          * systems.
501          */
502         a = fork();
503         if (a!=0) {
504                 /* wait for the download to finish */
505                 while (wait(&b)!=a) ;;
506                 sttybbs(0);
507                 /* close the download file at the server */
508                 serv_puts("CLOS");
509                 serv_gets(buf);
510                 if (buf[0]!='2') {
511                         printf("%s\n",&buf[4]);
512                         }
513                 /* clean up the temporary directory */
514                 nukedir(tempdir);
515                 return;
516                 }
517
518         sprintf(buf,"%s/%s",tempdir,filename);  /* full pathname */
519
520         /* The next fork() creates a second child process that is used for
521          * the actual file transfer program (usually sz).
522          */
523         proto_pid = fork();
524         if (proto_pid == 0) {
525                 if (proto==0)  {
526                         sttybbs(0);
527                         signal(SIGINT,SIG_DFL);
528                         signal(SIGQUIT,SIG_DFL);
529                         sprintf(dbuf,"SHELL=/dev/null; export SHELL; TERM=dumb; export TERM; exec more -d <%s",buf);
530                         system(dbuf);
531                         sttybbs(SB_NO_INTR);
532                         exit(0);
533                         }
534                 sttybbs(3);
535                 signal(SIGINT,SIG_DFL);
536                 signal(SIGQUIT,SIG_DFL);
537                 if (proto==1) execlp("sx","sx",buf,NULL);
538                 if (proto==2) execlp("cat","cat",buf,NULL);
539                 if (proto==3) execlp("sb","sb",buf,NULL);
540                 if (proto==4) execlp("sz","sz",buf,NULL);
541                 execlp("cat","cat",buf,NULL);
542                 exit(1);
543                 }
544
545         tpipe = fopen(buf,"w");
546
547         while ( (transmitted_bytes < total_bytes) && (broken == 0) ) {
548                 bb = total_bytes - transmitted_bytes;
549                 aa = ((bb < 4096) ? bb : 4096);
550                 sprintf(buf,"READ %ld|%ld",transmitted_bytes,aa);
551                 serv_puts(buf);
552                 serv_gets(buf);
553                 if (buf[0]!='6') {
554                         printf("%s\n",&buf[4]);
555                         return;
556                         }
557                 packet = extract_int(&buf[4],0);
558                 serv_read(dbuf,packet);
559                 if (fwrite(dbuf,packet,1,tpipe) < 1) broken = 1;
560                 transmitted_bytes = transmitted_bytes + (long)packet;
561                 }
562         if (tpipe!=NULL) fclose(tpipe);
563
564         /* Hang out and wait for the file transfer program to finish */
565         while (wait(&a) != proto_pid) ;;
566
567
568         putc(7,stdout);
569         exit(0);        /* transfer control back to the main program */
570         }
571
572
573 /*
574  * read directory of this room
575  */
576 void roomdir(void) {
577         char flnm[256];
578         char flsz[32];
579         char comment[256];
580         char buf[256];
581
582         serv_puts("RDIR");
583         serv_gets(buf);
584         if (buf[0]!='1') {
585                 printf("%s\n",&buf[4]);
586                 return;
587                 }
588
589         extract(comment,&buf[4],0);
590         extract(flnm,&buf[4],1);
591         printf("\nDirectory of %s on %s\n",flnm,comment);
592         printf("-----------------------\n");
593         while (serv_gets(buf), strcmp(buf,"000")) {
594                 extract(flnm,buf,0);
595                 extract(flsz,buf,1);
596                 extract(comment,buf,2);
597                 if (strlen(flnm)<=14)
598                         printf("%-14s %8s %s\n",flnm,flsz,comment);
599                 else
600                         printf("%s\n%14s %8s %s\n",flnm,"",flsz,comment);
601                 }
602         }
603
604
605 /*
606  * add a user to a private room
607  */
608 void invite(void) {
609         char aaa[31],bbb[256];
610
611         if ((room_flags & QR_PRIVATE)==0) {
612                 printf("This is not a private room.\n");
613                 return;
614                 }
615
616         newprompt("Name of user? ",aaa,30);
617         if (aaa[0]==0) return;
618
619         sprintf(bbb,"INVT %s",aaa);
620         serv_puts(bbb);
621         serv_gets(bbb);
622         printf("%s\n",&bbb[4]);
623         }
624
625
626 /*
627  * kick a user out of a room
628  */
629 void kickout(void) {
630         char aaa[31],bbb[256];
631
632         if ((room_flags & QR_PRIVATE)==0) {
633                 printf("Note: this is not a private room.  Kicking a user ");
634                 printf("out of this room will only\nhave the same effect ");
635                 printf("as if they <Z>apped the room.\n\n");
636                 }
637
638         newprompt("Name of user? ",aaa,30);
639         if (aaa[0]==0) return;
640
641         sprintf(bbb,"KICK %s",aaa);
642         serv_puts(bbb);
643         serv_gets(bbb);
644         printf("%s\n",&bbb[4]);
645         }
646
647
648 /*
649  * aide command: kill the current room
650  */
651 void killroom(void) {
652         char aaa[100];
653
654         serv_puts("KILL 0");
655         serv_gets(aaa);
656         if (aaa[0]!='2') {
657                 printf("%s\n",&aaa[4]);
658                 return;
659                 }
660
661         printf("Are you sure you want to kill this room? ");
662         if (yesno()==0) return;
663
664         serv_puts("KILL 1");
665         serv_gets(aaa);
666         printf("%s\n",&aaa[4]);
667         if (aaa[0]!='2') return;
668         dotgoto("_BASEROOM_",0);
669         }
670
671 void forget(void) {     /* forget the current room */
672         char cmd[256];
673
674         printf("Are you sure you want to forget this room? ");
675         if (yesno()==0) return;
676
677         serv_puts("FORG");
678         serv_gets(cmd);
679         if (cmd[0]!='2') {
680                 printf("%s\n",&cmd[4]);
681                 return;
682                 }
683
684         /* now return to the lobby */
685         dotgoto("_BASEROOM_",0);
686         }
687
688
689 /*
690  * create a new room
691  */
692 void entroom(void) {
693         char cmd[256];
694         char new_room_name[20];
695         int new_room_type;
696         char new_room_pass[10];
697         int new_room_floor;
698         int a,b;
699
700         serv_puts("CRE8 0");
701         serv_gets(cmd);
702         
703         if (cmd[0]!='2') {
704                 printf("%s\n",&cmd[4]);
705                 return;
706                 }
707         
708         newprompt("Name for new room? ",new_room_name,19);
709         if (strlen(new_room_name)==0) return;
710         for (a=0; a<strlen(new_room_name); ++a)
711                 if (new_room_name[a] == '|') new_room_name[a]='_';
712
713         new_room_floor = select_floor((int)curr_floor);
714
715         IFNEXPERT formout("roomaccess");
716         do {
717                 printf("<?>Help\n<1>Public room\n<2>Guess-name room\n");
718                 printf("<3>Passworded room\n<4>Invitation-only room\n");
719                 printf("Enter room type: ");
720                 do {
721                         b=inkey();
722                         } while (((b<'1')||(b>'4')) && (b!='?'));
723                 if (b=='?') {
724                         printf("?\n");
725                         formout("roomaccess");
726                         }
727                 } while ((b<'1')||(b>'4'));
728         b=b-48;
729         printf("%d\n",b);
730         new_room_type = b - 1;
731         if (new_room_type==2) {
732                 newprompt("Enter a room password: ",new_room_pass,9);
733                 for (a=0; a<strlen(new_room_pass); ++a)
734                         if (new_room_pass[a] == '|') new_room_pass[a]='_';
735                 }
736         else strcpy(new_room_pass,"");
737
738         printf("\042%s\042, a",new_room_name);
739         if (b==1) printf(" public room.");
740         if (b==2) printf(" guess-name room.");
741         if (b==3) printf(" passworded room, password: %s",new_room_pass);
742         if (b==4) printf("n invitation-only room.");
743         printf("\nInstall it? (y/n) : ");
744         a=yesno();
745         if (a==0) return;
746
747         sprintf(cmd, "CRE8 1|%s|%d|%s|%d", new_room_name,
748                 new_room_type, new_room_pass, new_room_floor);
749         serv_puts(cmd);
750         serv_gets(cmd);
751         if (cmd[0]!='2') {
752                 printf("%s\n",&cmd[4]);
753                 return;
754                 }
755
756         /* command succeeded... now GO to the new room! */
757         dotgoto(new_room_name,0);
758         }
759
760
761
762 void readinfo(void) {   /* read info file for current room */
763         char cmd[256];
764         
765         sprintf(cmd,"RINF");
766         serv_puts(cmd);
767         serv_gets(cmd);
768
769         if (cmd[0]!='1') return;
770
771         fmout(screenwidth,NULL,
772                 ((userflags & US_PAGINATOR) ? 1 : 0),
773                 screenheight,0,1);
774         }
775
776
777 /*
778  * <W>ho knows room...
779  */
780 void whoknows(void) {
781         char buf[256];
782         serv_puts("WHOK");
783         serv_gets(buf);
784         if (buf[0]!='1') {
785                 printf("%s\n",&buf[5]);
786                 return;
787                 }
788         sigcaught = 0;
789         sttybbs(SB_YES_INTR);
790         while (serv_gets(buf), strncmp(buf,"000",3)) {
791                 if (sigcaught==0) printf("%s\n",buf);
792                 }
793         sttybbs(SB_NO_INTR);
794         }
795
796
797 void do_edit(char *desc, char *read_cmd, char *check_cmd, char *write_cmd)
798 {
799         FILE *fp;
800         char cmd[256];
801         int b,cksum,editor_exit;
802
803
804         if (strlen(editor_path)==0) {
805                 printf("Do you wish to re-enter %s? ",desc);
806                 if (yesno()==0) return;
807                 }
808
809         fp = fopen(temp,"w");
810         fclose(fp);
811
812         serv_puts(check_cmd);
813         serv_gets(cmd);
814         if (cmd[0]!='2') {
815                 printf("%s\n",&cmd[4]);
816                 return;
817                 }
818
819         if (strlen(editor_path)>0) {
820                 serv_puts(read_cmd);
821                 serv_gets(cmd);
822                 if (cmd[0]=='1') {
823                         fp = fopen(temp,"w");
824                         while (serv_gets(cmd), strcmp(cmd,"000")) {
825                                 fprintf(fp,"%s\n",cmd);
826                                 }
827                         fclose(fp);
828                         }
829                 }
830
831         cksum = file_checksum(temp);
832
833         if (strlen(editor_path)>0) {
834                 editor_pid=fork();
835                 if (editor_pid==0) {
836                         chmod(temp,0600);
837                         sttybbs(SB_RESTORE);
838                         execlp(editor_path,editor_path,temp,NULL);
839                         exit(1);
840                         }
841                 if (editor_pid>0) do {
842                         editor_exit = 0;
843                         b=wait(&editor_exit);
844                         } while((b!=editor_pid)&&(b>=0));
845                 editor_pid = (-1);
846                 printf("Executed %s\n", editor_path);
847                 sttybbs(0);
848                 }
849         else {
850                 printf("Entering %s.  ",desc);
851                 printf("Press return twice when finished.\n");
852                 fp=fopen(temp,"r+");
853                 citedit(fp,0);
854                 fclose(fp);
855                 }
856
857         if (file_checksum(temp) == cksum) {
858                 printf("*** Aborted.\n");
859                 }
860
861         else {
862                 serv_puts(write_cmd);
863                 serv_gets(cmd);
864                 if (cmd[0]!='4') {
865                         printf("%s\n",&cmd[4]);
866                         return;
867                         }
868
869                 fp=fopen(temp,"r");
870                 while (fgets(cmd,255,fp)!=NULL) {
871                         cmd[strlen(cmd)-1] = 0;
872                         serv_puts(cmd);
873                         }
874                 fclose(fp);
875                 serv_puts("000");
876                 }
877
878         unlink(temp);
879         }
880
881
882 void enterinfo(void) {          /* edit info file for current room */
883         do_edit("the Info file for this room","RINF","EINF 0","EINF 1");
884         }
885
886 void enter_bio(void) {
887         char cmd[256];
888         sprintf(cmd,"RBIO %s",fullname);
889         do_edit("your Bio",cmd,"NOOP","EBIO");
890         }
891
892 /*
893  * create a new floor
894  */
895 void create_floor(void) {
896         char buf[256];
897         char newfloorname[256];
898
899         serv_puts("CFLR xx|0");
900         serv_gets(buf);
901         if (buf[0]!='2') {
902                 printf("%s\n",&buf[4]);
903                 return;
904                 }
905
906         newprompt("Name for new floor: ",newfloorname,255);
907         sprintf(buf,"CFLR %s|1",newfloorname);
908         serv_puts(buf);
909         serv_gets(buf);
910         if (buf[0]=='2') {
911                 printf("Floor has been created.\n");
912                 }
913         else {
914                 printf("%s\n",&buf[4]);
915                 }
916         }
917
918 /*
919  * edit the current floor
920  */
921 void edit_floor(void) {
922         char buf[256];
923
924         if (floorlist[(int)curr_floor][0]==0) load_floorlist();
925         strprompt("New floor name",&floorlist[(int)curr_floor][0],255);
926         sprintf(buf,"EFLR %d|%s",curr_floor,&floorlist[(int)curr_floor][0]);
927         serv_puts(buf);
928         serv_gets(buf);
929         printf("%s\n",&buf[4]);
930         load_floorlist();
931         }
932
933
934
935
936 /*
937  * kill the current floor 
938  */
939 void kill_floor(void) {
940         int floornum_to_delete,a;
941         char buf[256];
942
943         if (floorlist[(int)curr_floor][0]==0) load_floorlist();
944         do {
945                 floornum_to_delete = (-1);
946                 printf("(Press return to abort)\n");
947                 newprompt("Delete which floor? ",buf,255);
948                 if (strlen(buf)==0) return;
949                 for (a=0; a<128; ++a)
950                         if (!strucmp(&floorlist[a][0],buf))
951                                 floornum_to_delete = a;
952                 if (floornum_to_delete < 0) {
953                         printf("No such floor.  Select one of:\n");
954                         for (a=0; a<128; ++a)
955                                 if (floorlist[a][0]!=0)
956                                         printf("%s\n",&floorlist[a][0]);
957                         }
958                 } while (floornum_to_delete < 0);
959         sprintf(buf,"KFLR %d|1",floornum_to_delete);
960         serv_puts(buf);
961         serv_gets(buf);
962         printf("%s\n",&buf[4]);
963         }