5 * Client-side functions which perform room operations
17 #include <sys/types.h>
22 #include <libcitadel.h>
24 #include "citadel_ipc.h"
25 #include "citadel_decls.h"
33 #include "citadel_dirs.h"
35 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
38 void stty_ctdl(int cmd
);
39 void dotgoto(CtdlIPC
*ipc
, char *towhere
, int display_name
, int fromungoto
);
40 void progress(CtdlIPC
* ipc
, unsigned long curr
, unsigned long cmax
);
41 int pattern(char *search
, char *patn
);
42 int file_checksum(char *filename
);
43 int nukedir(char *dirname
);
45 extern unsigned room_flags
;
46 extern char room_name
[];
48 extern char tempdir
[];
49 extern int editor_pid
;
50 extern int screenwidth
;
51 extern int screenheight
;
52 extern char fullname
[];
53 extern char sigcaught
;
54 extern char floor_mode
;
55 extern char curr_floor
;
60 extern char *uglist
[];
61 extern long uglistlsn
[];
62 extern int uglistsize
;
64 extern char floorlist
[128][SIZ
];
67 void load_floorlist(CtdlIPC
*ipc
)
72 int r
; /* IPC response code */
74 for (a
= 0; a
< 128; ++a
)
77 r
= CtdlIPCFloorListing(ipc
, &listing
, buf
);
79 strcpy(floorlist
[0], "Main Floor");
82 while (*listing
&& !IsEmptyStr(listing
)) {
83 extract_token(buf
, listing
, 0, '\n', sizeof buf
);
84 remove_token(listing
, 0, '\n');
85 extract_token(floorlist
[extract_int(buf
, 0)], buf
, 1, '|', SIZ
);
91 void room_tree_list(struct ctdlroomlisting
*rp
)
94 char rmname
[ROOMNAMELEN
];
102 if (rp
->lnext
!= NULL
) {
103 room_tree_list(rp
->lnext
);
106 if (sigcaught
== 0) {
107 strcpy(rmname
, rp
->rlname
);
109 if ((c
+ strlen(rmname
) + 4) > screenwidth
) {
111 /* line break, check the paginator */
115 if (f
& QR_MAILBOX
) {
116 color(BRIGHT_YELLOW
);
117 } else if (f
& QR_PRIVATE
) {
122 pprintf("%s", rmname
);
123 if ((f
& QR_DIRECTORY
) && (f
& QR_NETWORK
))
125 else if (f
& QR_DIRECTORY
)
127 else if (f
& QR_NETWORK
)
131 c
= c
+ strlen(rmname
) + 3;
134 if (rp
->rnext
!= NULL
) {
135 room_tree_list(rp
->rnext
);
143 * Room ordering stuff (compare first by floor, then by order)
145 int rordercmp(struct ctdlroomlisting
*r1
, struct ctdlroomlisting
*r2
)
147 if ((r1
== NULL
) && (r2
== NULL
))
153 if (r1
->rlfloor
< r2
->rlfloor
)
155 if (r1
->rlfloor
> r2
->rlfloor
)
157 if (r1
->rlorder
< r2
->rlorder
)
159 if (r1
->rlorder
> r2
->rlorder
)
166 * Common code for all room listings
168 static void listrms(struct march
*listing
, int new_only
, int floor_only
, unsigned int flags
, char *match
)
171 struct ctdlroomlisting
*rl
= NULL
;
172 struct ctdlroomlisting
*rp
;
173 struct ctdlroomlisting
*rs
;
176 for (mptr
= listing
; mptr
!= NULL
; mptr
= mptr
->next
) {
179 if ( (new_only
== LISTRMS_NEW_ONLY
)
180 && ((mptr
->march_access
& UA_HASNEWMSGS
) == 0))
183 if ( (new_only
== LISTRMS_OLD_ONLY
)
184 && ((mptr
->march_access
& UA_HASNEWMSGS
) != 0))
187 if ( (floor_only
>= 0)
188 && (mptr
->march_floor
!= floor_only
))
191 if (flags
&& (mptr
->march_flags
& flags
) == 0)
194 if (match
&& (pattern(mptr
->march_name
, match
) == -1))
198 rp
= malloc(sizeof(struct ctdlroomlisting
));
199 strncpy(rp
->rlname
, mptr
->march_name
, ROOMNAMELEN
);
200 rp
->rlflags
= mptr
->march_flags
;
201 rp
->rlfloor
= mptr
->march_floor
;
202 rp
->rlorder
= mptr
->march_order
;
211 if (rordercmp(rp
, rs
) < 0) {
212 if (rs
->lnext
== NULL
) {
219 if (rs
->rnext
== NULL
) {
231 room_tree_list(NULL
);
237 void list_other_floors(void)
242 for (a
= 0; a
< 128; ++a
) {
243 if ((strlen(floorlist
[a
]) > 0) && (a
!= curr_floor
)) {
244 if ((c
+ strlen(floorlist
[a
]) + 4) > screenwidth
) {
248 pprintf("%s: ", floorlist
[a
]);
249 c
= c
+ strlen(floorlist
[a
]) + 3;
256 * List known rooms. kn_floor_mode should be set to 0 for a 'flat' listing,
257 * 1 to list rooms on the current floor, or 2 to list rooms on all floors.
259 void knrooms(CtdlIPC
*ipc
, int kn_floor_mode
)
262 struct march
*listing
= NULL
;
264 int r
; /* IPC response code */
268 /* Ask the server for a room list */
269 r
= CtdlIPCKnownRooms(ipc
, SubscribedRooms
, (-1), &listing
, buf
);
277 if (kn_floor_mode
== 0) {
279 pprintf("\n Rooms with unread messages:\n");
280 listrms(listing
, LISTRMS_NEW_ONLY
, -1, 0, NULL
);
282 pprintf("\n\n No unseen messages in:\n");
283 listrms(listing
, LISTRMS_OLD_ONLY
, -1, 0, NULL
);
287 if (kn_floor_mode
== 1) {
289 pprintf("\n Rooms with unread messages on %s:\n",
290 floorlist
[(int) curr_floor
]);
291 listrms(listing
, LISTRMS_NEW_ONLY
, curr_floor
, 0, NULL
);
293 pprintf("\n\n Rooms with no new messages on %s:\n",
294 floorlist
[(int) curr_floor
]);
295 listrms(listing
, LISTRMS_OLD_ONLY
, curr_floor
, 0, NULL
);
297 pprintf("\n\n Other floors:\n");
302 if (kn_floor_mode
== 2) {
303 for (a
= 0; a
< 128; ++a
) {
304 if (floorlist
[a
][0] != 0) {
306 pprintf("\n Rooms on %s:\n",
308 listrms(listing
, LISTRMS_ALL
, a
, 0, NULL
);
314 /* Free the room list */
316 mptr
= listing
->next
;
322 IFNEXPERT
hit_any_key(ipc
);
326 void listzrooms(CtdlIPC
*ipc
)
327 { /* list public forgotten rooms */
328 struct march
*listing
= NULL
;
330 int r
; /* IPC response code */
334 /* Ask the server for a room list */
335 r
= CtdlIPCKnownRooms(ipc
, UnsubscribedRooms
, (-1), &listing
, buf
);
341 pprintf("\n Forgotten public rooms:\n");
342 listrms(listing
, LISTRMS_ALL
, -1, 0, NULL
);
345 /* Free the room list */
347 mptr
= listing
->next
;
353 IFNEXPERT
hit_any_key(ipc
);
356 void dotknown(CtdlIPC
*ipc
, int what
, char *match
)
357 { /* list rooms according to attribute */
358 struct march
*listing
= NULL
;
360 int r
; /* IPC response code */
363 /* Ask the server for a room list */
364 r
= CtdlIPCKnownRooms(ipc
, AllAccessibleRooms
, (-1), &listing
, buf
);
373 pprintf("\n Anonymous rooms:\n");
374 listrms(listing
, LISTRMS_ALL
, -1, QR_ANONONLY
|QR_ANONOPT
, NULL
);
378 pprintf("\n Directory rooms:\n");
379 listrms(listing
, LISTRMS_ALL
, -1, QR_DIRECTORY
, NULL
);
383 pprintf("\n Matching \"%s\" rooms:\n", match
);
384 listrms(listing
, LISTRMS_ALL
, -1, 0, match
);
388 pprintf("\n Preferred only rooms:\n");
389 listrms(listing
, LISTRMS_ALL
, -1, QR_PREFONLY
, NULL
);
393 pprintf("\n Private rooms:\n");
394 listrms(listing
, LISTRMS_ALL
, -1, QR_PRIVATE
, NULL
);
398 pprintf("\n Read only rooms:\n");
399 listrms(listing
, LISTRMS_ALL
, -1, QR_READONLY
, NULL
);
403 pprintf("\n Shared rooms:\n");
404 listrms(listing
, LISTRMS_ALL
, -1, QR_NETWORK
, NULL
);
409 /* Free the room list */
411 mptr
= listing
->next
;
417 IFNEXPERT
hit_any_key(ipc
);
421 int set_room_attr(CtdlIPC
*ipc
, unsigned int ibuf
, char *prompt
, unsigned int sbit
)
425 a
= boolprompt(prompt
, (ibuf
& sbit
));
426 ibuf
= (ibuf
| sbit
);
428 ibuf
= (ibuf
^ sbit
);
436 * Select a floor (used in several commands)
437 * The supplied argument is the 'default' floor number.
438 * This function returns the selected floor number.
440 int select_floor(CtdlIPC
*ipc
, int rfloor
)
445 if (floor_mode
== 1) {
446 if (floorlist
[(int) curr_floor
][0] == 0) {
452 safestrncpy(floorstr
, floorlist
[rfloor
],
454 strprompt("Which floor", floorstr
, 255);
455 for (a
= 0; a
< 128; ++a
) {
457 (floorstr
, &floorlist
[a
][0]))
462 (floorstr
, &floorlist
[a
][0],
466 && (pattern(&floorlist
[a
][0], floorstr
)
471 scr_printf("\n One of:\n");
472 for (a
= 0; a
< 128; ++a
) {
473 if (floorlist
[a
][0] != 0) {
479 } while (newfloor
< 0);
484 scr_printf("Floor selection bypassed because you have "
485 "floor mode disabled.\n");
495 * .<A>ide <E>dit room
497 void editthisroom(CtdlIPC
*ipc
)
500 char raide
[USERNAME_SIZE
];
502 struct ctdlroom
*attr
= NULL
;
503 struct ExpirePolicy
*eptr
= NULL
;
504 int r
; /* IPC response code */
506 /* Fetch the existing room config */
507 r
= CtdlIPCGetRoomAttributes(ipc
, &attr
, buf
);
509 scr_printf("%s\n", buf
);
512 eptr
= &(attr
->QRep
);
514 /* Fetch the name of the current room aide */
515 r
= CtdlIPCGetRoomAide(ipc
, buf
);
517 safestrncpy(raide
, buf
, sizeof raide
);
521 if (IsEmptyStr(raide
)) {
522 strcpy(raide
, "none");
525 /* Fetch the expire policy (this will silently fail on old servers,
526 * resulting in "default" policy)
528 r
= CtdlIPCGetMessageExpirationPolicy(ipc
, 0, &eptr
, buf
);
530 /* Now interact with the user. */
532 strprompt("Room name", attr
->QRname
, ROOMNAMELEN
-1);
533 attr
->QRfloor
= select_floor(ipc
, attr
->QRfloor
);
534 attr
->QRflags
= set_room_attr(ipc
, attr
->QRflags
, "Private room", QR_PRIVATE
);
535 if (attr
->QRflags
& QR_PRIVATE
) {
536 attr
->QRflags
= set_room_attr(ipc
, attr
->QRflags
,
537 "Accessible by guessing room name",
541 /* if it's public, clear the privacy classes */
542 if ((attr
->QRflags
& QR_PRIVATE
) == 0) {
543 if (attr
->QRflags
& QR_GUESSNAME
) {
544 attr
->QRflags
= attr
->QRflags
- QR_GUESSNAME
;
546 if (attr
->QRflags
& QR_PASSWORDED
) {
547 attr
->QRflags
= attr
->QRflags
- QR_PASSWORDED
;
551 /* if it's private, choose the privacy classes */
552 if ((attr
->QRflags
& QR_PRIVATE
)
553 && ((attr
->QRflags
& QR_GUESSNAME
) == 0)) {
554 attr
->QRflags
= set_room_attr(ipc
, attr
->QRflags
,
555 "Accessible by entering a password",
558 if ((attr
->QRflags
& QR_PRIVATE
)
559 && ((attr
->QRflags
& QR_PASSWORDED
) == QR_PASSWORDED
)) {
560 strprompt("Room password", attr
->QRpasswd
, 9);
563 if ((attr
->QRflags
& QR_PRIVATE
) == QR_PRIVATE
) {
564 rbump
= boolprompt("Cause current users to forget room", 0);
567 attr
->QRflags
= set_room_attr(ipc
, attr
->QRflags
,
568 "Preferred users only", QR_PREFONLY
);
569 attr
->QRflags
= set_room_attr(ipc
, attr
->QRflags
,
570 "Read-only room", QR_READONLY
);
571 attr
->QRflags2
= set_room_attr(ipc
, attr
->QRflags2
,
572 "Allow message deletion by anyone who can post",
574 attr
->QRflags
= set_room_attr(ipc
, attr
->QRflags
,
575 "Permanent room", QR_PERMANENT
);
576 attr
->QRflags2
= set_room_attr(ipc
, attr
->QRflags2
,
577 "Subject Required (Force "
578 "users to specify a message "
579 "subject)", QR2_SUBJECTREQ
);
580 attr
->QRflags
= set_room_attr(ipc
, attr
->QRflags
,
581 "Directory room", QR_DIRECTORY
);
582 if (attr
->QRflags
& QR_DIRECTORY
) {
583 strprompt("Directory name", attr
->QRdirname
, 14);
585 set_room_attr(ipc
, attr
->QRflags
,
586 "Uploading allowed", QR_UPLOAD
);
588 set_room_attr(ipc
, attr
->QRflags
, "Downloading allowed",
591 set_room_attr(ipc
, attr
->QRflags
,
592 "Visible directory", QR_VISDIR
);
594 attr
->QRflags
= set_room_attr(ipc
, attr
->QRflags
,
595 "Network shared room", QR_NETWORK
);
596 attr
->QRflags2
= set_room_attr(ipc
, attr
->QRflags2
,
597 "Self-service list subscribe/unsubscribe",
599 attr
->QRflags2
= set_room_attr(ipc
, attr
->QRflags2
,
600 "public posting to this room via room_roomname@yourcitadel.org",
602 attr
->QRflags2
= set_room_attr(ipc
, attr
->QRflags2
,
603 "moderated mailinglist",
605 attr
->QRflags
= set_room_attr(ipc
, attr
->QRflags
,
606 "Automatically make all messages anonymous",
608 if ((attr
->QRflags
& QR_ANONONLY
) == 0) {
609 attr
->QRflags
= set_room_attr(ipc
, attr
->QRflags
,
610 "Ask users whether to make messages anonymous",
613 attr
->QRorder
= intprompt("Listing order", attr
->QRorder
, 0, 127);
615 /* Ask about the room aide */
617 strprompt("Room aide (or 'none')", raide
, 29);
618 if (!strcasecmp(raide
, "none")) {
622 r
= CtdlIPCQueryUsername(ipc
, raide
, buf
);
624 scr_printf("%s\n", buf
);
626 } while (r
/ 100 != 2);
628 /* Angels and demons dancing in my head... */
630 snprintf(buf
, sizeof buf
, "%d", attr
->QRep
.expire_mode
);
631 strprompt("Message expire policy (? for list)", buf
, 1);
634 "0. Use the default for this floor\n"
635 "1. Never automatically expire messages\n"
636 "2. Expire by message count\n"
637 "3. Expire by message age\n");
639 } while ((buf
[0] < 48) || (buf
[0] > 51));
640 attr
->QRep
.expire_mode
= buf
[0] - 48;
642 /* ...lunatics and monsters underneath my bed */
643 if (attr
->QRep
.expire_mode
== 2) {
644 snprintf(buf
, sizeof buf
, "%d", attr
->QRep
.expire_value
);
645 strprompt("Keep how many messages online?", buf
, 10);
646 attr
->QRep
.expire_value
= atol(buf
);
649 if (attr
->QRep
.expire_mode
== 3) {
650 snprintf(buf
, sizeof buf
, "%d", attr
->QRep
.expire_value
);
651 strprompt("Keep messages for how many days?", buf
, 10);
652 attr
->QRep
.expire_value
= atol(buf
);
655 /* Give 'em a chance to change their minds */
656 scr_printf("Save changes (y/n)? ");
659 r
= CtdlIPCSetRoomAide(ipc
, raide
, buf
);
661 scr_printf("%s\n", buf
);
664 r
= CtdlIPCSetMessageExpirationPolicy(ipc
, 0, eptr
, buf
);
666 scr_printf("%s\n", buf
);
669 r
= CtdlIPCSetRoomAttributes(ipc
, rbump
, attr
, buf
);
670 scr_printf("%s\n", buf
);
671 strncpy(buf
, attr
->QRname
, ROOMNAMELEN
);
674 dotgoto(ipc
, buf
, 2, 0);
681 * un-goto the previous room, or a specified room
683 void dotungoto(CtdlIPC
*ipc
, char *towhere
)
685 /* Find this 'towhere' room in the list ungoto from this room to
686 that at the messagepointer position in that room in our ungoto list.
687 I suppose I could be a real dick and just ungoto that many places
692 struct ctdlipcroom
*rret
= NULL
; /* ignored */
697 scr_printf("No rooms to ungoto.\n");
702 scr_printf("Must specify a room to ungoto.\n");
705 if (IsEmptyStr(towhere
))
707 scr_printf("Must specify a room to ungoto.\n");
710 for (lp
= uglistsize
-1; lp
>= 0; lp
--)
712 if (strcasecmp(towhere
, uglist
[lp
]) == 0)
720 scr_printf("Room: %s not in ungoto list.\n", towhere
);
724 r
= CtdlIPCGotoRoom(ipc
, uglist
[found
], "", &rret
, buf
);
725 if (rret
) free(rret
); /* ignored */
727 scr_printf("%s\n", buf
);
730 r
= CtdlIPCSetLastRead(ipc
, uglistlsn
[found
] ? uglistlsn
[found
] : 1, buf
);
732 scr_printf("%s\n", buf
);
734 safestrncpy(buf
, uglist
[found
], sizeof(buf
));
735 /* we queue ungoto information here, because we're not really
736 ungotoing, we're really going to a random spot in some arbitrary
738 dotgoto(ipc
, buf
, 0, 0);
741 void ungoto(CtdlIPC
*ipc
)
744 struct ctdlipcroom
*rret
= NULL
; /* ignored */
750 r
= CtdlIPCGotoRoom(ipc
, uglist
[uglistsize
-1], "", &rret
, buf
);
751 if (rret
) free(rret
); /* ignored */
753 scr_printf("%s\n", buf
);
756 r
= CtdlIPCSetLastRead(ipc
, uglistlsn
[uglistsize
-1] ? uglistlsn
[uglistsize
-1] : 1, buf
);
758 scr_printf("%s\n", buf
);
760 safestrncpy(buf
, uglist
[uglistsize
-1], sizeof(buf
));
762 free(uglist
[uglistsize
]);
763 /* Don't queue ungoto info or we end up in a loop */
764 dotgoto(ipc
, buf
, 0, 1);
769 * saves filelen bytes from file at pathname
771 int save_buffer(void *file
, size_t filelen
, const char *pathname
)
774 size_t bytes_written
= 0;
777 fp
= fopen(pathname
, "w");
779 err_printf("Cannot open '%s': %s\n", pathname
, strerror(errno
));
783 block
= fwrite((char *)file
+ bytes_written
, 1,
784 filelen
- bytes_written
, fp
);
785 bytes_written
+= block
;
786 } while (errno
== EINTR
&& bytes_written
< filelen
);
789 if (bytes_written
< filelen
) {
790 err_printf("Trouble saving '%s': %s\n", pathname
,
799 * Save supplied_filename in dest directory; gets the name only
801 void destination_directory(char *dest
, const char *supplied_filename
)
803 static char save_dir
[SIZ
] = { 0 };
805 if (IsEmptyStr(save_dir
)) {
806 if (getenv("HOME") == NULL
) {
807 strcpy(save_dir
, ".");
810 sprintf(save_dir
, "%s/Desktop", getenv("HOME"));
811 if (access(save_dir
, W_OK
) != 0) {
812 sprintf(save_dir
, "%s", getenv("HOME"));
813 if (access(save_dir
, W_OK
) != 0) {
814 sprintf(save_dir
, ".");
820 sprintf(dest
, "%s/%s", save_dir
, supplied_filename
);
821 strprompt("Save as", dest
, PATH_MAX
);
823 /* Remember the directory for next time */
824 strcpy(save_dir
, dest
);
825 if (strrchr(save_dir
, '/') != NULL
) {
826 strcpy(strrchr(save_dir
, '/'), "");
829 strcpy(save_dir
, ".");
835 * download() - download a file or files. The argument passed to this
836 * function determines which protocol to use.
837 * proto - 0 = paginate, 1 = xmodem, 2 = raw, 3 = ymodem, 4 = zmodem, 5 = save
839 void download(CtdlIPC
*ipc
, int proto
)
842 char filename
[PATH_MAX
];
843 char tempname
[PATH_MAX
];
844 char transmit_cmd
[SIZ
];
848 void *file
= NULL
; /* The downloaded file */
849 size_t filelen
= 0L; /* The downloaded file length */
851 if ((room_flags
& QR_DOWNLOAD
) == 0) {
852 scr_printf("*** You cannot download from this room.\n");
856 newprompt("Enter filename: ", filename
, PATH_MAX
);
858 /* Save to local disk, for folks with their own copy of the client */
860 destination_directory(tempname
, filename
);
861 r
= CtdlIPCFileDownload(ipc
, filename
, &file
, 0, progress
, buf
);
863 scr_printf("%s\n", buf
);
866 save_buffer(file
, (size_t)extract_long(buf
, 0), tempname
);
871 r
= CtdlIPCFileDownload(ipc
, filename
, &file
, 0, progress
, buf
);
873 scr_printf("%s\n", buf
);
876 filelen
= extract_unsigned_long(buf
, 0);
878 /* Meta-download for public clients */
879 /* scr_printf("Fetching file from Citadel server...\n"); */
880 mkdir(tempdir
, 0700);
881 snprintf(tempname
, sizeof tempname
, "%s/%s", tempdir
, filename
);
882 tpipe
= fopen(tempname
, "wb");
883 if (fwrite(file
, filelen
, 1, tpipe
) < filelen
) {
884 /* FIXME: restart syscall on EINTR */
888 if (file
) free(file
);
891 /* FIXME: display internally instead */
892 snprintf(transmit_cmd
, sizeof transmit_cmd
,
893 "SHELL=/dev/null; export SHELL; TERM=dumb; export TERM; exec more -d <%s",
897 snprintf(transmit_cmd
, sizeof transmit_cmd
, "exec sx %s", tempname
);
899 snprintf(transmit_cmd
, sizeof transmit_cmd
, "exec sb %s", tempname
);
901 snprintf(transmit_cmd
, sizeof transmit_cmd
, "exec sz %s", tempname
);
903 /* FIXME: display internally instead */
904 snprintf(transmit_cmd
, sizeof transmit_cmd
, "exec cat %s", tempname
);
907 stty_ctdl(SB_RESTORE
);
908 system(transmit_cmd
);
909 stty_ctdl(SB_NO_INTR
);
912 /* clean up the temporary directory */
914 ctdl_beep(); /* Beep beep! */
919 * read directory of this room
921 void roomdir(CtdlIPC
*ipc
)
928 char *listing
= NULL
; /* Returned directory listing */
931 r
= CtdlIPCReadDirectory(ipc
, &listing
, buf
);
933 pprintf("%s\n", buf
);
937 extract_token(comment
, buf
, 0, '|', sizeof comment
);
938 extract_token(flnm
, buf
, 1, '|', sizeof flnm
);
939 pprintf("\nDirectory of %s on %s\n", flnm
, comment
);
940 pprintf("-----------------------\n");
941 while (listing
&& *listing
&& !IsEmptyStr(listing
)) {
942 extract_token(buf
, listing
, 0, '\n', sizeof buf
);
943 remove_token(listing
, 0, '\n');
945 extract_token(flnm
, buf
, 0, '|', sizeof flnm
);
946 extract_token(flsz
, buf
, 1, '|', sizeof flsz
);
947 extract_token(mimetype
, buf
, 2, '|', sizeof mimetype
);
948 extract_token(comment
, buf
, 3, '|', sizeof comment
);
949 if (strlen(flnm
) <= 14)
950 pprintf("%-14s %8s %s [%s]\n", flnm
, flsz
, comment
, mimetype
);
952 pprintf("%s\n%14s %8s %s [%s]\n", flnm
, "", flsz
,
955 if (listing
) free(listing
);
960 * add a user to a private room
962 void invite(CtdlIPC
*ipc
)
964 char username
[USERNAME_SIZE
];
966 int r
; /* IPC response code */
968 newprompt("Name of user? ", username
, USERNAME_SIZE
);
969 if (username
[0] == 0)
972 r
= CtdlIPCInviteUserToRoom(ipc
, username
, buf
);
973 scr_printf("%s\n", buf
);
978 * kick a user out of a room
980 void kickout(CtdlIPC
*ipc
)
982 char username
[USERNAME_SIZE
];
984 int r
; /* IPC response code */
986 newprompt("Name of user? ", username
, USERNAME_SIZE
);
987 if (username
[0] == 0)
990 r
= CtdlIPCKickoutUserFromRoom(ipc
, username
, buf
);
991 scr_printf("%s\n", buf
);
996 * aide command: kill the current room
998 void killroom(CtdlIPC
*ipc
)
1003 r
= CtdlIPCDeleteRoom(ipc
, 0, aaa
);
1005 scr_printf("%s\n", aaa
);
1009 scr_printf("Are you sure you want to kill this room? ");
1013 r
= CtdlIPCDeleteRoom(ipc
, 1, aaa
);
1014 scr_printf("%s\n", aaa
);
1017 dotgoto(ipc
, "_BASEROOM_", 0, 0);
1020 void forget(CtdlIPC
*ipc
)
1021 { /* forget the current room */
1024 scr_printf("Are you sure you want to forget this room? ");
1028 remove_march(room_name
, 0);
1029 if (CtdlIPCForgetRoom(ipc
, buf
) / 100 != 2) {
1030 scr_printf("%s\n", buf
);
1034 /* now return to the lobby */
1035 dotgoto(ipc
, "_BASEROOM_", 0, 0);
1042 void entroom(CtdlIPC
*ipc
)
1045 char new_room_name
[ROOMNAMELEN
];
1047 char new_room_pass
[10];
1050 int r
; /* IPC response code */
1052 /* Check permission to create room */
1053 r
= CtdlIPCCreateRoom(ipc
, 0, "", 1, "", 0, buf
);
1055 scr_printf("%s\n", buf
);
1059 newprompt("Name for new room? ", new_room_name
, ROOMNAMELEN
- 1);
1060 if (IsEmptyStr(new_room_name
)) {
1063 for (a
= 0; !IsEmptyStr(&new_room_name
[a
]); ++a
) {
1064 if (new_room_name
[a
] == '|') {
1065 new_room_name
[a
] = '_';
1069 new_room_floor
= select_floor(ipc
, (int) curr_floor
);
1071 IFNEXPERT
formout(ipc
, "roomaccess");
1073 scr_printf("<?>Help\n<1>Public room\n<2>Guess-name room\n"
1074 "<3>Passworded room\n<4>Invitation-only room\n"
1075 "<5>Personal room\n"
1076 "Enter room type: ");
1079 } while (((b
< '1') || (b
> '5')) && (b
!= '?'));
1082 formout(ipc
, "roomaccess");
1084 } while ((b
< '1') || (b
> '5'));
1085 b
-= '0'; /* Portable */
1086 scr_printf("%d\n", b
);
1087 new_room_type
= b
- 1;
1088 if (new_room_type
== 2) {
1089 newprompt("Enter a room password: ", new_room_pass
, 9);
1090 for (a
= 0; !IsEmptyStr(&new_room_pass
[a
]); ++a
)
1091 if (new_room_pass
[a
] == '|')
1092 new_room_pass
[a
] = '_';
1094 strcpy(new_room_pass
, "");
1097 scr_printf("\042%s\042, a", new_room_name
);
1099 scr_printf(" public room.");
1101 scr_printf(" guess-name room.");
1103 scr_printf(" passworded room, password: %s", new_room_pass
);
1105 scr_printf("n invitation-only room.");
1107 scr_printf(" personal room.");
1108 scr_printf("\nInstall it? (y/n) : ");
1113 r
= CtdlIPCCreateRoom(ipc
, 1, new_room_name
, new_room_type
,
1114 new_room_pass
, new_room_floor
, buf
);
1116 scr_printf("%s\n", buf
);
1120 /* command succeeded... now GO to the new room! */
1121 dotgoto(ipc
, new_room_name
, 0, 0);
1126 void readinfo(CtdlIPC
*ipc
)
1127 { /* read info file for current room */
1130 int r
; /* IPC response code */
1133 /* Name of currernt room aide */
1134 r
= CtdlIPCGetRoomAide(ipc
, buf
);
1136 safestrncpy(raide
, buf
, sizeof raide
);
1140 if (!IsEmptyStr(raide
))
1141 scr_printf("Room aide is %s.\n\n", raide
);
1143 r
= CtdlIPCRoomInfo(ipc
, &text
, buf
);
1148 fmout(screenwidth
, NULL
, text
, NULL
,
1149 ((userflags
& US_PAGINATOR
) ? 1 : 0), screenheight
,
1150 (*raide
) ? 2 : 0, 1);
1157 * <W>ho knows room...
1159 void whoknows(CtdlIPC
*ipc
)
1162 char *listing
= NULL
;
1165 r
= CtdlIPCWhoKnowsRoom(ipc
, &listing
, buf
);
1167 pprintf("%s\n", buf
);
1170 while (!IsEmptyStr(listing
)) {
1171 extract_token(buf
, listing
, 0, '\n', sizeof buf
);
1172 remove_token(listing
, 0, '\n');
1174 pprintf("%s\n", buf
);
1180 void do_edit(CtdlIPC
*ipc
,
1181 char *desc
, char *read_cmd
, char *check_cmd
, char *write_cmd
)
1185 int b
, cksum
, editor_exit
;
1187 if (IsEmptyStr(editor_paths
[0])) {
1188 scr_printf("Do you wish to re-enter %s? ", desc
);
1193 fp
= fopen(temp
, "w");
1196 CtdlIPC_chat_send(ipc
, check_cmd
);
1197 CtdlIPC_chat_recv(ipc
, cmd
);
1198 if (cmd
[0] != '2') {
1199 scr_printf("%s\n", &cmd
[4]);
1203 if (!IsEmptyStr(editor_paths
[0])) {
1204 CtdlIPC_chat_send(ipc
, read_cmd
);
1205 CtdlIPC_chat_recv(ipc
, cmd
);
1206 if (cmd
[0] == '1') {
1207 fp
= fopen(temp
, "w");
1208 while (CtdlIPC_chat_recv(ipc
, cmd
), strcmp(cmd
, "000")) {
1209 fprintf(fp
, "%s\n", cmd
);
1215 cksum
= file_checksum(temp
);
1217 if (!IsEmptyStr(editor_paths
[0])) {
1220 snprintf(tmp
, sizeof tmp
, "WINDOW_TITLE=%s", desc
);
1223 stty_ctdl(SB_RESTORE
);
1224 editor_pid
= fork();
1225 if (editor_pid
== 0) {
1227 execlp(editor_paths
[0], editor_paths
[0], temp
, NULL
);
1233 b
= ka_wait(&editor_exit
);
1234 } while ((b
!= editor_pid
) && (b
>= 0));
1236 scr_printf("Executed %s\n", editor_paths
[0]);
1240 scr_printf("Entering %s. "
1241 "Press return twice when finished.\n", desc
);
1242 fp
= fopen(temp
, "r+");
1247 if (file_checksum(temp
) == cksum
) {
1248 scr_printf("*** Aborted.\n");
1252 CtdlIPC_chat_send(ipc
, write_cmd
);
1253 CtdlIPC_chat_recv(ipc
, cmd
);
1254 if (cmd
[0] != '4') {
1255 scr_printf("%s\n", &cmd
[4]);
1259 fp
= fopen(temp
, "r");
1260 while (fgets(cmd
, SIZ
- 1, fp
) != NULL
) {
1261 cmd
[strlen(cmd
) - 1] = 0;
1262 CtdlIPC_chat_send(ipc
, cmd
);
1265 CtdlIPC_chat_send(ipc
, "000");
1272 void enterinfo(CtdlIPC
*ipc
)
1273 { /* edit info file for current room */
1274 do_edit(ipc
, "the Info file for this room", "RINF", "EINF 0", "EINF 1");
1277 void enter_bio(CtdlIPC
*ipc
)
1280 snprintf(cmd
, sizeof cmd
, "RBIO %s", fullname
);
1281 do_edit(ipc
, "your Bio", cmd
, "NOOP", "EBIO");
1285 * create a new floor
1287 void create_floor(CtdlIPC
*ipc
)
1290 char newfloorname
[SIZ
];
1291 int r
; /* IPC response code */
1293 load_floorlist(ipc
);
1295 r
= CtdlIPCCreateFloor(ipc
, 0, "", buf
);
1296 if ( (r
/ 100 != 2) && (r
!= ERROR
+ ILLEGAL_VALUE
) ) {
1297 scr_printf("%s\n", buf
);
1301 newprompt("Name for new floor: ", newfloorname
, 255);
1302 if (!*newfloorname
) return;
1303 r
= CtdlIPCCreateFloor(ipc
, 1, newfloorname
, buf
);
1305 scr_printf("Floor has been created.\n");
1307 scr_printf("%s\n", buf
);
1310 load_floorlist(ipc
);
1314 * edit the current floor
1316 void edit_floor(CtdlIPC
*ipc
)
1319 struct ExpirePolicy
*ep
= NULL
;
1320 int r
; /* IPC response code */
1322 load_floorlist(ipc
);
1324 /* Fetch the expire policy (this will silently fail on old servers,
1325 * resulting in "default" policy)
1327 r
= CtdlIPCGetMessageExpirationPolicy(ipc
, 1, &ep
, buf
);
1329 /* Interact with the user */
1330 scr_printf("You are editing the floor called \"%s\"\n",
1331 &floorlist
[(int) curr_floor
][0] );
1332 strprompt("Floor name", &floorlist
[(int) curr_floor
][0], 255);
1334 /* Angels and demons dancing in my head... */
1336 snprintf(buf
, sizeof buf
, "%d", ep
->expire_mode
);
1338 ("Floor default message expire policy (? for list)",
1340 if (buf
[0] == '?') {
1342 "0. Use the system default\n"
1343 "1. Never automatically expire messages\n"
1344 "2. Expire by message count\n"
1345 "3. Expire by message age\n");
1347 } while ((buf
[0] < '0') || (buf
[0] > '3'));
1348 ep
->expire_mode
= buf
[0] - '0';
1350 /* ...lunatics and monsters underneath my bed */
1351 if (ep
->expire_mode
== 2) {
1352 snprintf(buf
, sizeof buf
, "%d", ep
->expire_value
);
1353 strprompt("Keep how many messages online?", buf
, 10);
1354 ep
->expire_value
= atol(buf
);
1357 if (ep
->expire_mode
== 3) {
1358 snprintf(buf
, sizeof buf
, "%d", ep
->expire_value
);
1359 strprompt("Keep messages for how many days?", buf
, 10);
1360 ep
->expire_value
= atol(buf
);
1364 r
= CtdlIPCSetMessageExpirationPolicy(ipc
, 1, ep
, buf
);
1365 r
= CtdlIPCEditFloor(ipc
, curr_floor
, &floorlist
[(int)curr_floor
][0], buf
);
1366 scr_printf("%s\n", buf
);
1367 load_floorlist(ipc
);
1374 * kill the current floor
1376 void kill_floor(CtdlIPC
*ipc
)
1378 int floornum_to_delete
, a
;
1381 load_floorlist(ipc
);
1383 floornum_to_delete
= (-1);
1384 scr_printf("(Press return to abort)\n");
1385 newprompt("Delete which floor? ", buf
, 255);
1386 if (IsEmptyStr(buf
))
1388 for (a
= 0; a
< 128; ++a
)
1389 if (!strcasecmp(&floorlist
[a
][0], buf
))
1390 floornum_to_delete
= a
;
1391 if (floornum_to_delete
< 0) {
1392 scr_printf("No such floor. Select one of:\n");
1393 for (a
= 0; a
< 128; ++a
)
1394 if (floorlist
[a
][0] != 0)
1395 scr_printf("%s\n", &floorlist
[a
][0]);
1397 } while (floornum_to_delete
< 0);
1398 CtdlIPCDeleteFloor(ipc
, 1, floornum_to_delete
, buf
);
1399 scr_printf("%s\n", buf
);
1400 load_floorlist(ipc
);