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