1 /* Citadel/UX room-oriented routines */
10 #include <sys/types.h>
17 #define IFEXPERT if (userflags&US_EXPERT)
18 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
19 #define IFAIDE if (axlevel>=6)
20 #define IFNAIDE if (axlevel<6)
23 void sttybbs(int cmd);
24 void extract(char *dest, char *source, int parmnum);
25 int extract_int(char *source, int parmnum);
26 void hit_any_key(void);
29 void strprompt(char *prompt, char *str, int len);
30 void newprompt(char *prompt, char *str, int len);
31 int struncmp(char *lstr, char *rstr, int len);
32 void dotgoto(char *towhere, int display_name);
33 long extract_long(char *source, int parmnum);
34 void serv_read(char *buf, int bytes);
35 void formout(char *name);
37 int fmout(int width, FILE *fp, char pagin, int height, int starting_lp, char subst);
38 void citedit(FILE *fp, long int base_pos);
39 void progress(long int curr, long int cmax);
40 int pattern(char *search, char *patn);
41 int file_checksum(char *filename);
42 int nukedir(char *dirname);
43 void color(int colornum);
45 extern unsigned room_flags;
46 extern char room_name[];
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[];
55 extern char sigcaught;
56 extern char floor_mode;
57 extern char curr_floor;
64 extern char floorlist[128][256];
67 void load_floorlist(void) {
71 for (a=0; a<128; ++a) floorlist[a][0] = 0;
76 strcpy(floorlist[0],"Main Floor");
79 while (serv_gets(buf), strcmp(buf,"000")) {
80 extract(floorlist[extract_int(buf,0)],buf,1);
84 void listrms(char *variety)
92 if (buf[0]!='1') return;
96 while (serv_gets(buf), strcmp(buf,"000")) if (sigcaught==0) {
97 extract(rmname,buf,0);
98 if ((c + strlen(rmname) + 4) > screenwidth) {
102 f = extract_int(buf,1);
103 if (f & QR_PRIVATE) {
110 if ((f & QR_DIRECTORY) && (f & QR_NETWORK)) printf("} ");
111 else if (f & QR_DIRECTORY) printf("] ");
112 else if (f & QR_NETWORK) printf(") ");
114 c = c + strlen(rmname) + 3;
121 void list_other_floors(void) {
125 for (a=0; a<128; ++a) if ((strlen(floorlist[a])>0)&&(a!=curr_floor)) {
126 if ((c + strlen(floorlist[a]) + 4) > screenwidth) {
130 printf("%s: ",floorlist[a]);
131 c = c + strlen(floorlist[a]) + 3;
137 * List known rooms. kn_floor_mode should be set to 0 for a 'flat' listing,
138 * 1 to list rooms on the current floor, or 1 to list rooms on all floors.
140 void knrooms(int kn_floor_mode)
147 if (kn_floor_mode == 0) {
149 printf("\n Rooms with unread messages:\n");
152 printf("\n\n No unseen messages in:\n");
157 if (kn_floor_mode == 1) {
159 printf("\n Rooms with unread messages on %s:\n",
160 floorlist[(int)curr_floor]);
161 sprintf(buf,"LKRN %d",curr_floor);
164 printf("\n\n Rooms with no new messages on %s:\n",
165 floorlist[(int)curr_floor]);
166 sprintf(buf,"LKRO %d",curr_floor);
169 printf("\n\n Other floors:\n");
174 if (kn_floor_mode == 2) {
175 for (a=0; a<128; ++a) if (floorlist[a][0]!=0) {
177 printf("\n Rooms on %s:\n",floorlist[a]);
178 sprintf(buf,"LKRA %d",a);
185 IFNEXPERT hit_any_key();
189 void listzrooms(void) { /* list public forgotten rooms */
191 printf("\n Forgotten public rooms:\n");
195 IFNEXPERT hit_any_key();
199 int set_room_attr(int ibuf, char *prompt, unsigned int sbit)
203 printf("%s [%s]? ",prompt,((ibuf&sbit) ? "Yes":"No"));
204 a=yesno_d(ibuf&sbit);
206 if (!a) ibuf=(ibuf^sbit);
213 * Select a floor (used in several commands)
214 * The supplied argument is the 'default' floor number.
215 * This function returns the selected floor number.
217 int select_floor(int rfloor)
222 if (floor_mode == 1) {
223 if (floorlist[(int)curr_floor][0]==0) load_floorlist();
227 strcpy(floorstr,&floorlist[rfloor][0]);
228 strprompt("Which floor",floorstr,256);
229 for (a=0; a<128; ++a) {
230 if (!strucmp(floorstr,&floorlist[a][0]))
232 if ((newfloor<0)&&(!struncmp(floorstr,
233 &floorlist[a][0],strlen(floorstr))))
235 if ((newfloor<0)&&(pattern(&floorlist[a][0],
236 floorstr)>=0)) newfloor = a;
239 printf("\n One of:\n");
240 for (a=0; a<128; ++a)
241 if (floorlist[a][0]!=0)
245 } while(newfloor < 0);
255 * .<A>ide <E>dit room
257 void editthisroom(void) {
258 char rname[ROOMNAMELEN];
270 printf("%s\n",&buf[4]);
274 extract(rname,&buf[4],0);
275 extract(rpass,&buf[4],1);
276 extract(rdir, &buf[4],2);
277 rflags = extract_int(&buf[4],3);
278 rfloor = extract_int(&buf[4],4);
283 if (buf[0]=='2') strcpy(raide,&buf[4]);
284 else strcpy(raide,"");
285 if (strlen(raide)==0) strcpy(raide,"none");
287 strprompt("Room name",rname,ROOMNAMELEN-1);
289 rfloor = select_floor(rfloor);
290 rflags = set_room_attr(rflags,"Private room",QR_PRIVATE);
291 if (rflags & QR_PRIVATE)
292 rflags = set_room_attr(rflags,
293 "Accessible by guessing room name",QR_GUESSNAME);
295 /* if it's public, clear the privacy classes */
296 if ((rflags & QR_PRIVATE)==0) {
297 if (rflags & QR_GUESSNAME) rflags = rflags - QR_GUESSNAME;
298 if (rflags & QR_PASSWORDED) rflags = rflags - QR_PASSWORDED;
301 /* if it's private, choose the privacy classes */
302 if ( (rflags & QR_PRIVATE)
303 && ( (rflags & QR_GUESSNAME) == 0) ) {
304 rflags = set_room_attr(rflags,
305 "Accessible by entering a password",QR_PASSWORDED);
307 if ( (rflags & QR_PRIVATE)
308 && ((rflags&QR_PASSWORDED)==QR_PASSWORDED) ) {
309 strprompt("Room password",rpass,9);
312 if ((rflags&QR_PRIVATE)==QR_PRIVATE) {
313 printf("Cause current users to forget room [No] ? ");
314 if (yesno_d(0)==1) rbump = 1;
317 rflags = set_room_attr(rflags,"Preferred users only",QR_PREFONLY);
318 rflags = set_room_attr(rflags,"Read-only room",QR_READONLY);
319 rflags = set_room_attr(rflags,"Directory room",QR_DIRECTORY);
320 rflags = set_room_attr(rflags,"Permanent room",QR_PERMANENT);
321 if (rflags & QR_DIRECTORY) {
322 strprompt("Directory name",rdir,14);
323 rflags = set_room_attr(rflags,"Uploading allowed",QR_UPLOAD);
324 rflags = set_room_attr(rflags,"Downloading allowed",
326 rflags = set_room_attr(rflags,"Visible directory",QR_VISDIR);
328 rflags = set_room_attr(rflags,"Network shared room",QR_NETWORK);
329 rflags = set_room_attr(rflags,
330 "Automatically make all messages anonymous",QR_ANONONLY);
331 if ( (rflags & QR_ANONONLY) == 0) {
332 rflags = set_room_attr(rflags,
333 "Ask users whether to make messages anonymous",
338 strprompt("Room aide (or 'none')",raide,29);
339 if (!strucmp(raide,"none")) {
344 sprintf(buf,"QUSR %s",raide);
347 if (buf[0]!='2') printf("%s\n",&buf[4]);
349 } while(buf[0]!='2');
351 if (!strucmp(raide,"none")) strcpy(raide,"");
353 printf("Save changes (y/n)? ");
355 sprintf(buf,"SETR %s|%s|%s|%d|%d|%d",
356 rname,rpass,rdir,rflags,rbump,rfloor);
359 printf("%s\n",&buf[4]);
360 sprintf(buf,"SETA %s",raide);
363 if (buf[0]=='2') dotgoto(rname,2);
369 * un-goto the previous room
374 if (!strcmp(ugname,"")) return;
375 sprintf(buf,"GOTO %s",ugname);
379 printf("%s\n",&buf[4]);
382 sprintf(buf,"SLRP %ld",uglsn);
385 if (buf[0]!='2') printf("%s\n",&buf[4]);
393 * download() - download a file or files. The argument passed to this
394 * function determines which protocol to use.
396 void download(int proto)
400 - 0 = paginate, 1 = xmodem, 2 = raw, 3 = ymodem, 4 = zmodem, 5 = save
407 long total_bytes = 0L;
408 long transmitted_bytes = 0L;
417 if ((room_flags & QR_DOWNLOAD) == 0) {
418 printf("*** You cannot download from this room.\n");
422 newprompt("Enter filename: ",filename,255);
424 sprintf(buf,"OPEN %s",filename);
428 printf("%s\n",&buf[4]);
431 total_bytes = extract_long(&buf[4],0);
434 /* Here's the code for simply transferring the file to the client,
435 * for folks who have their own clientware. It's a lot simpler than
436 * the [XYZ]modem code below...
439 printf("Enter the name of the directory to save '%s'\n",
441 printf("to, or press return for the current directory.\n");
442 newprompt("Directory: ",dbuf,256);
443 if (strlen(dbuf)==0) strcpy(dbuf,".");
445 strcat(dbuf,filename);
447 savefp = fopen(dbuf,"w");
448 if (savefp == NULL) {
449 printf("Cannot open '%s': %s\n",dbuf,strerror(errno));
450 /* close the download file at the server */
454 printf("%s\n",&buf[4]);
458 progress(0,total_bytes);
459 while ( (transmitted_bytes < total_bytes) && (broken == 0) ) {
460 bb = total_bytes - transmitted_bytes;
461 aa = ((bb < 4096) ? bb : 4096);
462 sprintf(buf,"READ %ld|%ld",transmitted_bytes,aa);
466 printf("%s\n",&buf[4]);
469 packet = extract_int(&buf[4],0);
470 serv_read(dbuf,packet);
471 if (fwrite(dbuf,packet,1,savefp) < 1) broken = 1;
472 transmitted_bytes = transmitted_bytes + (long)packet;
473 progress(transmitted_bytes,total_bytes);
476 /* close the download file at the server */
480 printf("%s\n",&buf[4]);
487 sprintf(buf,"%s/%s",tempdir,filename);
490 /* We do the remainder of this function as a separate process in
491 * order to allow recovery if the transfer is aborted. If the
492 * file transfer program aborts, the first child process receives a
493 * "broken pipe" signal and aborts. We *should* be able to catch
494 * this condition with signal(), but it doesn't seem to work on all
499 /* wait for the download to finish */
500 while (wait(&b)!=a) ;;
502 /* close the download file at the server */
506 printf("%s\n",&buf[4]);
508 /* clean up the temporary directory */
513 sprintf(buf,"%s/%s",tempdir,filename); /* full pathname */
515 /* The next fork() creates a second child process that is used for
516 * the actual file transfer program (usually sz).
519 if (proto_pid == 0) {
522 signal(SIGINT,SIG_DFL);
523 signal(SIGQUIT,SIG_DFL);
524 sprintf(dbuf,"SHELL=/dev/null; export SHELL; TERM=dumb; export TERM; exec more -d <%s",buf);
530 signal(SIGINT,SIG_DFL);
531 signal(SIGQUIT,SIG_DFL);
532 if (proto==1) execlp("sx","sx",buf,NULL);
533 if (proto==2) execlp("cat","cat",buf,NULL);
534 if (proto==3) execlp("sb","sb",buf,NULL);
535 if (proto==4) execlp("sz","sz",buf,NULL);
536 execlp("cat","cat",buf,NULL);
540 tpipe = fopen(buf,"w");
542 while ( (transmitted_bytes < total_bytes) && (broken == 0) ) {
543 bb = total_bytes - transmitted_bytes;
544 aa = ((bb < 4096) ? bb : 4096);
545 sprintf(buf,"READ %ld|%ld",transmitted_bytes,aa);
549 printf("%s\n",&buf[4]);
552 packet = extract_int(&buf[4],0);
553 serv_read(dbuf,packet);
554 if (fwrite(dbuf,packet,1,tpipe) < 1) broken = 1;
555 transmitted_bytes = transmitted_bytes + (long)packet;
557 if (tpipe!=NULL) fclose(tpipe);
559 /* Hang out and wait for the file transfer program to finish */
560 while (wait(&a) != proto_pid) ;;
564 exit(0); /* transfer control back to the main program */
569 * read directory of this room
580 printf("%s\n",&buf[4]);
584 extract(comment,&buf[4],0);
585 extract(flnm,&buf[4],1);
586 printf("\nDirectory of %s on %s\n",flnm,comment);
587 printf("-----------------------\n");
588 while (serv_gets(buf), strcmp(buf,"000")) {
591 extract(comment,buf,2);
592 if (strlen(flnm)<=14)
593 printf("%-14s %8s %s\n",flnm,flsz,comment);
595 printf("%s\n%14s %8s %s\n",flnm,"",flsz,comment);
601 * add a user to a private room
604 char aaa[31],bbb[256];
606 if ((room_flags & QR_PRIVATE)==0) {
607 printf("This is not a private room.\n");
611 newprompt("Name of user? ",aaa,30);
612 if (aaa[0]==0) return;
614 sprintf(bbb,"INVT %s",aaa);
617 printf("%s\n",&bbb[4]);
622 * kick a user out of a room
625 char aaa[31],bbb[256];
627 if ((room_flags & QR_PRIVATE)==0) {
628 printf("Note: this is not a private room. Kicking a user ");
629 printf("out of this room will only\nhave the same effect ");
630 printf("as if they <Z>apped the room.\n\n");
633 newprompt("Name of user? ",aaa,30);
634 if (aaa[0]==0) return;
636 sprintf(bbb,"KICK %s",aaa);
639 printf("%s\n",&bbb[4]);
644 * aide command: kill the current room
646 void killroom(void) {
652 printf("%s\n",&aaa[4]);
656 printf("Are you sure you want to kill this room? ");
657 if (yesno()==0) return;
661 printf("%s\n",&aaa[4]);
662 if (aaa[0]!='2') return;
663 dotgoto("_BASEROOM_",0);
666 void forget(void) { /* forget the current room */
669 printf("Are you sure you want to forget this room? ");
670 if (yesno()==0) return;
675 printf("%s\n",&cmd[4]);
679 /* now return to the lobby */
680 dotgoto("_BASEROOM_",0);
689 char new_room_name[ROOMNAMELEN];
691 char new_room_pass[10];
699 printf("%s\n",&cmd[4]);
703 newprompt("Name for new room? ",new_room_name,ROOMNAMELEN-1);
704 if (strlen(new_room_name)==0) return;
705 for (a=0; a<strlen(new_room_name); ++a)
706 if (new_room_name[a] == '|') new_room_name[a]='_';
708 new_room_floor = select_floor((int)curr_floor);
710 IFNEXPERT formout("roomaccess");
712 printf("<?>Help\n<1>Public room\n<2>Guess-name room\n");
713 printf("<3>Passworded room\n<4>Invitation-only room\n");
714 printf("Enter room type: ");
717 } while (((b<'1')||(b>'4')) && (b!='?'));
720 formout("roomaccess");
722 } while ((b<'1')||(b>'4'));
725 new_room_type = b - 1;
726 if (new_room_type==2) {
727 newprompt("Enter a room password: ",new_room_pass,9);
728 for (a=0; a<strlen(new_room_pass); ++a)
729 if (new_room_pass[a] == '|') new_room_pass[a]='_';
731 else strcpy(new_room_pass,"");
733 printf("\042%s\042, a",new_room_name);
734 if (b==1) printf(" public room.");
735 if (b==2) printf(" guess-name room.");
736 if (b==3) printf(" passworded room, password: %s",new_room_pass);
737 if (b==4) printf("n invitation-only room.");
738 printf("\nInstall it? (y/n) : ");
742 sprintf(cmd, "CRE8 1|%s|%d|%s|%d", new_room_name,
743 new_room_type, new_room_pass, new_room_floor);
747 printf("%s\n",&cmd[4]);
751 /* command succeeded... now GO to the new room! */
752 dotgoto(new_room_name,0);
757 void readinfo(void) { /* read info file for current room */
764 if (cmd[0]!='1') return;
766 fmout(screenwidth,NULL,
767 ((userflags & US_PAGINATOR) ? 1 : 0),
773 * <W>ho knows room...
775 void whoknows(void) {
780 printf("%s\n",&buf[5]);
784 sttybbs(SB_YES_INTR);
785 while (serv_gets(buf), strncmp(buf,"000",3)) {
786 if (sigcaught==0) printf("%s\n",buf);
792 void do_edit(char *desc, char *read_cmd, char *check_cmd, char *write_cmd)
796 int b,cksum,editor_exit;
799 if (strlen(editor_path)==0) {
800 printf("Do you wish to re-enter %s? ",desc);
801 if (yesno()==0) return;
804 fp = fopen(temp,"w");
807 serv_puts(check_cmd);
810 printf("%s\n",&cmd[4]);
814 if (strlen(editor_path)>0) {
818 fp = fopen(temp,"w");
819 while (serv_gets(cmd), strcmp(cmd,"000")) {
820 fprintf(fp,"%s\n",cmd);
826 cksum = file_checksum(temp);
828 if (strlen(editor_path)>0) {
833 execlp(editor_path,editor_path,temp,NULL);
836 if (editor_pid>0) do {
838 b=wait(&editor_exit);
839 } while((b!=editor_pid)&&(b>=0));
841 printf("Executed %s\n", editor_path);
845 printf("Entering %s. ",desc);
846 printf("Press return twice when finished.\n");
852 if (file_checksum(temp) == cksum) {
853 printf("*** Aborted.\n");
857 serv_puts(write_cmd);
860 printf("%s\n",&cmd[4]);
865 while (fgets(cmd,255,fp)!=NULL) {
866 cmd[strlen(cmd)-1] = 0;
877 void enterinfo(void) { /* edit info file for current room */
878 do_edit("the Info file for this room","RINF","EINF 0","EINF 1");
881 void enter_bio(void) {
883 sprintf(cmd,"RBIO %s",fullname);
884 do_edit("your Bio",cmd,"NOOP","EBIO");
890 void create_floor(void) {
892 char newfloorname[256];
894 serv_puts("CFLR xx|0");
897 printf("%s\n",&buf[4]);
901 newprompt("Name for new floor: ",newfloorname,255);
902 sprintf(buf,"CFLR %s|1",newfloorname);
906 printf("Floor has been created.\n");
909 printf("%s\n",&buf[4]);
914 * edit the current floor
916 void edit_floor(void) {
919 if (floorlist[(int)curr_floor][0]==0) load_floorlist();
920 strprompt("New floor name",&floorlist[(int)curr_floor][0],255);
921 sprintf(buf,"EFLR %d|%s",curr_floor,&floorlist[(int)curr_floor][0]);
924 printf("%s\n",&buf[4]);
932 * kill the current floor
934 void kill_floor(void) {
935 int floornum_to_delete,a;
938 if (floorlist[(int)curr_floor][0]==0) load_floorlist();
940 floornum_to_delete = (-1);
941 printf("(Press return to abort)\n");
942 newprompt("Delete which floor? ",buf,255);
943 if (strlen(buf)==0) return;
944 for (a=0; a<128; ++a)
945 if (!strucmp(&floorlist[a][0],buf))
946 floornum_to_delete = a;
947 if (floornum_to_delete < 0) {
948 printf("No such floor. Select one of:\n");
949 for (a=0; a<128; ++a)
950 if (floorlist[a][0]!=0)
951 printf("%s\n",&floorlist[a][0]);
953 } while (floornum_to_delete < 0);
954 sprintf(buf,"KFLR %d|1",floornum_to_delete);
957 printf("%s\n",&buf[4]);