"Fossies" - the Fresh Open Source Software Archive

Member "citadel/modules/ctdlproto/serv_rooms.c" (5 Jun 2021, 29514 Bytes) of package /linux/www/citadel.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "serv_rooms.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.01_vs_902.

    1 /* 
    2  * Server functions which perform operations on room objects.
    3  *
    4  * Copyright (c) 1987-2020 by the citadel.org team
    5  *
    6  * This program is open source software; you can redistribute it and/or modify
    7  * it under the terms of the GNU General Public License, version 3.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  */
   14 
   15 #include <stdlib.h>
   16 #include <unistd.h>
   17 #include <stdio.h>
   18 #include <sys/types.h>
   19 #include <sys/stat.h>
   20 #include <dirent.h> /* for cmd_rdir to read contents of the directory */
   21 #include <libcitadel.h>
   22 
   23 #include "citserver.h"
   24 #include "ctdl_module.h"
   25 #include "room_ops.h"
   26 #include "config.h"
   27 
   28 /*
   29  * Back-back-end for all room listing commands
   30  */
   31 void list_roomname(struct ctdlroom *qrbuf, int ra, int current_view, int default_view)
   32 {
   33     char truncated_roomname[ROOMNAMELEN];
   34 
   35     /* For my own mailbox rooms, chop off the owner prefix */
   36     if ( (qrbuf->QRflags & QR_MAILBOX)
   37          && (atol(qrbuf->QRname) == CC->user.usernum) ) {
   38         safestrncpy(truncated_roomname, qrbuf->QRname, sizeof truncated_roomname);
   39         safestrncpy(truncated_roomname, &truncated_roomname[11], sizeof truncated_roomname);
   40         cprintf("%s", truncated_roomname);
   41     }
   42     /* For all other rooms, just display the name in its entirety */
   43     else {
   44         cprintf("%s", qrbuf->QRname);
   45     }
   46 
   47     /* ...and now the other parameters */
   48     cprintf("|%u|%d|%d|%d|%d|%d|%d|%ld|\n",
   49         qrbuf->QRflags,
   50         (int) qrbuf->QRfloor,
   51         (int) qrbuf->QRorder,
   52         (int) qrbuf->QRflags2,
   53         ra,
   54         current_view,
   55         default_view,
   56         qrbuf->QRmtime
   57     );
   58 }
   59 
   60 
   61 /* 
   62  * cmd_lrms()   -  List all accessible rooms, known or forgotten
   63  */
   64 void cmd_lrms_backend(struct ctdlroom *qrbuf, void *data)
   65 {
   66     int FloorBeingSearched = (-1);
   67     int ra;
   68     int view;
   69 
   70     FloorBeingSearched = *(int *)data;
   71     CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
   72 
   73     if ((( ra & (UA_KNOWN | UA_ZAPPED)))
   74         && ((qrbuf->QRfloor == (FloorBeingSearched))
   75         || ((FloorBeingSearched) < 0)))
   76         list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
   77 }
   78 
   79 void cmd_lrms(char *argbuf)
   80 {
   81     int FloorBeingSearched = (-1);
   82     if (!IsEmptyStr(argbuf))
   83         FloorBeingSearched = extract_int(argbuf, 0);
   84 
   85     if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
   86 
   87     CtdlGetUser(&CC->user, CC->curr_user);
   88     cprintf("%d Accessible rooms:\n", LISTING_FOLLOWS);
   89 
   90     CtdlForEachRoom(cmd_lrms_backend, &FloorBeingSearched);
   91     cprintf("000\n");
   92 }
   93 
   94 
   95 
   96 /* 
   97  * cmd_lkra()   -  List all known rooms
   98  */
   99 void cmd_lkra_backend(struct ctdlroom *qrbuf, void *data)
  100 {
  101     int FloorBeingSearched = (-1);
  102     int ra;
  103     int view;
  104 
  105     FloorBeingSearched = *(int *)data;
  106     CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
  107 
  108     if ((( ra & (UA_KNOWN)))
  109         && ((qrbuf->QRfloor == (FloorBeingSearched))
  110         || ((FloorBeingSearched) < 0)))
  111         list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
  112 }
  113 
  114 void cmd_lkra(char *argbuf)
  115 {
  116     int FloorBeingSearched = (-1);
  117     if (!IsEmptyStr(argbuf))
  118         FloorBeingSearched = extract_int(argbuf, 0);
  119 
  120     if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
  121     
  122     CtdlGetUser(&CC->user, CC->curr_user);
  123     cprintf("%d Known rooms:\n", LISTING_FOLLOWS);
  124 
  125     CtdlForEachRoom(cmd_lkra_backend, &FloorBeingSearched);
  126     cprintf("000\n");
  127 }
  128 
  129 
  130 
  131 void cmd_lprm_backend(struct ctdlroom *qrbuf, void *data)
  132 {
  133     int FloorBeingSearched = (-1);
  134     int ra;
  135     int view;
  136 
  137     FloorBeingSearched = *(int *)data;
  138     CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
  139 
  140     if (   ((qrbuf->QRflags & QR_PRIVATE) == 0)
  141         && ((qrbuf->QRflags & QR_MAILBOX) == 0)
  142         && ((qrbuf->QRfloor == (FloorBeingSearched))
  143         || ((FloorBeingSearched) < 0)))
  144         list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
  145 }
  146 
  147 void cmd_lprm(char *argbuf)
  148 {
  149     int FloorBeingSearched = (-1);
  150     if (!IsEmptyStr(argbuf))
  151         FloorBeingSearched = extract_int(argbuf, 0);
  152 
  153     cprintf("%d Public rooms:\n", LISTING_FOLLOWS);
  154 
  155     CtdlForEachRoom(cmd_lprm_backend, &FloorBeingSearched);
  156     cprintf("000\n");
  157 }
  158 
  159 
  160 
  161 /* 
  162  * cmd_lkrn()   -  List all known rooms with new messages
  163  */
  164 void cmd_lkrn_backend(struct ctdlroom *qrbuf, void *data)
  165 {
  166     int FloorBeingSearched = (-1);
  167     int ra;
  168     int view;
  169 
  170     FloorBeingSearched = *(int *)data;
  171     CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
  172 
  173     if ((ra & UA_KNOWN)
  174         && (ra & UA_HASNEWMSGS)
  175         && ((qrbuf->QRfloor == (FloorBeingSearched))
  176         || ((FloorBeingSearched) < 0)))
  177         list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
  178 }
  179 
  180 void cmd_lkrn(char *argbuf)
  181 {
  182     int FloorBeingSearched = (-1);
  183     if (!IsEmptyStr(argbuf))
  184         FloorBeingSearched = extract_int(argbuf, 0);
  185 
  186     if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
  187     
  188     CtdlGetUser(&CC->user, CC->curr_user);
  189     cprintf("%d Rooms w/ new msgs:\n", LISTING_FOLLOWS);
  190 
  191     CtdlForEachRoom(cmd_lkrn_backend, &FloorBeingSearched);
  192     cprintf("000\n");
  193 }
  194 
  195 
  196 
  197 /* 
  198  * cmd_lkro()   -  List all known rooms
  199  */
  200 void cmd_lkro_backend(struct ctdlroom *qrbuf, void *data)
  201 {
  202     int FloorBeingSearched = (-1);
  203     int ra;
  204     int view;
  205 
  206     FloorBeingSearched = *(int *)data;
  207     CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
  208 
  209     if ((ra & UA_KNOWN)
  210         && ((ra & UA_HASNEWMSGS) == 0)
  211         && ((qrbuf->QRfloor == (FloorBeingSearched))
  212         || ((FloorBeingSearched) < 0)))
  213         list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
  214 }
  215 
  216 void cmd_lkro(char *argbuf)
  217 {
  218     int FloorBeingSearched = (-1);
  219     if (!IsEmptyStr(argbuf))
  220         FloorBeingSearched = extract_int(argbuf, 0);
  221 
  222     if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
  223     
  224     CtdlGetUser(&CC->user, CC->curr_user);
  225     cprintf("%d Rooms w/o new msgs:\n", LISTING_FOLLOWS);
  226 
  227     CtdlForEachRoom(cmd_lkro_backend, &FloorBeingSearched);
  228     cprintf("000\n");
  229 }
  230 
  231 
  232 
  233 /* 
  234  * cmd_lzrm()   -  List all forgotten rooms
  235  */
  236 void cmd_lzrm_backend(struct ctdlroom *qrbuf, void *data)
  237 {
  238     int FloorBeingSearched = (-1);
  239     int ra;
  240     int view;
  241 
  242     FloorBeingSearched = *(int *)data;
  243     CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
  244 
  245     if ((ra & UA_GOTOALLOWED)
  246         && (ra & UA_ZAPPED)
  247         && ((qrbuf->QRfloor == (FloorBeingSearched))
  248         || ((FloorBeingSearched) < 0)))
  249         list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
  250 }
  251 
  252 void cmd_lzrm(char *argbuf)
  253 {
  254     int FloorBeingSearched = (-1);
  255     if (!IsEmptyStr(argbuf))
  256         FloorBeingSearched = extract_int(argbuf, 0);
  257 
  258     if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
  259     
  260     CtdlGetUser(&CC->user, CC->curr_user);
  261     cprintf("%d Zapped rooms:\n", LISTING_FOLLOWS);
  262 
  263     CtdlForEachRoom(cmd_lzrm_backend, &FloorBeingSearched);
  264     cprintf("000\n");
  265 }
  266 
  267 
  268 /* 
  269  * cmd_goto()  -  goto a new room
  270  */
  271 void cmd_goto(char *gargs)
  272 {
  273     struct CitContext *CCC = CC;
  274     struct ctdlroom QRscratch;
  275     int c;
  276     int ok = 0;
  277     int ra;
  278     char augmented_roomname[ROOMNAMELEN];
  279     char towhere[ROOMNAMELEN];
  280     char password[32];
  281     int transiently = 0;
  282 
  283     if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
  284 
  285     extract_token(towhere, gargs, 0, '|', sizeof towhere);
  286     extract_token(password, gargs, 1, '|', sizeof password);
  287     transiently = extract_int(gargs, 2);
  288 
  289     CtdlGetUser(&CCC->user, CCC->curr_user);
  290 
  291     /*
  292      * Handle some of the macro named rooms
  293      */
  294     convert_room_name_macros(towhere, sizeof towhere);
  295 
  296     /* First try a regular match */
  297     c = CtdlGetRoom(&QRscratch, towhere);
  298 
  299     /* Then try a mailbox name match */
  300     if (c != 0) {
  301         CtdlMailboxName(augmented_roomname, sizeof augmented_roomname,
  302                 &CCC->user, towhere);
  303         c = CtdlGetRoom(&QRscratch, augmented_roomname);
  304         if (c == 0)
  305             safestrncpy(towhere, augmented_roomname, sizeof towhere);
  306     }
  307 
  308     /* And if the room was found... */
  309     if (c == 0) {
  310 
  311         /* Let internal programs go directly to any room. */
  312         if (CCC->internal_pgm) {
  313             memcpy(&CCC->room, &QRscratch,
  314                 sizeof(struct ctdlroom));
  315             CtdlUserGoto(NULL, 1, transiently, NULL, NULL, NULL, NULL);
  316             return;
  317         }
  318 
  319         /* See if there is an existing user/room relationship */
  320         CtdlRoomAccess(&QRscratch, &CCC->user, &ra, NULL);
  321 
  322         /* normal clients have to pass through security */
  323         if (ra & UA_GOTOALLOWED) {
  324             ok = 1;
  325         }
  326 
  327         if (ok == 1) {
  328             if ((QRscratch.QRflags & QR_MAILBOX) &&
  329                 ((ra & UA_GOTOALLOWED))) {
  330                 memcpy(&CCC->room, &QRscratch,
  331                     sizeof(struct ctdlroom));
  332                 CtdlUserGoto(NULL, 1, transiently, NULL, NULL, NULL, NULL);
  333                 return;
  334             } else if ((QRscratch.QRflags & QR_PASSWORDED) &&
  335                 ((ra & UA_KNOWN) == 0) &&
  336                 (strcasecmp(QRscratch.QRpasswd, password)) &&
  337                 (CCC->user.axlevel < AxAideU)
  338                 ) {
  339                 cprintf("%d wrong or missing passwd\n",
  340                     ERROR + PASSWORD_REQUIRED);
  341                 return;
  342             } else if ((QRscratch.QRflags & QR_PRIVATE) &&
  343                    ((QRscratch.QRflags & QR_PASSWORDED) == 0) &&
  344                    ((QRscratch.QRflags & QR_GUESSNAME) == 0) &&
  345                    ((ra & UA_KNOWN) == 0) &&
  346                        (CCC->user.axlevel < AxAideU)
  347                                   ) {
  348                 syslog(LOG_DEBUG, "rooms: failed to acquire private room");
  349             } else {
  350                 memcpy(&CCC->room, &QRscratch,
  351                     sizeof(struct ctdlroom));
  352                 CtdlUserGoto(NULL, 1, transiently, NULL, NULL, NULL, NULL);
  353                 return;
  354             }
  355         }
  356     }
  357 
  358     cprintf("%d room '%s' not found\n", ERROR + ROOM_NOT_FOUND, towhere);
  359 }
  360 
  361 
  362 void cmd_whok(char *cmdbuf)
  363 {
  364     struct ctdluser temp;
  365     struct cdbdata *cdbus;
  366     int ra;
  367 
  368     cprintf("%d Who knows room:\n", LISTING_FOLLOWS);
  369     cdb_rewind(CDB_USERS);
  370     while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
  371         memset(&temp, 0, sizeof temp);
  372         memcpy(&temp, cdbus->ptr, sizeof temp);
  373         cdb_free(cdbus);
  374 
  375         CtdlRoomAccess(&CC->room, &temp, &ra, NULL);
  376         if ((!IsEmptyStr(temp.fullname)) && 
  377             (CC->room.QRflags & QR_INUSE) &&
  378             (ra & UA_KNOWN)
  379             )
  380             cprintf("%s\n", temp.fullname);
  381     }
  382     cprintf("000\n");
  383 }
  384 
  385 
  386 /*
  387  * RDIR command for room directory
  388  */
  389 void cmd_rdir(char *cmdbuf)
  390 {
  391     char buf[256];
  392     char comment[256];
  393     FILE *fd;
  394     struct stat statbuf;
  395     DIR *filedir = NULL;
  396     struct dirent *filedir_entry;
  397     int d_namelen;
  398     char buf2[SIZ];
  399     char mimebuf[64];
  400     long len;
  401     
  402     if (CtdlAccessCheck(ac_logged_in)) return;
  403     
  404     CtdlGetRoom(&CC->room, CC->room.QRname);
  405     CtdlGetUser(&CC->user, CC->curr_user);
  406 
  407     if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
  408         cprintf("%d not here.\n", ERROR + NOT_HERE);
  409         return;
  410     }
  411     if (((CC->room.QRflags & QR_VISDIR) == 0)
  412         && (CC->user.axlevel < AxAideU)
  413         && (CC->user.usernum != CC->room.QRroomaide)) {
  414         cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
  415         return;
  416     }
  417 
  418     snprintf(buf, sizeof buf, "%s/%s", ctdl_file_dir, CC->room.QRdirname);
  419     filedir = opendir (buf);
  420     
  421     if (filedir == NULL) {
  422         cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
  423         return;
  424     }
  425     cprintf("%d %s|%s/%s\n", LISTING_FOLLOWS, CtdlGetConfigStr("c_fqdn"), ctdl_file_dir, CC->room.QRdirname);
  426     
  427     snprintf(buf, sizeof buf, "%s/%s/filedir", ctdl_file_dir, CC->room.QRdirname);
  428     fd = fopen(buf, "r");
  429     if (fd == NULL)
  430         fd = fopen("/dev/null", "r");
  431     while ((filedir_entry = readdir(filedir)))
  432     {
  433         if (strcasecmp(filedir_entry->d_name, "filedir") && filedir_entry->d_name[0] != '.')
  434         {
  435 #ifdef _DIRENT_HAVE_D_NAMELEN
  436             d_namelen = filedir_entry->d_namlen;
  437 #else
  438             d_namelen = strlen(filedir_entry->d_name);
  439 #endif
  440             snprintf(buf, sizeof buf, "%s/%s/%s", ctdl_file_dir, CC->room.QRdirname, filedir_entry->d_name);
  441             stat(buf, &statbuf);    /* stat the file */
  442             if (!(statbuf.st_mode & S_IFREG))
  443             {
  444                 snprintf(buf2, sizeof buf2,
  445                     "\"%s\" appears in the file directory for room \"%s\" but is not a regular file.  Directories, named pipes, sockets, etc. are not usable in Citadel room directories.\n",
  446                     buf, CC->room.QRname
  447                 );
  448                 CtdlAideMessage(buf2, "Unusable data found in room directory");
  449                 continue;   /* not a useable file type so don't show it */
  450             }
  451             safestrncpy(comment, "", sizeof comment);
  452             fseek(fd, 0L, 0);   /* rewind descriptions file */
  453             /* Get the description from the descriptions file */
  454             while ((fgets(buf, sizeof buf, fd) != NULL) && (IsEmptyStr(comment))) 
  455             {
  456                 buf[strlen(buf) - 1] = 0;
  457                 if ((!strncasecmp(buf, filedir_entry->d_name, d_namelen)) && (buf[d_namelen] == ' '))
  458                     safestrncpy(comment, &buf[d_namelen + 1], sizeof comment);
  459             }
  460             len = extract_token (mimebuf, comment, 0,' ', 64);
  461             if ((len <0) || strchr(mimebuf, '/') == NULL)
  462             {
  463                 snprintf (mimebuf, 64, "application/octetstream");
  464                 len = 0;
  465             }
  466             cprintf("%s|%ld|%s|%s\n", 
  467                 filedir_entry->d_name, 
  468                 (long)statbuf.st_size, 
  469                 mimebuf, 
  470                 &comment[len]);
  471         }
  472     }
  473     fclose(fd);
  474     closedir(filedir);
  475     
  476     cprintf("000\n");
  477 }
  478 
  479 /*
  480  * get room parameters (admin or room admin command)
  481  */
  482 void cmd_getr(char *cmdbuf)
  483 {
  484     if (CtdlAccessCheck(ac_room_aide)) return;
  485 
  486     CtdlGetRoom(&CC->room, CC->room.QRname);
  487     cprintf("%d%c%s|%s|%s|%d|%d|%d|%d|%d|\n",
  488         CIT_OK,
  489         CtdlCheckExpress(),
  490 
  491         ((CC->room.QRflags & QR_MAILBOX) ?
  492             &CC->room.QRname[11] : CC->room.QRname),
  493 
  494         ((CC->room.QRflags & QR_PASSWORDED) ?
  495             CC->room.QRpasswd : ""),
  496 
  497         ((CC->room.QRflags & QR_DIRECTORY) ?
  498             CC->room.QRdirname : ""),
  499 
  500         CC->room.QRflags,
  501         (int) CC->room.QRfloor,
  502         (int) CC->room.QRorder,
  503 
  504         CC->room.QRdefaultview,
  505         CC->room.QRflags2
  506         );
  507 }
  508 
  509 /*
  510  * set room parameters (admin or room admin command)
  511  */
  512 void cmd_setr(char *args)
  513 {
  514     char buf[256];
  515     int new_order = 0;
  516     int r;
  517     int new_floor;
  518     char new_name[ROOMNAMELEN];
  519 
  520     if (CtdlAccessCheck(ac_logged_in)) return;
  521 
  522     if (num_parms(args) >= 6) {
  523         new_floor = extract_int(args, 5);
  524     } else {
  525         new_floor = (-1);   /* don't change the floor */
  526     }
  527 
  528     /* When is a new name more than just a new name?  When the old name
  529      * has a namespace prefix.
  530      */
  531     if (CC->room.QRflags & QR_MAILBOX) {
  532         sprintf(new_name, "%010ld.", atol(CC->room.QRname) );
  533     } else {
  534         safestrncpy(new_name, "", sizeof new_name);
  535     }
  536     extract_token(&new_name[strlen(new_name)], args, 0, '|', (sizeof new_name - strlen(new_name)));
  537 
  538     r = CtdlRenameRoom(CC->room.QRname, new_name, new_floor);
  539 
  540     if (r == crr_room_not_found) {
  541         cprintf("%d Internal error - room not found?\n", ERROR + INTERNAL_ERROR);
  542     } else if (r == crr_already_exists) {
  543         cprintf("%d '%s' already exists.\n",
  544             ERROR + ALREADY_EXISTS, new_name);
  545     } else if (r == crr_noneditable) {
  546         cprintf("%d Cannot edit this room.\n", ERROR + NOT_HERE);
  547     } else if (r == crr_invalid_floor) {
  548         cprintf("%d Target floor does not exist.\n",
  549             ERROR + INVALID_FLOOR_OPERATION);
  550     } else if (r == crr_access_denied) {
  551         cprintf("%d You do not have permission to edit '%s'\n",
  552             ERROR + HIGHER_ACCESS_REQUIRED,
  553             CC->room.QRname);
  554     } else if (r != crr_ok) {
  555         cprintf("%d Error: CtdlRenameRoom() returned %d\n",
  556             ERROR + INTERNAL_ERROR, r);
  557     }
  558 
  559     if (r != crr_ok) {
  560         return;
  561     }
  562 
  563     CtdlGetRoom(&CC->room, new_name);
  564 
  565     /* Now we have to do a bunch of other stuff */
  566 
  567     if (num_parms(args) >= 7) {
  568         new_order = extract_int(args, 6);
  569         if (new_order < 1)
  570             new_order = 1;
  571         if (new_order > 127)
  572             new_order = 127;
  573     }
  574 
  575     CtdlGetRoomLock(&CC->room, CC->room.QRname);
  576 
  577     /* Directory room */
  578     extract_token(buf, args, 2, '|', sizeof buf);
  579     buf[15] = 0;
  580     safestrncpy(CC->room.QRdirname, buf,
  581         sizeof CC->room.QRdirname);
  582 
  583     /* Default view */
  584     if (num_parms(args) >= 8) {
  585         CC->room.QRdefaultview = extract_int(args, 7);
  586     }
  587 
  588     /* Second set of flags */
  589     if (num_parms(args) >= 9) {
  590         CC->room.QRflags2 = extract_int(args, 8);
  591     }
  592 
  593     /* Misc. flags */
  594     CC->room.QRflags = (extract_int(args, 3) | QR_INUSE);
  595     /* Clean up a client boo-boo: if the client set the room to
  596      * guess-name or passworded, ensure that the private flag is
  597      * also set.
  598      */
  599     if ((CC->room.QRflags & QR_GUESSNAME)
  600         || (CC->room.QRflags & QR_PASSWORDED))
  601         CC->room.QRflags |= QR_PRIVATE;
  602 
  603     /* Some changes can't apply to BASEROOM */
  604     if (!strncasecmp(CC->room.QRname, CtdlGetConfigStr("c_baseroom"), ROOMNAMELEN)) {
  605         CC->room.QRorder = 0;
  606         CC->room.QRpasswd[0] = '\0';
  607         CC->room.QRflags &= ~(QR_PRIVATE & QR_PASSWORDED &
  608             QR_GUESSNAME & QR_PREFONLY & QR_MAILBOX);
  609         CC->room.QRflags |= QR_PERMANENT;
  610     } else {    
  611         /* March order (doesn't apply to AIDEROOM) */
  612         if (num_parms(args) >= 7)
  613             CC->room.QRorder = (char) new_order;
  614         /* Room password */
  615         extract_token(buf, args, 1, '|', sizeof buf);
  616         buf[10] = 0;
  617         safestrncpy(CC->room.QRpasswd, buf,
  618                 sizeof CC->room.QRpasswd);
  619         /* Kick everyone out if the client requested it
  620          * (by changing the room's generation number)
  621          */
  622         if (extract_int(args, 4)) {
  623             time(&CC->room.QRgen);
  624         }
  625     }
  626     /* Some changes can't apply to AIDEROOM */
  627     if (!strncasecmp(CC->room.QRname, CtdlGetConfigStr("c_baseroom"), ROOMNAMELEN)) {
  628         CC->room.QRorder = 0;
  629         CC->room.QRflags &= ~QR_MAILBOX;
  630         CC->room.QRflags |= QR_PERMANENT;
  631     }
  632 
  633     /* Write the room record back to disk */
  634     CtdlPutRoomLock(&CC->room);
  635 
  636     /* Create a room directory if necessary */
  637     if (CC->room.QRflags & QR_DIRECTORY) {
  638         snprintf(buf, sizeof buf,"%s/%s",
  639                  ctdl_file_dir,
  640                  CC->room.QRdirname);
  641         mkdir(buf, 0755);
  642     }
  643     snprintf(buf, sizeof buf, "The room \"%s\" has been edited by %s.\n",
  644         CC->room.QRname,
  645         (CC->logged_in ? CC->curr_user : "an administrator")
  646     );
  647     CtdlAideMessage(buf, "Room modification Message");
  648     cprintf("%d Ok\n", CIT_OK);
  649 }
  650 
  651 
  652 
  653 /* 
  654  * get the name of the room admin for this room
  655  */
  656 void cmd_geta(char *cmdbuf)
  657 {
  658     struct ctdluser usbuf;
  659 
  660     if (CtdlAccessCheck(ac_logged_in)) return;
  661 
  662     if (CtdlGetUserByNumber(&usbuf, CC->room.QRroomaide) == 0) {
  663         cprintf("%d %s\n", CIT_OK, usbuf.fullname);
  664     } else {
  665         cprintf("%d \n", CIT_OK);
  666     }
  667 }
  668 
  669 
  670 /* 
  671  * set the room admin for this room
  672  */
  673 void cmd_seta(char *new_ra)
  674 {
  675     struct ctdluser usbuf;
  676     long newu;
  677     char buf[SIZ];
  678     int post_notice;
  679 
  680     if (CtdlAccessCheck(ac_room_aide)) return;
  681 
  682     if (CtdlGetUser(&usbuf, new_ra) != 0) {
  683         newu = (-1L);
  684     } else {
  685         newu = usbuf.usernum;
  686     }
  687 
  688     CtdlGetRoomLock(&CC->room, CC->room.QRname);
  689     post_notice = 0;
  690     if (CC->room.QRroomaide != newu) {
  691         post_notice = 1;
  692     }
  693     CC->room.QRroomaide = newu;
  694     CtdlPutRoomLock(&CC->room);
  695 
  696     /*
  697      * We have to post the change notice _after_ writing changes to 
  698      * the room table, otherwise it would deadlock!
  699      */
  700     if (post_notice == 1) {
  701         if (!IsEmptyStr(usbuf.fullname))
  702             snprintf(buf, sizeof buf,
  703                 "%s is now the room admin for \"%s\".\n",
  704                 usbuf.fullname, CC->room.QRname);
  705         else
  706             snprintf(buf, sizeof buf,
  707                 "There is now no room admin for \"%s\".\n",
  708                 CC->room.QRname);
  709         CtdlAideMessage(buf, "Admin Room Modification");
  710     }
  711     cprintf("%d Ok\n", CIT_OK);
  712 }
  713 
  714 /* 
  715  * Retrieve info file for this room (this ought to be upgraded to handle non-plain-text)
  716  */
  717 void cmd_rinf(char *argbuf)
  718 {
  719     struct CtdlMessage *msg = CtdlFetchMessage(CC->room.msgnum_info, 1);
  720     if (msg != NULL) {
  721         cprintf("%d Info:\n", LISTING_FOLLOWS);
  722         CtdlOutputPreLoadedMsg(msg, MT_CITADEL, HEADERS_NONE, 0, 0, 0);
  723         CM_Free(msg);
  724         cprintf("000\n");
  725     }
  726     else {
  727         cprintf("%d No info file.\n", ERROR + FILE_NOT_FOUND);
  728     }
  729 }
  730 
  731 
  732 /*
  733  * admin command: kill the current room
  734  */
  735 void cmd_kill(char *argbuf)
  736 {
  737     char deleted_room_name[ROOMNAMELEN];
  738     char msg[SIZ];
  739     int kill_ok;
  740 
  741     kill_ok = extract_int(argbuf, 0);
  742 
  743     if (CtdlDoIHavePermissionToDeleteThisRoom(&CC->room) == 0) {
  744         cprintf("%d Can't delete this room.\n", ERROR + NOT_HERE);
  745         return;
  746     }
  747     if (kill_ok) {
  748         if (CC->room.QRflags & QR_MAILBOX) {
  749             safestrncpy(deleted_room_name, &CC->room.QRname[11], sizeof deleted_room_name);
  750         }
  751         else {
  752             safestrncpy(deleted_room_name, CC->room.QRname, sizeof deleted_room_name);
  753         }
  754 
  755         /* Do the dirty work */
  756         CtdlScheduleRoomForDeletion(&CC->room);
  757 
  758         /* Return to the Lobby */
  759         CtdlUserGoto(CtdlGetConfigStr("c_baseroom"), 0, 0, NULL, NULL, NULL, NULL);
  760 
  761         /* tell the world what we did */
  762         snprintf(msg, sizeof msg, "The room \"%s\" has been deleted by %s.\n",
  763              deleted_room_name,
  764             (CC->logged_in ? CC->curr_user : "an administrator")
  765         );
  766         CtdlAideMessage(msg, "Room Purger Message");
  767         cprintf("%d '%s' deleted.\n", CIT_OK, deleted_room_name);
  768     } else {
  769         cprintf("%d ok to delete.\n", CIT_OK);
  770     }
  771 }
  772 
  773 
  774 /*
  775  * create a new room
  776  */
  777 void cmd_cre8(char *args)
  778 {
  779     int cre8_ok;
  780     char new_room_name[ROOMNAMELEN];
  781     int new_room_type;
  782     char new_room_pass[32];
  783     int new_room_floor;
  784     int new_room_view;
  785     char *notification_message = NULL;
  786     unsigned newflags;
  787     struct floor *fl;
  788     int avoid_access = 0;
  789 
  790     cre8_ok = extract_int(args, 0);
  791     extract_token(new_room_name, args, 1, '|', sizeof new_room_name);
  792     new_room_name[ROOMNAMELEN - 1] = 0;
  793     new_room_type = extract_int(args, 2);
  794     extract_token(new_room_pass, args, 3, '|', sizeof new_room_pass);
  795     avoid_access = extract_int(args, 5);
  796     new_room_view = extract_int(args, 6);
  797     new_room_pass[9] = 0;
  798     new_room_floor = 0;
  799 
  800     if ((IsEmptyStr(new_room_name)) && (cre8_ok == 1)) {
  801         cprintf("%d Invalid room name.\n", ERROR + ILLEGAL_VALUE);
  802         return;
  803     }
  804 
  805     if (!strcasecmp(new_room_name, MAILROOM)) {
  806         cprintf("%d '%s' already exists.\n",
  807             ERROR + ALREADY_EXISTS, new_room_name);
  808         return;
  809     }
  810 
  811     if (num_parms(args) >= 5) {
  812         fl = CtdlGetCachedFloor(extract_int(args, 4));
  813         if (fl == NULL) {
  814             cprintf("%d Invalid floor number.\n",
  815                 ERROR + INVALID_FLOOR_OPERATION);
  816             return;
  817         }
  818         else if ((fl->f_flags & F_INUSE) == 0) {
  819             cprintf("%d Invalid floor number.\n",
  820                 ERROR + INVALID_FLOOR_OPERATION);
  821             return;
  822         } else {
  823             new_room_floor = extract_int(args, 4);
  824         }
  825     }
  826 
  827     if (CtdlAccessCheck(ac_logged_in)) return;
  828 
  829     if (CC->user.axlevel < CtdlGetConfigInt("c_createax") && !CC->internal_pgm) {
  830         cprintf("%d You need higher access to create rooms.\n",
  831             ERROR + HIGHER_ACCESS_REQUIRED);
  832         return;
  833     }
  834 
  835     if ((IsEmptyStr(new_room_name)) && (cre8_ok == 0)) {
  836         cprintf("%d Ok to create rooms.\n", CIT_OK);
  837         return;
  838     }
  839 
  840     if ((new_room_type < 0) || (new_room_type > 5)) {
  841         cprintf("%d Invalid room type.\n", ERROR + ILLEGAL_VALUE);
  842         return;
  843     }
  844 
  845     if (new_room_type == 5) {
  846         if (CC->user.axlevel < AxAideU) {
  847             cprintf("%d Higher access required\n", 
  848                 ERROR + HIGHER_ACCESS_REQUIRED);
  849             return;
  850         }
  851     }
  852 
  853     /* Check to make sure the requested room name doesn't already exist */
  854     newflags = CtdlCreateRoom(new_room_name,
  855                 new_room_type, new_room_pass, new_room_floor,
  856                 0, avoid_access, new_room_view);
  857     if (newflags == 0) {
  858         cprintf("%d '%s' already exists.\n",
  859             ERROR + ALREADY_EXISTS, new_room_name);
  860         return;
  861     }
  862 
  863     if (cre8_ok == 0) {
  864         cprintf("%d OK to create '%s'\n", CIT_OK, new_room_name);
  865         return;
  866     }
  867 
  868     /* If we reach this point, the room needs to be created. */
  869 
  870     newflags = CtdlCreateRoom(new_room_name,
  871                new_room_type, new_room_pass, new_room_floor, 1, 0,
  872                new_room_view);
  873 
  874     /* post a message in Aide> describing the new room */
  875     notification_message = malloc(1024);
  876     snprintf(notification_message, 1024,
  877         "A new room called \"%s\" has been created by %s%s%s%s%s%s\n",
  878         new_room_name,
  879         (CC->logged_in ? CC->curr_user : "an administrator"),
  880         ((newflags & QR_MAILBOX) ? " [personal]" : ""),
  881         ((newflags & QR_PRIVATE) ? " [private]" : ""),
  882         ((newflags & QR_GUESSNAME) ? " [hidden]" : ""),
  883         ((newflags & QR_PASSWORDED) ? " Password: " : ""),
  884         ((newflags & QR_PASSWORDED) ? new_room_pass : "")
  885     );
  886     CtdlAideMessage(notification_message, "Room Creation Message");
  887     free(notification_message);
  888 
  889     cprintf("%d '%s' has been created.\n", CIT_OK, new_room_name);
  890 }
  891 
  892 
  893 /*
  894  * Upload the room banner text for this room.
  895  * This should be amended to handle content types other than plain text.
  896  */
  897 void cmd_einf(char *ok)
  898 {               /* enter info file for current room */
  899     char buf[SIZ];
  900     unbuffer_output();
  901 
  902     if (CtdlAccessCheck(ac_room_aide)) return;
  903 
  904     if (atoi(ok) == 0) {
  905         cprintf("%d Ok.\n", CIT_OK);
  906         return;
  907     }
  908 
  909     StrBuf *NewBanner = NewStrBufPlain("Content-type: text/plain; charset=UTF-8\nContent-transfer-encoding: 8bit\n\n", -1);
  910 
  911     cprintf("%d Transmit new banner in plain text now.\n", SEND_LISTING);
  912     while(client_getln(buf, sizeof buf) >= 0 && strcmp(buf,"000")) {
  913         StrBufAppendBufPlain(NewBanner, buf, -1, 0);
  914         StrBufAppendBufPlain(NewBanner, HKEY("\n"), 0);
  915     }
  916 
  917     // We have read the new banner from the user , now save it
  918     long new_msgnum = quickie_message("Citadel", NULL, NULL, SYSCONFIGROOM, ChrPtr(NewBanner), FMT_RFC822, "Banner submitted with EINF command");
  919     FreeStrBuf(&NewBanner);
  920 
  921     // Update the room record with a pointer to our new banner
  922     CtdlGetRoomLock(&CC->room, CC->room.QRname);
  923     long old_msgnum = CC->room.msgnum_info;
  924     CC->room.msgnum_info = new_msgnum;
  925     CtdlPutRoomLock(&CC->room);
  926 
  927     // Delete the old one
  928     CtdlDeleteMessages(SYSCONFIGROOM, &old_msgnum, 1, "");
  929 }
  930 
  931 
  932 /* 
  933  * cmd_lflr()   -  List all known floors
  934  */
  935 void cmd_lflr(char *gargs)
  936 {
  937     int a;
  938     struct floor flbuf;
  939 
  940     if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
  941 
  942     cprintf("%d Known floors:\n", LISTING_FOLLOWS);
  943 
  944     for (a = 0; a < MAXFLOORS; ++a) {
  945         CtdlGetFloor(&flbuf, a);
  946         if (flbuf.f_flags & F_INUSE) {
  947             cprintf("%d|%s|%d\n",
  948                 a,
  949                 flbuf.f_name,
  950                 flbuf.f_ref_count);
  951         }
  952     }
  953     cprintf("000\n");
  954 }
  955 
  956 
  957 
  958 /*
  959  * create a new floor
  960  */
  961 void cmd_cflr(char *argbuf)
  962 {
  963     char new_floor_name[256];
  964     struct floor flbuf;
  965     int cflr_ok;
  966     int free_slot = (-1);
  967     int a;
  968 
  969     extract_token(new_floor_name, argbuf, 0, '|', sizeof new_floor_name);
  970     cflr_ok = extract_int(argbuf, 1);
  971 
  972     if (CtdlAccessCheck(ac_aide)) return;
  973 
  974     if (IsEmptyStr(new_floor_name)) {
  975         cprintf("%d Blank floor name not allowed.\n",
  976             ERROR + ILLEGAL_VALUE);
  977         return;
  978     }
  979 
  980     for (a = 0; a < MAXFLOORS; ++a) {
  981         CtdlGetFloor(&flbuf, a);
  982 
  983         /* note any free slots while we're scanning... */
  984         if (((flbuf.f_flags & F_INUSE) == 0)
  985             && (free_slot < 0))
  986             free_slot = a;
  987 
  988         /* check to see if it already exists */
  989         if ((!strcasecmp(flbuf.f_name, new_floor_name))
  990             && (flbuf.f_flags & F_INUSE)) {
  991             cprintf("%d Floor '%s' already exists.\n",
  992                 ERROR + ALREADY_EXISTS,
  993                 flbuf.f_name);
  994             return;
  995         }
  996     }
  997 
  998     if (free_slot < 0) {
  999         cprintf("%d There is no space available for a new floor.\n",
 1000             ERROR + INVALID_FLOOR_OPERATION);
 1001         return;
 1002     }
 1003     if (cflr_ok == 0) {
 1004         cprintf("%d ok to create...\n", CIT_OK);
 1005         return;
 1006     }
 1007     lgetfloor(&flbuf, free_slot);
 1008     flbuf.f_flags = F_INUSE;
 1009     flbuf.f_ref_count = 0;
 1010     safestrncpy(flbuf.f_name, new_floor_name, sizeof flbuf.f_name);
 1011     lputfloor(&flbuf, free_slot);
 1012     cprintf("%d %d\n", CIT_OK, free_slot);
 1013 }
 1014 
 1015 
 1016 
 1017 /*
 1018  * delete a floor
 1019  */
 1020 void cmd_kflr(char *argbuf)
 1021 {
 1022     struct floor flbuf;
 1023     int floor_to_delete;
 1024     int kflr_ok;
 1025     int delete_ok;
 1026 
 1027     floor_to_delete = extract_int(argbuf, 0);
 1028     kflr_ok = extract_int(argbuf, 1);
 1029 
 1030     if (CtdlAccessCheck(ac_aide)) return;
 1031 
 1032     lgetfloor(&flbuf, floor_to_delete);
 1033 
 1034     delete_ok = 1;
 1035     if ((flbuf.f_flags & F_INUSE) == 0) {
 1036         cprintf("%d Floor %d not in use.\n",
 1037             ERROR + INVALID_FLOOR_OPERATION, floor_to_delete);
 1038         delete_ok = 0;
 1039     } else {
 1040         if (flbuf.f_ref_count != 0) {
 1041             cprintf("%d Cannot delete; floor contains %d rooms.\n",
 1042                 ERROR + INVALID_FLOOR_OPERATION,
 1043                 flbuf.f_ref_count);
 1044             delete_ok = 0;
 1045         } else {
 1046             if (kflr_ok == 1) {
 1047                 cprintf("%d Ok\n", CIT_OK);
 1048             } else {
 1049                 cprintf("%d Ok to delete...\n", CIT_OK);
 1050             }
 1051 
 1052         }
 1053 
 1054     }
 1055 
 1056     if ((delete_ok == 1) && (kflr_ok == 1))
 1057         flbuf.f_flags = 0;
 1058     lputfloor(&flbuf, floor_to_delete);
 1059 }
 1060 
 1061 /*
 1062  * edit a floor
 1063  */
 1064 void cmd_eflr(char *argbuf)
 1065 {
 1066     struct floor flbuf;
 1067     int floor_num;
 1068     int np;
 1069 
 1070     np = num_parms(argbuf);
 1071     if (np < 1) {
 1072         cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
 1073         return;
 1074     }
 1075 
 1076     if (CtdlAccessCheck(ac_aide)) return;
 1077 
 1078     floor_num = extract_int(argbuf, 0);
 1079     lgetfloor(&flbuf, floor_num);
 1080     if ((flbuf.f_flags & F_INUSE) == 0) {
 1081         lputfloor(&flbuf, floor_num);
 1082         cprintf("%d Floor %d is not in use.\n",
 1083             ERROR + INVALID_FLOOR_OPERATION, floor_num);
 1084         return;
 1085     }
 1086     if (np >= 2)
 1087         extract_token(flbuf.f_name, argbuf, 1, '|', sizeof flbuf.f_name);
 1088     lputfloor(&flbuf, floor_num);
 1089 
 1090     cprintf("%d Ok\n", CIT_OK);
 1091 }
 1092 
 1093 
 1094 
 1095 /* 
 1096  * cmd_stat()  -  return the modification time of the current room (maybe other things in the future)
 1097  */
 1098 void cmd_stat(char *gargs)
 1099 {
 1100     if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
 1101     CtdlGetRoom(&CC->room, CC->room.QRname);
 1102     cprintf("%d %s|%ld|\n", CIT_OK, CC->room.QRname, CC->room.QRmtime);
 1103 }
 1104 
 1105 
 1106 
 1107 
 1108 /*****************************************************************************/
 1109 /*                      MODULE INITIALIZATION STUFF                          */
 1110 /*****************************************************************************/
 1111 
 1112 CTDL_MODULE_INIT(rooms)
 1113 {
 1114     if (!threading) {
 1115         CtdlRegisterProtoHook(cmd_lrms, "LRMS", "List rooms");
 1116         CtdlRegisterProtoHook(cmd_lkra, "LKRA", "List all known rooms");
 1117         CtdlRegisterProtoHook(cmd_lkrn, "LKRN", "List known rooms with new messages");
 1118         CtdlRegisterProtoHook(cmd_lkro, "LKRO", "List known rooms without new messages");
 1119         CtdlRegisterProtoHook(cmd_lzrm, "LZRM", "List zapped rooms");
 1120         CtdlRegisterProtoHook(cmd_lprm, "LPRM", "List public rooms");
 1121         CtdlRegisterProtoHook(cmd_goto, "GOTO", "Goto a named room");
 1122         CtdlRegisterProtoHook(cmd_stat, "STAT", "Get mtime of the current room");
 1123         CtdlRegisterProtoHook(cmd_whok, "WHOK", "List users who know this room");
 1124         CtdlRegisterProtoHook(cmd_rdir, "RDIR", "List files in room directory");
 1125         CtdlRegisterProtoHook(cmd_getr, "GETR", "Get room parameters");
 1126         CtdlRegisterProtoHook(cmd_setr, "SETR", "Set room parameters");
 1127         CtdlRegisterProtoHook(cmd_geta, "GETA", "Get the room admin name");
 1128         CtdlRegisterProtoHook(cmd_seta, "SETA", "Set the room admin for this room");
 1129         CtdlRegisterProtoHook(cmd_rinf, "RINF", "Fetch room info file");
 1130         CtdlRegisterProtoHook(cmd_kill, "KILL", "Kill (delete) the current room");
 1131         CtdlRegisterProtoHook(cmd_cre8, "CRE8", "Create a new room");
 1132         CtdlRegisterProtoHook(cmd_einf, "EINF", "Enter info file for the current room");
 1133         CtdlRegisterProtoHook(cmd_lflr, "LFLR", "List all known floors");
 1134         CtdlRegisterProtoHook(cmd_cflr, "CFLR", "Create a new floor");
 1135         CtdlRegisterProtoHook(cmd_kflr, "KFLR", "Kill a floor");
 1136         CtdlRegisterProtoHook(cmd_eflr, "EFLR", "Edit a floor");
 1137     }
 1138         /* return our Subversion id for the Log */
 1139     return "rooms";
 1140 }