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