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