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