807a5bea858b1622fa9cac320f85acbbc329b8cc
[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      /* Because kicking people out of public rooms now sets a LOCKOUT
729       * flag, we need to be able to invite people into public rooms
730       * in order to let them back in again.
731       *        - cough
732       */
733
734      /*
735       * if ((room_flags & QR_PRIVATE)==0) {
736       *         printf("This is not a private room.\n");
737       *         return;
738       *         }
739       */
740
741         newprompt("Name of user? ",aaa,30);
742         if (aaa[0]==0) return;
743
744         snprintf(bbb,sizeof bbb,"INVT %s",aaa);
745         serv_puts(bbb);
746         serv_gets(bbb);
747         printf("%s\n",&bbb[4]);
748         }
749
750
751 /*
752  * kick a user out of a room
753  */
754 void kickout(void) {
755         char aaa[31],bbb[SIZ];
756
757         newprompt("Name of user? ",aaa,30);
758         if (aaa[0]==0) return;
759
760         snprintf(bbb,sizeof bbb,"KICK %s",aaa);
761         serv_puts(bbb);
762         serv_gets(bbb);
763         printf("%s\n",&bbb[4]);
764         }
765
766
767 /*
768  * aide command: kill the current room
769  */
770 void killroom(void) {
771         char aaa[100];
772
773         serv_puts("KILL 0");
774         serv_gets(aaa);
775         if (aaa[0]!='2') {
776                 printf("%s\n",&aaa[4]);
777                 return;
778                 }
779
780         printf("Are you sure you want to kill this room? ");
781         if (yesno()==0) return;
782
783         serv_puts("KILL 1");
784         serv_gets(aaa);
785         printf("%s\n",&aaa[4]);
786         if (aaa[0]!='2') return;
787         dotgoto("_BASEROOM_",0);
788         }
789
790 void forget(void) {     /* forget the current room */
791         char cmd[SIZ];
792
793         printf("Are you sure you want to forget this room? ");
794         if (yesno()==0) return;
795
796         serv_puts("FORG");
797         serv_gets(cmd);
798         if (cmd[0]!='2') {
799                 printf("%s\n",&cmd[4]);
800                 return;
801                 }
802
803         /* now return to the lobby */
804         dotgoto("_BASEROOM_",0);
805         }
806
807
808 /*
809  * create a new room
810  */
811 void entroom(void) {
812         char cmd[SIZ];
813         char new_room_name[ROOMNAMELEN];
814         int new_room_type;
815         char new_room_pass[10];
816         int new_room_floor;
817         int a,b;
818
819         serv_puts("CRE8 0");
820         serv_gets(cmd);
821         
822         if (cmd[0]!='2') {
823                 printf("%s\n",&cmd[4]);
824                 return;
825                 }
826         
827         newprompt("Name for new room? ",new_room_name,ROOMNAMELEN-1);
828         if (strlen(new_room_name)==0) return;
829         for (a=0; a<strlen(new_room_name); ++a)
830                 if (new_room_name[a] == '|') new_room_name[a]='_';
831
832         new_room_floor = select_floor((int)curr_floor);
833
834         IFNEXPERT formout("roomaccess");
835         do {
836                 printf( "<?>Help\n<1>Public room\n<2>Guess-name room\n"
837                         "<3>Passworded room\n<4>Invitation-only room\n"
838                         "<5>Personal room\n");
839                 printf("Enter room type: ");
840                 do {
841                         b=inkey();
842                         } while (((b<'1')||(b>'5')) && (b!='?'));
843                 if (b=='?') {
844                         printf("?\n");
845                         formout("roomaccess");
846                         }
847                 } while ((b<'1')||(b>'5'));
848         b=b-48;
849         printf("%d\n",b);
850         new_room_type = b - 1;
851         if (new_room_type==2) {
852                 newprompt("Enter a room password: ",new_room_pass,9);
853                 for (a=0; a<strlen(new_room_pass); ++a)
854                         if (new_room_pass[a] == '|') new_room_pass[a]='_';
855                 }
856         else strcpy(new_room_pass,"");
857
858         printf("\042%s\042, a",new_room_name);
859         if (b==1) printf(" public room.");
860         if (b==2) printf(" guess-name room.");
861         if (b==3) printf(" passworded room, password: %s",new_room_pass);
862         if (b==4) printf("n invitation-only room.");
863         if (b==5) printf(" personal room.");
864         printf("\nInstall it? (y/n) : ");
865         a=yesno();
866         if (a==0) return;
867
868         snprintf(cmd, sizeof cmd, "CRE8 1|%s|%d|%s|%d", new_room_name,
869                 new_room_type, new_room_pass, new_room_floor);
870         serv_puts(cmd);
871         serv_gets(cmd);
872         if (cmd[0]!='2') {
873                 printf("%s\n",&cmd[4]);
874                 return;
875                 }
876
877         /* command succeeded... now GO to the new room! */
878         dotgoto(new_room_name,0);
879         }
880
881
882
883 void readinfo(void) {   /* read info file for current room */
884         char cmd[SIZ];
885         
886         sprintf(cmd,"RINF");
887         serv_puts(cmd);
888         serv_gets(cmd);
889
890         if (cmd[0]!='1') return;
891
892         fmout(screenwidth,NULL,
893                 ((userflags & US_PAGINATOR) ? 1 : 0),
894                 screenheight,0,1);
895         }
896
897
898 /*
899  * <W>ho knows room...
900  */
901 void whoknows(void) {
902         char buf[SIZ];
903         serv_puts("WHOK");
904         serv_gets(buf);
905         if (buf[0]!='1') {
906                 pprintf("%s\n",&buf[5]);
907                 return;
908         }
909         while (serv_gets(buf), strncmp(buf,"000",3)) {
910                 if (sigcaught==0) pprintf("%s\n",buf);
911         }
912 }
913
914
915 void do_edit(char *desc, char *read_cmd, char *check_cmd, char *write_cmd)
916 {
917         FILE *fp;
918         char cmd[SIZ];
919         int b,cksum,editor_exit;
920
921
922         if (strlen(editor_path)==0) {
923                 printf("Do you wish to re-enter %s? ",desc);
924                 if (yesno()==0) return;
925                 }
926
927         fp = fopen(temp,"w");
928         fclose(fp);
929
930         serv_puts(check_cmd);
931         serv_gets(cmd);
932         if (cmd[0]!='2') {
933                 printf("%s\n",&cmd[4]);
934                 return;
935                 }
936
937         if (strlen(editor_path)>0) {
938                 serv_puts(read_cmd);
939                 serv_gets(cmd);
940                 if (cmd[0]=='1') {
941                         fp = fopen(temp,"w");
942                         while (serv_gets(cmd), strcmp(cmd,"000")) {
943                                 fprintf(fp,"%s\n",cmd);
944                                 }
945                         fclose(fp);
946                         }
947                 }
948
949         cksum = file_checksum(temp);
950
951         if (strlen(editor_path)>0) {
952                 editor_pid=fork();
953                 if (editor_pid==0) {
954                         chmod(temp,0600);
955                         sttybbs(SB_RESTORE);
956                         execlp(editor_path,editor_path,temp,NULL);
957                         exit(1);
958                         }
959                 if (editor_pid>0) do {
960                         editor_exit = 0;
961                         b=wait(&editor_exit);
962                         } while((b!=editor_pid)&&(b>=0));
963                 editor_pid = (-1);
964                 printf("Executed %s\n", editor_path);
965                 sttybbs(0);
966                 }
967         else {
968                 printf("Entering %s.  ",desc);
969                 printf("Press return twice when finished.\n");
970                 fp=fopen(temp,"r+");
971                 citedit(fp);
972                 fclose(fp);
973                 }
974
975         if (file_checksum(temp) == cksum) {
976                 printf("*** Aborted.\n");
977                 }
978
979         else {
980                 serv_puts(write_cmd);
981                 serv_gets(cmd);
982                 if (cmd[0]!='4') {
983                         printf("%s\n",&cmd[4]);
984                         return;
985                         }
986
987                 fp=fopen(temp,"r");
988                 while (fgets(cmd,SIZ-1,fp)!=NULL) {
989                         cmd[strlen(cmd)-1] = 0;
990                         serv_puts(cmd);
991                         }
992                 fclose(fp);
993                 serv_puts("000");
994                 }
995
996         unlink(temp);
997         }
998
999
1000 void enterinfo(void) {          /* edit info file for current room */
1001         do_edit("the Info file for this room","RINF","EINF 0","EINF 1");
1002         }
1003
1004 void enter_bio(void) {
1005         char cmd[SIZ];
1006         snprintf(cmd,sizeof cmd,"RBIO %s",fullname);
1007         do_edit("your Bio",cmd,"NOOP","EBIO");
1008         }
1009
1010 /*
1011  * create a new floor
1012  */
1013 void create_floor(void) {
1014         char buf[SIZ];
1015         char newfloorname[SIZ];
1016
1017         serv_puts("CFLR xx|0");
1018         serv_gets(buf);
1019         if (buf[0]!='2') {
1020                 printf("%s\n",&buf[4]);
1021                 return;
1022                 }
1023
1024         newprompt("Name for new floor: ",newfloorname,255);
1025         snprintf(buf,sizeof buf,"CFLR %s|1",newfloorname);
1026         serv_puts(buf);
1027         serv_gets(buf);
1028         if (buf[0]=='2') {
1029                 printf("Floor has been created.\n");
1030                 }
1031         else {
1032                 printf("%s\n",&buf[4]);
1033                 }
1034         }
1035
1036 /*
1037  * edit the current floor
1038  */
1039 void edit_floor(void) {
1040         char buf[SIZ];
1041         int expire_mode = 0;
1042         int expire_value = 0;
1043
1044         if (floorlist[(int)curr_floor][0]==0) load_floorlist();
1045
1046         /* Fetch the expire policy (this will silently fail on old servers,
1047          * resulting in "default" policy)
1048          */
1049         serv_puts("GPEX floor");
1050         serv_gets(buf);
1051         if (buf[0]=='2') {
1052                 expire_mode = extract_int(&buf[4], 0);
1053                 expire_value = extract_int(&buf[4], 1);
1054                 }
1055
1056         /* Interact with the user */
1057         strprompt("Floor name",&floorlist[(int)curr_floor][0],255);
1058
1059         /* Angels and demons dancing in my head... */
1060         do {
1061                 sprintf(buf, "%d", expire_mode);
1062                 strprompt("Floor default essage expire policy (? for list)",
1063                         buf, 1);
1064                 if (buf[0] == '?') {
1065                         printf("\n");
1066                         printf("0. Use the system default\n");
1067                         printf("1. Never automatically expire messages\n");
1068                         printf("2. Expire by message count\n");
1069                         printf("3. Expire by message age\n");
1070                         }
1071                 } while((buf[0]<48)||(buf[0]>51));
1072         expire_mode = buf[0] - 48;
1073
1074         /* ...lunatics and monsters underneath my bed */
1075         if (expire_mode == 2) {
1076                 sprintf(buf, "%d", expire_value);
1077                 strprompt("Keep how many messages online?", buf, 10);
1078                 expire_value = atol(buf);
1079                 }
1080
1081         if (expire_mode == 3) {
1082                 sprintf(buf, "%d", expire_value);
1083                 strprompt("Keep messages for how many days?", buf, 10);
1084                 expire_value = atol(buf);
1085                 }
1086
1087         /* Save it */
1088         snprintf(buf, sizeof buf, "SPEX floor|%d|%d",
1089                 expire_mode, expire_value);
1090         serv_puts(buf);
1091         serv_gets(buf);
1092
1093         snprintf(buf,sizeof buf,"EFLR %d|%s",curr_floor,
1094                  &floorlist[(int)curr_floor][0]);
1095         serv_puts(buf);
1096         serv_gets(buf);
1097         printf("%s\n",&buf[4]);
1098         load_floorlist();
1099         }
1100
1101
1102
1103
1104 /*
1105  * kill the current floor 
1106  */
1107 void kill_floor(void) {
1108         int floornum_to_delete,a;
1109         char buf[SIZ];
1110
1111         if (floorlist[(int)curr_floor][0]==0) load_floorlist();
1112         do {
1113                 floornum_to_delete = (-1);
1114                 printf("(Press return to abort)\n");
1115                 newprompt("Delete which floor? ",buf,255);
1116                 if (strlen(buf)==0) return;
1117                 for (a=0; a<128; ++a)
1118                         if (!strcasecmp(&floorlist[a][0],buf))
1119                                 floornum_to_delete = a;
1120                 if (floornum_to_delete < 0) {
1121                         printf("No such floor.  Select one of:\n");
1122                         for (a=0; a<128; ++a)
1123                                 if (floorlist[a][0]!=0)
1124                                         printf("%s\n",&floorlist[a][0]);
1125                         }
1126                 } while (floornum_to_delete < 0);
1127         sprintf(buf,"KFLR %d|1",floornum_to_delete);
1128         serv_puts(buf);
1129         serv_gets(buf);
1130         printf("%s\n",&buf[4]);
1131         }