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