Back to index

citadel  8.12
room_ops.c
Go to the documentation of this file.
00001 /* 
00002  * Server functions which perform operations on room objects.
00003  *
00004  * Copyright (c) 1987-2011 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 as published by
00008  * the Free Software Foundation; either version 3 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00019  */
00020 
00021 #include "sysdep.h"
00022 #include <stdlib.h>
00023 #include <unistd.h>
00024 #include <stdio.h>
00025 #include <sys/stat.h>
00026 #include <ctype.h>
00027 #include <string.h>
00028 #include <dirent.h>  /* for cmd_rdir to read contents of the directory */
00029 
00030 #if TIME_WITH_SYS_TIME
00031 # include <sys/time.h>
00032 # include <time.h>
00033 #else
00034 # if HAVE_SYS_TIME_H
00035 #  include <sys/time.h>
00036 # else
00037 #  include <time.h>
00038 # endif
00039 #endif
00040 
00041 #include <limits.h>
00042 #include <errno.h>
00043 #include "citadel.h"
00044 #include <libcitadel.h>
00045 #include "server.h"
00046 #include "database.h"
00047 #include "config.h"
00048 #include "room_ops.h"
00049 #include "sysdep_decls.h"
00050 #include "support.h"
00051 #include "msgbase.h"
00052 #include "citserver.h"
00053 #include "control.h"
00054 #include "citadel_dirs.h"
00055 #include "threads.h"
00056 
00057 #include "ctdl_module.h"
00058 #include "user_ops.h"
00059 
00060 struct floor *floorcache[MAXFLOORS];
00061 
00062 /*
00063  * Retrieve access control information for any user/room pair
00064  */
00065 void CtdlRoomAccess(struct ctdlroom *roombuf, struct ctdluser *userbuf,
00066               int *result, int *view)
00067 {
00068        int retval = 0;
00069        visit vbuf;
00070        int is_me = 0;
00071        int is_guest = 0;
00072 
00073        if (userbuf == &CC->user) {
00074               is_me = 1;
00075        }
00076 
00077        if ((is_me) && (config.c_guest_logins) && (!CC->logged_in)) {
00078               is_guest = 1;
00079        }
00080 
00081        /* for internal programs, always do everything */
00082        if (((CC->internal_pgm)) && (roombuf->QRflags & QR_INUSE)) {
00083               retval = (UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED);
00084               vbuf.v_view = 0;
00085               goto SKIP_EVERYTHING;
00086        }
00087 
00088        /* If guest mode is enabled, always grant access to the Lobby */
00089        if ((is_guest) && (!strcasecmp(roombuf->QRname, BASEROOM))) {
00090               retval = (UA_KNOWN | UA_GOTOALLOWED);
00091               vbuf.v_view = 0;
00092               goto SKIP_EVERYTHING;
00093        }
00094 
00095        /* Locate any applicable user/room relationships */
00096        if (is_guest) {
00097               memset(&vbuf, 0, sizeof vbuf);
00098        }
00099        else {
00100               CtdlGetRelationship(&vbuf, userbuf, roombuf);
00101        }
00102 
00103        /* Force the properties of the Aide room */
00104        if (!strcasecmp(roombuf->QRname, config.c_aideroom)) {
00105               if (userbuf->axlevel >= AxAideU) {
00106                      retval = UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED;
00107               } else {
00108                      retval = 0;
00109               }
00110               goto NEWMSG;
00111        }
00112 
00113        /* If this is a public room, it's accessible... */
00114        if (   ((roombuf->QRflags & QR_PRIVATE) == 0) 
00115               && ((roombuf->QRflags & QR_MAILBOX) == 0)
00116        ) {
00117               retval = retval | UA_KNOWN | UA_GOTOALLOWED;
00118        }
00119 
00120        /* If this is a preferred users only room, check access level */
00121        if (roombuf->QRflags & QR_PREFONLY) {
00122               if (userbuf->axlevel < AxPrefU) {
00123                      retval = retval & ~UA_KNOWN & ~UA_GOTOALLOWED;
00124               }
00125        }
00126 
00127        /* For private rooms, check the generation number matchups */
00128        if (   (roombuf->QRflags & QR_PRIVATE) 
00129               && ((roombuf->QRflags & QR_MAILBOX) == 0)
00130        ) {
00131 
00132               /* An explicit match means the user belongs in this room */
00133               if (vbuf.v_flags & V_ACCESS) {
00134                      retval = retval | UA_KNOWN | UA_GOTOALLOWED;
00135               }
00136               /* Otherwise, check if this is a guess-name or passworded
00137                * room.  If it is, a goto may at least be attempted
00138                */
00139               else if (     (roombuf->QRflags & QR_PRIVATE)
00140                             || (roombuf->QRflags & QR_PASSWORDED)
00141               ) {
00142                      retval = retval & ~UA_KNOWN;
00143                      retval = retval | UA_GOTOALLOWED;
00144               }
00145        }
00146 
00147        /* For mailbox rooms, also check the namespace */
00148        /* Also, mailbox owners can delete their messages */
00149        if ( (roombuf->QRflags & QR_MAILBOX) && (atol(roombuf->QRname) != 0)) {
00150               if (userbuf->usernum == atol(roombuf->QRname)) {
00151                      retval = retval | UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED;
00152               }
00153               /* An explicit match means the user belongs in this room */
00154               if (vbuf.v_flags & V_ACCESS) {
00155                      retval = retval | UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED;
00156               }
00157        }
00158 
00159        /* For non-mailbox rooms... */
00160        else {
00161 
00162               /* User is allowed to post in the room unless:
00163                * - User is not validated
00164                * - User has no net privileges and it is a shared network room
00165                * - It is a read-only room
00166                * - It is a blog room (in which case we only allow replies to existing messages)
00167                */
00168               int post_allowed = 1;
00169               int reply_allowed = 1;
00170               if (userbuf->axlevel < AxProbU) {
00171                      post_allowed = 0;
00172                      reply_allowed = 0;
00173               }
00174               if ((userbuf->axlevel < AxNetU) && (roombuf->QRflags & QR_NETWORK)) {
00175                      post_allowed = 0;
00176                      reply_allowed = 0;
00177               }
00178               if (roombuf->QRflags & QR_READONLY) {
00179                      post_allowed = 0;
00180                      reply_allowed = 0;
00181               }
00182               if (roombuf->QRdefaultview == VIEW_BLOG) {
00183                      post_allowed = 0;
00184               }
00185               if (post_allowed) {
00186                      retval = retval | UA_POSTALLOWED | UA_REPLYALLOWED;
00187               }
00188               if (reply_allowed) {
00189                      retval = retval | UA_REPLYALLOWED;
00190               }
00191 
00192               /* If "collaborative deletion" is active for this room, any user who can post
00193                * is also allowed to delete
00194                */
00195               if (roombuf->QRflags2 & QR2_COLLABDEL) {
00196                      if (retval & UA_POSTALLOWED) {
00197                             retval = retval | UA_DELETEALLOWED;
00198                      }
00199               }
00200 
00201        }
00202 
00203        /* Check to see if the user has forgotten this room */
00204        if (vbuf.v_flags & V_FORGET) {
00205               retval = retval & ~UA_KNOWN;
00206               if (   ( ((roombuf->QRflags & QR_PRIVATE) == 0) 
00207                      && ((roombuf->QRflags & QR_MAILBOX) == 0)
00208               ) || ( (roombuf->QRflags & QR_MAILBOX) 
00209                      && (atol(roombuf->QRname) == CC->user.usernum))
00210               ) {
00211                      retval = retval | UA_ZAPPED;
00212               }
00213        }
00214 
00215        /* If user is explicitly locked out of this room, deny everything */
00216        if (vbuf.v_flags & V_LOCKOUT) {
00217               retval = retval & ~UA_KNOWN & ~UA_GOTOALLOWED & ~UA_POSTALLOWED & ~UA_REPLYALLOWED;
00218        }
00219 
00220        /* Aides get access to all private rooms */
00221        if (   (userbuf->axlevel >= AxAideU)
00222               && ((roombuf->QRflags & QR_MAILBOX) == 0)
00223        ) {
00224               if (vbuf.v_flags & V_FORGET) {
00225                      retval = retval | UA_GOTOALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
00226               }
00227               else {
00228                      retval = retval | UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
00229               }
00230        }
00231 
00232        /* Aides can gain access to mailboxes as well, but they don't show
00233         * by default.
00234         */
00235        if (   (userbuf->axlevel >= AxAideU)
00236               && (roombuf->QRflags & QR_MAILBOX)
00237        ) {
00238               retval = retval | UA_GOTOALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
00239        }
00240 
00241        /* Aides and Room Aides have admin privileges */
00242        if (   (userbuf->axlevel >= AxAideU)
00243               || (userbuf->usernum == roombuf->QRroomaide)
00244        ) {
00245               retval = retval | UA_ADMINALLOWED | UA_DELETEALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
00246        }
00247 
00248 NEWMSG:       /* By the way, we also check for the presence of new messages */
00249        if (is_msg_in_sequence_set(vbuf.v_seen, roombuf->QRhighest) == 0) {
00250               retval = retval | UA_HASNEWMSGS;
00251        }
00252 
00253        /* System rooms never show up in the list. */
00254        if (roombuf->QRflags2 & QR2_SYSTEM) {
00255               retval = retval & ~UA_KNOWN;
00256        }
00257 
00258 SKIP_EVERYTHING:
00259        /* Now give the caller the information it wants. */
00260        if (result != NULL) *result = retval;
00261        if (view != NULL) *view = vbuf.v_view;
00262 }
00263 
00264 /*
00265  * Self-checking stuff for a room record read into memory
00266  */
00267 void room_sanity_check(struct ctdlroom *qrbuf)
00268 {
00269        /* Mailbox rooms are always on the lowest floor */
00270        if (qrbuf->QRflags & QR_MAILBOX) {
00271               qrbuf->QRfloor = 0;
00272        }
00273        /* Listing order of 0 is illegal except for base rooms */
00274        if (qrbuf->QRorder == 0)
00275               if (!(qrbuf->QRflags & QR_MAILBOX) &&
00276                   strncasecmp(qrbuf->QRname, config.c_baseroom, ROOMNAMELEN)
00277                   &&
00278                   strncasecmp(qrbuf->QRname, config.c_aideroom, ROOMNAMELEN))
00279                      qrbuf->QRorder = 64;
00280 }
00281 
00282 
00283 /*
00284  * CtdlGetRoom()  -  retrieve room data from disk
00285  */
00286 int CtdlGetRoom(struct ctdlroom *qrbuf, char *room_name)
00287 {
00288        struct cdbdata *cdbqr;
00289        char lowercase_name[ROOMNAMELEN];
00290        char personal_lowercase_name[ROOMNAMELEN];
00291        char *dptr, *sptr, *eptr;
00292 
00293        dptr = lowercase_name;
00294        sptr = room_name;
00295        eptr = (dptr + (sizeof lowercase_name - 1));
00296        while (!IsEmptyStr(sptr) && (dptr < eptr)){
00297               *dptr = tolower(*sptr);
00298               sptr++; dptr++;
00299        }
00300        *dptr = '\0';
00301 
00302        memset(qrbuf, 0, sizeof(struct ctdlroom));
00303 
00304        /* First, try the public namespace */
00305        cdbqr = cdb_fetch(CDB_ROOMS,
00306                        lowercase_name, strlen(lowercase_name));
00307 
00308        /* If that didn't work, try the user's personal namespace */
00309        if (cdbqr == NULL) {
00310               snprintf(personal_lowercase_name,
00311                       sizeof personal_lowercase_name, "%010ld.%s",
00312                       CC->user.usernum, lowercase_name);
00313               cdbqr = cdb_fetch(CDB_ROOMS,
00314                               personal_lowercase_name,
00315                               strlen(personal_lowercase_name));
00316        }
00317        if (cdbqr != NULL) {
00318               memcpy(qrbuf, cdbqr->ptr,
00319                      ((cdbqr->len > sizeof(struct ctdlroom)) ?
00320                      sizeof(struct ctdlroom) : cdbqr->len));
00321               cdb_free(cdbqr);
00322 
00323               room_sanity_check(qrbuf);
00324 
00325               return (0);
00326        } else {
00327               return (1);
00328        }
00329 }
00330 
00331 /*
00332  * CtdlGetRoomLock()  -  same as getroom() but locks the record (if supported)
00333  */
00334 int CtdlGetRoomLock(struct ctdlroom *qrbuf, char *room_name)
00335 {
00336        register int retval;
00337        retval = CtdlGetRoom(qrbuf, room_name);
00338        if (retval == 0) begin_critical_section(S_ROOMS);
00339        return(retval);
00340 }
00341 
00342 
00343 /*
00344  * b_putroom()  -  back end to putroom() and b_deleteroom()
00345  *              (if the supplied buffer is NULL, delete the room record)
00346  */
00347 void b_putroom(struct ctdlroom *qrbuf, char *room_name)
00348 {
00349        char lowercase_name[ROOMNAMELEN];
00350        char *aptr, *bptr;
00351        long len;
00352 
00353        aptr = room_name;
00354        bptr = lowercase_name;
00355        while (!IsEmptyStr(aptr))
00356        {
00357               *bptr = tolower(*aptr);
00358               aptr++;
00359               bptr++;
00360        }
00361        *bptr='\0';
00362 
00363        len = bptr - lowercase_name;
00364        if (qrbuf == NULL) {
00365               cdb_delete(CDB_ROOMS,
00366                         lowercase_name, len);
00367        } else {
00368               time(&qrbuf->QRmtime);
00369               cdb_store(CDB_ROOMS,
00370                        lowercase_name, len,
00371                        qrbuf, sizeof(struct ctdlroom));
00372        }
00373 }
00374 
00375 
00376 /* 
00377  * CtdlPutRoom()  -  store room data to disk
00378  */
00379 void CtdlPutRoom(struct ctdlroom *qrbuf) {
00380        b_putroom(qrbuf, qrbuf->QRname);
00381 }
00382 
00383 
00384 /*
00385  * b_deleteroom()  -  delete a room record from disk
00386  */
00387 void b_deleteroom(char *room_name) {
00388        b_putroom(NULL, room_name);
00389 }
00390 
00391 
00392 
00393 /*
00394  * CtdlPutRoomLock()  -  same as CtdlPutRoom() but unlocks the record (if supported)
00395  */
00396 void CtdlPutRoomLock(struct ctdlroom *qrbuf)
00397 {
00398 
00399        CtdlPutRoom(qrbuf);
00400        end_critical_section(S_ROOMS);
00401 
00402 }
00403 
00404 /****************************************************************************/
00405 
00406 
00407 /*
00408  * CtdlGetFloorByName()  -  retrieve the number of the named floor
00409  * return < 0 if not found else return floor number
00410  */
00411 int CtdlGetFloorByName(const char *floor_name)
00412 {
00413        int a;
00414        struct floor *flbuf = NULL;
00415 
00416        for (a = 0; a < MAXFLOORS; ++a) {
00417               flbuf = CtdlGetCachedFloor(a);
00418 
00419               /* check to see if it already exists */
00420               if ((!strcasecmp(flbuf->f_name, floor_name))
00421                   && (flbuf->f_flags & F_INUSE)) {
00422                      return a;
00423               }
00424        }
00425        return -1;
00426 }
00427 
00428 
00429 
00430 /*
00431  * CtdlGetFloorByNameLock()  -  retrieve floor number for given floor and lock the floor list.
00432  */
00433 int CtdlGetFloorByNameLock(const char *floor_name)
00434 {
00435        begin_critical_section(S_FLOORTAB);
00436        return CtdlGetFloorByName(floor_name);
00437 }
00438 
00439 
00440 
00441 /*
00442  * CtdlGetAvailableFloor()  -  Return number of first unused floor
00443  * return < 0 if none available
00444  */
00445 int CtdlGetAvailableFloor(void)
00446 {
00447        int a;
00448        struct floor *flbuf = NULL;
00449 
00450        for (a = 0; a < MAXFLOORS; a++) {
00451               flbuf = CtdlGetCachedFloor(a);
00452 
00453               /* check to see if it already exists */
00454               if ((flbuf->f_flags & F_INUSE) == 0) {
00455                      return a;
00456               }
00457        }
00458        return -1;
00459 }
00460 
00461 
00462 /*
00463  * CtdlGetFloor()  -  retrieve floor data from disk
00464  */
00465 void CtdlGetFloor(struct floor *flbuf, int floor_num)
00466 {
00467        struct cdbdata *cdbfl;
00468 
00469        memset(flbuf, 0, sizeof(struct floor));
00470        cdbfl = cdb_fetch(CDB_FLOORTAB, &floor_num, sizeof(int));
00471        if (cdbfl != NULL) {
00472               memcpy(flbuf, cdbfl->ptr,
00473                      ((cdbfl->len > sizeof(struct floor)) ?
00474                      sizeof(struct floor) : cdbfl->len));
00475               cdb_free(cdbfl);
00476        } else {
00477               if (floor_num == 0) {
00478                      safestrncpy(flbuf->f_name, "Main Floor", 
00479                             sizeof flbuf->f_name);
00480                      flbuf->f_flags = F_INUSE;
00481                      flbuf->f_ref_count = 3;
00482               }
00483        }
00484 
00485 }
00486 
00487 /*
00488  * lgetfloor()  -  same as CtdlGetFloor() but locks the record (if supported)
00489  */
00490 void lgetfloor(struct floor *flbuf, int floor_num)
00491 {
00492 
00493        begin_critical_section(S_FLOORTAB);
00494        CtdlGetFloor(flbuf, floor_num);
00495 }
00496 
00497 
00498 /*
00499  * CtdlGetCachedFloor()  -  Get floor record from *cache* (loads from disk if needed)
00500  *    
00501  * This is strictly a performance hack.
00502  */
00503 struct floor *CtdlGetCachedFloor(int floor_num) {
00504        static int initialized = 0;
00505        int i;
00506        int fetch_new = 0;
00507        struct floor *fl = NULL;
00508 
00509        begin_critical_section(S_FLOORCACHE);
00510        if (initialized == 0) {
00511               for (i=0; i<MAXFLOORS; ++i) {
00512                      floorcache[floor_num] = NULL;
00513               }
00514        initialized = 1;
00515        }
00516        if (floorcache[floor_num] == NULL) {
00517               fetch_new = 1;
00518        }
00519        end_critical_section(S_FLOORCACHE);
00520 
00521        if (fetch_new) {
00522               fl = malloc(sizeof(struct floor));
00523               CtdlGetFloor(fl, floor_num);
00524               begin_critical_section(S_FLOORCACHE);
00525               if (floorcache[floor_num] != NULL) {
00526                      free(floorcache[floor_num]);
00527               }
00528               floorcache[floor_num] = fl;
00529               end_critical_section(S_FLOORCACHE);
00530        }
00531 
00532        return(floorcache[floor_num]);
00533 }
00534 
00535 
00536 
00537 /*
00538  * CtdlPutFloor()  -  store floor data on disk
00539  */
00540 void CtdlPutFloor(struct floor *flbuf, int floor_num)
00541 {
00542        /* If we've cached this, clear it out, 'cuz it's WRONG now! */
00543        begin_critical_section(S_FLOORCACHE);
00544        if (floorcache[floor_num] != NULL) {
00545               free(floorcache[floor_num]);
00546               floorcache[floor_num] = malloc(sizeof(struct floor));
00547               memcpy(floorcache[floor_num], flbuf, sizeof(struct floor));
00548        }
00549        end_critical_section(S_FLOORCACHE);
00550 
00551        cdb_store(CDB_FLOORTAB, &floor_num, sizeof(int),
00552                 flbuf, sizeof(struct floor));
00553 }
00554 
00555 
00556 
00557 /*
00558  * CtdlPutFloorLock()  -  same as CtdlPutFloor() but unlocks the record (if supported)
00559  */
00560 void CtdlPutFloorLock(struct floor *flbuf, int floor_num)
00561 {
00562 
00563        CtdlPutFloor(flbuf, floor_num);
00564        end_critical_section(S_FLOORTAB);
00565 
00566 }
00567 
00568 
00569 
00570 /*
00571  * lputfloor()  -  same as CtdlPutFloor() but unlocks the record (if supported)
00572  */
00573 void lputfloor(struct floor *flbuf, int floor_num)
00574 {
00575        CtdlPutFloorLock(flbuf, floor_num);
00576 }
00577 
00578 
00579 /* 
00580  *  Traverse the room file...
00581  */
00582 void CtdlForEachRoom(void (*CallBack) (struct ctdlroom *EachRoom, void *out_data),
00583               void *in_data)
00584 {
00585        struct ctdlroom qrbuf;
00586        struct cdbdata *cdbqr;
00587 
00588        cdb_rewind(CDB_ROOMS);
00589 
00590        while (cdbqr = cdb_next_item(CDB_ROOMS), cdbqr != NULL) {
00591               memset(&qrbuf, 0, sizeof(struct ctdlroom));
00592               memcpy(&qrbuf, cdbqr->ptr,
00593                      ((cdbqr->len > sizeof(struct ctdlroom)) ?
00594                      sizeof(struct ctdlroom) : cdbqr->len));
00595               cdb_free(cdbqr);
00596               room_sanity_check(&qrbuf);
00597               if (qrbuf.QRflags & QR_INUSE)
00598                      (*CallBack)(&qrbuf, in_data);
00599        }
00600 }
00601 
00602 
00603 /*
00604  * delete_msglist()  -  delete room message pointers
00605  */
00606 void delete_msglist(struct ctdlroom *whichroom)
00607 {
00608         struct cdbdata *cdbml;
00609 
00610        /* Make sure the msglist we're deleting actually exists, otherwise
00611         * gdbm will complain when we try to delete an invalid record
00612         */
00613         cdbml = cdb_fetch(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
00614         if (cdbml != NULL) {
00615               cdb_free(cdbml);
00616 
00617               /* Go ahead and delete it */
00618               cdb_delete(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
00619        }
00620 }
00621 
00622 
00623 
00624 /*
00625  * Message pointer compare function for sort_msglist()
00626  */
00627 int sort_msglist_cmp(const void *m1, const void *m2) {
00628        if ((*(const long *)m1) > (*(const long *)m2)) return(1);
00629        if ((*(const long *)m1) < (*(const long *)m2)) return(-1);
00630        return(0);
00631 }
00632 
00633 
00634 /*
00635  * sort message pointers
00636  * (returns new msg count)
00637  */
00638 int sort_msglist(long listptrs[], int oldcount)
00639 {
00640        int numitems;
00641 
00642        numitems = oldcount;
00643        if (numitems < 2) {
00644               return (oldcount);
00645        }
00646 
00647        /* do the sort */
00648        qsort(listptrs, numitems, sizeof(long), sort_msglist_cmp);
00649 
00650        /* and yank any nulls */
00651        while ((numitems > 0) && (listptrs[0] == 0L)) {
00652               memmove(&listptrs[0], &listptrs[1],
00653                      (sizeof(long) * (numitems - 1)));
00654               --numitems;
00655        }
00656 
00657        return (numitems);
00658 }
00659 
00660 
00661 /*
00662  * Determine whether a given room is non-editable.
00663  */
00664 int CtdlIsNonEditable(struct ctdlroom *qrbuf)
00665 {
00666 
00667        /* Mail> rooms are non-editable */
00668        if ( (qrbuf->QRflags & QR_MAILBOX)
00669             && (!strcasecmp(&qrbuf->QRname[11], MAILROOM)) )
00670               return (1);
00671 
00672        /* Everything else is editable */
00673        return (0);
00674 }
00675 
00676 
00677 
00678 /*
00679  * Back-back-end for all room listing commands
00680  */
00681 void list_roomname(struct ctdlroom *qrbuf, int ra, int current_view, int default_view)
00682 {
00683        char truncated_roomname[ROOMNAMELEN];
00684 
00685        /* For my own mailbox rooms, chop off the owner prefix */
00686        if ( (qrbuf->QRflags & QR_MAILBOX)
00687             && (atol(qrbuf->QRname) == CC->user.usernum) ) {
00688               safestrncpy(truncated_roomname, qrbuf->QRname, sizeof truncated_roomname);
00689               safestrncpy(truncated_roomname, &truncated_roomname[11], sizeof truncated_roomname);
00690               cprintf("%s", truncated_roomname);
00691        }
00692        /* For all other rooms, just display the name in its entirety */
00693        else {
00694               cprintf("%s", qrbuf->QRname);
00695        }
00696 
00697        /* ...and now the other parameters */
00698        cprintf("|%u|%d|%d|%d|%d|%d|%d|%ld|\n",
00699               qrbuf->QRflags,
00700               (int) qrbuf->QRfloor,
00701               (int) qrbuf->QRorder,
00702               (int) qrbuf->QRflags2,
00703               ra,
00704               current_view,
00705               default_view,
00706               qrbuf->QRmtime
00707        );
00708 }
00709 
00710 
00711 /* 
00712  * cmd_lrms()   -  List all accessible rooms, known or forgotten
00713  */
00714 void cmd_lrms_backend(struct ctdlroom *qrbuf, void *data)
00715 {
00716        int FloorBeingSearched = (-1);
00717        int ra;
00718        int view;
00719 
00720        FloorBeingSearched = *(int *)data;
00721        CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
00722 
00723        if ((( ra & (UA_KNOWN | UA_ZAPPED)))
00724            && ((qrbuf->QRfloor == (FloorBeingSearched))
00725               || ((FloorBeingSearched) < 0)))
00726               list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
00727 }
00728 
00729 void cmd_lrms(char *argbuf)
00730 {
00731        int FloorBeingSearched = (-1);
00732        if (!IsEmptyStr(argbuf))
00733               FloorBeingSearched = extract_int(argbuf, 0);
00734 
00735        if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
00736 
00737        CtdlGetUser(&CC->user, CC->curr_user);
00738        cprintf("%d Accessible rooms:\n", LISTING_FOLLOWS);
00739 
00740        CtdlForEachRoom(cmd_lrms_backend, &FloorBeingSearched);
00741        cprintf("000\n");
00742 }
00743 
00744 
00745 
00746 /* 
00747  * cmd_lkra()   -  List all known rooms
00748  */
00749 void cmd_lkra_backend(struct ctdlroom *qrbuf, void *data)
00750 {
00751        int FloorBeingSearched = (-1);
00752        int ra;
00753        int view;
00754 
00755        FloorBeingSearched = *(int *)data;
00756        CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
00757 
00758        if ((( ra & (UA_KNOWN)))
00759            && ((qrbuf->QRfloor == (FloorBeingSearched))
00760               || ((FloorBeingSearched) < 0)))
00761               list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
00762 }
00763 
00764 void cmd_lkra(char *argbuf)
00765 {
00766        int FloorBeingSearched = (-1);
00767        if (!IsEmptyStr(argbuf))
00768               FloorBeingSearched = extract_int(argbuf, 0);
00769 
00770        if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
00771        
00772        CtdlGetUser(&CC->user, CC->curr_user);
00773        cprintf("%d Known rooms:\n", LISTING_FOLLOWS);
00774 
00775        CtdlForEachRoom(cmd_lkra_backend, &FloorBeingSearched);
00776        cprintf("000\n");
00777 }
00778 
00779 
00780 
00781 void cmd_lprm_backend(struct ctdlroom *qrbuf, void *data)
00782 {
00783        int FloorBeingSearched = (-1);
00784        int ra;
00785        int view;
00786 
00787        FloorBeingSearched = *(int *)data;
00788        CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
00789 
00790        if (   ((qrbuf->QRflags & QR_PRIVATE) == 0)
00791               && ((qrbuf->QRflags & QR_MAILBOX) == 0)
00792            && ((qrbuf->QRfloor == (FloorBeingSearched))
00793               || ((FloorBeingSearched) < 0)))
00794               list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
00795 }
00796 
00797 void cmd_lprm(char *argbuf)
00798 {
00799        int FloorBeingSearched = (-1);
00800        if (!IsEmptyStr(argbuf))
00801               FloorBeingSearched = extract_int(argbuf, 0);
00802 
00803        cprintf("%d Public rooms:\n", LISTING_FOLLOWS);
00804 
00805        CtdlForEachRoom(cmd_lprm_backend, &FloorBeingSearched);
00806        cprintf("000\n");
00807 }
00808 
00809 
00810 
00811 /* 
00812  * cmd_lkrn()   -  List all known rooms with new messages
00813  */
00814 void cmd_lkrn_backend(struct ctdlroom *qrbuf, void *data)
00815 {
00816        int FloorBeingSearched = (-1);
00817        int ra;
00818        int view;
00819 
00820        FloorBeingSearched = *(int *)data;
00821        CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
00822 
00823        if ((ra & UA_KNOWN)
00824            && (ra & UA_HASNEWMSGS)
00825            && ((qrbuf->QRfloor == (FloorBeingSearched))
00826               || ((FloorBeingSearched) < 0)))
00827               list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
00828 }
00829 
00830 void cmd_lkrn(char *argbuf)
00831 {
00832        int FloorBeingSearched = (-1);
00833        if (!IsEmptyStr(argbuf))
00834               FloorBeingSearched = extract_int(argbuf, 0);
00835 
00836        if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
00837        
00838        CtdlGetUser(&CC->user, CC->curr_user);
00839        cprintf("%d Rooms w/ new msgs:\n", LISTING_FOLLOWS);
00840 
00841        CtdlForEachRoom(cmd_lkrn_backend, &FloorBeingSearched);
00842        cprintf("000\n");
00843 }
00844 
00845 
00846 
00847 /* 
00848  * cmd_lkro()   -  List all known rooms
00849  */
00850 void cmd_lkro_backend(struct ctdlroom *qrbuf, void *data)
00851 {
00852        int FloorBeingSearched = (-1);
00853        int ra;
00854        int view;
00855 
00856        FloorBeingSearched = *(int *)data;
00857        CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
00858 
00859        if ((ra & UA_KNOWN)
00860            && ((ra & UA_HASNEWMSGS) == 0)
00861            && ((qrbuf->QRfloor == (FloorBeingSearched))
00862               || ((FloorBeingSearched) < 0)))
00863               list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
00864 }
00865 
00866 void cmd_lkro(char *argbuf)
00867 {
00868        int FloorBeingSearched = (-1);
00869        if (!IsEmptyStr(argbuf))
00870               FloorBeingSearched = extract_int(argbuf, 0);
00871 
00872        if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
00873        
00874        CtdlGetUser(&CC->user, CC->curr_user);
00875        cprintf("%d Rooms w/o new msgs:\n", LISTING_FOLLOWS);
00876 
00877        CtdlForEachRoom(cmd_lkro_backend, &FloorBeingSearched);
00878        cprintf("000\n");
00879 }
00880 
00881 
00882 
00883 /* 
00884  * cmd_lzrm()   -  List all forgotten rooms
00885  */
00886 void cmd_lzrm_backend(struct ctdlroom *qrbuf, void *data)
00887 {
00888        int FloorBeingSearched = (-1);
00889        int ra;
00890        int view;
00891 
00892        FloorBeingSearched = *(int *)data;
00893        CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
00894 
00895        if ((ra & UA_GOTOALLOWED)
00896            && (ra & UA_ZAPPED)
00897            && ((qrbuf->QRfloor == (FloorBeingSearched))
00898               || ((FloorBeingSearched) < 0)))
00899               list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
00900 }
00901 
00902 void cmd_lzrm(char *argbuf)
00903 {
00904        int FloorBeingSearched = (-1);
00905        if (!IsEmptyStr(argbuf))
00906               FloorBeingSearched = extract_int(argbuf, 0);
00907 
00908        if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
00909        
00910        CtdlGetUser(&CC->user, CC->curr_user);
00911        cprintf("%d Zapped rooms:\n", LISTING_FOLLOWS);
00912 
00913        CtdlForEachRoom(cmd_lzrm_backend, &FloorBeingSearched);
00914        cprintf("000\n");
00915 }
00916 
00917 
00918 /*
00919  * Make the specified room the current room for this session.  No validation
00920  * or access control is done here -- the caller should make sure that the
00921  * specified room exists and is ok to access.
00922  */
00923 void CtdlUserGoto(char *where, int display_result, int transiently,
00924               int *retmsgs, int *retnew)
00925 {
00926        struct CitContext *CCC = CC;
00927        int a;
00928        int new_messages = 0;
00929        int old_messages = 0;
00930        int total_messages = 0;
00931        int info = 0;
00932        int rmailflag;
00933        int raideflag;
00934        int newmailcount = 0;
00935        visit vbuf;
00936        char truncated_roomname[ROOMNAMELEN];
00937         struct cdbdata *cdbfr;
00938        long *msglist = NULL;
00939        int num_msgs = 0;
00940        unsigned int original_v_flags;
00941        int num_sets;
00942        int s;
00943        char setstr[128], lostr[64], histr[64];
00944        long lo, hi;
00945        int is_trash = 0;
00946 
00947        /* If the supplied room name is NULL, the caller wants us to know that
00948         * it has already copied the room record into CC->room, so
00949         * we can skip the extra database fetch.
00950         */
00951        if (where != NULL) {
00952               safestrncpy(CCC->room.QRname, where, sizeof CCC->room.QRname);
00953               CtdlGetRoom(&CCC->room, where);
00954        }
00955 
00956        /* Take care of all the formalities. */
00957 
00958        begin_critical_section(S_USERS);
00959        CtdlGetRelationship(&vbuf, &CCC->user, &CCC->room);
00960        original_v_flags = vbuf.v_flags;
00961 
00962        /* Know the room ... but not if it's the page log room, or if the
00963         * caller specified that we're only entering this room transiently.
00964         */
00965        if ((strcasecmp(CCC->room.QRname, config.c_logpages))
00966           && (transiently == 0) ) {
00967               vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
00968               vbuf.v_flags = vbuf.v_flags | V_ACCESS;
00969        }
00970        
00971        /* Only rewrite the database record if we changed something */
00972        if (vbuf.v_flags != original_v_flags) {
00973               CtdlSetRelationship(&vbuf, &CCC->user, &CCC->room);
00974        }
00975        end_critical_section(S_USERS);
00976 
00977        /* Check for new mail */
00978        newmailcount = NewMailCount();
00979 
00980        /* set info to 1 if the user needs to read the room's info file */
00981        if (CCC->room.QRinfo > vbuf.v_lastseen) {
00982               info = 1;
00983        }
00984 
00985         cdbfr = cdb_fetch(CDB_MSGLISTS, &CCC->room.QRnumber, sizeof(long));
00986         if (cdbfr != NULL) {
00987               msglist = (long *) cdbfr->ptr;
00988               cdbfr->ptr = NULL;   /* CtdlUserGoto() now owns this memory */
00989               num_msgs = cdbfr->len / sizeof(long);
00990               cdb_free(cdbfr);
00991        }
00992 
00993        total_messages = 0;
00994        for (a=0; a<num_msgs; ++a) {
00995               if (msglist[a] > 0L) ++total_messages;
00996        }
00997 
00998        num_sets = num_tokens(vbuf.v_seen, ',');
00999        for (s=0; s<num_sets; ++s) {
01000               extract_token(setstr, vbuf.v_seen, s, ',', sizeof setstr);
01001 
01002               extract_token(lostr, setstr, 0, ':', sizeof lostr);
01003               if (num_tokens(setstr, ':') >= 2) {
01004                      extract_token(histr, setstr, 1, ':', sizeof histr);
01005                      if (!strcmp(histr, "*")) {
01006                             snprintf(histr, sizeof histr, "%ld", LONG_MAX);
01007                      }
01008               } 
01009               else {
01010                      strcpy(histr, lostr);
01011               }
01012               lo = atol(lostr);
01013               hi = atol(histr);
01014 
01015               for (a=0; a<num_msgs; ++a) if (msglist[a] > 0L) {
01016                      if ((msglist[a] >= lo) && (msglist[a] <= hi)) {
01017                             ++old_messages;
01018                             msglist[a] = 0L;
01019                      }
01020               }
01021        }
01022        new_messages = total_messages - old_messages;
01023 
01024        if (msglist != NULL) free(msglist);
01025 
01026        if (CCC->room.QRflags & QR_MAILBOX)
01027               rmailflag = 1;
01028        else
01029               rmailflag = 0;
01030 
01031        if ((CCC->room.QRroomaide == CCC->user.usernum)
01032            || (CCC->user.axlevel >= AxAideU))
01033               raideflag = 1;
01034        else
01035               raideflag = 0;
01036 
01037        safestrncpy(truncated_roomname, CCC->room.QRname, sizeof truncated_roomname);
01038        if ( (CCC->room.QRflags & QR_MAILBOX)
01039           && (atol(CCC->room.QRname) == CCC->user.usernum) ) {
01040               safestrncpy(truncated_roomname, &truncated_roomname[11], sizeof truncated_roomname);
01041        }
01042 
01043        if (!strcasecmp(truncated_roomname, USERTRASHROOM)) {
01044               is_trash = 1;
01045        }
01046 
01047        if (retmsgs != NULL) *retmsgs = total_messages;
01048        if (retnew != NULL) *retnew = new_messages;
01049        MSG_syslog(LOG_INFO, "<%s> %d new of %d total messages\n",
01050                  CCC->room.QRname,
01051                  new_messages, total_messages
01052               );
01053 
01054        CCC->curr_view = (int)vbuf.v_view;
01055 
01056        if (display_result) {
01057               cprintf("%d%c%s|%d|%d|%d|%d|%ld|%ld|%d|%d|%d|%d|%d|%d|%d|%d|\n",
01058                      CIT_OK, CtdlCheckExpress(),
01059                      truncated_roomname,
01060                      (int)new_messages,
01061                      (int)total_messages,
01062                      (int)info,
01063                      (int)CCC->room.QRflags,
01064                      (long)CCC->room.QRhighest,
01065                      (long)vbuf.v_lastseen,
01066                      (int)rmailflag,
01067                      (int)raideflag,
01068                      (int)newmailcount,
01069                      (int)CCC->room.QRfloor,
01070                      (int)vbuf.v_view,
01071                      (int)CCC->room.QRdefaultview,
01072                      (int)is_trash,
01073                      (int)CCC->room.QRflags2
01074               );
01075        }
01076 }
01077 
01078 
01079 /*
01080  * Handle some of the macro named rooms
01081  */
01082 void convert_room_name_macros(char *towhere, size_t maxlen) {
01083        if (!strcasecmp(towhere, "_BASEROOM_")) {
01084               safestrncpy(towhere, config.c_baseroom, maxlen);
01085        }
01086        else if (!strcasecmp(towhere, "_MAIL_")) {
01087               safestrncpy(towhere, MAILROOM, maxlen);
01088        }
01089        else if (!strcasecmp(towhere, "_TRASH_")) {
01090               safestrncpy(towhere, USERTRASHROOM, maxlen);
01091        }
01092        else if (!strcasecmp(towhere, "_DRAFTS_")) {
01093               safestrncpy(towhere, USERDRAFTROOM, maxlen);
01094        }
01095        else if (!strcasecmp(towhere, "_BITBUCKET_")) {
01096               safestrncpy(towhere, config.c_twitroom, maxlen);
01097        }
01098        else if (!strcasecmp(towhere, "_CALENDAR_")) {
01099               safestrncpy(towhere, USERCALENDARROOM, maxlen);
01100        }
01101        else if (!strcasecmp(towhere, "_TASKS_")) {
01102               safestrncpy(towhere, USERTASKSROOM, maxlen);
01103        }
01104        else if (!strcasecmp(towhere, "_CONTACTS_")) {
01105               safestrncpy(towhere, USERCONTACTSROOM, maxlen);
01106        }
01107        else if (!strcasecmp(towhere, "_NOTES_")) {
01108               safestrncpy(towhere, USERNOTESROOM, maxlen);
01109        }
01110 }
01111 
01112 
01113 /* 
01114  * cmd_goto()  -  goto a new room
01115  */
01116 void cmd_goto(char *gargs)
01117 {
01118        struct ctdlroom QRscratch;
01119        int c;
01120        int ok = 0;
01121        int ra;
01122        char augmented_roomname[ROOMNAMELEN];
01123        char towhere[ROOMNAMELEN];
01124        char password[32];
01125        int transiently = 0;
01126 
01127        if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
01128 
01129        extract_token(towhere, gargs, 0, '|', sizeof towhere);
01130        extract_token(password, gargs, 1, '|', sizeof password);
01131        transiently = extract_int(gargs, 2);
01132 
01133        CtdlGetUser(&CC->user, CC->curr_user);
01134 
01135        /*
01136         * Handle some of the macro named rooms
01137         */
01138        convert_room_name_macros(towhere, sizeof towhere);
01139 
01140        /* First try a regular match */
01141        c = CtdlGetRoom(&QRscratch, towhere);
01142 
01143        /* Then try a mailbox name match */
01144        if (c != 0) {
01145               CtdlMailboxName(augmented_roomname, sizeof augmented_roomname,
01146                          &CC->user, towhere);
01147               c = CtdlGetRoom(&QRscratch, augmented_roomname);
01148               if (c == 0)
01149                      safestrncpy(towhere, augmented_roomname, sizeof towhere);
01150        }
01151 
01152        /* And if the room was found... */
01153        if (c == 0) {
01154 
01155               /* Let internal programs go directly to any room. */
01156               if (CC->internal_pgm) {
01157                      memcpy(&CC->room, &QRscratch,
01158                             sizeof(struct ctdlroom));
01159                      CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
01160                      return;
01161               }
01162 
01163               /* See if there is an existing user/room relationship */
01164               CtdlRoomAccess(&QRscratch, &CC->user, &ra, NULL);
01165 
01166               /* normal clients have to pass through security */
01167               if (ra & UA_GOTOALLOWED) {
01168                      ok = 1;
01169               }
01170 
01171               if (ok == 1) {
01172                      if ((QRscratch.QRflags & QR_MAILBOX) &&
01173                          ((ra & UA_GOTOALLOWED))) {
01174                             memcpy(&CC->room, &QRscratch,
01175                                    sizeof(struct ctdlroom));
01176                             CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
01177                             return;
01178                      } else if ((QRscratch.QRflags & QR_PASSWORDED) &&
01179                          ((ra & UA_KNOWN) == 0) &&
01180                          (strcasecmp(QRscratch.QRpasswd, password)) &&
01181                          (CC->user.axlevel < AxAideU)
01182                          ) {
01183                             cprintf("%d wrong or missing passwd\n",
01184                                    ERROR + PASSWORD_REQUIRED);
01185                             return;
01186                      } else if ((QRscratch.QRflags & QR_PRIVATE) &&
01187                                ((QRscratch.QRflags & QR_PASSWORDED) == 0) &&
01188                                ((QRscratch.QRflags & QR_GUESSNAME) == 0) &&
01189                                ((ra & UA_KNOWN) == 0) &&
01190                                 (CC->user.axlevel < AxAideU)
01191                                   ) {
01192                             syslog(LOG_DEBUG, "Failed to acquire private room\n");
01193                      } else {
01194                             memcpy(&CC->room, &QRscratch,
01195                                    sizeof(struct ctdlroom));
01196                             CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
01197                             return;
01198                      }
01199               }
01200        }
01201 
01202        cprintf("%d room '%s' not found\n", ERROR + ROOM_NOT_FOUND, towhere);
01203 }
01204 
01205 
01206 void cmd_whok(char *cmdbuf)
01207 {
01208        struct ctdluser temp;
01209        struct cdbdata *cdbus;
01210        int ra;
01211 
01212        cprintf("%d Who knows room:\n", LISTING_FOLLOWS);
01213        cdb_rewind(CDB_USERS);
01214        while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
01215               memset(&temp, 0, sizeof temp);
01216               memcpy(&temp, cdbus->ptr, sizeof temp);
01217               cdb_free(cdbus);
01218 
01219               CtdlRoomAccess(&CC->room, &temp, &ra, NULL);
01220               if ((!IsEmptyStr(temp.fullname)) && 
01221                   (CC->room.QRflags & QR_INUSE) &&
01222                   (ra & UA_KNOWN)
01223                      )
01224                      cprintf("%s\n", temp.fullname);
01225        }
01226        cprintf("000\n");
01227 }
01228 
01229 
01230 /*
01231  * RDIR command for room directory
01232  */
01233 void cmd_rdir(char *cmdbuf)
01234 {
01235        char buf[256];
01236        char comment[256];
01237        FILE *fd;
01238        struct stat statbuf;
01239        DIR *filedir = NULL;
01240        struct dirent *filedir_entry;
01241        int d_namelen;
01242        char buf2[SIZ];
01243        char mimebuf[64];
01244        long len;
01245        
01246        if (CtdlAccessCheck(ac_logged_in)) return;
01247        
01248        CtdlGetRoom(&CC->room, CC->room.QRname);
01249        CtdlGetUser(&CC->user, CC->curr_user);
01250 
01251        if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
01252               cprintf("%d not here.\n", ERROR + NOT_HERE);
01253               return;
01254        }
01255        if (((CC->room.QRflags & QR_VISDIR) == 0)
01256            && (CC->user.axlevel < AxAideU)
01257            && (CC->user.usernum != CC->room.QRroomaide)) {
01258               cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
01259               return;
01260        }
01261 
01262        snprintf(buf, sizeof buf, "%s/%s", ctdl_file_dir, CC->room.QRdirname);
01263        filedir = opendir (buf);
01264        
01265        if (filedir == NULL) {
01266               cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
01267               return;
01268        }
01269        cprintf("%d %s|%s/%s\n", LISTING_FOLLOWS, config.c_fqdn, ctdl_file_dir, CC->room.QRdirname);
01270        
01271        snprintf(buf, sizeof buf, "%s/%s/filedir", ctdl_file_dir, CC->room.QRdirname);
01272        fd = fopen(buf, "r");
01273        if (fd == NULL)
01274               fd = fopen("/dev/null", "r");
01275        while ((filedir_entry = readdir(filedir)))
01276        {
01277               if (strcasecmp(filedir_entry->d_name, "filedir") && filedir_entry->d_name[0] != '.')
01278               {
01279 #ifdef _DIRENT_HAVE_D_NAMELEN
01280                      d_namelen = filedir_entry->d_namelen;
01281 #else
01282                      d_namelen = strlen(filedir_entry->d_name);
01283 #endif
01284                      snprintf(buf, sizeof buf, "%s/%s/%s", ctdl_file_dir, CC->room.QRdirname, filedir_entry->d_name);
01285                      stat(buf, &statbuf); /* stat the file */
01286                      if (!(statbuf.st_mode & S_IFREG))
01287                      {
01288                             snprintf(buf2, sizeof buf2,
01289                                    "\"%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",
01290                                    buf, CC->room.QRname
01291                             );
01292                             CtdlAideMessage(buf2, "Unusable data found in room directory");
01293                             continue;     /* not a useable file type so don't show it */
01294                      }
01295                      safestrncpy(comment, "", sizeof comment);
01296                      fseek(fd, 0L, 0);    /* rewind descriptions file */
01297                      /* Get the description from the descriptions file */
01298                      while ((fgets(buf, sizeof buf, fd) != NULL) && (IsEmptyStr(comment))) 
01299                      {
01300                             buf[strlen(buf) - 1] = 0;
01301                             if ((!strncasecmp(buf, filedir_entry->d_name, d_namelen)) && (buf[d_namelen] == ' '))
01302                                    safestrncpy(comment, &buf[d_namelen + 1], sizeof comment);
01303                      }
01304                      len = extract_token (mimebuf, comment, 0,' ', 64);
01305                      if ((len <0) || strchr(mimebuf, '/') == NULL)
01306                      {
01307                             snprintf (mimebuf, 64, "application/octetstream");
01308                             len = 0;
01309                      }
01310                      cprintf("%s|%ld|%s|%s\n", 
01311                             filedir_entry->d_name, 
01312                             (long)statbuf.st_size, 
01313                             mimebuf, 
01314                             &comment[len]);
01315               }
01316        }
01317        fclose(fd);
01318        closedir(filedir);
01319        
01320        cprintf("000\n");
01321 }
01322 
01323 /*
01324  * get room parameters (aide or room aide command)
01325  */
01326 void cmd_getr(char *cmdbuf)
01327 {
01328        if (CtdlAccessCheck(ac_room_aide)) return;
01329 
01330        CtdlGetRoom(&CC->room, CC->room.QRname);
01331        cprintf("%d%c%s|%s|%s|%d|%d|%d|%d|%d|\n",
01332               CIT_OK,
01333               CtdlCheckExpress(),
01334 
01335               ((CC->room.QRflags & QR_MAILBOX) ?
01336                      &CC->room.QRname[11] : CC->room.QRname),
01337 
01338               ((CC->room.QRflags & QR_PASSWORDED) ?
01339                      CC->room.QRpasswd : ""),
01340 
01341               ((CC->room.QRflags & QR_DIRECTORY) ?
01342                      CC->room.QRdirname : ""),
01343 
01344               CC->room.QRflags,
01345               (int) CC->room.QRfloor,
01346               (int) CC->room.QRorder,
01347 
01348               CC->room.QRdefaultview,
01349               CC->room.QRflags2
01350               );
01351 }
01352 
01353 
01354 /*
01355  * Back end function to rename a room.
01356  * You can also specify which floor to move the room to, or specify -1 to
01357  * keep the room on the same floor it was on.
01358  *
01359  * If you are renaming a mailbox room, you must supply the namespace prefix
01360  * in *at least* the old name!
01361  */
01362 int CtdlRenameRoom(char *old_name, char *new_name, int new_floor) {
01363        int old_floor = 0;
01364        struct ctdlroom qrbuf;
01365        struct ctdlroom qrtmp;
01366        int ret = 0;
01367        struct floor *fl;
01368        struct floor flbuf;
01369        long owner = 0L;
01370        char actual_old_name[ROOMNAMELEN];
01371 
01372        syslog(LOG_DEBUG, "CtdlRenameRoom(%s, %s, %d)\n",
01373               old_name, new_name, new_floor);
01374 
01375        if (new_floor >= 0) {
01376               fl = CtdlGetCachedFloor(new_floor);
01377               if ((fl->f_flags & F_INUSE) == 0) {
01378                      return(crr_invalid_floor);
01379               }
01380        }
01381 
01382        begin_critical_section(S_ROOMS);
01383 
01384        if ( (CtdlGetRoom(&qrtmp, new_name) == 0) 
01385           && (strcasecmp(new_name, old_name)) ) {
01386               ret = crr_already_exists;
01387        }
01388 
01389        else if (CtdlGetRoom(&qrbuf, old_name) != 0) {
01390               ret = crr_room_not_found;
01391        }
01392 
01393        else if ( (CC->user.axlevel < AxAideU) && (!CC->internal_pgm)
01394                 && (CC->user.usernum != qrbuf.QRroomaide)
01395                 && ( (((qrbuf.QRflags & QR_MAILBOX) == 0) || (atol(qrbuf.QRname) != CC->user.usernum))) )  {
01396               ret = crr_access_denied;
01397        }
01398 
01399        else if (CtdlIsNonEditable(&qrbuf)) {
01400               ret = crr_noneditable;
01401        }
01402 
01403        else {
01404               /* Rename it */
01405               safestrncpy(actual_old_name, qrbuf.QRname, sizeof actual_old_name);
01406               if (qrbuf.QRflags & QR_MAILBOX) {
01407                      owner = atol(qrbuf.QRname);
01408               }
01409               if ( (owner > 0L) && (atol(new_name) == 0L) ) {
01410                      snprintf(qrbuf.QRname, sizeof(qrbuf.QRname),
01411                                    "%010ld.%s", owner, new_name);
01412               }
01413               else {
01414                      safestrncpy(qrbuf.QRname, new_name,
01415                                           sizeof(qrbuf.QRname));
01416               }
01417 
01418               /* Reject change of floor for baseroom/aideroom */
01419               if (!strncasecmp(old_name, config.c_baseroom, ROOMNAMELEN) ||
01420                   !strncasecmp(old_name, config.c_aideroom, ROOMNAMELEN)) {
01421                      new_floor = 0;
01422               }
01423 
01424               /* Take care of floor stuff */
01425               old_floor = qrbuf.QRfloor;
01426               if (new_floor < 0) {
01427                      new_floor = old_floor;
01428               }
01429               qrbuf.QRfloor = new_floor;
01430               CtdlPutRoom(&qrbuf);
01431 
01432               begin_critical_section(S_CONFIG);
01433        
01434               /* If baseroom/aideroom name changes, update config */
01435               if (!strncasecmp(old_name, config.c_baseroom, ROOMNAMELEN)) {
01436                      safestrncpy(config.c_baseroom, new_name, ROOMNAMELEN);
01437                      put_config();
01438               }
01439               if (!strncasecmp(old_name, config.c_aideroom, ROOMNAMELEN)) {
01440                      safestrncpy(config.c_aideroom, new_name, ROOMNAMELEN);
01441                      put_config();
01442               }
01443        
01444               end_critical_section(S_CONFIG);
01445        
01446               /* If the room name changed, then there are now two room
01447                * records, so we have to delete the old one.
01448                */
01449               if (strcasecmp(new_name, old_name)) {
01450                      b_deleteroom(actual_old_name);
01451               }
01452 
01453               ret = crr_ok;
01454        }
01455 
01456        end_critical_section(S_ROOMS);
01457 
01458        /* Adjust the floor reference counts if necessary */
01459        if (new_floor != old_floor) {
01460               lgetfloor(&flbuf, old_floor);
01461               --flbuf.f_ref_count;
01462               lputfloor(&flbuf, old_floor);
01463               syslog(LOG_DEBUG, "Reference count for floor %d is now %d\n", old_floor, flbuf.f_ref_count);
01464               lgetfloor(&flbuf, new_floor);
01465               ++flbuf.f_ref_count;
01466               lputfloor(&flbuf, new_floor);
01467               syslog(LOG_DEBUG, "Reference count for floor %d is now %d\n", new_floor, flbuf.f_ref_count);
01468        }
01469 
01470        /* ...and everybody say "YATTA!" */       
01471        return(ret);
01472 }
01473 
01474 
01475 /*
01476  * set room parameters (aide or room aide command)
01477  */
01478 void cmd_setr(char *args)
01479 {
01480        char buf[256];
01481        int new_order = 0;
01482        int r;
01483        int new_floor;
01484        char new_name[ROOMNAMELEN];
01485 
01486        if (CtdlAccessCheck(ac_logged_in)) return;
01487 
01488        if (num_parms(args) >= 6) {
01489               new_floor = extract_int(args, 5);
01490        } else {
01491               new_floor = (-1);    /* don't change the floor */
01492        }
01493 
01494        /* When is a new name more than just a new name?  When the old name
01495         * has a namespace prefix.
01496         */
01497        if (CC->room.QRflags & QR_MAILBOX) {
01498               sprintf(new_name, "%010ld.", atol(CC->room.QRname) );
01499        } else {
01500               safestrncpy(new_name, "", sizeof new_name);
01501        }
01502        extract_token(&new_name[strlen(new_name)], args, 0, '|', (sizeof new_name - strlen(new_name)));
01503 
01504        r = CtdlRenameRoom(CC->room.QRname, new_name, new_floor);
01505 
01506        if (r == crr_room_not_found) {
01507               cprintf("%d Internal error - room not found?\n", ERROR + INTERNAL_ERROR);
01508        } else if (r == crr_already_exists) {
01509               cprintf("%d '%s' already exists.\n",
01510                      ERROR + ALREADY_EXISTS, new_name);
01511        } else if (r == crr_noneditable) {
01512               cprintf("%d Cannot edit this room.\n", ERROR + NOT_HERE);
01513        } else if (r == crr_invalid_floor) {
01514               cprintf("%d Target floor does not exist.\n",
01515                      ERROR + INVALID_FLOOR_OPERATION);
01516        } else if (r == crr_access_denied) {
01517               cprintf("%d You do not have permission to edit '%s'\n",
01518                      ERROR + HIGHER_ACCESS_REQUIRED,
01519                      CC->room.QRname);
01520        } else if (r != crr_ok) {
01521               cprintf("%d Error: CtdlRenameRoom() returned %d\n",
01522                      ERROR + INTERNAL_ERROR, r);
01523        }
01524 
01525        if (r != crr_ok) {
01526               return;
01527        }
01528 
01529        CtdlGetRoom(&CC->room, new_name);
01530 
01531        /* Now we have to do a bunch of other stuff */
01532 
01533        if (num_parms(args) >= 7) {
01534               new_order = extract_int(args, 6);
01535               if (new_order < 1)
01536                      new_order = 1;
01537               if (new_order > 127)
01538                      new_order = 127;
01539        }
01540 
01541        CtdlGetRoomLock(&CC->room, CC->room.QRname);
01542 
01543        /* Directory room */
01544        extract_token(buf, args, 2, '|', sizeof buf);
01545        buf[15] = 0;
01546        safestrncpy(CC->room.QRdirname, buf,
01547               sizeof CC->room.QRdirname);
01548 
01549        /* Default view */
01550        if (num_parms(args) >= 8) {
01551               CC->room.QRdefaultview = extract_int(args, 7);
01552        }
01553 
01554        /* Second set of flags */
01555        if (num_parms(args) >= 9) {
01556               CC->room.QRflags2 = extract_int(args, 8);
01557        }
01558 
01559        /* Misc. flags */
01560        CC->room.QRflags = (extract_int(args, 3) | QR_INUSE);
01561        /* Clean up a client boo-boo: if the client set the room to
01562         * guess-name or passworded, ensure that the private flag is
01563         * also set.
01564         */
01565        if ((CC->room.QRflags & QR_GUESSNAME)
01566            || (CC->room.QRflags & QR_PASSWORDED))
01567               CC->room.QRflags |= QR_PRIVATE;
01568 
01569        /* Some changes can't apply to BASEROOM */
01570        if (!strncasecmp(CC->room.QRname, config.c_baseroom,
01571                       ROOMNAMELEN)) {
01572               CC->room.QRorder = 0;
01573               CC->room.QRpasswd[0] = '\0';
01574               CC->room.QRflags &= ~(QR_PRIVATE & QR_PASSWORDED &
01575                      QR_GUESSNAME & QR_PREFONLY & QR_MAILBOX);
01576               CC->room.QRflags |= QR_PERMANENT;
01577        } else {      
01578               /* March order (doesn't apply to AIDEROOM) */
01579               if (num_parms(args) >= 7)
01580                      CC->room.QRorder = (char) new_order;
01581               /* Room password */
01582               extract_token(buf, args, 1, '|', sizeof buf);
01583               buf[10] = 0;
01584               safestrncpy(CC->room.QRpasswd, buf,
01585                          sizeof CC->room.QRpasswd);
01586               /* Kick everyone out if the client requested it
01587                * (by changing the room's generation number)
01588                */
01589               if (extract_int(args, 4)) {
01590                      time(&CC->room.QRgen);
01591               }
01592        }
01593        /* Some changes can't apply to AIDEROOM */
01594        if (!strncasecmp(CC->room.QRname, config.c_baseroom,
01595                       ROOMNAMELEN)) {
01596               CC->room.QRorder = 0;
01597               CC->room.QRflags &= ~QR_MAILBOX;
01598               CC->room.QRflags |= QR_PERMANENT;
01599        }
01600 
01601        /* Write the room record back to disk */
01602        CtdlPutRoomLock(&CC->room);
01603 
01604        /* Create a room directory if necessary */
01605        if (CC->room.QRflags & QR_DIRECTORY) {
01606               snprintf(buf, sizeof buf,"%s/%s",
01607                              ctdl_file_dir,
01608                              CC->room.QRdirname);
01609               mkdir(buf, 0755);
01610        }
01611        snprintf(buf, sizeof buf, "The room \"%s\" has been edited by %s.\n",
01612               CC->room.QRname,
01613               (CC->logged_in ? CC->curr_user : "an administrator")
01614        );
01615        CtdlAideMessage(buf, "Room modification Message");
01616        cprintf("%d Ok\n", CIT_OK);
01617 }
01618 
01619 
01620 
01621 /* 
01622  * get the name of the room aide for this room
01623  */
01624 void cmd_geta(char *cmdbuf)
01625 {
01626        struct ctdluser usbuf;
01627 
01628        if (CtdlAccessCheck(ac_logged_in)) return;
01629 
01630        if (CtdlGetUserByNumber(&usbuf, CC->room.QRroomaide) == 0) {
01631               cprintf("%d %s\n", CIT_OK, usbuf.fullname);
01632        } else {
01633               cprintf("%d \n", CIT_OK);
01634        }
01635 }
01636 
01637 
01638 /* 
01639  * set the room aide for this room
01640  */
01641 void cmd_seta(char *new_ra)
01642 {
01643        struct ctdluser usbuf;
01644        long newu;
01645        char buf[SIZ];
01646        int post_notice;
01647 
01648        if (CtdlAccessCheck(ac_room_aide)) return;
01649 
01650        if (CtdlGetUser(&usbuf, new_ra) != 0) {
01651               newu = (-1L);
01652        } else {
01653               newu = usbuf.usernum;
01654        }
01655 
01656        CtdlGetRoomLock(&CC->room, CC->room.QRname);
01657        post_notice = 0;
01658        if (CC->room.QRroomaide != newu) {
01659               post_notice = 1;
01660        }
01661        CC->room.QRroomaide = newu;
01662        CtdlPutRoomLock(&CC->room);
01663 
01664        /*
01665         * We have to post the change notice _after_ writing changes to 
01666         * the room table, otherwise it would deadlock!
01667         */
01668        if (post_notice == 1) {
01669               if (!IsEmptyStr(usbuf.fullname))
01670                      snprintf(buf, sizeof buf,
01671                             "%s is now the room aide for \"%s\".\n",
01672                             usbuf.fullname, CC->room.QRname);
01673               else
01674                      snprintf(buf, sizeof buf,
01675                             "There is now no room aide for \"%s\".\n",
01676                             CC->room.QRname);
01677               CtdlAideMessage(buf, "Aide Room Modification");
01678        }
01679        cprintf("%d Ok\n", CIT_OK);
01680 }
01681 
01682 /* 
01683  * retrieve info file for this room
01684  */
01685 void cmd_rinf(char *gargs)
01686 {
01687        char filename[128];
01688        char buf[SIZ];
01689        FILE *info_fp;
01690 
01691        assoc_file_name(filename, sizeof filename, &CC->room, ctdl_info_dir);
01692        info_fp = fopen(filename, "r");
01693 
01694        if (info_fp == NULL) {
01695               cprintf("%d No info file.\n", ERROR + FILE_NOT_FOUND);
01696               return;
01697        }
01698        cprintf("%d Info:\n", LISTING_FOLLOWS);
01699        while (fgets(buf, sizeof buf, info_fp) != NULL) {
01700               if (!IsEmptyStr(buf))
01701                      buf[strlen(buf) - 1] = 0;
01702               cprintf("%s\n", buf);
01703        }
01704        cprintf("000\n");
01705        fclose(info_fp);
01706 }
01707 
01708 /*
01709  * Asynchronously schedule a room for deletion.  The room will appear
01710  * deleted to the user(s), but it won't actually get purged from the
01711  * database until THE DREADED AUTO-PURGER makes its next run.
01712  */
01713 void CtdlScheduleRoomForDeletion(struct ctdlroom *qrbuf)
01714 {
01715        char old_name[ROOMNAMELEN];
01716        static int seq = 0;
01717 
01718        syslog(LOG_NOTICE, "Scheduling room <%s> for deletion\n",
01719               qrbuf->QRname);
01720 
01721        safestrncpy(old_name, qrbuf->QRname, sizeof old_name);
01722 
01723        CtdlGetRoom(qrbuf, qrbuf->QRname);
01724 
01725        /* Turn the room into a private mailbox owned by a user who doesn't
01726         * exist.  This will immediately make the room invisible to everyone,
01727         * and qualify the room for purging.
01728         */
01729        snprintf(qrbuf->QRname, sizeof qrbuf->QRname, "9999999999.%08lx.%04d.%s",
01730               time(NULL),
01731               ++seq,
01732               old_name
01733        );
01734        qrbuf->QRflags |= QR_MAILBOX;
01735        time(&qrbuf->QRgen); /* Use a timestamp as the new generation number  */
01736 
01737        CtdlPutRoom(qrbuf);
01738 
01739        b_deleteroom(old_name);
01740 }
01741 
01742 
01743 
01744 /*
01745  * Back end processing to delete a room and everything associated with it
01746  * (This one is synchronous and should only get called by THE DREADED
01747  * AUTO-PURGER in serv_expire.c.  All user-facing code should call
01748  * the asynchronous schedule_room_for_deletion() instead.)
01749  */
01750 void CtdlDeleteRoom(struct ctdlroom *qrbuf)
01751 {
01752        struct floor flbuf;
01753        char filename[100];
01754        /* TODO: filename magic? does this realy work? */
01755 
01756        syslog(LOG_NOTICE, "Deleting room <%s>\n", qrbuf->QRname);
01757 
01758        /* Delete the info file */
01759        assoc_file_name(filename, sizeof filename, qrbuf, ctdl_info_dir);
01760        unlink(filename);
01761 
01762        /* Delete the image file */
01763        assoc_file_name(filename, sizeof filename, qrbuf, ctdl_image_dir);
01764        unlink(filename);
01765 
01766        /* Delete the room's network config file */
01767        assoc_file_name(filename, sizeof filename, qrbuf, ctdl_netcfg_dir);
01768        unlink(filename);
01769 
01770        /* Delete the messages in the room
01771         * (Careful: this opens an S_ROOMS critical section!)
01772         */
01773        CtdlDeleteMessages(qrbuf->QRname, NULL, 0, "");
01774 
01775        /* Flag the room record as not in use */
01776        CtdlGetRoomLock(qrbuf, qrbuf->QRname);
01777        qrbuf->QRflags = 0;
01778        CtdlPutRoomLock(qrbuf);
01779 
01780        /* then decrement the reference count for the floor */
01781        lgetfloor(&flbuf, (int) (qrbuf->QRfloor));
01782        flbuf.f_ref_count = flbuf.f_ref_count - 1;
01783        lputfloor(&flbuf, (int) (qrbuf->QRfloor));
01784 
01785        /* Delete the room record from the database! */
01786        b_deleteroom(qrbuf->QRname);
01787 }
01788 
01789 
01790 
01791 /*
01792  * Check access control for deleting a room
01793  */
01794 int CtdlDoIHavePermissionToDeleteThisRoom(struct ctdlroom *qr) {
01795 
01796        if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
01797               return(0);
01798        }
01799 
01800        if (CtdlIsNonEditable(qr)) {
01801               return(0);
01802        }
01803 
01804        /*
01805         * For mailboxes, check stuff
01806         */
01807        if (qr->QRflags & QR_MAILBOX) {
01808 
01809               if (strlen(qr->QRname) < 12) return(0); /* bad name */
01810 
01811               if (atol(qr->QRname) != CC->user.usernum) {
01812                      return(0);    /* not my room */
01813               }
01814 
01815               /* Can't delete your Mail> room */
01816               if (!strcasecmp(&qr->QRname[11], MAILROOM)) return(0);
01817 
01818               /* Otherwise it's ok */
01819               return(1);
01820        }
01821 
01822        /*
01823         * For normal rooms, just check for aide or room aide status.
01824         */
01825        return(is_room_aide());
01826 }
01827 
01828 /*
01829  * aide command: kill the current room
01830  */
01831 void cmd_kill(char *argbuf)
01832 {
01833        char deleted_room_name[ROOMNAMELEN];
01834        char msg[SIZ];
01835        int kill_ok;
01836 
01837        kill_ok = extract_int(argbuf, 0);
01838 
01839        if (CtdlDoIHavePermissionToDeleteThisRoom(&CC->room) == 0) {
01840               cprintf("%d Can't delete this room.\n", ERROR + NOT_HERE);
01841               return;
01842        }
01843        if (kill_ok) {
01844               if (CC->room.QRflags & QR_MAILBOX) {
01845                      safestrncpy(deleted_room_name, &CC->room.QRname[11], sizeof deleted_room_name);
01846               }
01847               else {
01848                      safestrncpy(deleted_room_name, CC->room.QRname, sizeof deleted_room_name);
01849               }
01850 
01851               /* Do the dirty work */
01852               CtdlScheduleRoomForDeletion(&CC->room);
01853 
01854               /* Return to the Lobby */
01855               CtdlUserGoto(config.c_baseroom, 0, 0, NULL, NULL);
01856 
01857               /* tell the world what we did */
01858               snprintf(msg, sizeof msg, "The room \"%s\" has been deleted by %s.\n",
01859                       deleted_room_name,
01860                      (CC->logged_in ? CC->curr_user : "an administrator")
01861               );
01862               CtdlAideMessage(msg, "Room Purger Message");
01863               cprintf("%d '%s' deleted.\n", CIT_OK, deleted_room_name);
01864        } else {
01865               cprintf("%d ok to delete.\n", CIT_OK);
01866        }
01867 }
01868 
01869 
01870 /*
01871  * Internal code to create a new room (returns room flags)
01872  *
01873  * Room types:  0=public, 1=hidden, 2=passworded, 3=invitation-only,
01874  *              4=mailbox, 5=mailbox, but caller supplies namespace
01875  */
01876 unsigned CtdlCreateRoom(char *new_room_name,
01877                    int new_room_type,
01878                    char *new_room_pass,
01879                    int new_room_floor,
01880                    int really_create,
01881                    int avoid_access,
01882                    int new_room_view)
01883 {
01884 
01885        struct ctdlroom qrbuf;
01886        struct floor flbuf;
01887        visit vbuf;
01888 
01889        syslog(LOG_DEBUG, "CtdlCreateRoom(name=%s, type=%d, view=%d)\n",
01890               new_room_name, new_room_type, new_room_view);
01891 
01892        if (CtdlGetRoom(&qrbuf, new_room_name) == 0) {
01893               syslog(LOG_DEBUG, "%s already exists.\n", new_room_name);
01894               return(0);
01895        }
01896 
01897        memset(&qrbuf, 0, sizeof(struct ctdlroom));
01898        safestrncpy(qrbuf.QRpasswd, new_room_pass, sizeof qrbuf.QRpasswd);
01899        qrbuf.QRflags = QR_INUSE;
01900        if (new_room_type > 0)
01901               qrbuf.QRflags = (qrbuf.QRflags | QR_PRIVATE);
01902        if (new_room_type == 1)
01903               qrbuf.QRflags = (qrbuf.QRflags | QR_GUESSNAME);
01904        if (new_room_type == 2)
01905               qrbuf.QRflags = (qrbuf.QRflags | QR_PASSWORDED);
01906        if ( (new_room_type == 4) || (new_room_type == 5) ) {
01907               qrbuf.QRflags = (qrbuf.QRflags | QR_MAILBOX);
01908               /* qrbuf.QRflags2 |= QR2_SUBJECTREQ; */
01909        }
01910 
01911        /* If the user is requesting a personal room, set up the room
01912         * name accordingly (prepend the user number)
01913         */
01914        if (new_room_type == 4) {
01915               CtdlMailboxName(qrbuf.QRname, sizeof qrbuf.QRname, &CC->user, new_room_name);
01916        }
01917        else {
01918               safestrncpy(qrbuf.QRname, new_room_name, sizeof qrbuf.QRname);
01919        }
01920 
01921        /* If the room is private, and the system administrator has elected
01922         * to automatically grant room aide privileges, do so now.
01923         */
01924        if ((qrbuf.QRflags & QR_PRIVATE) && (CREATAIDE == 1)) {
01925               qrbuf.QRroomaide = CC->user.usernum;
01926        }
01927        /* Blog owners automatically become room aides of their blogs.
01928         * (In the future we will offer a site-wide configuration setting to suppress this behavior.)
01929         */
01930        else if (new_room_view == VIEW_BLOG) {
01931               qrbuf.QRroomaide = CC->user.usernum;
01932        }
01933        /* Otherwise, set the room aide to undefined.
01934         */
01935        else {
01936               qrbuf.QRroomaide = (-1L);
01937        }
01938 
01939        /* 
01940         * If the caller is only interested in testing whether this will work,
01941         * return now without creating the room.
01942         */
01943        if (!really_create) return (qrbuf.QRflags);
01944 
01945        qrbuf.QRnumber = get_new_room_number();
01946        qrbuf.QRhighest = 0L;       /* No messages in this room yet */
01947        time(&qrbuf.QRgen);  /* Use a timestamp as the generation number */
01948        qrbuf.QRfloor = new_room_floor;
01949        qrbuf.QRdefaultview = new_room_view;
01950 
01951        /* save what we just did... */
01952        CtdlPutRoom(&qrbuf);
01953 
01954        /* bump the reference count on whatever floor the room is on */
01955        lgetfloor(&flbuf, (int) qrbuf.QRfloor);
01956        flbuf.f_ref_count = flbuf.f_ref_count + 1;
01957        lputfloor(&flbuf, (int) qrbuf.QRfloor);
01958 
01959        /* Grant the creator access to the room unless the avoid_access
01960         * parameter was specified.
01961         */
01962        if ( (CC->logged_in) && (avoid_access == 0) ) {
01963               CtdlGetRelationship(&vbuf, &CC->user, &qrbuf);
01964               vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
01965               vbuf.v_flags = vbuf.v_flags | V_ACCESS;
01966               CtdlSetRelationship(&vbuf, &CC->user, &qrbuf);
01967        }
01968 
01969        /* resume our happy day */
01970        return (qrbuf.QRflags);
01971 }
01972 
01973 
01974 /*
01975  * create a new room
01976  */
01977 void cmd_cre8(char *args)
01978 {
01979        int cre8_ok;
01980        char new_room_name[ROOMNAMELEN];
01981        int new_room_type;
01982        char new_room_pass[32];
01983        int new_room_floor;
01984        int new_room_view;
01985        char *notification_message = NULL;
01986        unsigned newflags;
01987        struct floor *fl;
01988        int avoid_access = 0;
01989 
01990        cre8_ok = extract_int(args, 0);
01991        extract_token(new_room_name, args, 1, '|', sizeof new_room_name);
01992        new_room_name[ROOMNAMELEN - 1] = 0;
01993        new_room_type = extract_int(args, 2);
01994        extract_token(new_room_pass, args, 3, '|', sizeof new_room_pass);
01995        avoid_access = extract_int(args, 5);
01996        new_room_view = extract_int(args, 6);
01997        new_room_pass[9] = 0;
01998        new_room_floor = 0;
01999 
02000        if ((IsEmptyStr(new_room_name)) && (cre8_ok == 1)) {
02001               cprintf("%d Invalid room name.\n", ERROR + ILLEGAL_VALUE);
02002               return;
02003        }
02004 
02005        if (!strcasecmp(new_room_name, MAILROOM)) {
02006               cprintf("%d '%s' already exists.\n",
02007                      ERROR + ALREADY_EXISTS, new_room_name);
02008               return;
02009        }
02010 
02011        if (num_parms(args) >= 5) {
02012               fl = CtdlGetCachedFloor(extract_int(args, 4));
02013               if (fl == NULL) {
02014                      cprintf("%d Invalid floor number.\n",
02015                             ERROR + INVALID_FLOOR_OPERATION);
02016                      return;
02017               }
02018               else if ((fl->f_flags & F_INUSE) == 0) {
02019                      cprintf("%d Invalid floor number.\n",
02020                             ERROR + INVALID_FLOOR_OPERATION);
02021                      return;
02022               } else {
02023                      new_room_floor = extract_int(args, 4);
02024               }
02025        }
02026 
02027        if (CtdlAccessCheck(ac_logged_in)) return;
02028 
02029        if (CC->user.axlevel < config.c_createax && !CC->internal_pgm) {
02030               cprintf("%d You need higher access to create rooms.\n",
02031                      ERROR + HIGHER_ACCESS_REQUIRED);
02032               return;
02033        }
02034 
02035        if ((IsEmptyStr(new_room_name)) && (cre8_ok == 0)) {
02036               cprintf("%d Ok to create rooms.\n", CIT_OK);
02037               return;
02038        }
02039 
02040        if ((new_room_type < 0) || (new_room_type > 5)) {
02041               cprintf("%d Invalid room type.\n", ERROR + ILLEGAL_VALUE);
02042               return;
02043        }
02044 
02045        if (new_room_type == 5) {
02046               if (CC->user.axlevel < AxAideU) {
02047                      cprintf("%d Higher access required\n", 
02048                             ERROR + HIGHER_ACCESS_REQUIRED);
02049                      return;
02050               }
02051        }
02052 
02053        /* Check to make sure the requested room name doesn't already exist */
02054        newflags = CtdlCreateRoom(new_room_name,
02055                             new_room_type, new_room_pass, new_room_floor,
02056                             0, avoid_access, new_room_view);
02057        if (newflags == 0) {
02058               cprintf("%d '%s' already exists.\n",
02059                      ERROR + ALREADY_EXISTS, new_room_name);
02060               return;
02061        }
02062 
02063        if (cre8_ok == 0) {
02064               cprintf("%d OK to create '%s'\n", CIT_OK, new_room_name);
02065               return;
02066        }
02067 
02068        /* If we reach this point, the room needs to be created. */
02069 
02070        newflags = CtdlCreateRoom(new_room_name,
02071                         new_room_type, new_room_pass, new_room_floor, 1, 0,
02072                         new_room_view);
02073 
02074        /* post a message in Aide> describing the new room */
02075        notification_message = malloc(1024);
02076        snprintf(notification_message, 1024,
02077               "A new room called \"%s\" has been created by %s%s%s%s%s%s\n",
02078               new_room_name,
02079               (CC->logged_in ? CC->curr_user : "an administrator"),
02080               ((newflags & QR_MAILBOX) ? " [personal]" : ""),
02081               ((newflags & QR_PRIVATE) ? " [private]" : ""),
02082               ((newflags & QR_GUESSNAME) ? " [hidden]" : ""),
02083               ((newflags & QR_PASSWORDED) ? " Password: " : ""),
02084               ((newflags & QR_PASSWORDED) ? new_room_pass : "")
02085        );
02086        CtdlAideMessage(notification_message, "Room Creation Message");
02087        free(notification_message);
02088 
02089        cprintf("%d '%s' has been created.\n", CIT_OK, new_room_name);
02090 }
02091 
02092 
02093 
02094 void cmd_einf(char *ok)
02095 {                           /* enter info file for current room */
02096        FILE *fp;
02097        char infofilename[SIZ];
02098        char buf[SIZ];
02099 
02100        unbuffer_output();
02101 
02102        if (CtdlAccessCheck(ac_room_aide)) return;
02103 
02104        if (atoi(ok) == 0) {
02105               cprintf("%d Ok.\n", CIT_OK);
02106               return;
02107        }
02108        assoc_file_name(infofilename, sizeof infofilename, &CC->room, ctdl_info_dir);
02109        syslog(LOG_DEBUG, "opening\n");
02110        fp = fopen(infofilename, "w");
02111        syslog(LOG_DEBUG, "checking\n");
02112        if (fp == NULL) {
02113               cprintf("%d Cannot open %s: %s\n",
02114                 ERROR + INTERNAL_ERROR, infofilename, strerror(errno));
02115               return;
02116        }
02117        cprintf("%d Send info...\n", SEND_LISTING);
02118 
02119        do {
02120               client_getln(buf, sizeof buf);
02121               if (strcmp(buf, "000"))
02122                      fprintf(fp, "%s\n", buf);
02123        } while (strcmp(buf, "000"));
02124        fclose(fp);
02125 
02126        /* now update the room index so people will see our new info */
02127        CtdlGetRoomLock(&CC->room, CC->room.QRname);            /* lock so no one steps on us */
02128        CC->room.QRinfo = CC->room.QRhighest + 1L;
02129        CtdlPutRoomLock(&CC->room);
02130 }
02131 
02132 
02133 /* 
02134  * cmd_lflr()   -  List all known floors
02135  */
02136 void cmd_lflr(char *gargs)
02137 {
02138        int a;
02139        struct floor flbuf;
02140 
02141        if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
02142 
02143        cprintf("%d Known floors:\n", LISTING_FOLLOWS);
02144 
02145        for (a = 0; a < MAXFLOORS; ++a) {
02146               CtdlGetFloor(&flbuf, a);
02147               if (flbuf.f_flags & F_INUSE) {
02148                      cprintf("%d|%s|%d\n",
02149                             a,
02150                             flbuf.f_name,
02151                             flbuf.f_ref_count);
02152               }
02153        }
02154        cprintf("000\n");
02155 }
02156 
02157 
02158 
02159 /*
02160  * create a new floor
02161  */
02162 void cmd_cflr(char *argbuf)
02163 {
02164        char new_floor_name[256];
02165        struct floor flbuf;
02166        int cflr_ok;
02167        int free_slot = (-1);
02168        int a;
02169 
02170        extract_token(new_floor_name, argbuf, 0, '|', sizeof new_floor_name);
02171        cflr_ok = extract_int(argbuf, 1);
02172 
02173        if (CtdlAccessCheck(ac_aide)) return;
02174 
02175        if (IsEmptyStr(new_floor_name)) {
02176               cprintf("%d Blank floor name not allowed.\n",
02177                      ERROR + ILLEGAL_VALUE);
02178               return;
02179        }
02180 
02181        for (a = 0; a < MAXFLOORS; ++a) {
02182               CtdlGetFloor(&flbuf, a);
02183 
02184               /* note any free slots while we're scanning... */
02185               if (((flbuf.f_flags & F_INUSE) == 0)
02186                   && (free_slot < 0))
02187                      free_slot = a;
02188 
02189               /* check to see if it already exists */
02190               if ((!strcasecmp(flbuf.f_name, new_floor_name))
02191                   && (flbuf.f_flags & F_INUSE)) {
02192                      cprintf("%d Floor '%s' already exists.\n",
02193                             ERROR + ALREADY_EXISTS,
02194                             flbuf.f_name);
02195                      return;
02196               }
02197        }
02198 
02199        if (free_slot < 0) {
02200               cprintf("%d There is no space available for a new floor.\n",
02201                      ERROR + INVALID_FLOOR_OPERATION);
02202               return;
02203        }
02204        if (cflr_ok == 0) {
02205               cprintf("%d ok to create...\n", CIT_OK);
02206               return;
02207        }
02208        lgetfloor(&flbuf, free_slot);
02209        flbuf.f_flags = F_INUSE;
02210        flbuf.f_ref_count = 0;
02211        safestrncpy(flbuf.f_name, new_floor_name, sizeof flbuf.f_name);
02212        lputfloor(&flbuf, free_slot);
02213        cprintf("%d %d\n", CIT_OK, free_slot);
02214 }
02215 
02216 
02217 
02218 /*
02219  * delete a floor
02220  */
02221 void cmd_kflr(char *argbuf)
02222 {
02223        struct floor flbuf;
02224        int floor_to_delete;
02225        int kflr_ok;
02226        int delete_ok;
02227 
02228        floor_to_delete = extract_int(argbuf, 0);
02229        kflr_ok = extract_int(argbuf, 1);
02230 
02231        if (CtdlAccessCheck(ac_aide)) return;
02232 
02233        lgetfloor(&flbuf, floor_to_delete);
02234 
02235        delete_ok = 1;
02236        if ((flbuf.f_flags & F_INUSE) == 0) {
02237               cprintf("%d Floor %d not in use.\n",
02238                      ERROR + INVALID_FLOOR_OPERATION, floor_to_delete);
02239               delete_ok = 0;
02240        } else {
02241               if (flbuf.f_ref_count != 0) {
02242                      cprintf("%d Cannot delete; floor contains %d rooms.\n",
02243                             ERROR + INVALID_FLOOR_OPERATION,
02244                             flbuf.f_ref_count);
02245                      delete_ok = 0;
02246               } else {
02247                      if (kflr_ok == 1) {
02248                             cprintf("%d Ok\n", CIT_OK);
02249                      } else {
02250                             cprintf("%d Ok to delete...\n", CIT_OK);
02251                      }
02252 
02253               }
02254 
02255        }
02256 
02257        if ((delete_ok == 1) && (kflr_ok == 1))
02258               flbuf.f_flags = 0;
02259        lputfloor(&flbuf, floor_to_delete);
02260 }
02261 
02262 /*
02263  * edit a floor
02264  */
02265 void cmd_eflr(char *argbuf)
02266 {
02267        struct floor flbuf;
02268        int floor_num;
02269        int np;
02270 
02271        np = num_parms(argbuf);
02272        if (np < 1) {
02273               cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
02274               return;
02275        }
02276 
02277        if (CtdlAccessCheck(ac_aide)) return;
02278 
02279        floor_num = extract_int(argbuf, 0);
02280        lgetfloor(&flbuf, floor_num);
02281        if ((flbuf.f_flags & F_INUSE) == 0) {
02282               lputfloor(&flbuf, floor_num);
02283               cprintf("%d Floor %d is not in use.\n",
02284                      ERROR + INVALID_FLOOR_OPERATION, floor_num);
02285               return;
02286        }
02287        if (np >= 2)
02288               extract_token(flbuf.f_name, argbuf, 1, '|', sizeof flbuf.f_name);
02289        lputfloor(&flbuf, floor_num);
02290 
02291        cprintf("%d Ok\n", CIT_OK);
02292 }
02293 
02294 
02295 /*****************************************************************************/
02296 /*                      MODULE INITIALIZATION STUFF                          */
02297 /*****************************************************************************/
02298 
02299 CTDL_MODULE_INIT(room_ops)
02300 {
02301        if (!threading) {
02302               CtdlRegisterProtoHook(cmd_lrms, "LRMS", "List rooms");
02303               CtdlRegisterProtoHook(cmd_lkra, "LKRA", "List all known rooms");
02304               CtdlRegisterProtoHook(cmd_lkrn, "LKRN", "List known rooms with new messages");
02305               CtdlRegisterProtoHook(cmd_lkro, "LKRO", "List known rooms without new messages");
02306               CtdlRegisterProtoHook(cmd_lzrm, "LZRM", "List zapped rooms");
02307               CtdlRegisterProtoHook(cmd_lprm, "LPRM", "List public rooms");
02308               CtdlRegisterProtoHook(cmd_goto, "GOTO", "Goto a named room");
02309               CtdlRegisterProtoHook(cmd_whok, "WHOK", "List users who know this room");
02310               CtdlRegisterProtoHook(cmd_rdir, "RDIR", "List files in room directory");
02311               CtdlRegisterProtoHook(cmd_getr, "GETR", "Get room parameters");
02312               CtdlRegisterProtoHook(cmd_setr, "SETR", "Set room parameters");
02313               CtdlRegisterProtoHook(cmd_geta, "GETA", "Get the room aide name");
02314               CtdlRegisterProtoHook(cmd_seta, "SETA", "Set the room aide for this room");
02315               CtdlRegisterProtoHook(cmd_rinf, "RINF", "Fetch room info file");
02316               CtdlRegisterProtoHook(cmd_kill, "KILL", "Kill (delete) the current room");
02317               CtdlRegisterProtoHook(cmd_cre8, "CRE8", "Create a new room");
02318               CtdlRegisterProtoHook(cmd_einf, "EINF", "Enter info file for the current room");
02319               CtdlRegisterProtoHook(cmd_lflr, "LFLR", "List all known floors");
02320               CtdlRegisterProtoHook(cmd_cflr, "CFLR", "Create a new floor");
02321               CtdlRegisterProtoHook(cmd_kflr, "KFLR", "Kill a floor");
02322               CtdlRegisterProtoHook(cmd_eflr, "EFLR", "Edit a floor");
02323        }
02324         /* return our Subversion id for the Log */
02325        return "room_ops";
02326 }