Back to index

citadel  8.12
rooms.c
Go to the documentation of this file.
00001 /*
00002  * Client-side functions which perform room operations
00003  *
00004  * Copyright (c) 1987-2012 by the citadel.org team
00005  *
00006  * This program is open source software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License version 3.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "sysdep.h"
00016 #include <stdlib.h>
00017 #include <unistd.h>
00018 #include <fcntl.h>
00019 #include <stdio.h>
00020 #include <ctype.h>
00021 #include <string.h>
00022 #include <signal.h>
00023 #include <sys/types.h>
00024 #include <sys/stat.h>
00025 #include <sys/wait.h>
00026 #include <errno.h>
00027 #include <stdarg.h>
00028 #include <libcitadel.h>
00029 #include "citadel.h"
00030 #include "citadel_ipc.h"
00031 #include "citadel_decls.h"
00032 #include "rooms.h"
00033 #include "commands.h"
00034 #include "messages.h"
00035 #include "tuiconfig.h"
00036 #ifndef HAVE_SNPRINTF
00037 #include "snprintf.h"
00038 #endif
00039 #include "screen.h"
00040 #include "citadel_dirs.h"
00041 
00042 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
00043 
00044 
00045 void stty_ctdl(int cmd);
00046 void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto);
00047 void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax);
00048 int pattern(char *search, char *patn);
00049 int file_checksum(char *filename);
00050 int nukedir(char *dirname);
00051 
00052 extern unsigned room_flags;
00053 extern char room_name[];
00054 extern char temp[];
00055 extern char tempdir[];
00056 extern int editor_pid;
00057 extern int screenwidth;
00058 extern int screenheight;
00059 extern char fullname[];
00060 extern char sigcaught;
00061 extern char floor_mode;
00062 extern char curr_floor;
00063 
00064 
00065 extern int ugnum;
00066 extern long uglsn;
00067 extern char *uglist[];
00068 extern long uglistlsn[];
00069 extern int uglistsize;
00070 
00071 extern char floorlist[128][SIZ];
00072 
00073 
00074 void load_floorlist(CtdlIPC *ipc)
00075 {
00076        int a;
00077        char buf[SIZ];
00078        char *listing = NULL;
00079        int r;               /* IPC response code */
00080 
00081        for (a = 0; a < 128; ++a)
00082               floorlist[a][0] = 0;
00083 
00084        r = CtdlIPCFloorListing(ipc, &listing, buf);
00085        if (r / 100 != 1) {
00086               strcpy(floorlist[0], "Main Floor");
00087               return;
00088        }
00089        while (*listing && !IsEmptyStr(listing)) {
00090               extract_token(buf, listing, 0, '\n', sizeof buf);
00091               remove_token(listing, 0, '\n');
00092               extract_token(floorlist[extract_int(buf, 0)], buf, 1, '|', SIZ);
00093        }
00094        free(listing);
00095 }
00096 
00097 
00098 void room_tree_list(struct ctdlroomlisting *rp)
00099 {
00100        static int c = 0;
00101        char rmname[ROOMNAMELEN];
00102        int f;
00103 
00104        if (rp == NULL) {
00105               c = 1;
00106               return;
00107        }
00108 
00109        if (rp->lnext != NULL) {
00110               room_tree_list(rp->lnext);
00111        }
00112 
00113        if (sigcaught == 0) {
00114               strcpy(rmname, rp->rlname);
00115               f = rp->rlflags;
00116               if ((c + strlen(rmname) + 4) > screenwidth) {
00117 
00118                      /* line break, check the paginator */
00119                      scr_printf("\n");
00120                      c = 1;
00121               }
00122               if (f & QR_MAILBOX) {
00123                      color(BRIGHT_YELLOW);
00124               } else if (f & QR_PRIVATE) {
00125                      color(BRIGHT_RED);
00126               } else {
00127                      color(DIM_WHITE);
00128               }
00129               scr_printf("%s", rmname);
00130               if ((f & QR_DIRECTORY) && (f & QR_NETWORK))
00131                      scr_printf("}  ");
00132               else if (f & QR_DIRECTORY)
00133                      scr_printf("]  ");
00134               else if (f & QR_NETWORK)
00135                      scr_printf(")  ");
00136               else
00137                      scr_printf(">  ");
00138               c = c + strlen(rmname) + 3;
00139        }
00140 
00141        if (rp->rnext != NULL) {
00142               room_tree_list(rp->rnext);
00143        }
00144 
00145        free(rp);
00146 }
00147 
00148 
00149 /* 
00150  * Room ordering stuff (compare first by floor, then by order)
00151  */
00152 int rordercmp(struct ctdlroomlisting *r1, struct ctdlroomlisting *r2)
00153 {
00154        if ((r1 == NULL) && (r2 == NULL))
00155               return (0);
00156        if (r1 == NULL)
00157               return (-1);
00158        if (r2 == NULL)
00159               return (1);
00160        if (r1->rlfloor < r2->rlfloor)
00161               return (-1);
00162        if (r1->rlfloor > r2->rlfloor)
00163               return (1);
00164        if (r1->rlorder < r2->rlorder)
00165               return (-1);
00166        if (r1->rlorder > r2->rlorder)
00167               return (1);
00168        return (0);
00169 }
00170 
00171 
00172 /*
00173  * Common code for all room listings
00174  */
00175 static void listrms(struct march *listing, int new_only, int floor_only, unsigned int flags, char *match)
00176 {
00177        struct march *mptr;
00178        struct ctdlroomlisting *rl = NULL;
00179        struct ctdlroomlisting *rp;
00180        struct ctdlroomlisting *rs;
00181        int list_it;
00182 
00183        for (mptr = listing; mptr != NULL; mptr = mptr->next) {
00184               list_it = 1;
00185 
00186               if ( (new_only == LISTRMS_NEW_ONLY)
00187                  && ((mptr->march_access & UA_HASNEWMSGS) == 0)) 
00188                      list_it = 0;
00189 
00190               if ( (new_only == LISTRMS_OLD_ONLY)
00191                  && ((mptr->march_access & UA_HASNEWMSGS) != 0)) 
00192                      list_it = 0;
00193 
00194               if ( (floor_only >= 0)
00195                  && (mptr->march_floor != floor_only))
00196                      list_it = 0;
00197 
00198               if (flags && (mptr->march_flags & flags) == 0)
00199                   list_it = 0;
00200 
00201            if (match && (pattern(mptr->march_name, match) == -1))
00202                      list_it = 0;
00203 
00204               if (list_it) {
00205                      rp = malloc(sizeof(struct ctdlroomlisting));
00206                      strncpy(rp->rlname, mptr->march_name, ROOMNAMELEN);
00207                      rp->rlflags = mptr->march_flags;
00208                      rp->rlfloor = mptr->march_floor;
00209                      rp->rlorder = mptr->march_order;
00210                      rp->lnext = NULL;
00211                      rp->rnext = NULL;
00212        
00213                      rs = rl;
00214                      if (rl == NULL) {
00215                             rl = rp;
00216                      } else {
00217                             while (rp != NULL) {
00218                                    if (rordercmp(rp, rs) < 0) {
00219                                           if (rs->lnext == NULL) {
00220                                                  rs->lnext = rp;
00221                                                  rp = NULL;
00222                                           } else {
00223                                                  rs = rs->lnext;
00224                                           }
00225                                    } else {
00226                                           if (rs->rnext == NULL) {
00227                                                  rs->rnext = rp;
00228                                                  rp = NULL;
00229                                           } else {
00230                                                  rs = rs->rnext;
00231                                           }
00232                                    }
00233                             }
00234                      }
00235               }
00236        }
00237 
00238        room_tree_list(NULL);
00239        room_tree_list(rl);
00240        color(DIM_WHITE);
00241 }
00242 
00243 
00244 void list_other_floors(void)
00245 {
00246        int a, c;
00247 
00248        c = 1;
00249        for (a = 0; a < 128; ++a) {
00250               if ((strlen(floorlist[a]) > 0) && (a != curr_floor)) {
00251                      if ((c + strlen(floorlist[a]) + 4) > screenwidth) {
00252                             scr_printf("\n");
00253                             c = 1;
00254                      }
00255                      scr_printf("%s:  ", floorlist[a]);
00256                      c = c + strlen(floorlist[a]) + 3;
00257               }
00258        }
00259 }
00260 
00261 
00262 /*
00263  * List known rooms.  kn_floor_mode should be set to 0 for a 'flat' listing,
00264  * 1 to list rooms on the current floor, or 2 to list rooms on all floors.
00265  */
00266 void knrooms(CtdlIPC *ipc, int kn_floor_mode)
00267 {
00268        int a;
00269        struct march *listing = NULL;
00270        struct march *mptr;
00271        int r;        /* IPC response code */
00272        char buf[SIZ];
00273 
00274 
00275        /* Ask the server for a room list */
00276        r = CtdlIPCKnownRooms(ipc, SubscribedRooms, (-1), &listing, buf);
00277        if (r / 100 != 1) {
00278               listing = NULL;
00279        }
00280 
00281        load_floorlist(ipc);
00282 
00283 
00284        if (kn_floor_mode == 0) {
00285               color(BRIGHT_CYAN);
00286               scr_printf("\n   Rooms with unread messages:\n");
00287               listrms(listing, LISTRMS_NEW_ONLY, -1, 0, NULL);
00288               color(BRIGHT_CYAN);
00289               scr_printf("\n\n   No unseen messages in:\n");
00290               listrms(listing, LISTRMS_OLD_ONLY, -1, 0, NULL);
00291               scr_printf("\n");
00292        }
00293 
00294        if (kn_floor_mode == 1) {
00295               color(BRIGHT_CYAN);
00296               scr_printf("\n   Rooms with unread messages on %s:\n",
00297                      floorlist[(int) curr_floor]);
00298               listrms(listing, LISTRMS_NEW_ONLY, curr_floor, 0, NULL);
00299               color(BRIGHT_CYAN);
00300               scr_printf("\n\n   Rooms with no new messages on %s:\n",
00301                      floorlist[(int) curr_floor]);
00302               listrms(listing, LISTRMS_OLD_ONLY, curr_floor, 0, NULL);
00303               color(BRIGHT_CYAN);
00304               scr_printf("\n\n   Other floors:\n");
00305               list_other_floors();
00306               scr_printf("\n");
00307        }
00308 
00309        if (kn_floor_mode == 2) {
00310               for (a = 0; a < 128; ++a) {
00311                      if (floorlist[a][0] != 0) {
00312                             color(BRIGHT_CYAN);
00313                             scr_printf("\n   Rooms on %s:\n",
00314                                    floorlist[a]);
00315                             listrms(listing, LISTRMS_ALL, a, 0, NULL);
00316                             scr_printf("\n");
00317                      }
00318               }
00319        }
00320 
00321        /* Free the room list */
00322        while (listing) {
00323               mptr = listing->next;
00324               free(listing);
00325               listing = mptr;
00326        };
00327 
00328        color(DIM_WHITE);
00329 }
00330 
00331 
00332 void listzrooms(CtdlIPC *ipc)
00333 {                           /* list public forgotten rooms */
00334        struct march *listing = NULL;
00335        struct march *mptr;
00336        int r;        /* IPC response code */
00337        char buf[SIZ];
00338 
00339 
00340        /* Ask the server for a room list */
00341        r = CtdlIPCKnownRooms(ipc, UnsubscribedRooms, (-1), &listing, buf);
00342        if (r / 100 != 1) {
00343               listing = NULL;
00344        }
00345 
00346        color(BRIGHT_CYAN);
00347        scr_printf("\n   Forgotten public rooms:\n");
00348        listrms(listing, LISTRMS_ALL, -1, 0, NULL);
00349        scr_printf("\n");
00350 
00351        /* Free the room list */
00352        while (listing) {
00353               mptr = listing->next;
00354               free(listing);
00355               listing = mptr;
00356        };
00357 
00358        color(DIM_WHITE);
00359 }
00360 
00361 void dotknown(CtdlIPC *ipc, int what, char *match)
00362 {                           /* list rooms according to attribute */
00363        struct march *listing = NULL;
00364        struct march *mptr;
00365        int r;        /* IPC response code */
00366        char buf[SIZ];
00367 
00368        /* Ask the server for a room list */
00369        r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, (-1), &listing, buf);
00370        if (r / 100 != 1) {
00371               listing = NULL;
00372        }
00373 
00374        color(BRIGHT_CYAN);
00375 
00376        switch (what) {
00377     case 0:
00378        scr_printf("\n   Anonymous rooms:\n");
00379            listrms(listing, LISTRMS_ALL, -1, QR_ANONONLY|QR_ANONOPT, NULL);
00380        scr_printf("\n");
00381               break;
00382     case 1:
00383        scr_printf("\n   Directory rooms:\n");
00384            listrms(listing, LISTRMS_ALL, -1, QR_DIRECTORY, NULL);
00385        scr_printf("\n");
00386               break;
00387     case 2:
00388        scr_printf("\n   Matching \"%s\" rooms:\n", match);
00389            listrms(listing, LISTRMS_ALL, -1, 0, match);
00390        scr_printf("\n");
00391               break;
00392     case 3:
00393        scr_printf("\n   Preferred only rooms:\n");
00394            listrms(listing, LISTRMS_ALL, -1, QR_PREFONLY, NULL);
00395        scr_printf("\n");
00396               break;
00397     case 4:
00398        scr_printf("\n   Private rooms:\n");
00399            listrms(listing, LISTRMS_ALL, -1, QR_PRIVATE, NULL);
00400        scr_printf("\n");
00401               break;
00402     case 5:
00403        scr_printf("\n   Read only rooms:\n");
00404            listrms(listing, LISTRMS_ALL, -1, QR_READONLY, NULL);
00405        scr_printf("\n");
00406               break;
00407     case 6:
00408        scr_printf("\n   Shared rooms:\n");
00409            listrms(listing, LISTRMS_ALL, -1, QR_NETWORK, NULL);
00410        scr_printf("\n");
00411               break;
00412        }
00413 
00414        /* Free the room list */
00415        while (listing) {
00416               mptr = listing->next;
00417               free(listing);
00418               listing = mptr;
00419        };
00420 
00421        color(DIM_WHITE);
00422 }
00423 
00424 
00425 int set_room_attr(CtdlIPC *ipc, unsigned int ibuf, char *prompt, unsigned int sbit)
00426 {
00427        int a;
00428 
00429        a = boolprompt(prompt, (ibuf & sbit));
00430        ibuf = (ibuf | sbit);
00431        if (!a) {
00432               ibuf = (ibuf ^ sbit);
00433        }
00434        return (ibuf);
00435 }
00436 
00437 
00438 
00439 /*
00440  * Select a floor (used in several commands)
00441  * The supplied argument is the 'default' floor number.
00442  * This function returns the selected floor number.
00443  */
00444 int select_floor(CtdlIPC *ipc, int rfloor)
00445 {
00446        int a, newfloor;
00447        char floorstr[SIZ];
00448 
00449        if (floor_mode == 1) {
00450               if (floorlist[(int) curr_floor][0] == 0) {
00451                      load_floorlist(ipc);
00452               }
00453 
00454               do {
00455                      newfloor = (-1);
00456                      safestrncpy(floorstr, floorlist[rfloor],
00457                                 sizeof floorstr);
00458                      strprompt("Which floor", floorstr, 255);
00459                      for (a = 0; a < 128; ++a) {
00460                             if (!strcasecmp
00461                                 (floorstr, &floorlist[a][0]))
00462                                    newfloor = a;
00463                             if ((newfloor < 0)
00464                                 &&
00465                                 (!strncasecmp
00466                                  (floorstr, &floorlist[a][0],
00467                                   strlen(floorstr))))
00468                                    newfloor = a;
00469                             if ((newfloor < 0)
00470                                 && (pattern(&floorlist[a][0], floorstr)
00471                                    >= 0))
00472                                    newfloor = a;
00473                      }
00474                      if (newfloor < 0) {
00475                             scr_printf("\n One of:\n");
00476                             for (a = 0; a < 128; ++a) {
00477                                    if (floorlist[a][0] != 0) {
00478                                           scr_printf("%s\n",
00479                                                  &floorlist[a][0]);
00480                                    }
00481                             }
00482                      }
00483               } while (newfloor < 0);
00484               return (newfloor);
00485        }
00486 
00487        else {
00488               scr_printf("Floor selection bypassed because you have "
00489                      "floor mode disabled.\n");
00490        }
00491 
00492        return (rfloor);
00493 }
00494 
00495 
00496 
00497 
00498 /*
00499  * .<A>ide <E>dit room
00500  */
00501 void editthisroom(CtdlIPC *ipc)
00502 {
00503        int rbump = 0;
00504        char raide[USERNAME_SIZE];
00505        char buf[SIZ];
00506        struct ctdlroom *attr = NULL;
00507        struct ExpirePolicy *eptr = NULL;
00508        int r;                      /* IPC response code */
00509 
00510        /* Fetch the existing room config */
00511        r = CtdlIPCGetRoomAttributes(ipc, &attr, buf);
00512        if (r / 100 != 2) {
00513               scr_printf("%s\n", buf);
00514               return;
00515        }
00516        eptr = &(attr->QRep);
00517 
00518        /* Fetch the name of the current room aide */
00519        r = CtdlIPCGetRoomAide(ipc, buf);
00520        if (r / 100 == 2) {
00521               safestrncpy(raide, buf, sizeof raide);
00522        } else {
00523               strcpy(raide, "");
00524        }
00525        if (IsEmptyStr(raide)) {
00526               strcpy(raide, "none");
00527        }
00528 
00529        /* Fetch the expire policy (this will silently fail on old servers,
00530         * resulting in "default" policy)
00531         */
00532        r = CtdlIPCGetMessageExpirationPolicy(ipc, 0, &eptr, buf);
00533 
00534        /* Now interact with the user. */
00535 
00536        strprompt("Room name", attr->QRname, ROOMNAMELEN-1);
00537        attr->QRfloor = select_floor(ipc, attr->QRfloor);
00538        attr->QRflags = set_room_attr(ipc, attr->QRflags, "Private room", QR_PRIVATE);
00539        if (attr->QRflags & QR_PRIVATE) {
00540               attr->QRflags = set_room_attr(ipc, attr->QRflags,
00541                                    "Hidden room (accessible to anyone who knows the room name)",
00542                                    QR_GUESSNAME);
00543        }
00544 
00545        /* if it's public, clear the privacy classes */
00546        if ((attr->QRflags & QR_PRIVATE) == 0) {
00547               if (attr->QRflags & QR_GUESSNAME) {
00548                      attr->QRflags = attr->QRflags - QR_GUESSNAME;
00549               }
00550               if (attr->QRflags & QR_PASSWORDED) {
00551                      attr->QRflags = attr->QRflags - QR_PASSWORDED;
00552               }
00553        }
00554 
00555        /* if it's private, choose the privacy classes */
00556        if ((attr->QRflags & QR_PRIVATE)
00557            && ((attr->QRflags & QR_GUESSNAME) == 0)) {
00558               attr->QRflags = set_room_attr(ipc, attr->QRflags,
00559                                    "Accessible by entering a password",
00560                                    QR_PASSWORDED);
00561        }
00562        if ((attr->QRflags & QR_PRIVATE)
00563            && ((attr->QRflags & QR_PASSWORDED) == QR_PASSWORDED)) {
00564               strprompt("Room password", attr->QRpasswd, 9);
00565        }
00566 
00567        if ((attr->QRflags & QR_PRIVATE) == QR_PRIVATE) {
00568               rbump = boolprompt("Cause current users to forget room", 0);
00569        }
00570 
00571        attr->QRflags = set_room_attr(ipc, attr->QRflags,
00572                                    "Preferred users only", QR_PREFONLY);
00573        attr->QRflags = set_room_attr(ipc, attr->QRflags,
00574                                    "Read-only room", QR_READONLY);
00575        attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
00576                             "Allow message deletion by anyone who can post",
00577                             QR2_COLLABDEL);
00578        attr->QRflags = set_room_attr(ipc, attr->QRflags,
00579                                    "Permanent room", QR_PERMANENT);
00580        attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
00581                                                            "Subject Required (Force "
00582                                                            "users to specify a message "
00583                                    "subject)", QR2_SUBJECTREQ);
00584        attr->QRflags = set_room_attr(ipc, attr->QRflags,
00585                                    "Directory room", QR_DIRECTORY);
00586        if (attr->QRflags & QR_DIRECTORY) {
00587               strprompt("Directory name", attr->QRdirname, 14);
00588               attr->QRflags =
00589                   set_room_attr(ipc, attr->QRflags,
00590                                           "Uploading allowed", QR_UPLOAD);
00591               attr->QRflags =
00592                   set_room_attr(ipc, attr->QRflags, "Downloading allowed",
00593                               QR_DOWNLOAD);
00594               attr->QRflags =
00595                   set_room_attr(ipc, attr->QRflags,
00596                                           "Visible directory", QR_VISDIR);
00597        }
00598        attr->QRflags = set_room_attr(ipc, attr->QRflags,
00599                                    "Network shared room", QR_NETWORK);
00600        attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
00601                             "Self-service list subscribe/unsubscribe",
00602                             QR2_SELFLIST);
00603        attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
00604                             "public posting to this room via room_roomname@yourcitadel.org",
00605                             QR2_SMTP_PUBLIC);
00606        attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
00607                             "moderated mailinglist",
00608                             QR2_MODERATED);
00609        attr->QRflags = set_room_attr(ipc, attr->QRflags,
00610                             "Automatically make all messages anonymous",
00611                             QR_ANONONLY);
00612        if ((attr->QRflags & QR_ANONONLY) == 0) {
00613               attr->QRflags = set_room_attr(ipc, attr->QRflags,
00614                                    "Ask users whether to make messages anonymous",
00615                                    QR_ANONOPT);
00616        }
00617        attr->QRorder = intprompt("Listing order", attr->QRorder, 0, 127);
00618 
00619        /* Ask about the room aide */
00620        do {
00621               strprompt("Room aide (or 'none')", raide, 29);
00622               if (!strcasecmp(raide, "none")) {
00623                      strcpy(raide, "");
00624                      break;
00625               } else {
00626                      r = CtdlIPCQueryUsername(ipc, raide, buf);
00627                      if (r / 100 != 2)
00628                             scr_printf("%s\n", buf);
00629               }
00630        } while (r / 100 != 2);
00631 
00632        /* Angels and demons dancing in my head... */
00633        do {
00634               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_mode);
00635               strprompt("Message expire policy (? for list)", buf, 1);
00636               if (buf[0] == '?') {
00637                      scr_printf("\n"
00638                             "0. Use the default for this floor\n"
00639                             "1. Never automatically expire messages\n"
00640                             "2. Expire by message count\n"
00641                             "3. Expire by message age\n");
00642               }
00643        } while ((buf[0] < 48) || (buf[0] > 51));
00644        attr->QRep.expire_mode = buf[0] - 48;
00645 
00646        /* ...lunatics and monsters underneath my bed */
00647        if (attr->QRep.expire_mode == 2) {
00648               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_value);
00649               strprompt("Keep how many messages online?", buf, 10);
00650               attr->QRep.expire_value = atol(buf);
00651        }
00652 
00653        if (attr->QRep.expire_mode == 3) {
00654               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_value);
00655               strprompt("Keep messages for how many days?", buf, 10);
00656               attr->QRep.expire_value = atol(buf);
00657        }
00658 
00659        /* Give 'em a chance to change their minds */
00660        scr_printf("Save changes (y/n)? ");
00661 
00662        if (yesno() == 1) {
00663               r = CtdlIPCSetRoomAide(ipc, raide, buf);
00664               if (r / 100 != 2) {
00665                      scr_printf("%s\n", buf);
00666               }
00667 
00668               r = CtdlIPCSetMessageExpirationPolicy(ipc, 0, eptr, buf);
00669               if (r / 100 != 2) {
00670                      scr_printf("%s\n", buf);
00671               }
00672 
00673               r = CtdlIPCSetRoomAttributes(ipc, rbump, attr, buf);
00674               scr_printf("%s\n", buf);
00675               strncpy(buf, attr->QRname, ROOMNAMELEN);
00676               free(attr);
00677               if (r / 100 == 2)
00678                      dotgoto(ipc, buf, 2, 0);
00679        }
00680        else free(attr);
00681 }
00682 
00683 
00684 /*
00685  * un-goto the previous room, or a specified room
00686  */
00687 void dotungoto(CtdlIPC *ipc, char *towhere)
00688   {
00689     /* Find this 'towhere' room in the list ungoto from this room to
00690        that at the messagepointer position in that room in our ungoto list.
00691        I suppose I could be a real dick and just ungoto that many places
00692        in our list. */
00693     int found = -1;
00694     int lp;
00695        char buf[SIZ];
00696        struct ctdlipcroom *rret = NULL;   /* ignored */
00697        int r;
00698 
00699        if (uglistsize == 0)
00700       {
00701               scr_printf("No rooms to ungoto.\n");
00702               return;
00703       }
00704        if (towhere == NULL)
00705       {
00706               scr_printf("Must specify a room to ungoto.\n");
00707               return;
00708       }
00709        if (IsEmptyStr(towhere))
00710       {
00711               scr_printf("Must specify a room to ungoto.\n");
00712               return;
00713       }
00714     for (lp = uglistsize-1; lp >= 0; lp--)
00715       {
00716         if (strcasecmp(towhere, uglist[lp]) == 0)
00717           {
00718             found = lp;
00719             break;
00720           }
00721       }
00722     if (found == -1)
00723       {
00724               scr_printf("Room: %s not in ungoto list.\n", towhere);
00725        return;
00726       }
00727 
00728        r = CtdlIPCGotoRoom(ipc, uglist[found], "", &rret, buf);
00729        if (rret) free(rret);       /* ignored */
00730        if (r / 100 != 2) {
00731               scr_printf("%s\n", buf);
00732               return;
00733        }
00734        r = CtdlIPCSetLastRead(ipc, uglistlsn[found] ? uglistlsn[found] : 1, buf);
00735        if (r / 100 != 2) {
00736               scr_printf("%s\n", buf);
00737        }
00738        safestrncpy(buf, uglist[found], sizeof(buf));
00739     /* we queue ungoto information here, because we're not really
00740        ungotoing, we're really going to a random spot in some arbitrary
00741        room list. */
00742        dotgoto(ipc, buf, 0, 0);
00743   }
00744 
00745 void ungoto(CtdlIPC *ipc)
00746 {
00747        char buf[SIZ];
00748        struct ctdlipcroom *rret = NULL;   /* ignored */
00749        int r;
00750 
00751        if (uglistsize == 0)
00752               return;
00753 
00754        r = CtdlIPCGotoRoom(ipc, uglist[uglistsize-1], "", &rret, buf);
00755        if (rret) free(rret);       /* ignored */
00756        if (r / 100 != 2) {
00757               scr_printf("%s\n", buf);
00758               return;
00759        }
00760        r = CtdlIPCSetLastRead(ipc, uglistlsn[uglistsize-1] ? uglistlsn[uglistsize-1] : 1, buf);
00761        if (r / 100 != 2) {
00762               scr_printf("%s\n", buf);
00763        }
00764        safestrncpy(buf, uglist[uglistsize-1], sizeof(buf));
00765        uglistsize--;
00766        free(uglist[uglistsize]);
00767        /* Don't queue ungoto info or we end up in a loop */
00768        dotgoto(ipc, buf, 0, 1);
00769 }
00770 
00771 
00772 /*
00773  * saves filelen bytes from file at pathname
00774  */
00775 int save_buffer(void *file, size_t filelen, const char *pathname)
00776 {
00777        size_t block = 0;
00778        size_t bytes_written = 0;
00779        FILE *fp;
00780 
00781        fp = fopen(pathname, "w");
00782        if (!fp) {
00783               scr_printf("Cannot open '%s': %s\n", pathname, strerror(errno));
00784               return 0;
00785        }
00786        do {
00787               block = fwrite((char *)file + bytes_written, 1,
00788                             filelen - bytes_written, fp);
00789               bytes_written += block;
00790        } while (errno == EINTR && bytes_written < filelen);
00791        fclose(fp);
00792 
00793        if (bytes_written < filelen) {
00794               scr_printf("Trouble saving '%s': %s\n", pathname,
00795                             strerror(errno));
00796               return 0;
00797        }
00798        return 1;
00799 }
00800 
00801 
00802 /*
00803  * Save supplied_filename in dest directory; gets the name only
00804  */
00805 void destination_directory(char *dest, const char *supplied_filename)
00806 {
00807        static char save_dir[SIZ] = { 0 };
00808 
00809        if (IsEmptyStr(save_dir)) {
00810               if (getenv("HOME") == NULL) {
00811                      strcpy(save_dir, ".");
00812               }
00813               else {
00814                      sprintf(save_dir, "%s/Desktop", getenv("HOME"));
00815                      if (access(save_dir, W_OK) != 0) {
00816                             sprintf(save_dir, "%s", getenv("HOME"));
00817                             if (access(save_dir, W_OK) != 0) {
00818                                    sprintf(save_dir, ".");
00819                             }
00820                      }
00821               }
00822        }
00823 
00824        sprintf(dest, "%s/%s", save_dir, supplied_filename);
00825        strprompt("Save as", dest, PATH_MAX);
00826 
00827        /* Remember the directory for next time */
00828        strcpy(save_dir, dest);
00829        if (strrchr(save_dir, '/') != NULL) {
00830               strcpy(strrchr(save_dir, '/'), "");
00831        }
00832        else {
00833               strcpy(save_dir, ".");
00834        }
00835 }
00836 
00837 
00838 /*
00839  * download()  -  download a file or files.  The argument passed to this
00840  *                function determines which protocol to use.
00841  *  proto - 0 = paginate, 1 = xmodem, 2 = raw, 3 = ymodem, 4 = zmodem, 5 = save
00842  */
00843 void download(CtdlIPC *ipc, int proto)
00844 {
00845        char buf[SIZ];
00846        char filename[PATH_MAX];
00847        char tempname[PATH_MAX];
00848        char transmit_cmd[SIZ];
00849        FILE *tpipe = NULL;
00850 /*     int broken = 0;*/
00851        int r;
00852        int rv = 0;
00853        void *file = NULL;   /* The downloaded file */
00854        size_t filelen = 0L; /* The downloaded file length */
00855 
00856        if ((room_flags & QR_DOWNLOAD) == 0) {
00857               scr_printf("*** You cannot download from this room.\n");
00858               return;
00859        }
00860 
00861        newprompt("Enter filename: ", filename, PATH_MAX);
00862 
00863        /* Save to local disk, for folks with their own copy of the client */
00864        if (proto == 5) {
00865               destination_directory(tempname, filename);
00866               r = CtdlIPCFileDownload(ipc, filename, &file, 0, progress, buf);
00867               if (r / 100 != 2) {
00868                      scr_printf("%s\n", buf);
00869                      return;
00870               }
00871               save_buffer(file, (size_t)extract_long(buf, 0), tempname);
00872               free(file);
00873               return;
00874        }
00875 
00876        r = CtdlIPCFileDownload(ipc, filename, &file, 0, progress, buf);
00877        if (r / 100 != 2) {
00878               scr_printf("%s\n", buf);
00879               return;
00880        }
00881        filelen = extract_unsigned_long(buf, 0);
00882 
00883        /* Meta-download for public clients */
00884        /* scr_printf("Fetching file from Citadel server...\n"); */
00885        mkdir(tempdir, 0700);
00886        snprintf(tempname, sizeof tempname, "%s/%s", tempdir, filename);
00887        tpipe = fopen(tempname, "wb");
00888        if (fwrite(file, filelen, 1, tpipe) < filelen) {
00889               /* FIXME: restart syscall on EINTR 
00890                  broken = 1;*/
00891        }
00892        fclose(tpipe);
00893        if (file) free(file);
00894 
00895        if (proto == 0) {
00896               /* FIXME: display internally instead */
00897               snprintf(transmit_cmd, sizeof transmit_cmd,
00898                      "SHELL=/dev/null; export SHELL; TERM=dumb; export TERM; exec more -d <%s",
00899                      tempname);
00900        }
00901        else if (proto == 1)
00902               snprintf(transmit_cmd, sizeof transmit_cmd, "exec sx %s", tempname);
00903        else if (proto == 3)
00904               snprintf(transmit_cmd, sizeof transmit_cmd, "exec sb %s", tempname);
00905        else if (proto == 4)
00906               snprintf(transmit_cmd, sizeof transmit_cmd, "exec sz %s", tempname);
00907        else
00908               /* FIXME: display internally instead */
00909               snprintf(transmit_cmd, sizeof transmit_cmd, "exec cat %s", tempname);
00910 
00911        stty_ctdl(SB_RESTORE);
00912        rv = system(transmit_cmd);
00913        if (rv != 0)
00914               scr_printf("failed to download '%s': %d\n", transmit_cmd, rv);
00915        stty_ctdl(SB_NO_INTR);
00916 
00917        /* clean up the temporary directory */
00918        nukedir(tempdir);
00919        ctdl_beep();  /* Beep beep! */
00920 }
00921 
00922 
00923 /*
00924  * read directory of this room
00925  */
00926 void roomdir(CtdlIPC *ipc)
00927 {
00928        char flnm[256];
00929        char flsz[32];
00930        char comment[256];
00931        char mimetype[256];
00932        char buf[256];
00933        char *listing = NULL;       /* Returned directory listing */
00934        int r;
00935 
00936        r = CtdlIPCReadDirectory(ipc, &listing, buf);
00937        if (r / 100 != 1) {
00938               scr_printf("%s\n", buf);
00939               return;
00940        }
00941 
00942        extract_token(comment, buf, 0, '|', sizeof comment);
00943        extract_token(flnm, buf, 1, '|', sizeof flnm);
00944        scr_printf("\nDirectory of %s on %s\n", flnm, comment);
00945        scr_printf("-----------------------\n");
00946        while (listing && *listing && !IsEmptyStr(listing)) {
00947               extract_token(buf, listing, 0, '\n', sizeof buf);
00948               remove_token(listing, 0, '\n');
00949 
00950               extract_token(flnm, buf, 0, '|', sizeof flnm);
00951               extract_token(flsz, buf, 1, '|', sizeof flsz);
00952               extract_token(mimetype, buf, 2, '|', sizeof mimetype);
00953               extract_token(comment, buf, 3, '|', sizeof comment);
00954               if (strlen(flnm) <= 14)
00955                      scr_printf("%-14s %8s %s [%s]\n", flnm, flsz, comment, mimetype);
00956               else
00957                      scr_printf("%s\n%14s %8s %s [%s]\n", flnm, "", flsz,
00958                             comment, mimetype);
00959        }
00960        if (listing) free(listing);
00961 }
00962 
00963 
00964 /*
00965  * add a user to a private room
00966  */
00967 void invite(CtdlIPC *ipc)
00968 {
00969        char username[USERNAME_SIZE];
00970        char buf[SIZ];
00971 
00972        newprompt("Name of user? ", username, USERNAME_SIZE);
00973        if (username[0] == 0)
00974               return;
00975 
00976        CtdlIPCInviteUserToRoom(ipc, username, buf);
00977        scr_printf("%s\n", buf);
00978 }
00979 
00980 
00981 /*
00982  * kick a user out of a room
00983  */
00984 void kickout(CtdlIPC *ipc)
00985 {
00986        char username[USERNAME_SIZE];
00987        char buf[SIZ];
00988 
00989        newprompt("Name of user? ", username, USERNAME_SIZE);
00990        if (username[0] == 0)
00991               return;
00992 
00993        CtdlIPCKickoutUserFromRoom(ipc, username, buf);
00994        scr_printf("%s\n", buf);
00995 }
00996 
00997 
00998 /*
00999  * aide command: kill the current room
01000  */
01001 void killroom(CtdlIPC *ipc)
01002 {
01003        char aaa[100];
01004        int r;
01005 
01006        r = CtdlIPCDeleteRoom(ipc, 0, aaa);
01007        if (r / 100 != 2) {
01008               scr_printf("%s\n", aaa);
01009               return;
01010        }
01011 
01012        scr_printf("Are you sure you want to kill this room? ");
01013        if (yesno() == 0)
01014               return;
01015 
01016        r = CtdlIPCDeleteRoom(ipc, 1, aaa);
01017        scr_printf("%s\n", aaa);
01018        if (r / 100 != 2)
01019               return;
01020        dotgoto(ipc, "_BASEROOM_", 0, 0);
01021 }
01022 
01023 void forget(CtdlIPC *ipc)
01024 {                           /* forget the current room */
01025        char buf[SIZ];
01026 
01027        scr_printf("Are you sure you want to forget this room? ");
01028        if (yesno() == 0)
01029               return;
01030 
01031        remove_march(room_name, 0);
01032        if (CtdlIPCForgetRoom(ipc, buf) / 100 != 2) {
01033               scr_printf("%s\n", buf);
01034               return;
01035        }
01036 
01037        /* now return to the lobby */
01038        dotgoto(ipc, "_BASEROOM_", 0, 0);
01039 }
01040 
01041 
01042 /*
01043  * create a new room
01044  */
01045 void entroom(CtdlIPC *ipc)
01046 {
01047        char buf[SIZ];
01048        char new_room_name[ROOMNAMELEN];
01049        int new_room_type;
01050        char new_room_pass[10];
01051        int new_room_floor;
01052        int a, b;
01053        int r;                      /* IPC response code */
01054 
01055        /* Check permission to create room */
01056        r = CtdlIPCCreateRoom(ipc, 0, "", 1, "", 0, buf);
01057        if (r / 100 != 2) {
01058               scr_printf("%s\n", buf);
01059               return;
01060        }
01061 
01062        newprompt("Name for new room? ", new_room_name, ROOMNAMELEN - 1);
01063        if (IsEmptyStr(new_room_name)) {
01064               return;
01065        }
01066        for (a = 0; !IsEmptyStr(&new_room_name[a]); ++a) {
01067               if (new_room_name[a] == '|') {
01068                      new_room_name[a] = '_';
01069               }
01070        }
01071 
01072        new_room_floor = select_floor(ipc, (int) curr_floor);
01073 
01074        IFNEXPERT formout(ipc, "roomaccess");
01075        do {
01076               scr_printf("<?>Help\n"
01077                      "<1>Public room (shown to all users by default)\n"
01078                      "<2>Hidden room (accessible to anyone who knows the room name)\n"
01079                      "<3>Passworded room (hidden, plus requires a password to enter)\n"
01080                      "<4>Invitation-only room (requires access to be granted by an Aide)\n"
01081                             "<5>Personal room (accessible to you only)\n"
01082                      "Enter room type: "
01083               );
01084               do {
01085                      b = inkey();
01086               } while (((b < '1') || (b > '5')) && (b != '?'));
01087               if (b == '?') {
01088                      scr_printf("?\n");
01089                      formout(ipc, "roomaccess");
01090               }
01091        } while ((b < '1') || (b > '5'));
01092        b -= '0';                   /* Portable */
01093        scr_printf("%d\n", b);
01094        new_room_type = b - 1;
01095        if (new_room_type == 2) {
01096               newprompt("Enter a room password: ", new_room_pass, 9);
01097               for (a = 0; !IsEmptyStr(&new_room_pass[a]); ++a)
01098                      if (new_room_pass[a] == '|')
01099                             new_room_pass[a] = '_';
01100        } else {
01101               strcpy(new_room_pass, "");
01102        }
01103 
01104        scr_printf("\042%s\042, a", new_room_name);
01105        if (b == 1)
01106               scr_printf(" public room.");
01107        if (b == 2)
01108               scr_printf(" hidden room.");
01109        if (b == 3)
01110               scr_printf(" passworded room, password: %s", new_room_pass);
01111        if (b == 4)
01112               scr_printf("n invitation-only room.");
01113        if (b == 5)
01114               scr_printf(" personal room.");
01115        scr_printf("\nInstall it? (y/n) : ");
01116        if (yesno() == 0) {
01117               return;
01118        }
01119 
01120        r = CtdlIPCCreateRoom(ipc, 1, new_room_name, new_room_type,
01121                            new_room_pass, new_room_floor, buf);
01122        if (r / 100 != 2) {
01123               scr_printf("%s\n", buf);
01124               return;
01125        }
01126 
01127        /* command succeeded... now GO to the new room! */
01128        dotgoto(ipc, new_room_name, 0, 0);
01129 }
01130 
01131 
01132 
01133 void readinfo(CtdlIPC *ipc)
01134 {                           /* read info file for current room */
01135        char buf[SIZ];
01136        char raide[64];
01137        int r;               /* IPC response code */
01138        char *text = NULL;
01139 
01140        /* Name of currernt room aide */
01141        r = CtdlIPCGetRoomAide(ipc, buf);
01142        if (r / 100 == 2)
01143               safestrncpy(raide, buf, sizeof raide);
01144        else
01145               strcpy(raide, "");
01146 
01147        if (!IsEmptyStr(raide))
01148               scr_printf("Room aide is %s.\n\n", raide);
01149 
01150        r = CtdlIPCRoomInfo(ipc, &text, buf);
01151        if (r / 100 != 1)
01152               return;
01153 
01154        if (text) {
01155               fmout(screenwidth, NULL, text, NULL, 1);
01156               free(text);
01157        }
01158 }
01159 
01160 
01161 /*
01162  * <W>ho knows room...
01163  */
01164 void whoknows(CtdlIPC *ipc)
01165 {
01166        char buf[256];
01167        char *listing = NULL;
01168        int r;
01169 
01170        r = CtdlIPCWhoKnowsRoom(ipc, &listing, buf);
01171        if (r / 100 != 1) {
01172               scr_printf("%s\n", buf);
01173               return;
01174        }
01175        while (!IsEmptyStr(listing)) {
01176               extract_token(buf, listing, 0, '\n', sizeof buf);
01177               remove_token(listing, 0, '\n');
01178               if (sigcaught == 0)
01179                      scr_printf("%s\n", buf);
01180        }
01181        free(listing);
01182 }
01183 
01184 
01185 void do_edit(CtdlIPC *ipc,
01186               char *desc, char *read_cmd, char *check_cmd, char *write_cmd)
01187 {
01188        FILE *fp;
01189        char cmd[SIZ];
01190        int b, cksum, editor_exit;
01191 
01192        if (IsEmptyStr(editor_path)) {
01193               scr_printf("Do you wish to re-enter %s? ", desc);
01194               if (yesno() == 0)
01195                      return;
01196        }
01197 
01198        fp = fopen(temp, "w");
01199        fclose(fp);
01200 
01201        CtdlIPC_chat_send(ipc, check_cmd);
01202        CtdlIPC_chat_recv(ipc, cmd);
01203        if (cmd[0] != '2') {
01204               scr_printf("%s\n", &cmd[4]);
01205               return;
01206        }
01207 
01208        if (!IsEmptyStr(editor_path)) {
01209               CtdlIPC_chat_send(ipc, read_cmd);
01210               CtdlIPC_chat_recv(ipc, cmd);
01211               if (cmd[0] == '1') {
01212                      fp = fopen(temp, "w");
01213                      while (CtdlIPC_chat_recv(ipc, cmd), strcmp(cmd, "000")) {
01214                             fprintf(fp, "%s\n", cmd);
01215                      }
01216                      fclose(fp);
01217               }
01218        }
01219 
01220        cksum = file_checksum(temp);
01221 
01222        if (!IsEmptyStr(editor_path)) {
01223               char tmp[SIZ];
01224 
01225               snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", desc);
01226               putenv(tmp);
01227               stty_ctdl(SB_RESTORE);
01228               editor_pid = fork();
01229               if (editor_pid == 0) {
01230                      chmod(temp, 0600);
01231                      execlp(editor_path, editor_path, temp, NULL);
01232                      exit(1);
01233               }
01234               if (editor_pid > 0)
01235                      do {
01236                             editor_exit = 0;
01237                             b = ka_wait(&editor_exit);
01238                      } while ((b != editor_pid) && (b >= 0));
01239               editor_pid = (-1);
01240               scr_printf("Executed %s\n", editor_path);
01241               stty_ctdl(0);
01242        } else {
01243               scr_printf("Entering %s.  Press return twice when finished.\n", desc);
01244               fp = fopen(temp, "r+");
01245               citedit(fp);
01246               fclose(fp);
01247        }
01248 
01249        if (file_checksum(temp) == cksum) {
01250               scr_printf("*** Aborted.\n");
01251        }
01252 
01253        else {
01254               CtdlIPC_chat_send(ipc, write_cmd);
01255               CtdlIPC_chat_recv(ipc, cmd);
01256               if (cmd[0] != '4') {
01257                      scr_printf("%s\n", &cmd[4]);
01258                      return;
01259               }
01260 
01261               fp = fopen(temp, "r");
01262               while (fgets(cmd, SIZ - 1, fp) != NULL) {
01263                      cmd[strlen(cmd) - 1] = 0;
01264                      CtdlIPC_chat_send(ipc, cmd);
01265               }
01266               fclose(fp);
01267               CtdlIPC_chat_send(ipc, "000");
01268        }
01269 
01270        unlink(temp);
01271 }
01272 
01273 
01274 void enterinfo(CtdlIPC *ipc)
01275 {                           /* edit info file for current room */
01276        do_edit(ipc, "the Info file for this room", "RINF", "EINF 0", "EINF 1");
01277 }
01278 
01279 void enter_bio(CtdlIPC *ipc)
01280 {
01281        char cmd[SIZ];
01282        snprintf(cmd, sizeof cmd, "RBIO %s", fullname);
01283        do_edit(ipc, "your Bio", cmd, "NOOP", "EBIO");
01284 }
01285 
01286 /*
01287  * create a new floor
01288  */
01289 void create_floor(CtdlIPC *ipc)
01290 {
01291        char buf[SIZ];
01292        char newfloorname[SIZ];
01293        int r;               /* IPC response code */
01294 
01295        load_floorlist(ipc);
01296 
01297        r = CtdlIPCCreateFloor(ipc, 0, "", buf);
01298        if ( (r / 100 != 2) && (r != ERROR + ILLEGAL_VALUE) ) {
01299               scr_printf("%s\n", buf);
01300               return;
01301        }
01302 
01303        newprompt("Name for new floor: ", newfloorname, 255);
01304        if (!*newfloorname) return;
01305        r = CtdlIPCCreateFloor(ipc, 1, newfloorname, buf);
01306        if (r / 100 == 2) {
01307               scr_printf("Floor has been created.\n");
01308        } else {
01309               scr_printf("%s\n", buf);
01310        }
01311 
01312        load_floorlist(ipc);
01313 }
01314 
01315 /*
01316  * edit the current floor
01317  */
01318 void edit_floor(CtdlIPC *ipc)
01319 {
01320        char buf[SIZ];
01321        struct ExpirePolicy *ep = NULL;
01322 
01323        load_floorlist(ipc);
01324 
01325        /* Fetch the expire policy (this will silently fail on old servers,
01326         * resulting in "default" policy)
01327         */
01328        CtdlIPCGetMessageExpirationPolicy(ipc, 1, &ep, buf);
01329 
01330        /* Interact with the user */
01331        scr_printf("You are editing the floor called \"%s\"\n", 
01332               &floorlist[(int) curr_floor][0] );
01333        strprompt("Floor name", &floorlist[(int) curr_floor][0], 255);
01334 
01335        /* Angels and demons dancing in my head... */
01336        do {
01337               snprintf(buf, sizeof buf, "%d", ep->expire_mode);
01338               strprompt
01339                   ("Floor default message expire policy (? for list)",
01340                    buf, 1);
01341               if (buf[0] == '?') {
01342                      scr_printf("\n"
01343                             "0. Use the system default\n"
01344                             "1. Never automatically expire messages\n"
01345                             "2. Expire by message count\n"
01346                             "3. Expire by message age\n");
01347               }
01348        } while ((buf[0] < '0') || (buf[0] > '3'));
01349        ep->expire_mode = buf[0] - '0';
01350 
01351        /* ...lunatics and monsters underneath my bed */
01352        if (ep->expire_mode == 2) {
01353               snprintf(buf, sizeof buf, "%d", ep->expire_value);
01354               strprompt("Keep how many messages online?", buf, 10);
01355               ep->expire_value = atol(buf);
01356        }
01357 
01358        if (ep->expire_mode == 3) {
01359               snprintf(buf, sizeof buf, "%d", ep->expire_value);
01360               strprompt("Keep messages for how many days?", buf, 10);
01361               ep->expire_value = atol(buf);
01362        }
01363 
01364        /* Save it */
01365        CtdlIPCSetMessageExpirationPolicy(ipc, 1, ep, buf);
01366        CtdlIPCEditFloor(ipc, curr_floor, &floorlist[(int)curr_floor][0], buf);
01367        scr_printf("%s\n", buf);
01368        load_floorlist(ipc);
01369 }
01370 
01371 
01372 
01373 
01374 /*
01375  * kill the current floor 
01376  */
01377 void kill_floor(CtdlIPC *ipc)
01378 {
01379        int floornum_to_delete, a;
01380        char buf[SIZ];
01381 
01382        load_floorlist(ipc);
01383        do {
01384               floornum_to_delete = (-1);
01385               scr_printf("(Press return to abort)\n");
01386               newprompt("Delete which floor? ", buf, 255);
01387               if (IsEmptyStr(buf))
01388                      return;
01389               for (a = 0; a < 128; ++a)
01390                      if (!strcasecmp(&floorlist[a][0], buf))
01391                             floornum_to_delete = a;
01392               if (floornum_to_delete < 0) {
01393                      scr_printf("No such floor.  Select one of:\n");
01394                      for (a = 0; a < 128; ++a)
01395                             if (floorlist[a][0] != 0)
01396                                    scr_printf("%s\n", &floorlist[a][0]);
01397               }
01398        } while (floornum_to_delete < 0);
01399        CtdlIPCDeleteFloor(ipc, 1, floornum_to_delete, buf);
01400        scr_printf("%s\n", buf);
01401        load_floorlist(ipc);
01402 }