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