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