"Fossies" - the Fresh Open Source Software Archive

Member "citadel/room_ops.c" (5 Jun 2021, 33339 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 "room_ops.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-2021 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 <stdio.h>
   16 #include <libcitadel.h>
   17 
   18 #include "citserver.h"
   19 #include "ctdl_module.h"
   20 #include "config.h"
   21 #include "control.h"
   22 #include "user_ops.h"
   23 #include "room_ops.h"
   24 
   25 struct floor *floorcache[MAXFLOORS];
   26 
   27 /* 
   28  * Determine whether the currently logged in session has permission to read
   29  * messages in the current room.
   30  */
   31 int CtdlDoIHavePermissionToReadMessagesInThisRoom(void) {
   32     if (    (!(CC->logged_in))
   33         && (!(CC->internal_pgm))
   34         && (!CtdlGetConfigInt("c_guest_logins"))
   35     ) {
   36         return(om_not_logged_in);
   37     }
   38     return(om_ok);
   39 }
   40 
   41 
   42 /*
   43  * Check to see whether we have permission to post a message in the current
   44  * room.  Returns a *CITADEL ERROR CODE* and puts a message in errmsgbuf, or
   45  * returns 0 on success.
   46  */
   47 int CtdlDoIHavePermissionToPostInThisRoom(
   48     char *errmsgbuf, 
   49     size_t n, 
   50     const char* RemoteIdentifier,
   51     PostType PostPublic,
   52     int is_reply
   53 ) {
   54     int ra;
   55 
   56     if (!(CC->logged_in) && (PostPublic == POST_LOGGED_IN)) {
   57         snprintf(errmsgbuf, n, "Not logged in.");
   58         return (ERROR + NOT_LOGGED_IN);
   59     }
   60     else if (PostPublic == CHECK_EXISTANCE) {
   61         return (0);                 // evaluate whether a recipient exists
   62     }
   63     else if (!(CC->logged_in)) {
   64         if ((CC->room.QRflags & QR_READONLY)) {
   65             snprintf(errmsgbuf, n, "Not logged in.");
   66             return (ERROR + NOT_LOGGED_IN);
   67         }
   68         if (CC->room.QRflags2 & QR2_MODERATED) {
   69             snprintf(errmsgbuf, n, "Not logged in Moderation feature not yet implemented!");
   70             return (ERROR + NOT_LOGGED_IN);
   71         }
   72         // FIXME what was this?  AJC 2021
   73         //if ((PostPublic != POST_LMTP) && (CC->room.QRflags2 & QR2_SMTP_PUBLIC) == 0) {
   74             //return CtdlNetconfigCheckRoomaccess(errmsgbuf, n, RemoteIdentifier);
   75         //}
   76         return (0);
   77     }
   78 
   79     if ((CC->user.axlevel < AxProbU) && ((CC->room.QRflags & QR_MAILBOX) == 0)) {
   80         snprintf(errmsgbuf, n, "Need to be validated to enter (except in %s> to sysop)", MAILROOM);
   81         return (ERROR + HIGHER_ACCESS_REQUIRED);
   82     }
   83 
   84     CtdlRoomAccess(&CC->room, &CC->user, &ra, NULL);
   85 
   86     if (ra & UA_POSTALLOWED) {
   87         strcpy(errmsgbuf, "OK to post or reply here");
   88         return(0);
   89     }
   90 
   91     if ( (ra & UA_REPLYALLOWED) && (is_reply) ) {
   92         /*
   93          * To be thorough, we ought to check to see if the message they are
   94          * replying to is actually a valid one in this room, but unless this
   95          * actually becomes a problem we'll go with high performance instead.
   96          */
   97         strcpy(errmsgbuf, "OK to reply here");
   98         return(0);
   99     }
  100 
  101     if ( (ra & UA_REPLYALLOWED) && (!is_reply) ) {
  102         /* Clarify what happened with a better error message */
  103         snprintf(errmsgbuf, n, "You may only reply to existing messages here.");
  104         return (ERROR + HIGHER_ACCESS_REQUIRED);
  105     }
  106 
  107     snprintf(errmsgbuf, n, "Higher access is required to post in this room.");
  108     return (ERROR + HIGHER_ACCESS_REQUIRED);
  109 
  110 }
  111 
  112 
  113 /*
  114  * Check whether the current user has permission to delete messages from
  115  * the current room (returns 1 for yes, 0 for no)
  116  */
  117 int CtdlDoIHavePermissionToDeleteMessagesFromThisRoom(void) {
  118     int ra;
  119     CtdlRoomAccess(&CC->room, &CC->user, &ra, NULL);
  120     if (ra & UA_DELETEALLOWED) return(1);
  121     return(0);
  122 }
  123 
  124 
  125 /*
  126  * Retrieve access control information for any user/room pair.
  127  * Yes, it has a couple of gotos.  If you don't like that, go die in a car fire.
  128  */
  129 void CtdlRoomAccess(struct ctdlroom *roombuf, struct ctdluser *userbuf, int *result, int *view) {
  130     int retval = 0;
  131     visit vbuf;
  132     int is_me = 0;
  133     int is_guest = 0;
  134 
  135     if (userbuf == &CC->user) {
  136         is_me = 1;
  137     }
  138 
  139     if ((is_me) && (CtdlGetConfigInt("c_guest_logins")) && (!CC->logged_in)) {
  140         is_guest = 1;
  141     }
  142 
  143     /* for internal programs, always do everything */
  144     if (((CC->internal_pgm)) && (roombuf->QRflags & QR_INUSE)) {
  145         retval = (UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED);
  146         vbuf.v_view = 0;
  147         goto SKIP_EVERYTHING;
  148     }
  149 
  150     /* If guest mode is enabled, always grant access to the Lobby */
  151     if ((is_guest) && (!strcasecmp(roombuf->QRname, BASEROOM))) {
  152         retval = (UA_KNOWN | UA_GOTOALLOWED);
  153         vbuf.v_view = 0;
  154         goto SKIP_EVERYTHING;
  155     }
  156 
  157     /* Locate any applicable user/room relationships */
  158     if (is_guest) {
  159         memset(&vbuf, 0, sizeof vbuf);
  160     }
  161     else {
  162         CtdlGetRelationship(&vbuf, userbuf, roombuf);
  163     }
  164 
  165     /* Force the properties of the Aide room */
  166     if (!strcasecmp(roombuf->QRname, CtdlGetConfigStr("c_aideroom"))) {
  167         if (userbuf->axlevel >= AxAideU) {
  168             retval = UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED;
  169         } else {
  170             retval = 0;
  171         }
  172         goto NEWMSG;
  173     }
  174 
  175     /* If this is a public room, it's accessible... */
  176     if (    ((roombuf->QRflags & QR_PRIVATE) == 0) 
  177         && ((roombuf->QRflags & QR_MAILBOX) == 0)
  178     ) {
  179         retval = retval | UA_KNOWN | UA_GOTOALLOWED;
  180     }
  181 
  182     /* If this is a preferred users only room, check access level */
  183     if (roombuf->QRflags & QR_PREFONLY) {
  184         if (userbuf->axlevel < AxPrefU) {
  185             retval = retval & ~UA_KNOWN & ~UA_GOTOALLOWED;
  186         }
  187     }
  188 
  189     /* For private rooms, check the generation number matchups */
  190     if (    (roombuf->QRflags & QR_PRIVATE) 
  191         && ((roombuf->QRflags & QR_MAILBOX) == 0)
  192     ) {
  193 
  194         /* An explicit match means the user belongs in this room */
  195         if (vbuf.v_flags & V_ACCESS) {
  196             retval = retval | UA_KNOWN | UA_GOTOALLOWED;
  197         }
  198         /* Otherwise, check if this is a guess-name or passworded
  199          * room.  If it is, a goto may at least be attempted
  200          */
  201         else if (   (roombuf->QRflags & QR_PRIVATE)
  202                 || (roombuf->QRflags & QR_PASSWORDED)
  203         ) {
  204             retval = retval & ~UA_KNOWN;
  205             retval = retval | UA_GOTOALLOWED;
  206         }
  207     }
  208 
  209     /* For mailbox rooms, also check the namespace */
  210     /* Also, mailbox owners can delete their messages */
  211     if ( (roombuf->QRflags & QR_MAILBOX) && (atol(roombuf->QRname) != 0)) {
  212         if (userbuf->usernum == atol(roombuf->QRname)) {
  213             retval = retval | UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED;
  214         }
  215         /* An explicit match means the user belongs in this room */
  216         if (vbuf.v_flags & V_ACCESS) {
  217             retval = retval | UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED;
  218         }
  219     }
  220 
  221     /* For non-mailbox rooms... */
  222     else {
  223 
  224         /* User is allowed to post in the room unless:
  225          * - User is not validated
  226          * - User has no net privileges and it is a shared network room
  227          * - It is a read-only room
  228          * - It is a blog room (in which case we only allow replies to existing messages)
  229          */
  230         int post_allowed = 1;
  231         int reply_allowed = 1;
  232         if (userbuf->axlevel < AxProbU) {
  233             post_allowed = 0;
  234             reply_allowed = 0;
  235         }
  236         if ((userbuf->axlevel < AxNetU) && (roombuf->QRflags & QR_NETWORK)) {
  237             post_allowed = 0;
  238             reply_allowed = 0;
  239         }
  240         if (roombuf->QRflags & QR_READONLY) {
  241             post_allowed = 0;
  242             reply_allowed = 0;
  243         }
  244         if (roombuf->QRdefaultview == VIEW_BLOG) {
  245             post_allowed = 0;
  246         }
  247         if (post_allowed) {
  248             retval = retval | UA_POSTALLOWED | UA_REPLYALLOWED;
  249         }
  250         if (reply_allowed) {
  251             retval = retval | UA_REPLYALLOWED;
  252         }
  253 
  254         /* If "collaborative deletion" is active for this room, any user who can post
  255          * is also allowed to delete
  256          */
  257         if (roombuf->QRflags2 & QR2_COLLABDEL) {
  258             if (retval & UA_POSTALLOWED) {
  259                 retval = retval | UA_DELETEALLOWED;
  260             }
  261         }
  262 
  263     }
  264 
  265     /* Check to see if the user has forgotten this room */
  266     if (vbuf.v_flags & V_FORGET) {
  267         retval = retval & ~UA_KNOWN;
  268         if (    ( ((roombuf->QRflags & QR_PRIVATE) == 0) 
  269             && ((roombuf->QRflags & QR_MAILBOX) == 0)
  270         ) || (  (roombuf->QRflags & QR_MAILBOX) 
  271             && (atol(roombuf->QRname) == CC->user.usernum))
  272         ) {
  273             retval = retval | UA_ZAPPED;
  274         }
  275     }
  276 
  277     /* If user is explicitly locked out of this room, deny everything */
  278     if (vbuf.v_flags & V_LOCKOUT) {
  279         retval = retval & ~UA_KNOWN & ~UA_GOTOALLOWED & ~UA_POSTALLOWED & ~UA_REPLYALLOWED;
  280     }
  281 
  282     /* Aides get access to all private rooms */
  283     if (    (userbuf->axlevel >= AxAideU)
  284         && ((roombuf->QRflags & QR_MAILBOX) == 0)
  285     ) {
  286         if (vbuf.v_flags & V_FORGET) {
  287             retval = retval | UA_GOTOALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
  288         }
  289         else {
  290             retval = retval | UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
  291         }
  292     }
  293 
  294     /* Aides can gain access to mailboxes as well, but they don't show
  295      * by default.
  296      */
  297     if (    (userbuf->axlevel >= AxAideU)
  298         && (roombuf->QRflags & QR_MAILBOX)
  299     ) {
  300         retval = retval | UA_GOTOALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
  301     }
  302 
  303     /* Aides and Room Aides have admin privileges */
  304     if (    (userbuf->axlevel >= AxAideU)
  305         || (userbuf->usernum == roombuf->QRroomaide)
  306     ) {
  307         retval = retval | UA_ADMINALLOWED | UA_DELETEALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
  308     }
  309 
  310 NEWMSG: /* By the way, we also check for the presence of new messages */
  311     if (is_msg_in_sequence_set(vbuf.v_seen, roombuf->QRhighest) == 0) {
  312         retval = retval | UA_HASNEWMSGS;
  313     }
  314 
  315     /* System rooms never show up in the list. */
  316     if (roombuf->QRflags2 & QR2_SYSTEM) {
  317         retval = retval & ~UA_KNOWN;
  318     }
  319 
  320 SKIP_EVERYTHING:
  321     /* Now give the caller the information it wants. */
  322     if (result != NULL) *result = retval;
  323     if (view != NULL) *view = vbuf.v_view;
  324 }
  325 
  326 
  327 /*
  328  * Self-checking stuff for a room record read into memory
  329  */
  330 void room_sanity_check(struct ctdlroom *qrbuf) {
  331     /* Mailbox rooms are always on the lowest floor */
  332     if (qrbuf->QRflags & QR_MAILBOX) {
  333         qrbuf->QRfloor = 0;
  334     }
  335     /* Listing order of 0 is illegal except for base rooms */
  336     if (qrbuf->QRorder == 0) {
  337         if (    !(qrbuf->QRflags & QR_MAILBOX)
  338             && strncasecmp(qrbuf->QRname, CtdlGetConfigStr("c_baseroom"), ROOMNAMELEN)
  339             && strncasecmp(qrbuf->QRname, CtdlGetConfigStr("c_aideroom"), ROOMNAMELEN)
  340         ) {
  341             qrbuf->QRorder = 64;
  342         }
  343     }
  344 }
  345 
  346 
  347 /*
  348  * CtdlGetRoom()  -  retrieve room data from disk
  349  */
  350 int CtdlGetRoom(struct ctdlroom *qrbuf, const char *room_name) {
  351     struct cdbdata *cdbqr;
  352     char lowercase_name[ROOMNAMELEN];
  353     char personal_lowercase_name[ROOMNAMELEN];
  354     const char *sptr;
  355     char *dptr, *eptr;
  356 
  357     dptr = lowercase_name;
  358     sptr = room_name;
  359     eptr = (dptr + (sizeof lowercase_name - 1));
  360     while (!IsEmptyStr(sptr) && (dptr < eptr)) {
  361         *dptr = tolower(*sptr);
  362         sptr++; dptr++;
  363     }
  364     *dptr = '\0';
  365 
  366     memset(qrbuf, 0, sizeof(struct ctdlroom));
  367 
  368     /* First, try the public namespace */
  369     cdbqr = cdb_fetch(CDB_ROOMS, lowercase_name, strlen(lowercase_name));
  370 
  371     /* If that didn't work, try the user's personal namespace */
  372     if (cdbqr == NULL) {
  373         snprintf(personal_lowercase_name, sizeof personal_lowercase_name, "%010ld.%s", CC->user.usernum, lowercase_name);
  374         cdbqr = cdb_fetch(CDB_ROOMS, personal_lowercase_name, strlen(personal_lowercase_name));
  375     }
  376     if (cdbqr != NULL) {
  377         memcpy(qrbuf, cdbqr->ptr, ((cdbqr->len > sizeof(struct ctdlroom)) ?  sizeof(struct ctdlroom) : cdbqr->len));
  378         cdb_free(cdbqr);
  379         room_sanity_check(qrbuf);
  380         return (0);
  381     }
  382     else {
  383         return (1);
  384     }
  385 }
  386 
  387 
  388 /*
  389  * CtdlGetRoomLock()  -  same as getroom() but locks the record (if supported)
  390  */
  391 int CtdlGetRoomLock(struct ctdlroom *qrbuf, const char *room_name) {
  392     register int retval;
  393     retval = CtdlGetRoom(qrbuf, room_name);
  394     if (retval == 0) begin_critical_section(S_ROOMS);
  395     return(retval);
  396 }
  397 
  398 
  399 /*
  400  * b_putroom()  -  back end to putroom() and b_deleteroom()
  401  *              (if the supplied buffer is NULL, delete the room record)
  402  */
  403 void b_putroom(struct ctdlroom *qrbuf, char *room_name)
  404 {
  405     char lowercase_name[ROOMNAMELEN];
  406     char *aptr, *bptr;
  407     long len;
  408 
  409     aptr = room_name;
  410     bptr = lowercase_name;
  411     while (!IsEmptyStr(aptr)) {
  412         *bptr = tolower(*aptr);
  413         aptr++;
  414         bptr++;
  415     }
  416     *bptr='\0';
  417 
  418     len = bptr - lowercase_name;
  419     if (qrbuf == NULL) {
  420         cdb_delete(CDB_ROOMS, lowercase_name, len);
  421     }
  422     else {
  423         time(&qrbuf->QRmtime);
  424         cdb_store(CDB_ROOMS, lowercase_name, len, qrbuf, sizeof(struct ctdlroom));
  425     }
  426 }
  427 
  428 
  429 /* 
  430  * CtdlPutRoom()  -  store room data to disk
  431  */
  432 void CtdlPutRoom(struct ctdlroom *qrbuf) {
  433     b_putroom(qrbuf, qrbuf->QRname);
  434 }
  435 
  436 
  437 /*
  438  * b_deleteroom()  -  delete a room record from disk
  439  */
  440 void b_deleteroom(char *room_name) {
  441     b_putroom(NULL, room_name);
  442 }
  443 
  444 
  445 /*
  446  * CtdlPutRoomLock()  -  same as CtdlPutRoom() but unlocks the record (if supported)
  447  */
  448 void CtdlPutRoomLock(struct ctdlroom *qrbuf) {
  449     CtdlPutRoom(qrbuf);
  450     end_critical_section(S_ROOMS);
  451 }
  452 
  453 
  454 /*
  455  * CtdlGetFloorByName()  -  retrieve the number of the named floor
  456  * return < 0 if not found else return floor number
  457  */
  458 int CtdlGetFloorByName(const char *floor_name) {
  459     int a;
  460     struct floor *flbuf = NULL;
  461 
  462     for (a = 0; a < MAXFLOORS; ++a) {
  463         flbuf = CtdlGetCachedFloor(a);
  464 
  465         /* check to see if it already exists */
  466         if ((!strcasecmp(flbuf->f_name, floor_name)) && (flbuf->f_flags & F_INUSE)) {
  467             return a;
  468         }
  469     }
  470     return -1;
  471 }
  472 
  473 
  474 /*
  475  * CtdlGetFloorByNameLock()  -  retrieve floor number for given floor and lock the floor list.
  476  */
  477 int CtdlGetFloorByNameLock(const char *floor_name)
  478 {
  479     begin_critical_section(S_FLOORTAB);
  480     return CtdlGetFloorByName(floor_name);
  481 }
  482 
  483 
  484 /*
  485  * CtdlGetAvailableFloor()  -  Return number of first unused floor
  486  * return < 0 if none available
  487  */
  488 int CtdlGetAvailableFloor(void) {
  489     int a;
  490     struct floor *flbuf = NULL;
  491 
  492     for (a = 0; a < MAXFLOORS; a++) {
  493         flbuf = CtdlGetCachedFloor(a);
  494 
  495         /* check to see if it already exists */
  496         if ((flbuf->f_flags & F_INUSE) == 0) {
  497             return a;
  498         }
  499     }
  500     return -1;
  501 }
  502 
  503 
  504 /*
  505  * CtdlGetFloor()  -  retrieve floor data from disk
  506  */
  507 void CtdlGetFloor(struct floor *flbuf, int floor_num) {
  508     struct cdbdata *cdbfl;
  509 
  510     memset(flbuf, 0, sizeof(struct floor));
  511     cdbfl = cdb_fetch(CDB_FLOORTAB, &floor_num, sizeof(int));
  512     if (cdbfl != NULL) {
  513         memcpy(flbuf, cdbfl->ptr, ((cdbfl->len > sizeof(struct floor)) ?  sizeof(struct floor) : cdbfl->len));
  514         cdb_free(cdbfl);
  515     } else {
  516         if (floor_num == 0) {
  517             safestrncpy(flbuf->f_name, "Main Floor", sizeof flbuf->f_name);
  518             flbuf->f_flags = F_INUSE;
  519             flbuf->f_ref_count = 3;
  520         }
  521     }
  522 }
  523 
  524 
  525 /*
  526  * lgetfloor()  -  same as CtdlGetFloor() but locks the record (if supported)
  527  */
  528 void lgetfloor(struct floor *flbuf, int floor_num) {
  529     begin_critical_section(S_FLOORTAB);
  530     CtdlGetFloor(flbuf, floor_num);
  531 }
  532 
  533 
  534 /*
  535  * CtdlGetCachedFloor()  -  Get floor record from *cache* (loads from disk if needed)
  536  *    
  537  * This is strictly a performance hack.
  538  */
  539 struct floor *CtdlGetCachedFloor(int floor_num) {
  540     static int initialized = 0;
  541     int i;
  542     int fetch_new = 0;
  543     struct floor *fl = NULL;
  544 
  545     begin_critical_section(S_FLOORCACHE);
  546     if (initialized == 0) {
  547         for (i=0; i<MAXFLOORS; ++i) {
  548             floorcache[floor_num] = NULL;
  549         }
  550     initialized = 1;
  551     }
  552     if (floorcache[floor_num] == NULL) {
  553         fetch_new = 1;
  554     }
  555     end_critical_section(S_FLOORCACHE);
  556 
  557     if (fetch_new) {
  558         fl = malloc(sizeof(struct floor));
  559         CtdlGetFloor(fl, floor_num);
  560         begin_critical_section(S_FLOORCACHE);
  561         if (floorcache[floor_num] != NULL) {
  562             free(floorcache[floor_num]);
  563         }
  564         floorcache[floor_num] = fl;
  565         end_critical_section(S_FLOORCACHE);
  566     }
  567 
  568     return(floorcache[floor_num]);
  569 }
  570 
  571 
  572 /*
  573  * CtdlPutFloor()  -  store floor data on disk
  574  */
  575 void CtdlPutFloor(struct floor *flbuf, int floor_num) {
  576     /* If we've cached this, clear it out, 'cuz it's WRONG now! */
  577     begin_critical_section(S_FLOORCACHE);
  578     if (floorcache[floor_num] != NULL) {
  579         free(floorcache[floor_num]);
  580         floorcache[floor_num] = malloc(sizeof(struct floor));
  581         memcpy(floorcache[floor_num], flbuf, sizeof(struct floor));
  582     }
  583     end_critical_section(S_FLOORCACHE);
  584 
  585     cdb_store(CDB_FLOORTAB, &floor_num, sizeof(int),
  586           flbuf, sizeof(struct floor));
  587 }
  588 
  589 
  590 /*
  591  * CtdlPutFloorLock()  -  same as CtdlPutFloor() but unlocks the record (if supported)
  592  */
  593 void CtdlPutFloorLock(struct floor *flbuf, int floor_num) {
  594     CtdlPutFloor(flbuf, floor_num);
  595     end_critical_section(S_FLOORTAB);
  596 
  597 }
  598 
  599 
  600 /*
  601  * lputfloor()  -  same as CtdlPutFloor() but unlocks the record (if supported)
  602  */
  603 void lputfloor(struct floor *flbuf, int floor_num) {
  604     CtdlPutFloorLock(flbuf, floor_num);
  605 }
  606 
  607 /* 
  608  * Iterate through the room table, performing a callback for each room.
  609  */
  610 void CtdlForEachRoom(ForEachRoomCallBack callback_func, void *in_data) {
  611     struct ctdlroom qrbuf;
  612     struct cdbdata *cdbqr;
  613 
  614     cdb_rewind(CDB_ROOMS);
  615 
  616     while (cdbqr = cdb_next_item(CDB_ROOMS), cdbqr != NULL) {
  617         memset(&qrbuf, 0, sizeof(struct ctdlroom));
  618         memcpy(&qrbuf, cdbqr->ptr, ((cdbqr->len > sizeof(struct ctdlroom)) ?  sizeof(struct ctdlroom) : cdbqr->len) );
  619         cdb_free(cdbqr);
  620         room_sanity_check(&qrbuf);
  621         if (qrbuf.QRflags & QR_INUSE) {
  622             callback_func(&qrbuf, in_data);
  623         }
  624     }
  625 }
  626 
  627 
  628 /*
  629  * delete_msglist()  -  delete room message pointers
  630  */
  631 void delete_msglist(struct ctdlroom *whichroom) {
  632         struct cdbdata *cdbml;
  633 
  634     /* Make sure the msglist we're deleting actually exists, otherwise
  635      * libdb will complain when we try to delete an invalid record
  636      */
  637         cdbml = cdb_fetch(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
  638         if (cdbml != NULL) {
  639             cdb_free(cdbml);
  640 
  641         /* Go ahead and delete it */
  642         cdb_delete(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
  643     }
  644 }
  645 
  646 
  647 /*
  648  * Message pointer compare function for sort_msglist()
  649  */
  650 int sort_msglist_cmp(const void *m1, const void *m2) {
  651     if ((*(const long *)m1) > (*(const long *)m2)) return(1);
  652     if ((*(const long *)m1) < (*(const long *)m2)) return(-1);
  653     return(0);
  654 }
  655 
  656 
  657 /*
  658  * sort message pointers
  659  * (returns new msg count)
  660  */
  661 int sort_msglist(long listptrs[], int oldcount) {
  662     int numitems;
  663     int i = 0;
  664 
  665     numitems = oldcount;
  666     if (numitems < 2) {
  667         return (oldcount);
  668     }
  669 
  670     /* do the sort */
  671     qsort(listptrs, numitems, sizeof(long), sort_msglist_cmp);
  672 
  673     /* and yank any nulls */
  674     while ((i < numitems) && (listptrs[i] == 0L)) i++;
  675 
  676     if (i > 0) {
  677         memmove(&listptrs[0], &listptrs[i], (sizeof(long) * (numitems - i)));
  678         numitems-=i;
  679     }
  680 
  681     return (numitems);
  682 }
  683 
  684 
  685 /*
  686  * Determine whether a given room is non-editable.
  687  */
  688 int CtdlIsNonEditable(struct ctdlroom *qrbuf) {
  689 
  690     /* Mail> rooms are non-editable */
  691     if ( (qrbuf->QRflags & QR_MAILBOX) && (!strcasecmp(&qrbuf->QRname[11], MAILROOM)) ) {
  692         return (1);
  693     }
  694 
  695     /* Everything else is editable */
  696     return (0);
  697 }
  698 
  699 
  700 
  701 /*
  702  * Make the specified room the current room for this session.  No validation
  703  * or access control is done here -- the caller should make sure that the
  704  * specified room exists and is ok to access.
  705  */
  706 void CtdlUserGoto(char *where, int display_result, int transiently,
  707         int *retmsgs, int *retnew, long *retoldest, long *retnewest)
  708 {
  709     int a;
  710     int new_messages = 0;
  711     int old_messages = 0;
  712     int total_messages = 0;
  713     long oldest_message = 0;
  714     long newest_message = 0;
  715     int info = 0;
  716     int rmailflag;
  717     int raideflag;
  718     int newmailcount = 0;
  719     visit vbuf;
  720     char truncated_roomname[ROOMNAMELEN];
  721         struct cdbdata *cdbfr;
  722     long *msglist = NULL;
  723     int num_msgs = 0;
  724     unsigned int original_v_flags;
  725     int num_sets;
  726     int s;
  727     char setstr[128], lostr[64], histr[64];
  728     long lo, hi;
  729     int is_trash = 0;
  730 
  731     /* If the supplied room name is NULL, the caller wants us to know that
  732      * it has already copied the room record into CC->room, so
  733      * we can skip the extra database fetch.
  734      */
  735     if (where != NULL) {
  736         safestrncpy(CC->room.QRname, where, sizeof CC->room.QRname);
  737         CtdlGetRoom(&CC->room, where);
  738     }
  739 
  740     /* Take care of all the formalities. */
  741 
  742     begin_critical_section(S_USERS);
  743     CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
  744     original_v_flags = vbuf.v_flags;
  745 
  746     /* Know the room ... but not if it's the page log room, or if the
  747      * caller specified that we're only entering this room transiently.
  748      */
  749     int add_room_to_known_list = 1;
  750     if (transiently == 1) {
  751         add_room_to_known_list = 0;
  752     }
  753     char *c_logpages = CtdlGetConfigStr("c_logpages");
  754     if ( (c_logpages != NULL) && (!strcasecmp(CC->room.QRname, c_logpages)) ) {
  755         add_room_to_known_list = 0;
  756     }
  757     if (add_room_to_known_list) {
  758         vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
  759         vbuf.v_flags = vbuf.v_flags | V_ACCESS;
  760     }
  761     
  762     /* Only rewrite the database record if we changed something */
  763     if (vbuf.v_flags != original_v_flags) {
  764         CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
  765     }
  766     end_critical_section(S_USERS);
  767 
  768     /* Check for new mail */
  769     newmailcount = NewMailCount();
  770 
  771     /* Set info to 1 if the room banner is new since our last visit.
  772      * Some clients only want to display it when it changes.
  773      */
  774     if (CC->room.msgnum_info > vbuf.v_lastseen) {
  775         info = 1;
  776     }
  777 
  778         cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
  779         if (cdbfr != NULL) {
  780             msglist = (long *) cdbfr->ptr;
  781         cdbfr->ptr = NULL;  /* CtdlUserGoto() now owns this memory */
  782             num_msgs = cdbfr->len / sizeof(long);
  783             cdb_free(cdbfr);
  784     }
  785 
  786     total_messages = 0;
  787     for (a=0; a<num_msgs; ++a) {
  788         if (msglist[a] > 0L) ++total_messages;
  789     }
  790 
  791     if (total_messages > 0) {
  792         oldest_message = msglist[0];
  793         newest_message = msglist[num_msgs - 1];
  794     }
  795 
  796     num_sets = num_tokens(vbuf.v_seen, ',');
  797     for (s=0; s<num_sets; ++s) {
  798         extract_token(setstr, vbuf.v_seen, s, ',', sizeof setstr);
  799 
  800         extract_token(lostr, setstr, 0, ':', sizeof lostr);
  801         if (num_tokens(setstr, ':') >= 2) {
  802             extract_token(histr, setstr, 1, ':', sizeof histr);
  803             if (!strcmp(histr, "*")) {
  804                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
  805             }
  806         } 
  807         else {
  808             strcpy(histr, lostr);
  809         }
  810         lo = atol(lostr);
  811         hi = atol(histr);
  812 
  813         for (a=0; a<num_msgs; ++a) if (msglist[a] > 0L) {
  814             if ((msglist[a] >= lo) && (msglist[a] <= hi)) {
  815                 ++old_messages;
  816                 msglist[a] = 0L;
  817             }
  818         }
  819     }
  820     new_messages = total_messages - old_messages;
  821 
  822     if (msglist != NULL) free(msglist);
  823 
  824     if (CC->room.QRflags & QR_MAILBOX)
  825         rmailflag = 1;
  826     else
  827         rmailflag = 0;
  828 
  829     if ((CC->room.QRroomaide == CC->user.usernum) || (CC->user.axlevel >= AxAideU))
  830         raideflag = 1;
  831     else
  832         raideflag = 0;
  833 
  834     safestrncpy(truncated_roomname, CC->room.QRname, sizeof truncated_roomname);
  835     if ( (CC->room.QRflags & QR_MAILBOX) && (atol(CC->room.QRname) == CC->user.usernum) ) {
  836         safestrncpy(truncated_roomname, &truncated_roomname[11], sizeof truncated_roomname);
  837     }
  838 
  839     if (!strcasecmp(truncated_roomname, USERTRASHROOM)) {
  840         is_trash = 1;
  841     }
  842 
  843     if (retmsgs != NULL) *retmsgs = total_messages;
  844     if (retnew != NULL) *retnew = new_messages;
  845     if (retoldest != NULL) *retoldest = oldest_message;
  846     if (retnewest != NULL) *retnewest = newest_message;
  847     syslog(LOG_DEBUG, "room_ops: %s : %d new of %d total messages, oldest=%ld, newest=%ld",
  848            CC->room.QRname, new_messages, total_messages, oldest_message, newest_message
  849     );
  850 
  851     CC->curr_view = (int)vbuf.v_view;
  852 
  853     if (display_result) {
  854         cprintf("%d%c%s|%d|%d|%d|%d|%ld|%ld|%d|%d|%d|%d|%d|%d|%d|%d|%ld|\n",
  855             CIT_OK, CtdlCheckExpress(),
  856             truncated_roomname,
  857             (int)new_messages,
  858             (int)total_messages,
  859             (int)info,
  860             (int)CC->room.QRflags,
  861             (long)CC->room.QRhighest,
  862             (long)vbuf.v_lastseen,
  863             (int)rmailflag,
  864             (int)raideflag,
  865             (int)newmailcount,
  866             (int)CC->room.QRfloor,
  867             (int)vbuf.v_view,
  868             (int)CC->room.QRdefaultview,
  869             (int)is_trash,
  870             (int)CC->room.QRflags2,
  871             (long)CC->room.QRmtime
  872         );
  873     }
  874 }
  875 
  876 
  877 /*
  878  * Handle some of the macro named rooms
  879  */
  880 void convert_room_name_macros(char *towhere, size_t maxlen) {
  881     if (!strcasecmp(towhere, "_BASEROOM_")) {
  882         safestrncpy(towhere, CtdlGetConfigStr("c_baseroom"), maxlen);
  883     }
  884     else if (!strcasecmp(towhere, "_MAIL_")) {
  885         safestrncpy(towhere, MAILROOM, maxlen);
  886     }
  887     else if (!strcasecmp(towhere, "_TRASH_")) {
  888         safestrncpy(towhere, USERTRASHROOM, maxlen);
  889     }
  890     else if (!strcasecmp(towhere, "_DRAFTS_")) {
  891         safestrncpy(towhere, USERDRAFTROOM, maxlen);
  892     }
  893     else if (!strcasecmp(towhere, "_BITBUCKET_")) {
  894         safestrncpy(towhere, CtdlGetConfigStr("c_twitroom"), maxlen);
  895     }
  896     else if (!strcasecmp(towhere, "_CALENDAR_")) {
  897         safestrncpy(towhere, USERCALENDARROOM, maxlen);
  898     }
  899     else if (!strcasecmp(towhere, "_TASKS_")) {
  900         safestrncpy(towhere, USERTASKSROOM, maxlen);
  901     }
  902     else if (!strcasecmp(towhere, "_CONTACTS_")) {
  903         safestrncpy(towhere, USERCONTACTSROOM, maxlen);
  904     }
  905     else if (!strcasecmp(towhere, "_NOTES_")) {
  906         safestrncpy(towhere, USERNOTESROOM, maxlen);
  907     }
  908 }
  909 
  910 
  911 /*
  912  * Back end function to rename a room.
  913  * You can also specify which floor to move the room to, or specify -1 to
  914  * keep the room on the same floor it was on.
  915  *
  916  * If you are renaming a mailbox room, you must supply the namespace prefix
  917  * in *at least* the old name!
  918  */
  919 int CtdlRenameRoom(char *old_name, char *new_name, int new_floor) {
  920     int old_floor = 0;
  921     struct ctdlroom qrbuf;
  922     struct ctdlroom qrtmp;
  923     int ret = 0;
  924     struct floor *fl;
  925     struct floor flbuf;
  926     long owner = 0L;
  927     char actual_old_name[ROOMNAMELEN];
  928 
  929     syslog(LOG_DEBUG, "room_ops: CtdlRenameRoom(%s, %s, %d)", old_name, new_name, new_floor);
  930 
  931     if (new_floor >= 0) {
  932         fl = CtdlGetCachedFloor(new_floor);
  933         if ((fl->f_flags & F_INUSE) == 0) {
  934             return(crr_invalid_floor);
  935         }
  936     }
  937 
  938     begin_critical_section(S_ROOMS);
  939 
  940     if ( (CtdlGetRoom(&qrtmp, new_name) == 0) && (strcasecmp(new_name, old_name)) ) {
  941         ret = crr_already_exists;
  942     }
  943 
  944     else if (CtdlGetRoom(&qrbuf, old_name) != 0) {
  945         ret = crr_room_not_found;
  946     }
  947 
  948     else if ( (CC->user.axlevel < AxAideU) && (!CC->internal_pgm)
  949           && (CC->user.usernum != qrbuf.QRroomaide)
  950           && ( (((qrbuf.QRflags & QR_MAILBOX) == 0) || (atol(qrbuf.QRname) != CC->user.usernum))) )  {
  951         ret = crr_access_denied;
  952     }
  953 
  954     else if (CtdlIsNonEditable(&qrbuf)) {
  955         ret = crr_noneditable;
  956     }
  957 
  958     else {
  959         /* Rename it */
  960         safestrncpy(actual_old_name, qrbuf.QRname, sizeof actual_old_name);
  961         if (qrbuf.QRflags & QR_MAILBOX) {
  962             owner = atol(qrbuf.QRname);
  963         }
  964         if ( (owner > 0L) && (atol(new_name) == 0L) ) {
  965             snprintf(qrbuf.QRname, sizeof(qrbuf.QRname),
  966                     "%010ld.%s", owner, new_name);
  967         }
  968         else {
  969             safestrncpy(qrbuf.QRname, new_name,
  970                         sizeof(qrbuf.QRname));
  971         }
  972 
  973         /* Reject change of floor for baseroom/aideroom */
  974         if (!strncasecmp(old_name, CtdlGetConfigStr("c_baseroom"), ROOMNAMELEN) ||
  975             !strncasecmp(old_name, CtdlGetConfigStr("c_aideroom"), ROOMNAMELEN))
  976         {
  977             new_floor = 0;
  978         }
  979 
  980         /* Take care of floor stuff */
  981         old_floor = qrbuf.QRfloor;
  982         if (new_floor < 0) {
  983             new_floor = old_floor;
  984         }
  985         qrbuf.QRfloor = new_floor;
  986         CtdlPutRoom(&qrbuf);
  987 
  988         begin_critical_section(S_CONFIG);
  989     
  990         /* If baseroom/aideroom name changes, update config */
  991         if (!strncasecmp(old_name, CtdlGetConfigStr("c_baseroom"), ROOMNAMELEN)) {
  992             CtdlSetConfigStr("c_baseroom", new_name);
  993         }
  994         if (!strncasecmp(old_name, CtdlGetConfigStr("c_aideroom"), ROOMNAMELEN)) {
  995             CtdlSetConfigStr("c_aideroom", new_name);
  996         }
  997     
  998         end_critical_section(S_CONFIG);
  999     
 1000         /* If the room name changed, then there are now two room
 1001          * records, so we have to delete the old one.
 1002          */
 1003         if (strcasecmp(new_name, old_name)) {
 1004             b_deleteroom(actual_old_name);
 1005         }
 1006 
 1007         ret = crr_ok;
 1008     }
 1009 
 1010     end_critical_section(S_ROOMS);
 1011 
 1012     /* Adjust the floor reference counts if necessary */
 1013     if (new_floor != old_floor) {
 1014         lgetfloor(&flbuf, old_floor);
 1015         --flbuf.f_ref_count;
 1016         lputfloor(&flbuf, old_floor);
 1017         syslog(LOG_DEBUG, "room_ops: reference count for floor %d is now %d", old_floor, flbuf.f_ref_count);
 1018         lgetfloor(&flbuf, new_floor);
 1019         ++flbuf.f_ref_count;
 1020         lputfloor(&flbuf, new_floor);
 1021         syslog(LOG_DEBUG, "room_ops: reference count for floor %d is now %d", new_floor, flbuf.f_ref_count);
 1022     }
 1023 
 1024     /* ...and everybody say "YATTA!" */ 
 1025     return(ret);
 1026 }
 1027 
 1028 
 1029 /*
 1030  * Asynchronously schedule a room for deletion.  By placing the room into an invalid private namespace,
 1031  * the room will appear deleted to the user(s), but the session doesn't need to block while waiting for
 1032  * database operations to complete.  Instead, the room gets purged when THE DREADED AUTO-PURGER makes
 1033  * its next run.  Aren't we so clever?!!
 1034  */
 1035 void CtdlScheduleRoomForDeletion(struct ctdlroom *qrbuf) {
 1036     char old_name[ROOMNAMELEN];
 1037     static int seq = 0;
 1038 
 1039     syslog(LOG_NOTICE, "room_ops: scheduling room <%s> for deletion", qrbuf->QRname);
 1040 
 1041     safestrncpy(old_name, qrbuf->QRname, sizeof old_name);
 1042     CtdlGetRoom(qrbuf, qrbuf->QRname);
 1043 
 1044     /* Turn the room into a private mailbox owned by a user who doesn't
 1045      * exist.  This will immediately make the room invisible to everyone,
 1046      * and qualify the room for purging.
 1047      */
 1048     snprintf(qrbuf->QRname, sizeof qrbuf->QRname, "9999999999.%08lx.%04d.%s",
 1049         time(NULL),
 1050         ++seq,
 1051         old_name
 1052     );
 1053     qrbuf->QRflags |= QR_MAILBOX;
 1054     time(&qrbuf->QRgen);    /* Use a timestamp as the new generation number  */
 1055     CtdlPutRoom(qrbuf);
 1056     b_deleteroom(old_name);
 1057 }
 1058 
 1059 
 1060 /*
 1061  * Back end processing to delete a room and everything associated with it
 1062  * (This one is synchronous and should only get called by THE DREADED
 1063  * AUTO-PURGER in serv_expire.c.  All user-facing code should call
 1064  * the asynchronous schedule_room_for_deletion() instead.)
 1065  */
 1066 void CtdlDeleteRoom(struct ctdlroom *qrbuf) {
 1067     struct floor flbuf;
 1068     char configdbkeyname[25];
 1069 
 1070     syslog(LOG_NOTICE, "room_ops: deleting room <%s>", qrbuf->QRname);
 1071 
 1072     /* Delete the room's network configdb entry */
 1073     netcfg_keyname(configdbkeyname, qrbuf->QRnumber);
 1074     CtdlDelConfig(configdbkeyname);
 1075 
 1076     /* Delete the messages in the room
 1077      * (Careful: this opens an S_ROOMS critical section!)
 1078      */
 1079     CtdlDeleteMessages(qrbuf->QRname, NULL, 0, "");
 1080 
 1081     /* Flag the room record as not in use */
 1082     CtdlGetRoomLock(qrbuf, qrbuf->QRname);
 1083     qrbuf->QRflags = 0;
 1084     CtdlPutRoomLock(qrbuf);
 1085 
 1086     /* then decrement the reference count for the floor */
 1087     lgetfloor(&flbuf, (int) (qrbuf->QRfloor));
 1088     flbuf.f_ref_count = flbuf.f_ref_count - 1;
 1089     lputfloor(&flbuf, (int) (qrbuf->QRfloor));
 1090 
 1091     /* Delete the room record from the database! */
 1092     b_deleteroom(qrbuf->QRname);
 1093 }
 1094 
 1095 
 1096 /*
 1097  * Check access control for deleting a room
 1098  */
 1099 int CtdlDoIHavePermissionToDeleteThisRoom(struct ctdlroom *qr) {
 1100 
 1101     if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
 1102         return(0);
 1103     }
 1104 
 1105     if (CtdlIsNonEditable(qr)) {
 1106         return(0);
 1107     }
 1108 
 1109     /*
 1110      * For mailboxes, check stuff
 1111      */
 1112     if (qr->QRflags & QR_MAILBOX) {
 1113 
 1114         if (strlen(qr->QRname) < 12) return(0); /* bad name */
 1115 
 1116         if (atol(qr->QRname) != CC->user.usernum) {
 1117             return(0);  /* not my room */
 1118         }
 1119 
 1120         /* Can't delete your Mail> room */
 1121         if (!strcasecmp(&qr->QRname[11], MAILROOM)) return(0);
 1122 
 1123         /* Otherwise it's ok */
 1124         return(1);
 1125     }
 1126 
 1127     /*
 1128      * For normal rooms, just check for admin or room admin status.
 1129      */
 1130     return(is_room_aide());
 1131 }
 1132 
 1133 
 1134 /*
 1135  * Internal code to create a new room (returns room flags)
 1136  *
 1137  * Room types:  0=public, 1=hidden, 2=passworded, 3=invitation-only,
 1138  *              4=mailbox, 5=mailbox, but caller supplies namespace
 1139  */
 1140 unsigned CtdlCreateRoom(char *new_room_name,
 1141              int new_room_type,
 1142              char *new_room_pass,
 1143              int new_room_floor,
 1144              int really_create,
 1145              int avoid_access,
 1146              int new_room_view)
 1147 {
 1148     struct ctdlroom qrbuf;
 1149     struct floor flbuf;
 1150     visit vbuf;
 1151 
 1152     syslog(LOG_DEBUG, "room_ops: CtdlCreateRoom(name=%s, type=%d, view=%d)", new_room_name, new_room_type, new_room_view);
 1153 
 1154     if (CtdlGetRoom(&qrbuf, new_room_name) == 0) {
 1155         syslog(LOG_DEBUG, "room_ops: cannot create room <%s> - already exists", new_room_name);
 1156         return(0);
 1157     }
 1158 
 1159     memset(&qrbuf, 0, sizeof(struct ctdlroom));
 1160     safestrncpy(qrbuf.QRpasswd, new_room_pass, sizeof qrbuf.QRpasswd);
 1161     qrbuf.QRflags = QR_INUSE;
 1162     if (new_room_type > 0)
 1163         qrbuf.QRflags = (qrbuf.QRflags | QR_PRIVATE);
 1164     if (new_room_type == 1)
 1165         qrbuf.QRflags = (qrbuf.QRflags | QR_GUESSNAME);
 1166     if (new_room_type == 2)
 1167         qrbuf.QRflags = (qrbuf.QRflags | QR_PASSWORDED);
 1168     if ( (new_room_type == 4) || (new_room_type == 5) ) {
 1169         qrbuf.QRflags = (qrbuf.QRflags | QR_MAILBOX);
 1170         /* qrbuf.QRflags2 |= QR2_SUBJECTREQ; */
 1171     }
 1172 
 1173     /* If the user is requesting a personal room, set up the room
 1174      * name accordingly (prepend the user number)
 1175      */
 1176     if (new_room_type == 4) {
 1177         CtdlMailboxName(qrbuf.QRname, sizeof qrbuf.QRname, &CC->user, new_room_name);
 1178     }
 1179     else {
 1180         safestrncpy(qrbuf.QRname, new_room_name, sizeof qrbuf.QRname);
 1181     }
 1182 
 1183     /* If the room is private, and the system administrator has elected
 1184      * to automatically grant room admin privileges, do so now.
 1185      */
 1186     if ((qrbuf.QRflags & QR_PRIVATE) && (CREATAIDE == 1)) {
 1187         qrbuf.QRroomaide = CC->user.usernum;
 1188     }
 1189     /* Blog owners automatically become room admins of their blogs.
 1190      * (In the future we will offer a site-wide configuration setting to suppress this behavior.)
 1191      */
 1192     else if (new_room_view == VIEW_BLOG) {
 1193         qrbuf.QRroomaide = CC->user.usernum;
 1194     }
 1195     /* Otherwise, set the room admin to undefined.
 1196      */
 1197     else {
 1198         qrbuf.QRroomaide = (-1L);
 1199     }
 1200 
 1201     /* 
 1202      * If the caller is only interested in testing whether this will work,
 1203      * return now without creating the room.
 1204      */
 1205     if (!really_create) return (qrbuf.QRflags);
 1206 
 1207     qrbuf.QRnumber = get_new_room_number();
 1208     qrbuf.QRhighest = 0L;   /* No messages in this room yet */
 1209     time(&qrbuf.QRgen); /* Use a timestamp as the generation number */
 1210     qrbuf.QRfloor = new_room_floor;
 1211     qrbuf.QRdefaultview = new_room_view;
 1212 
 1213     /* save what we just did... */
 1214     CtdlPutRoom(&qrbuf);
 1215 
 1216     /* bump the reference count on whatever floor the room is on */
 1217     lgetfloor(&flbuf, (int) qrbuf.QRfloor);
 1218     flbuf.f_ref_count = flbuf.f_ref_count + 1;
 1219     lputfloor(&flbuf, (int) qrbuf.QRfloor);
 1220 
 1221     /* Grant the creator access to the room unless the avoid_access
 1222      * parameter was specified.
 1223      */
 1224     if ( (CC->logged_in) && (avoid_access == 0) ) {
 1225         CtdlGetRelationship(&vbuf, &CC->user, &qrbuf);
 1226         vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
 1227         vbuf.v_flags = vbuf.v_flags | V_ACCESS;
 1228         CtdlSetRelationship(&vbuf, &CC->user, &qrbuf);
 1229     }
 1230 
 1231     /* resume our happy day */
 1232     return (qrbuf.QRflags);
 1233 }