make it const, baby.
[citadel.git] / citadel / room_ops.c
1 /* 
2  * Server functions which perform operations on room objects.
3  *
4  * Copyright (c) 1987-2012 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 "sysdep.h"
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <sys/stat.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include <dirent.h>     /* for cmd_rdir to read contents of the directory */
23
24 #if TIME_WITH_SYS_TIME
25 # include <sys/time.h>
26 # include <time.h>
27 #else
28 # if HAVE_SYS_TIME_H
29 #  include <sys/time.h>
30 # else
31 #  include <time.h>
32 # endif
33 #endif
34
35 #include <limits.h>
36 #include <errno.h>
37 #include "citadel.h"
38 #include <libcitadel.h>
39 #include "server.h"
40 #include "database.h"
41 #include "config.h"
42 #include "room_ops.h"
43 #include "sysdep_decls.h"
44 #include "support.h"
45 #include "msgbase.h"
46 #include "citserver.h"
47 #include "control.h"
48 #include "citadel_dirs.h"
49 #include "threads.h"
50
51 #include "ctdl_module.h"
52 #include "user_ops.h"
53
54 struct floor *floorcache[MAXFLOORS];
55
56 /*
57  * Retrieve access control information for any user/room pair
58  */
59 void CtdlRoomAccess(struct ctdlroom *roombuf, struct ctdluser *userbuf,
60                 int *result, int *view)
61 {
62         int retval = 0;
63         visit vbuf;
64         int is_me = 0;
65         int is_guest = 0;
66
67         if (userbuf == &CC->user) {
68                 is_me = 1;
69         }
70
71         if ((is_me) && (config.c_guest_logins) && (!CC->logged_in)) {
72                 is_guest = 1;
73         }
74
75         /* for internal programs, always do everything */
76         if (((CC->internal_pgm)) && (roombuf->QRflags & QR_INUSE)) {
77                 retval = (UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED);
78                 vbuf.v_view = 0;
79                 goto SKIP_EVERYTHING;
80         }
81
82         /* If guest mode is enabled, always grant access to the Lobby */
83         if ((is_guest) && (!strcasecmp(roombuf->QRname, BASEROOM))) {
84                 retval = (UA_KNOWN | UA_GOTOALLOWED);
85                 vbuf.v_view = 0;
86                 goto SKIP_EVERYTHING;
87         }
88
89         /* Locate any applicable user/room relationships */
90         if (is_guest) {
91                 memset(&vbuf, 0, sizeof vbuf);
92         }
93         else {
94                 CtdlGetRelationship(&vbuf, userbuf, roombuf);
95         }
96
97         /* Force the properties of the Aide room */
98         if (!strcasecmp(roombuf->QRname, config.c_aideroom)) {
99                 if (userbuf->axlevel >= AxAideU) {
100                         retval = UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED;
101                 } else {
102                         retval = 0;
103                 }
104                 goto NEWMSG;
105         }
106
107         /* If this is a public room, it's accessible... */
108         if (    ((roombuf->QRflags & QR_PRIVATE) == 0) 
109                 && ((roombuf->QRflags & QR_MAILBOX) == 0)
110         ) {
111                 retval = retval | UA_KNOWN | UA_GOTOALLOWED;
112         }
113
114         /* If this is a preferred users only room, check access level */
115         if (roombuf->QRflags & QR_PREFONLY) {
116                 if (userbuf->axlevel < AxPrefU) {
117                         retval = retval & ~UA_KNOWN & ~UA_GOTOALLOWED;
118                 }
119         }
120
121         /* For private rooms, check the generation number matchups */
122         if (    (roombuf->QRflags & QR_PRIVATE) 
123                 && ((roombuf->QRflags & QR_MAILBOX) == 0)
124         ) {
125
126                 /* An explicit match means the user belongs in this room */
127                 if (vbuf.v_flags & V_ACCESS) {
128                         retval = retval | UA_KNOWN | UA_GOTOALLOWED;
129                 }
130                 /* Otherwise, check if this is a guess-name or passworded
131                  * room.  If it is, a goto may at least be attempted
132                  */
133                 else if (       (roombuf->QRflags & QR_PRIVATE)
134                                 || (roombuf->QRflags & QR_PASSWORDED)
135                 ) {
136                         retval = retval & ~UA_KNOWN;
137                         retval = retval | UA_GOTOALLOWED;
138                 }
139         }
140
141         /* For mailbox rooms, also check the namespace */
142         /* Also, mailbox owners can delete their messages */
143         if ( (roombuf->QRflags & QR_MAILBOX) && (atol(roombuf->QRname) != 0)) {
144                 if (userbuf->usernum == atol(roombuf->QRname)) {
145                         retval = retval | UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED;
146                 }
147                 /* An explicit match means the user belongs in this room */
148                 if (vbuf.v_flags & V_ACCESS) {
149                         retval = retval | UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_DELETEALLOWED | UA_REPLYALLOWED;
150                 }
151         }
152
153         /* For non-mailbox rooms... */
154         else {
155
156                 /* User is allowed to post in the room unless:
157                  * - User is not validated
158                  * - User has no net privileges and it is a shared network room
159                  * - It is a read-only room
160                  * - It is a blog room (in which case we only allow replies to existing messages)
161                  */
162                 int post_allowed = 1;
163                 int reply_allowed = 1;
164                 if (userbuf->axlevel < AxProbU) {
165                         post_allowed = 0;
166                         reply_allowed = 0;
167                 }
168                 if ((userbuf->axlevel < AxNetU) && (roombuf->QRflags & QR_NETWORK)) {
169                         post_allowed = 0;
170                         reply_allowed = 0;
171                 }
172                 if (roombuf->QRflags & QR_READONLY) {
173                         post_allowed = 0;
174                         reply_allowed = 0;
175                 }
176                 if (roombuf->QRdefaultview == VIEW_BLOG) {
177                         post_allowed = 0;
178                 }
179                 if (post_allowed) {
180                         retval = retval | UA_POSTALLOWED | UA_REPLYALLOWED;
181                 }
182                 if (reply_allowed) {
183                         retval = retval | UA_REPLYALLOWED;
184                 }
185
186                 /* If "collaborative deletion" is active for this room, any user who can post
187                  * is also allowed to delete
188                  */
189                 if (roombuf->QRflags2 & QR2_COLLABDEL) {
190                         if (retval & UA_POSTALLOWED) {
191                                 retval = retval | UA_DELETEALLOWED;
192                         }
193                 }
194
195         }
196
197         /* Check to see if the user has forgotten this room */
198         if (vbuf.v_flags & V_FORGET) {
199                 retval = retval & ~UA_KNOWN;
200                 if (    ( ((roombuf->QRflags & QR_PRIVATE) == 0) 
201                         && ((roombuf->QRflags & QR_MAILBOX) == 0)
202                 ) || (  (roombuf->QRflags & QR_MAILBOX) 
203                         && (atol(roombuf->QRname) == CC->user.usernum))
204                 ) {
205                         retval = retval | UA_ZAPPED;
206                 }
207         }
208
209         /* If user is explicitly locked out of this room, deny everything */
210         if (vbuf.v_flags & V_LOCKOUT) {
211                 retval = retval & ~UA_KNOWN & ~UA_GOTOALLOWED & ~UA_POSTALLOWED & ~UA_REPLYALLOWED;
212         }
213
214         /* Aides get access to all private rooms */
215         if (    (userbuf->axlevel >= AxAideU)
216                 && ((roombuf->QRflags & QR_MAILBOX) == 0)
217         ) {
218                 if (vbuf.v_flags & V_FORGET) {
219                         retval = retval | UA_GOTOALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
220                 }
221                 else {
222                         retval = retval | UA_KNOWN | UA_GOTOALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
223                 }
224         }
225
226         /* Aides can gain access to mailboxes as well, but they don't show
227          * by default.
228          */
229         if (    (userbuf->axlevel >= AxAideU)
230                 && (roombuf->QRflags & QR_MAILBOX)
231         ) {
232                 retval = retval | UA_GOTOALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
233         }
234
235         /* Aides and Room Aides have admin privileges */
236         if (    (userbuf->axlevel >= AxAideU)
237                 || (userbuf->usernum == roombuf->QRroomaide)
238         ) {
239                 retval = retval | UA_ADMINALLOWED | UA_DELETEALLOWED | UA_POSTALLOWED | UA_REPLYALLOWED;
240         }
241
242 NEWMSG: /* By the way, we also check for the presence of new messages */
243         if (is_msg_in_sequence_set(vbuf.v_seen, roombuf->QRhighest) == 0) {
244                 retval = retval | UA_HASNEWMSGS;
245         }
246
247         /* System rooms never show up in the list. */
248         if (roombuf->QRflags2 & QR2_SYSTEM) {
249                 retval = retval & ~UA_KNOWN;
250         }
251
252 SKIP_EVERYTHING:
253         /* Now give the caller the information it wants. */
254         if (result != NULL) *result = retval;
255         if (view != NULL) *view = vbuf.v_view;
256 }
257
258
259 /*
260  * Self-checking stuff for a room record read into memory
261  */
262 void room_sanity_check(struct ctdlroom *qrbuf)
263 {
264         /* Mailbox rooms are always on the lowest floor */
265         if (qrbuf->QRflags & QR_MAILBOX) {
266                 qrbuf->QRfloor = 0;
267         }
268         /* Listing order of 0 is illegal except for base rooms */
269         if (qrbuf->QRorder == 0)
270                 if (!(qrbuf->QRflags & QR_MAILBOX) &&
271                     strncasecmp(qrbuf->QRname, config.c_baseroom, ROOMNAMELEN)
272                     &&
273                     strncasecmp(qrbuf->QRname, config.c_aideroom, ROOMNAMELEN))
274                         qrbuf->QRorder = 64;
275 }
276
277
278 /*
279  * CtdlGetRoom()  -  retrieve room data from disk
280  */
281 int CtdlGetRoom(struct ctdlroom *qrbuf, const char *room_name)
282 {
283         struct cdbdata *cdbqr;
284         char lowercase_name[ROOMNAMELEN];
285         char personal_lowercase_name[ROOMNAMELEN];
286         const char *sptr;
287         char *dptr, *eptr;
288
289         dptr = lowercase_name;
290         sptr = room_name;
291         eptr = (dptr + (sizeof lowercase_name - 1));
292         while (!IsEmptyStr(sptr) && (dptr < eptr)){
293                 *dptr = tolower(*sptr);
294                 sptr++; dptr++;
295         }
296         *dptr = '\0';
297
298         memset(qrbuf, 0, sizeof(struct ctdlroom));
299
300         /* First, try the public namespace */
301         cdbqr = cdb_fetch(CDB_ROOMS,
302                           lowercase_name, strlen(lowercase_name));
303
304         /* If that didn't work, try the user's personal namespace */
305         if (cdbqr == NULL) {
306                 snprintf(personal_lowercase_name,
307                          sizeof personal_lowercase_name, "%010ld.%s",
308                          CC->user.usernum, lowercase_name);
309                 cdbqr = cdb_fetch(CDB_ROOMS,
310                                   personal_lowercase_name,
311                                   strlen(personal_lowercase_name));
312         }
313         if (cdbqr != NULL) {
314                 memcpy(qrbuf, cdbqr->ptr,
315                        ((cdbqr->len > sizeof(struct ctdlroom)) ?
316                         sizeof(struct ctdlroom) : cdbqr->len));
317                 cdb_free(cdbqr);
318
319                 room_sanity_check(qrbuf);
320
321                 return (0);
322         } else {
323                 return (1);
324         }
325 }
326
327 /*
328  * CtdlGetRoomLock()  -  same as getroom() but locks the record (if supported)
329  */
330 int CtdlGetRoomLock(struct ctdlroom *qrbuf, char *room_name)
331 {
332         register int retval;
333         retval = CtdlGetRoom(qrbuf, room_name);
334         if (retval == 0) begin_critical_section(S_ROOMS);
335         return(retval);
336 }
337
338
339 /*
340  * b_putroom()  -  back end to putroom() and b_deleteroom()
341  *              (if the supplied buffer is NULL, delete the room record)
342  */
343 void b_putroom(struct ctdlroom *qrbuf, char *room_name)
344 {
345         char lowercase_name[ROOMNAMELEN];
346         char *aptr, *bptr;
347         long len;
348
349         aptr = room_name;
350         bptr = lowercase_name;
351         while (!IsEmptyStr(aptr))
352         {
353                 *bptr = tolower(*aptr);
354                 aptr++;
355                 bptr++;
356         }
357         *bptr='\0';
358
359         len = bptr - lowercase_name;
360         if (qrbuf == NULL) {
361                 cdb_delete(CDB_ROOMS, lowercase_name, len);
362         } else {
363                 time(&qrbuf->QRmtime);
364                 cdb_store(CDB_ROOMS, lowercase_name, len, qrbuf, sizeof(struct ctdlroom));
365         }
366 }
367
368
369 /* 
370  * CtdlPutRoom()  -  store room data to disk
371  */
372 void CtdlPutRoom(struct ctdlroom *qrbuf) {
373         b_putroom(qrbuf, qrbuf->QRname);
374 }
375
376
377 /*
378  * b_deleteroom()  -  delete a room record from disk
379  */
380 void b_deleteroom(char *room_name) {
381         b_putroom(NULL, room_name);
382 }
383
384
385 /*
386  * CtdlPutRoomLock()  -  same as CtdlPutRoom() but unlocks the record (if supported)
387  */
388 void CtdlPutRoomLock(struct ctdlroom *qrbuf)
389 {
390
391         CtdlPutRoom(qrbuf);
392         end_critical_section(S_ROOMS);
393
394 }
395
396
397 /*
398  * CtdlGetFloorByName()  -  retrieve the number of the named floor
399  * return < 0 if not found else return floor number
400  */
401 int CtdlGetFloorByName(const char *floor_name)
402 {
403         int a;
404         struct floor *flbuf = NULL;
405
406         for (a = 0; a < MAXFLOORS; ++a) {
407                 flbuf = CtdlGetCachedFloor(a);
408
409                 /* check to see if it already exists */
410                 if ((!strcasecmp(flbuf->f_name, floor_name))
411                     && (flbuf->f_flags & F_INUSE)) {
412                         return a;
413                 }
414         }
415         return -1;
416 }
417
418
419 /*
420  * CtdlGetFloorByNameLock()  -  retrieve floor number for given floor and lock the floor list.
421  */
422 int CtdlGetFloorByNameLock(const char *floor_name)
423 {
424         begin_critical_section(S_FLOORTAB);
425         return CtdlGetFloorByName(floor_name);
426 }
427
428
429
430 /*
431  * CtdlGetAvailableFloor()  -  Return number of first unused floor
432  * return < 0 if none available
433  */
434 int CtdlGetAvailableFloor(void)
435 {
436         int a;
437         struct floor *flbuf = NULL;
438
439         for (a = 0; a < MAXFLOORS; a++) {
440                 flbuf = CtdlGetCachedFloor(a);
441
442                 /* check to see if it already exists */
443                 if ((flbuf->f_flags & F_INUSE) == 0) {
444                         return a;
445                 }
446         }
447         return -1;
448 }
449
450
451 /*
452  * CtdlGetFloor()  -  retrieve floor data from disk
453  */
454 void CtdlGetFloor(struct floor *flbuf, int floor_num)
455 {
456         struct cdbdata *cdbfl;
457
458         memset(flbuf, 0, sizeof(struct floor));
459         cdbfl = cdb_fetch(CDB_FLOORTAB, &floor_num, sizeof(int));
460         if (cdbfl != NULL) {
461                 memcpy(flbuf, cdbfl->ptr,
462                        ((cdbfl->len > sizeof(struct floor)) ?
463                         sizeof(struct floor) : cdbfl->len));
464                 cdb_free(cdbfl);
465         } else {
466                 if (floor_num == 0) {
467                         safestrncpy(flbuf->f_name, "Main Floor", 
468                                 sizeof flbuf->f_name);
469                         flbuf->f_flags = F_INUSE;
470                         flbuf->f_ref_count = 3;
471                 }
472         }
473
474 }
475
476
477 /*
478  * lgetfloor()  -  same as CtdlGetFloor() but locks the record (if supported)
479  */
480 void lgetfloor(struct floor *flbuf, int floor_num)
481 {
482
483         begin_critical_section(S_FLOORTAB);
484         CtdlGetFloor(flbuf, floor_num);
485 }
486
487
488 /*
489  * CtdlGetCachedFloor()  -  Get floor record from *cache* (loads from disk if needed)
490  *    
491  * This is strictly a performance hack.
492  */
493 struct floor *CtdlGetCachedFloor(int floor_num) {
494         static int initialized = 0;
495         int i;
496         int fetch_new = 0;
497         struct floor *fl = NULL;
498
499         begin_critical_section(S_FLOORCACHE);
500         if (initialized == 0) {
501                 for (i=0; i<MAXFLOORS; ++i) {
502                         floorcache[floor_num] = NULL;
503                 }
504         initialized = 1;
505         }
506         if (floorcache[floor_num] == NULL) {
507                 fetch_new = 1;
508         }
509         end_critical_section(S_FLOORCACHE);
510
511         if (fetch_new) {
512                 fl = malloc(sizeof(struct floor));
513                 CtdlGetFloor(fl, floor_num);
514                 begin_critical_section(S_FLOORCACHE);
515                 if (floorcache[floor_num] != NULL) {
516                         free(floorcache[floor_num]);
517                 }
518                 floorcache[floor_num] = fl;
519                 end_critical_section(S_FLOORCACHE);
520         }
521
522         return(floorcache[floor_num]);
523 }
524
525
526 /*
527  * CtdlPutFloor()  -  store floor data on disk
528  */
529 void CtdlPutFloor(struct floor *flbuf, int floor_num)
530 {
531         /* If we've cached this, clear it out, 'cuz it's WRONG now! */
532         begin_critical_section(S_FLOORCACHE);
533         if (floorcache[floor_num] != NULL) {
534                 free(floorcache[floor_num]);
535                 floorcache[floor_num] = malloc(sizeof(struct floor));
536                 memcpy(floorcache[floor_num], flbuf, sizeof(struct floor));
537         }
538         end_critical_section(S_FLOORCACHE);
539
540         cdb_store(CDB_FLOORTAB, &floor_num, sizeof(int),
541                   flbuf, sizeof(struct floor));
542 }
543
544
545 /*
546  * CtdlPutFloorLock()  -  same as CtdlPutFloor() but unlocks the record (if supported)
547  */
548 void CtdlPutFloorLock(struct floor *flbuf, int floor_num)
549 {
550
551         CtdlPutFloor(flbuf, floor_num);
552         end_critical_section(S_FLOORTAB);
553
554 }
555
556
557 /*
558  * lputfloor()  -  same as CtdlPutFloor() but unlocks the record (if supported)
559  */
560 void lputfloor(struct floor *flbuf, int floor_num)
561 {
562         CtdlPutFloorLock(flbuf, floor_num);
563 }
564
565
566 /* 
567  *  Traverse the room file...
568  */
569 void CtdlForEachRoom(ForEachRoomCallBack CB, void *in_data)
570 {
571         struct ctdlroom qrbuf;
572         struct cdbdata *cdbqr;
573
574         cdb_rewind(CDB_ROOMS);
575
576         while (cdbqr = cdb_next_item(CDB_ROOMS), cdbqr != NULL) {
577                 memset(&qrbuf, 0, sizeof(struct ctdlroom));
578                 memcpy(&qrbuf, cdbqr->ptr,
579                        ((cdbqr->len > sizeof(struct ctdlroom)) ?
580                         sizeof(struct ctdlroom) : cdbqr->len)
581                 );
582                 cdb_free(cdbqr);
583                 room_sanity_check(&qrbuf);
584                 if (qrbuf.QRflags & QR_INUSE) {
585                         CB(&qrbuf, in_data);
586                 }
587         }
588 }
589
590 /* 
591  *  Traverse the room file...
592  */
593 void CtdlForEachNetCfgRoom(ForEachRoomNetCfgCallBack CB,
594                            void *in_data,
595                            RoomNetCfg filter)
596 {
597         struct ctdlroom qrbuf;
598         struct cdbdata *cdbqr;
599
600         cdb_rewind(CDB_ROOMS);
601
602         while (cdbqr = cdb_next_item(CDB_ROOMS), cdbqr != NULL) {
603                 memset(&qrbuf, 0, sizeof(struct ctdlroom));
604                 memcpy(&qrbuf, cdbqr->ptr,
605                        ((cdbqr->len > sizeof(struct ctdlroom)) ?
606                         sizeof(struct ctdlroom) : cdbqr->len)
607                 );
608                 cdb_free(cdbqr);
609                 room_sanity_check(&qrbuf);
610                 if (qrbuf.QRflags & QR_INUSE)
611                 {
612                         OneRoomNetCfg* RNCfg;
613                         RNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
614                         if ((RNCfg != NULL) &&
615                             ((filter == maxRoomNetCfg) ||
616                              (RNCfg->NetConfigs[filter] != NULL)))
617                         {
618                                 CB(&qrbuf, in_data, RNCfg);
619                         }
620                 }
621         }
622 }
623
624
625 /*
626  * delete_msglist()  -  delete room message pointers
627  */
628 void delete_msglist(struct ctdlroom *whichroom)
629 {
630         struct cdbdata *cdbml;
631
632         /* Make sure the msglist we're deleting actually exists, otherwise
633          * libdb will complain when we try to delete an invalid record
634          */
635         cdbml = cdb_fetch(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
636         if (cdbml != NULL) {
637                 cdb_free(cdbml);
638
639                 /* Go ahead and delete it */
640                 cdb_delete(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
641         }
642 }
643
644
645 /*
646  * Message pointer compare function for sort_msglist()
647  */
648 int sort_msglist_cmp(const void *m1, const void *m2) {
649         if ((*(const long *)m1) > (*(const long *)m2)) return(1);
650         if ((*(const long *)m1) < (*(const long *)m2)) return(-1);
651         return(0);
652 }
653
654
655 /*
656  * sort message pointers
657  * (returns new msg count)
658  */
659 int sort_msglist(long listptrs[], int oldcount)
660 {
661         int numitems;
662
663         numitems = oldcount;
664         if (numitems < 2) {
665                 return (oldcount);
666         }
667
668         /* do the sort */
669         qsort(listptrs, numitems, sizeof(long), sort_msglist_cmp);
670
671         /* and yank any nulls */
672         while ((numitems > 0) && (listptrs[0] == 0L)) {
673                 memmove(&listptrs[0], &listptrs[1], (sizeof(long) * (numitems - 1)));
674                 --numitems;
675         }
676
677         return (numitems);
678 }
679
680
681 /*
682  * Determine whether a given room is non-editable.
683  */
684 int CtdlIsNonEditable(struct ctdlroom *qrbuf)
685 {
686
687         /* Mail> rooms are non-editable */
688         if ( (qrbuf->QRflags & QR_MAILBOX)
689              && (!strcasecmp(&qrbuf->QRname[11], MAILROOM)) )
690                 return (1);
691
692         /* Everything else is editable */
693         return (0);
694 }
695
696
697 /*
698  * Back-back-end for all room listing commands
699  */
700 void list_roomname(struct ctdlroom *qrbuf, int ra, int current_view, int default_view)
701 {
702         char truncated_roomname[ROOMNAMELEN];
703
704         /* For my own mailbox rooms, chop off the owner prefix */
705         if ( (qrbuf->QRflags & QR_MAILBOX)
706              && (atol(qrbuf->QRname) == CC->user.usernum) ) {
707                 safestrncpy(truncated_roomname, qrbuf->QRname, sizeof truncated_roomname);
708                 safestrncpy(truncated_roomname, &truncated_roomname[11], sizeof truncated_roomname);
709                 cprintf("%s", truncated_roomname);
710         }
711         /* For all other rooms, just display the name in its entirety */
712         else {
713                 cprintf("%s", qrbuf->QRname);
714         }
715
716         /* ...and now the other parameters */
717         cprintf("|%u|%d|%d|%d|%d|%d|%d|%ld|\n",
718                 qrbuf->QRflags,
719                 (int) qrbuf->QRfloor,
720                 (int) qrbuf->QRorder,
721                 (int) qrbuf->QRflags2,
722                 ra,
723                 current_view,
724                 default_view,
725                 qrbuf->QRmtime
726         );
727 }
728
729
730 /* 
731  * cmd_lrms()   -  List all accessible rooms, known or forgotten
732  */
733 void cmd_lrms_backend(struct ctdlroom *qrbuf, void *data)
734 {
735         int FloorBeingSearched = (-1);
736         int ra;
737         int view;
738
739         FloorBeingSearched = *(int *)data;
740         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
741
742         if ((( ra & (UA_KNOWN | UA_ZAPPED)))
743             && ((qrbuf->QRfloor == (FloorBeingSearched))
744                 || ((FloorBeingSearched) < 0)))
745                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
746 }
747
748 void cmd_lrms(char *argbuf)
749 {
750         int FloorBeingSearched = (-1);
751         if (!IsEmptyStr(argbuf))
752                 FloorBeingSearched = extract_int(argbuf, 0);
753
754         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
755
756         CtdlGetUser(&CC->user, CC->curr_user);
757         cprintf("%d Accessible rooms:\n", LISTING_FOLLOWS);
758
759         CtdlForEachRoom(cmd_lrms_backend, &FloorBeingSearched);
760         cprintf("000\n");
761 }
762
763
764
765 /* 
766  * cmd_lkra()   -  List all known rooms
767  */
768 void cmd_lkra_backend(struct ctdlroom *qrbuf, void *data)
769 {
770         int FloorBeingSearched = (-1);
771         int ra;
772         int view;
773
774         FloorBeingSearched = *(int *)data;
775         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
776
777         if ((( ra & (UA_KNOWN)))
778             && ((qrbuf->QRfloor == (FloorBeingSearched))
779                 || ((FloorBeingSearched) < 0)))
780                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
781 }
782
783 void cmd_lkra(char *argbuf)
784 {
785         int FloorBeingSearched = (-1);
786         if (!IsEmptyStr(argbuf))
787                 FloorBeingSearched = extract_int(argbuf, 0);
788
789         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
790         
791         CtdlGetUser(&CC->user, CC->curr_user);
792         cprintf("%d Known rooms:\n", LISTING_FOLLOWS);
793
794         CtdlForEachRoom(cmd_lkra_backend, &FloorBeingSearched);
795         cprintf("000\n");
796 }
797
798
799
800 void cmd_lprm_backend(struct ctdlroom *qrbuf, void *data)
801 {
802         int FloorBeingSearched = (-1);
803         int ra;
804         int view;
805
806         FloorBeingSearched = *(int *)data;
807         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
808
809         if (   ((qrbuf->QRflags & QR_PRIVATE) == 0)
810                 && ((qrbuf->QRflags & QR_MAILBOX) == 0)
811             && ((qrbuf->QRfloor == (FloorBeingSearched))
812                 || ((FloorBeingSearched) < 0)))
813                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
814 }
815
816 void cmd_lprm(char *argbuf)
817 {
818         int FloorBeingSearched = (-1);
819         if (!IsEmptyStr(argbuf))
820                 FloorBeingSearched = extract_int(argbuf, 0);
821
822         cprintf("%d Public rooms:\n", LISTING_FOLLOWS);
823
824         CtdlForEachRoom(cmd_lprm_backend, &FloorBeingSearched);
825         cprintf("000\n");
826 }
827
828
829
830 /* 
831  * cmd_lkrn()   -  List all known rooms with new messages
832  */
833 void cmd_lkrn_backend(struct ctdlroom *qrbuf, void *data)
834 {
835         int FloorBeingSearched = (-1);
836         int ra;
837         int view;
838
839         FloorBeingSearched = *(int *)data;
840         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
841
842         if ((ra & UA_KNOWN)
843             && (ra & UA_HASNEWMSGS)
844             && ((qrbuf->QRfloor == (FloorBeingSearched))
845                 || ((FloorBeingSearched) < 0)))
846                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
847 }
848
849 void cmd_lkrn(char *argbuf)
850 {
851         int FloorBeingSearched = (-1);
852         if (!IsEmptyStr(argbuf))
853                 FloorBeingSearched = extract_int(argbuf, 0);
854
855         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
856         
857         CtdlGetUser(&CC->user, CC->curr_user);
858         cprintf("%d Rooms w/ new msgs:\n", LISTING_FOLLOWS);
859
860         CtdlForEachRoom(cmd_lkrn_backend, &FloorBeingSearched);
861         cprintf("000\n");
862 }
863
864
865
866 /* 
867  * cmd_lkro()   -  List all known rooms
868  */
869 void cmd_lkro_backend(struct ctdlroom *qrbuf, void *data)
870 {
871         int FloorBeingSearched = (-1);
872         int ra;
873         int view;
874
875         FloorBeingSearched = *(int *)data;
876         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
877
878         if ((ra & UA_KNOWN)
879             && ((ra & UA_HASNEWMSGS) == 0)
880             && ((qrbuf->QRfloor == (FloorBeingSearched))
881                 || ((FloorBeingSearched) < 0)))
882                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
883 }
884
885 void cmd_lkro(char *argbuf)
886 {
887         int FloorBeingSearched = (-1);
888         if (!IsEmptyStr(argbuf))
889                 FloorBeingSearched = extract_int(argbuf, 0);
890
891         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
892         
893         CtdlGetUser(&CC->user, CC->curr_user);
894         cprintf("%d Rooms w/o new msgs:\n", LISTING_FOLLOWS);
895
896         CtdlForEachRoom(cmd_lkro_backend, &FloorBeingSearched);
897         cprintf("000\n");
898 }
899
900
901
902 /* 
903  * cmd_lzrm()   -  List all forgotten rooms
904  */
905 void cmd_lzrm_backend(struct ctdlroom *qrbuf, void *data)
906 {
907         int FloorBeingSearched = (-1);
908         int ra;
909         int view;
910
911         FloorBeingSearched = *(int *)data;
912         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
913
914         if ((ra & UA_GOTOALLOWED)
915             && (ra & UA_ZAPPED)
916             && ((qrbuf->QRfloor == (FloorBeingSearched))
917                 || ((FloorBeingSearched) < 0)))
918                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
919 }
920
921 void cmd_lzrm(char *argbuf)
922 {
923         int FloorBeingSearched = (-1);
924         if (!IsEmptyStr(argbuf))
925                 FloorBeingSearched = extract_int(argbuf, 0);
926
927         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
928         
929         CtdlGetUser(&CC->user, CC->curr_user);
930         cprintf("%d Zapped rooms:\n", LISTING_FOLLOWS);
931
932         CtdlForEachRoom(cmd_lzrm_backend, &FloorBeingSearched);
933         cprintf("000\n");
934 }
935
936
937 /*
938  * Make the specified room the current room for this session.  No validation
939  * or access control is done here -- the caller should make sure that the
940  * specified room exists and is ok to access.
941  */
942 void CtdlUserGoto(char *where, int display_result, int transiently,
943                 int *retmsgs, int *retnew)
944 {
945         struct CitContext *CCC = CC;
946         int a;
947         int new_messages = 0;
948         int old_messages = 0;
949         int total_messages = 0;
950         int info = 0;
951         int rmailflag;
952         int raideflag;
953         int newmailcount = 0;
954         visit vbuf;
955         char truncated_roomname[ROOMNAMELEN];
956         struct cdbdata *cdbfr;
957         long *msglist = NULL;
958         int num_msgs = 0;
959         unsigned int original_v_flags;
960         int num_sets;
961         int s;
962         char setstr[128], lostr[64], histr[64];
963         long lo, hi;
964         int is_trash = 0;
965
966         /* If the supplied room name is NULL, the caller wants us to know that
967          * it has already copied the room record into CC->room, so
968          * we can skip the extra database fetch.
969          */
970         if (where != NULL) {
971                 safestrncpy(CCC->room.QRname, where, sizeof CCC->room.QRname);
972                 CtdlGetRoom(&CCC->room, where);
973         }
974
975         /* Take care of all the formalities. */
976
977         begin_critical_section(S_USERS);
978         CtdlGetRelationship(&vbuf, &CCC->user, &CCC->room);
979         original_v_flags = vbuf.v_flags;
980
981         /* Know the room ... but not if it's the page log room, or if the
982          * caller specified that we're only entering this room transiently.
983          */
984         if ((strcasecmp(CCC->room.QRname, config.c_logpages))
985            && (transiently == 0) ) {
986                 vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
987                 vbuf.v_flags = vbuf.v_flags | V_ACCESS;
988         }
989         
990         /* Only rewrite the database record if we changed something */
991         if (vbuf.v_flags != original_v_flags) {
992                 CtdlSetRelationship(&vbuf, &CCC->user, &CCC->room);
993         }
994         end_critical_section(S_USERS);
995
996         /* Check for new mail */
997         newmailcount = NewMailCount();
998
999         /* set info to 1 if the user needs to read the room's info file */
1000         if (CCC->room.QRinfo > vbuf.v_lastseen) {
1001                 info = 1;
1002         }
1003
1004         cdbfr = cdb_fetch(CDB_MSGLISTS, &CCC->room.QRnumber, sizeof(long));
1005         if (cdbfr != NULL) {
1006                 msglist = (long *) cdbfr->ptr;
1007                 cdbfr->ptr = NULL;      /* CtdlUserGoto() now owns this memory */
1008                 num_msgs = cdbfr->len / sizeof(long);
1009                 cdb_free(cdbfr);
1010         }
1011
1012         total_messages = 0;
1013         for (a=0; a<num_msgs; ++a) {
1014                 if (msglist[a] > 0L) ++total_messages;
1015         }
1016
1017         num_sets = num_tokens(vbuf.v_seen, ',');
1018         for (s=0; s<num_sets; ++s) {
1019                 extract_token(setstr, vbuf.v_seen, s, ',', sizeof setstr);
1020
1021                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
1022                 if (num_tokens(setstr, ':') >= 2) {
1023                         extract_token(histr, setstr, 1, ':', sizeof histr);
1024                         if (!strcmp(histr, "*")) {
1025                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
1026                         }
1027                 } 
1028                 else {
1029                         strcpy(histr, lostr);
1030                 }
1031                 lo = atol(lostr);
1032                 hi = atol(histr);
1033
1034                 for (a=0; a<num_msgs; ++a) if (msglist[a] > 0L) {
1035                         if ((msglist[a] >= lo) && (msglist[a] <= hi)) {
1036                                 ++old_messages;
1037                                 msglist[a] = 0L;
1038                         }
1039                 }
1040         }
1041         new_messages = total_messages - old_messages;
1042
1043         if (msglist != NULL) free(msglist);
1044
1045         if (CCC->room.QRflags & QR_MAILBOX)
1046                 rmailflag = 1;
1047         else
1048                 rmailflag = 0;
1049
1050         if ((CCC->room.QRroomaide == CCC->user.usernum)
1051             || (CCC->user.axlevel >= AxAideU))
1052                 raideflag = 1;
1053         else
1054                 raideflag = 0;
1055
1056         safestrncpy(truncated_roomname, CCC->room.QRname, sizeof truncated_roomname);
1057         if ( (CCC->room.QRflags & QR_MAILBOX)
1058            && (atol(CCC->room.QRname) == CCC->user.usernum) ) {
1059                 safestrncpy(truncated_roomname, &truncated_roomname[11], sizeof truncated_roomname);
1060         }
1061
1062         if (!strcasecmp(truncated_roomname, USERTRASHROOM)) {
1063                 is_trash = 1;
1064         }
1065
1066         if (retmsgs != NULL) *retmsgs = total_messages;
1067         if (retnew != NULL) *retnew = new_messages;
1068         MSG_syslog(LOG_INFO, "<%s> %d new of %d total messages\n",
1069                    CCC->room.QRname,
1070                    new_messages, total_messages
1071                 );
1072
1073         CCC->curr_view = (int)vbuf.v_view;
1074
1075         if (display_result) {
1076                 cprintf("%d%c%s|%d|%d|%d|%d|%ld|%ld|%d|%d|%d|%d|%d|%d|%d|%d|%ld|\n",
1077                         CIT_OK, CtdlCheckExpress(),
1078                         truncated_roomname,
1079                         (int)new_messages,
1080                         (int)total_messages,
1081                         (int)info,
1082                         (int)CCC->room.QRflags,
1083                         (long)CCC->room.QRhighest,
1084                         (long)vbuf.v_lastseen,
1085                         (int)rmailflag,
1086                         (int)raideflag,
1087                         (int)newmailcount,
1088                         (int)CCC->room.QRfloor,
1089                         (int)vbuf.v_view,
1090                         (int)CCC->room.QRdefaultview,
1091                         (int)is_trash,
1092                         (int)CCC->room.QRflags2,
1093                         (long)CCC->room.QRmtime
1094                 );
1095         }
1096 }
1097
1098
1099 /*
1100  * Handle some of the macro named rooms
1101  */
1102 void convert_room_name_macros(char *towhere, size_t maxlen) {
1103         if (!strcasecmp(towhere, "_BASEROOM_")) {
1104                 safestrncpy(towhere, config.c_baseroom, maxlen);
1105         }
1106         else if (!strcasecmp(towhere, "_MAIL_")) {
1107                 safestrncpy(towhere, MAILROOM, maxlen);
1108         }
1109         else if (!strcasecmp(towhere, "_TRASH_")) {
1110                 safestrncpy(towhere, USERTRASHROOM, maxlen);
1111         }
1112         else if (!strcasecmp(towhere, "_DRAFTS_")) {
1113                 safestrncpy(towhere, USERDRAFTROOM, maxlen);
1114         }
1115         else if (!strcasecmp(towhere, "_BITBUCKET_")) {
1116                 safestrncpy(towhere, config.c_twitroom, maxlen);
1117         }
1118         else if (!strcasecmp(towhere, "_CALENDAR_")) {
1119                 safestrncpy(towhere, USERCALENDARROOM, maxlen);
1120         }
1121         else if (!strcasecmp(towhere, "_TASKS_")) {
1122                 safestrncpy(towhere, USERTASKSROOM, maxlen);
1123         }
1124         else if (!strcasecmp(towhere, "_CONTACTS_")) {
1125                 safestrncpy(towhere, USERCONTACTSROOM, maxlen);
1126         }
1127         else if (!strcasecmp(towhere, "_NOTES_")) {
1128                 safestrncpy(towhere, USERNOTESROOM, maxlen);
1129         }
1130 }
1131
1132
1133 /* 
1134  * cmd_goto()  -  goto a new room
1135  */
1136 void cmd_goto(char *gargs)
1137 {
1138         struct ctdlroom QRscratch;
1139         int c;
1140         int ok = 0;
1141         int ra;
1142         char augmented_roomname[ROOMNAMELEN];
1143         char towhere[ROOMNAMELEN];
1144         char password[32];
1145         int transiently = 0;
1146
1147         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
1148
1149         extract_token(towhere, gargs, 0, '|', sizeof towhere);
1150         extract_token(password, gargs, 1, '|', sizeof password);
1151         transiently = extract_int(gargs, 2);
1152
1153         CtdlGetUser(&CC->user, CC->curr_user);
1154
1155         /*
1156          * Handle some of the macro named rooms
1157          */
1158         convert_room_name_macros(towhere, sizeof towhere);
1159
1160         /* First try a regular match */
1161         c = CtdlGetRoom(&QRscratch, towhere);
1162
1163         /* Then try a mailbox name match */
1164         if (c != 0) {
1165                 CtdlMailboxName(augmented_roomname, sizeof augmented_roomname,
1166                             &CC->user, towhere);
1167                 c = CtdlGetRoom(&QRscratch, augmented_roomname);
1168                 if (c == 0)
1169                         safestrncpy(towhere, augmented_roomname, sizeof towhere);
1170         }
1171
1172         /* And if the room was found... */
1173         if (c == 0) {
1174
1175                 /* Let internal programs go directly to any room. */
1176                 if (CC->internal_pgm) {
1177                         memcpy(&CC->room, &QRscratch,
1178                                 sizeof(struct ctdlroom));
1179                         CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
1180                         return;
1181                 }
1182
1183                 /* See if there is an existing user/room relationship */
1184                 CtdlRoomAccess(&QRscratch, &CC->user, &ra, NULL);
1185
1186                 /* normal clients have to pass through security */
1187                 if (ra & UA_GOTOALLOWED) {
1188                         ok = 1;
1189                 }
1190
1191                 if (ok == 1) {
1192                         if ((QRscratch.QRflags & QR_MAILBOX) &&
1193                             ((ra & UA_GOTOALLOWED))) {
1194                                 memcpy(&CC->room, &QRscratch,
1195                                         sizeof(struct ctdlroom));
1196                                 CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
1197                                 return;
1198                         } else if ((QRscratch.QRflags & QR_PASSWORDED) &&
1199                             ((ra & UA_KNOWN) == 0) &&
1200                             (strcasecmp(QRscratch.QRpasswd, password)) &&
1201                             (CC->user.axlevel < AxAideU)
1202                             ) {
1203                                 cprintf("%d wrong or missing passwd\n",
1204                                         ERROR + PASSWORD_REQUIRED);
1205                                 return;
1206                         } else if ((QRscratch.QRflags & QR_PRIVATE) &&
1207                                    ((QRscratch.QRflags & QR_PASSWORDED) == 0) &&
1208                                    ((QRscratch.QRflags & QR_GUESSNAME) == 0) &&
1209                                    ((ra & UA_KNOWN) == 0) &&
1210                                    (CC->user.axlevel < AxAideU)
1211                                   ) {
1212                                 syslog(LOG_DEBUG, "Failed to acquire private room\n");
1213                         } else {
1214                                 memcpy(&CC->room, &QRscratch,
1215                                         sizeof(struct ctdlroom));
1216                                 CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
1217                                 return;
1218                         }
1219                 }
1220         }
1221
1222         cprintf("%d room '%s' not found\n", ERROR + ROOM_NOT_FOUND, towhere);
1223 }
1224
1225
1226 void cmd_whok(char *cmdbuf)
1227 {
1228         struct ctdluser temp;
1229         struct cdbdata *cdbus;
1230         int ra;
1231
1232         cprintf("%d Who knows room:\n", LISTING_FOLLOWS);
1233         cdb_rewind(CDB_USERS);
1234         while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
1235                 memset(&temp, 0, sizeof temp);
1236                 memcpy(&temp, cdbus->ptr, sizeof temp);
1237                 cdb_free(cdbus);
1238
1239                 CtdlRoomAccess(&CC->room, &temp, &ra, NULL);
1240                 if ((!IsEmptyStr(temp.fullname)) && 
1241                     (CC->room.QRflags & QR_INUSE) &&
1242                     (ra & UA_KNOWN)
1243                         )
1244                         cprintf("%s\n", temp.fullname);
1245         }
1246         cprintf("000\n");
1247 }
1248
1249
1250 /*
1251  * RDIR command for room directory
1252  */
1253 void cmd_rdir(char *cmdbuf)
1254 {
1255         char buf[256];
1256         char comment[256];
1257         FILE *fd;
1258         struct stat statbuf;
1259         DIR *filedir = NULL;
1260         struct dirent *filedir_entry;
1261         int d_namelen;
1262         char buf2[SIZ];
1263         char mimebuf[64];
1264         long len;
1265         
1266         if (CtdlAccessCheck(ac_logged_in)) return;
1267         
1268         CtdlGetRoom(&CC->room, CC->room.QRname);
1269         CtdlGetUser(&CC->user, CC->curr_user);
1270
1271         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
1272                 cprintf("%d not here.\n", ERROR + NOT_HERE);
1273                 return;
1274         }
1275         if (((CC->room.QRflags & QR_VISDIR) == 0)
1276             && (CC->user.axlevel < AxAideU)
1277             && (CC->user.usernum != CC->room.QRroomaide)) {
1278                 cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
1279                 return;
1280         }
1281
1282         snprintf(buf, sizeof buf, "%s/%s", ctdl_file_dir, CC->room.QRdirname);
1283         filedir = opendir (buf);
1284         
1285         if (filedir == NULL) {
1286                 cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
1287                 return;
1288         }
1289         cprintf("%d %s|%s/%s\n", LISTING_FOLLOWS, config.c_fqdn, ctdl_file_dir, CC->room.QRdirname);
1290         
1291         snprintf(buf, sizeof buf, "%s/%s/filedir", ctdl_file_dir, CC->room.QRdirname);
1292         fd = fopen(buf, "r");
1293         if (fd == NULL)
1294                 fd = fopen("/dev/null", "r");
1295         while ((filedir_entry = readdir(filedir)))
1296         {
1297                 if (strcasecmp(filedir_entry->d_name, "filedir") && filedir_entry->d_name[0] != '.')
1298                 {
1299 #ifdef _DIRENT_HAVE_D_NAMELEN
1300                         d_namelen = filedir_entry->d_namelen;
1301 #else
1302                         d_namelen = strlen(filedir_entry->d_name);
1303 #endif
1304                         snprintf(buf, sizeof buf, "%s/%s/%s", ctdl_file_dir, CC->room.QRdirname, filedir_entry->d_name);
1305                         stat(buf, &statbuf);    /* stat the file */
1306                         if (!(statbuf.st_mode & S_IFREG))
1307                         {
1308                                 snprintf(buf2, sizeof buf2,
1309                                         "\"%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",
1310                                         buf, CC->room.QRname
1311                                 );
1312                                 CtdlAideMessage(buf2, "Unusable data found in room directory");
1313                                 continue;       /* not a useable file type so don't show it */
1314                         }
1315                         safestrncpy(comment, "", sizeof comment);
1316                         fseek(fd, 0L, 0);       /* rewind descriptions file */
1317                         /* Get the description from the descriptions file */
1318                         while ((fgets(buf, sizeof buf, fd) != NULL) && (IsEmptyStr(comment))) 
1319                         {
1320                                 buf[strlen(buf) - 1] = 0;
1321                                 if ((!strncasecmp(buf, filedir_entry->d_name, d_namelen)) && (buf[d_namelen] == ' '))
1322                                         safestrncpy(comment, &buf[d_namelen + 1], sizeof comment);
1323                         }
1324                         len = extract_token (mimebuf, comment, 0,' ', 64);
1325                         if ((len <0) || strchr(mimebuf, '/') == NULL)
1326                         {
1327                                 snprintf (mimebuf, 64, "application/octetstream");
1328                                 len = 0;
1329                         }
1330                         cprintf("%s|%ld|%s|%s\n", 
1331                                 filedir_entry->d_name, 
1332                                 (long)statbuf.st_size, 
1333                                 mimebuf, 
1334                                 &comment[len]);
1335                 }
1336         }
1337         fclose(fd);
1338         closedir(filedir);
1339         
1340         cprintf("000\n");
1341 }
1342
1343 /*
1344  * get room parameters (admin or room admin command)
1345  */
1346 void cmd_getr(char *cmdbuf)
1347 {
1348         if (CtdlAccessCheck(ac_room_aide)) return;
1349
1350         CtdlGetRoom(&CC->room, CC->room.QRname);
1351         cprintf("%d%c%s|%s|%s|%d|%d|%d|%d|%d|\n",
1352                 CIT_OK,
1353                 CtdlCheckExpress(),
1354
1355                 ((CC->room.QRflags & QR_MAILBOX) ?
1356                         &CC->room.QRname[11] : CC->room.QRname),
1357
1358                 ((CC->room.QRflags & QR_PASSWORDED) ?
1359                         CC->room.QRpasswd : ""),
1360
1361                 ((CC->room.QRflags & QR_DIRECTORY) ?
1362                         CC->room.QRdirname : ""),
1363
1364                 CC->room.QRflags,
1365                 (int) CC->room.QRfloor,
1366                 (int) CC->room.QRorder,
1367
1368                 CC->room.QRdefaultview,
1369                 CC->room.QRflags2
1370                 );
1371 }
1372
1373
1374 /*
1375  * Back end function to rename a room.
1376  * You can also specify which floor to move the room to, or specify -1 to
1377  * keep the room on the same floor it was on.
1378  *
1379  * If you are renaming a mailbox room, you must supply the namespace prefix
1380  * in *at least* the old name!
1381  */
1382 int CtdlRenameRoom(char *old_name, char *new_name, int new_floor) {
1383         int old_floor = 0;
1384         struct ctdlroom qrbuf;
1385         struct ctdlroom qrtmp;
1386         int ret = 0;
1387         struct floor *fl;
1388         struct floor flbuf;
1389         long owner = 0L;
1390         char actual_old_name[ROOMNAMELEN];
1391
1392         syslog(LOG_DEBUG, "CtdlRenameRoom(%s, %s, %d)\n",
1393                 old_name, new_name, new_floor);
1394
1395         if (new_floor >= 0) {
1396                 fl = CtdlGetCachedFloor(new_floor);
1397                 if ((fl->f_flags & F_INUSE) == 0) {
1398                         return(crr_invalid_floor);
1399                 }
1400         }
1401
1402         begin_critical_section(S_ROOMS);
1403
1404         if ( (CtdlGetRoom(&qrtmp, new_name) == 0) 
1405            && (strcasecmp(new_name, old_name)) ) {
1406                 ret = crr_already_exists;
1407         }
1408
1409         else if (CtdlGetRoom(&qrbuf, old_name) != 0) {
1410                 ret = crr_room_not_found;
1411         }
1412
1413         else if ( (CC->user.axlevel < AxAideU) && (!CC->internal_pgm)
1414                   && (CC->user.usernum != qrbuf.QRroomaide)
1415                   && ( (((qrbuf.QRflags & QR_MAILBOX) == 0) || (atol(qrbuf.QRname) != CC->user.usernum))) )  {
1416                 ret = crr_access_denied;
1417         }
1418
1419         else if (CtdlIsNonEditable(&qrbuf)) {
1420                 ret = crr_noneditable;
1421         }
1422
1423         else {
1424                 /* Rename it */
1425                 safestrncpy(actual_old_name, qrbuf.QRname, sizeof actual_old_name);
1426                 if (qrbuf.QRflags & QR_MAILBOX) {
1427                         owner = atol(qrbuf.QRname);
1428                 }
1429                 if ( (owner > 0L) && (atol(new_name) == 0L) ) {
1430                         snprintf(qrbuf.QRname, sizeof(qrbuf.QRname),
1431                                         "%010ld.%s", owner, new_name);
1432                 }
1433                 else {
1434                         safestrncpy(qrbuf.QRname, new_name,
1435                                                 sizeof(qrbuf.QRname));
1436                 }
1437
1438                 /* Reject change of floor for baseroom/aideroom */
1439                 if (!strncasecmp(old_name, config.c_baseroom, ROOMNAMELEN) ||
1440                     !strncasecmp(old_name, config.c_aideroom, ROOMNAMELEN)) {
1441                         new_floor = 0;
1442                 }
1443
1444                 /* Take care of floor stuff */
1445                 old_floor = qrbuf.QRfloor;
1446                 if (new_floor < 0) {
1447                         new_floor = old_floor;
1448                 }
1449                 qrbuf.QRfloor = new_floor;
1450                 CtdlPutRoom(&qrbuf);
1451
1452                 begin_critical_section(S_CONFIG);
1453         
1454                 /* If baseroom/aideroom name changes, update config */
1455                 if (!strncasecmp(old_name, config.c_baseroom, ROOMNAMELEN)) {
1456                         safestrncpy(config.c_baseroom, new_name, ROOMNAMELEN);
1457                         put_config();
1458                 }
1459                 if (!strncasecmp(old_name, config.c_aideroom, ROOMNAMELEN)) {
1460                         safestrncpy(config.c_aideroom, new_name, ROOMNAMELEN);
1461                         put_config();
1462                 }
1463         
1464                 end_critical_section(S_CONFIG);
1465         
1466                 /* If the room name changed, then there are now two room
1467                  * records, so we have to delete the old one.
1468                  */
1469                 if (strcasecmp(new_name, old_name)) {
1470                         b_deleteroom(actual_old_name);
1471                 }
1472
1473                 ret = crr_ok;
1474         }
1475
1476         end_critical_section(S_ROOMS);
1477
1478         /* Adjust the floor reference counts if necessary */
1479         if (new_floor != old_floor) {
1480                 lgetfloor(&flbuf, old_floor);
1481                 --flbuf.f_ref_count;
1482                 lputfloor(&flbuf, old_floor);
1483                 syslog(LOG_DEBUG, "Reference count for floor %d is now %d\n", old_floor, flbuf.f_ref_count);
1484                 lgetfloor(&flbuf, new_floor);
1485                 ++flbuf.f_ref_count;
1486                 lputfloor(&flbuf, new_floor);
1487                 syslog(LOG_DEBUG, "Reference count for floor %d is now %d\n", new_floor, flbuf.f_ref_count);
1488         }
1489
1490         /* ...and everybody say "YATTA!" */     
1491         return(ret);
1492 }
1493
1494
1495 /*
1496  * set room parameters (admin or room admin command)
1497  */
1498 void cmd_setr(char *args)
1499 {
1500         char buf[256];
1501         int new_order = 0;
1502         int r;
1503         int new_floor;
1504         char new_name[ROOMNAMELEN];
1505
1506         if (CtdlAccessCheck(ac_logged_in)) return;
1507
1508         if (num_parms(args) >= 6) {
1509                 new_floor = extract_int(args, 5);
1510         } else {
1511                 new_floor = (-1);       /* don't change the floor */
1512         }
1513
1514         /* When is a new name more than just a new name?  When the old name
1515          * has a namespace prefix.
1516          */
1517         if (CC->room.QRflags & QR_MAILBOX) {
1518                 sprintf(new_name, "%010ld.", atol(CC->room.QRname) );
1519         } else {
1520                 safestrncpy(new_name, "", sizeof new_name);
1521         }
1522         extract_token(&new_name[strlen(new_name)], args, 0, '|', (sizeof new_name - strlen(new_name)));
1523
1524         r = CtdlRenameRoom(CC->room.QRname, new_name, new_floor);
1525
1526         if (r == crr_room_not_found) {
1527                 cprintf("%d Internal error - room not found?\n", ERROR + INTERNAL_ERROR);
1528         } else if (r == crr_already_exists) {
1529                 cprintf("%d '%s' already exists.\n",
1530                         ERROR + ALREADY_EXISTS, new_name);
1531         } else if (r == crr_noneditable) {
1532                 cprintf("%d Cannot edit this room.\n", ERROR + NOT_HERE);
1533         } else if (r == crr_invalid_floor) {
1534                 cprintf("%d Target floor does not exist.\n",
1535                         ERROR + INVALID_FLOOR_OPERATION);
1536         } else if (r == crr_access_denied) {
1537                 cprintf("%d You do not have permission to edit '%s'\n",
1538                         ERROR + HIGHER_ACCESS_REQUIRED,
1539                         CC->room.QRname);
1540         } else if (r != crr_ok) {
1541                 cprintf("%d Error: CtdlRenameRoom() returned %d\n",
1542                         ERROR + INTERNAL_ERROR, r);
1543         }
1544
1545         if (r != crr_ok) {
1546                 return;
1547         }
1548
1549         CtdlGetRoom(&CC->room, new_name);
1550
1551         /* Now we have to do a bunch of other stuff */
1552
1553         if (num_parms(args) >= 7) {
1554                 new_order = extract_int(args, 6);
1555                 if (new_order < 1)
1556                         new_order = 1;
1557                 if (new_order > 127)
1558                         new_order = 127;
1559         }
1560
1561         CtdlGetRoomLock(&CC->room, CC->room.QRname);
1562
1563         /* Directory room */
1564         extract_token(buf, args, 2, '|', sizeof buf);
1565         buf[15] = 0;
1566         safestrncpy(CC->room.QRdirname, buf,
1567                 sizeof CC->room.QRdirname);
1568
1569         /* Default view */
1570         if (num_parms(args) >= 8) {
1571                 CC->room.QRdefaultview = extract_int(args, 7);
1572         }
1573
1574         /* Second set of flags */
1575         if (num_parms(args) >= 9) {
1576                 CC->room.QRflags2 = extract_int(args, 8);
1577         }
1578
1579         /* Misc. flags */
1580         CC->room.QRflags = (extract_int(args, 3) | QR_INUSE);
1581         /* Clean up a client boo-boo: if the client set the room to
1582          * guess-name or passworded, ensure that the private flag is
1583          * also set.
1584          */
1585         if ((CC->room.QRflags & QR_GUESSNAME)
1586             || (CC->room.QRflags & QR_PASSWORDED))
1587                 CC->room.QRflags |= QR_PRIVATE;
1588
1589         /* Some changes can't apply to BASEROOM */
1590         if (!strncasecmp(CC->room.QRname, config.c_baseroom,
1591                          ROOMNAMELEN)) {
1592                 CC->room.QRorder = 0;
1593                 CC->room.QRpasswd[0] = '\0';
1594                 CC->room.QRflags &= ~(QR_PRIVATE & QR_PASSWORDED &
1595                         QR_GUESSNAME & QR_PREFONLY & QR_MAILBOX);
1596                 CC->room.QRflags |= QR_PERMANENT;
1597         } else {        
1598                 /* March order (doesn't apply to AIDEROOM) */
1599                 if (num_parms(args) >= 7)
1600                         CC->room.QRorder = (char) new_order;
1601                 /* Room password */
1602                 extract_token(buf, args, 1, '|', sizeof buf);
1603                 buf[10] = 0;
1604                 safestrncpy(CC->room.QRpasswd, buf,
1605                             sizeof CC->room.QRpasswd);
1606                 /* Kick everyone out if the client requested it
1607                  * (by changing the room's generation number)
1608                  */
1609                 if (extract_int(args, 4)) {
1610                         time(&CC->room.QRgen);
1611                 }
1612         }
1613         /* Some changes can't apply to AIDEROOM */
1614         if (!strncasecmp(CC->room.QRname, config.c_baseroom,
1615                          ROOMNAMELEN)) {
1616                 CC->room.QRorder = 0;
1617                 CC->room.QRflags &= ~QR_MAILBOX;
1618                 CC->room.QRflags |= QR_PERMANENT;
1619         }
1620
1621         /* Write the room record back to disk */
1622         CtdlPutRoomLock(&CC->room);
1623
1624         /* Create a room directory if necessary */
1625         if (CC->room.QRflags & QR_DIRECTORY) {
1626                 snprintf(buf, sizeof buf,"%s/%s",
1627                                  ctdl_file_dir,
1628                                  CC->room.QRdirname);
1629                 mkdir(buf, 0755);
1630         }
1631         snprintf(buf, sizeof buf, "The room \"%s\" has been edited by %s.\n",
1632                 CC->room.QRname,
1633                 (CC->logged_in ? CC->curr_user : "an administrator")
1634         );
1635         CtdlAideMessage(buf, "Room modification Message");
1636         cprintf("%d Ok\n", CIT_OK);
1637 }
1638
1639
1640
1641 /* 
1642  * get the name of the room admin for this room
1643  */
1644 void cmd_geta(char *cmdbuf)
1645 {
1646         struct ctdluser usbuf;
1647
1648         if (CtdlAccessCheck(ac_logged_in)) return;
1649
1650         if (CtdlGetUserByNumber(&usbuf, CC->room.QRroomaide) == 0) {
1651                 cprintf("%d %s\n", CIT_OK, usbuf.fullname);
1652         } else {
1653                 cprintf("%d \n", CIT_OK);
1654         }
1655 }
1656
1657
1658 /* 
1659  * set the room admin for this room
1660  */
1661 void cmd_seta(char *new_ra)
1662 {
1663         struct ctdluser usbuf;
1664         long newu;
1665         char buf[SIZ];
1666         int post_notice;
1667
1668         if (CtdlAccessCheck(ac_room_aide)) return;
1669
1670         if (CtdlGetUser(&usbuf, new_ra) != 0) {
1671                 newu = (-1L);
1672         } else {
1673                 newu = usbuf.usernum;
1674         }
1675
1676         CtdlGetRoomLock(&CC->room, CC->room.QRname);
1677         post_notice = 0;
1678         if (CC->room.QRroomaide != newu) {
1679                 post_notice = 1;
1680         }
1681         CC->room.QRroomaide = newu;
1682         CtdlPutRoomLock(&CC->room);
1683
1684         /*
1685          * We have to post the change notice _after_ writing changes to 
1686          * the room table, otherwise it would deadlock!
1687          */
1688         if (post_notice == 1) {
1689                 if (!IsEmptyStr(usbuf.fullname))
1690                         snprintf(buf, sizeof buf,
1691                                 "%s is now the room admin for \"%s\".\n",
1692                                 usbuf.fullname, CC->room.QRname);
1693                 else
1694                         snprintf(buf, sizeof buf,
1695                                 "There is now no room admin for \"%s\".\n",
1696                                 CC->room.QRname);
1697                 CtdlAideMessage(buf, "Admin Room Modification");
1698         }
1699         cprintf("%d Ok\n", CIT_OK);
1700 }
1701
1702 /* 
1703  * retrieve info file for this room
1704  */
1705 void cmd_rinf(char *gargs)
1706 {
1707         char filename[128];
1708         char buf[SIZ];
1709         FILE *info_fp;
1710
1711         assoc_file_name(filename, sizeof filename, &CC->room, ctdl_info_dir);
1712         info_fp = fopen(filename, "r");
1713
1714         if (info_fp == NULL) {
1715                 cprintf("%d No info file.\n", ERROR + FILE_NOT_FOUND);
1716                 return;
1717         }
1718         cprintf("%d Info:\n", LISTING_FOLLOWS);
1719         while (fgets(buf, sizeof buf, info_fp) != NULL) {
1720                 if (!IsEmptyStr(buf))
1721                         buf[strlen(buf) - 1] = 0;
1722                 cprintf("%s\n", buf);
1723         }
1724         cprintf("000\n");
1725         fclose(info_fp);
1726 }
1727
1728 /*
1729  * Asynchronously schedule a room for deletion.  The room will appear
1730  * deleted to the user(s), but it won't actually get purged from the
1731  * database until THE DREADED AUTO-PURGER makes its next run.
1732  */
1733 void CtdlScheduleRoomForDeletion(struct ctdlroom *qrbuf)
1734 {
1735         char old_name[ROOMNAMELEN];
1736         static int seq = 0;
1737
1738         syslog(LOG_NOTICE, "Scheduling room <%s> for deletion\n",
1739                 qrbuf->QRname);
1740
1741         safestrncpy(old_name, qrbuf->QRname, sizeof old_name);
1742
1743         CtdlGetRoom(qrbuf, qrbuf->QRname);
1744
1745         /* Turn the room into a private mailbox owned by a user who doesn't
1746          * exist.  This will immediately make the room invisible to everyone,
1747          * and qualify the room for purging.
1748          */
1749         snprintf(qrbuf->QRname, sizeof qrbuf->QRname, "9999999999.%08lx.%04d.%s",
1750                 time(NULL),
1751                 ++seq,
1752                 old_name
1753         );
1754         qrbuf->QRflags |= QR_MAILBOX;
1755         time(&qrbuf->QRgen);    /* Use a timestamp as the new generation number  */
1756
1757         CtdlPutRoom(qrbuf);
1758
1759         b_deleteroom(old_name);
1760 }
1761
1762
1763
1764 /*
1765  * Back end processing to delete a room and everything associated with it
1766  * (This one is synchronous and should only get called by THE DREADED
1767  * AUTO-PURGER in serv_expire.c.  All user-facing code should call
1768  * the asynchronous schedule_room_for_deletion() instead.)
1769  */
1770 void CtdlDeleteRoom(struct ctdlroom *qrbuf)
1771 {
1772         struct floor flbuf;
1773         char filename[100];
1774         /* TODO: filename magic? does this realy work? */
1775
1776         syslog(LOG_NOTICE, "Deleting room <%s>\n", qrbuf->QRname);
1777
1778         /* Delete the info file */
1779         assoc_file_name(filename, sizeof filename, qrbuf, ctdl_info_dir);
1780         unlink(filename);
1781
1782         /* Delete the image file */
1783         assoc_file_name(filename, sizeof filename, qrbuf, ctdl_image_dir);
1784         unlink(filename);
1785
1786         /* Delete the room's network config file */
1787         assoc_file_name(filename, sizeof filename, qrbuf, ctdl_netcfg_dir);
1788         unlink(filename);
1789
1790         /* Delete the messages in the room
1791          * (Careful: this opens an S_ROOMS critical section!)
1792          */
1793         CtdlDeleteMessages(qrbuf->QRname, NULL, 0, "");
1794
1795         /* Flag the room record as not in use */
1796         CtdlGetRoomLock(qrbuf, qrbuf->QRname);
1797         qrbuf->QRflags = 0;
1798         CtdlPutRoomLock(qrbuf);
1799
1800         /* then decrement the reference count for the floor */
1801         lgetfloor(&flbuf, (int) (qrbuf->QRfloor));
1802         flbuf.f_ref_count = flbuf.f_ref_count - 1;
1803         lputfloor(&flbuf, (int) (qrbuf->QRfloor));
1804
1805         /* Delete the room record from the database! */
1806         b_deleteroom(qrbuf->QRname);
1807 }
1808
1809
1810
1811 /*
1812  * Check access control for deleting a room
1813  */
1814 int CtdlDoIHavePermissionToDeleteThisRoom(struct ctdlroom *qr) {
1815
1816         if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
1817                 return(0);
1818         }
1819
1820         if (CtdlIsNonEditable(qr)) {
1821                 return(0);
1822         }
1823
1824         /*
1825          * For mailboxes, check stuff
1826          */
1827         if (qr->QRflags & QR_MAILBOX) {
1828
1829                 if (strlen(qr->QRname) < 12) return(0); /* bad name */
1830
1831                 if (atol(qr->QRname) != CC->user.usernum) {
1832                         return(0);      /* not my room */
1833                 }
1834
1835                 /* Can't delete your Mail> room */
1836                 if (!strcasecmp(&qr->QRname[11], MAILROOM)) return(0);
1837
1838                 /* Otherwise it's ok */
1839                 return(1);
1840         }
1841
1842         /*
1843          * For normal rooms, just check for admin or room admin status.
1844          */
1845         return(is_room_aide());
1846 }
1847
1848 /*
1849  * admin command: kill the current room
1850  */
1851 void cmd_kill(char *argbuf)
1852 {
1853         char deleted_room_name[ROOMNAMELEN];
1854         char msg[SIZ];
1855         int kill_ok;
1856
1857         kill_ok = extract_int(argbuf, 0);
1858
1859         if (CtdlDoIHavePermissionToDeleteThisRoom(&CC->room) == 0) {
1860                 cprintf("%d Can't delete this room.\n", ERROR + NOT_HERE);
1861                 return;
1862         }
1863         if (kill_ok) {
1864                 if (CC->room.QRflags & QR_MAILBOX) {
1865                         safestrncpy(deleted_room_name, &CC->room.QRname[11], sizeof deleted_room_name);
1866                 }
1867                 else {
1868                         safestrncpy(deleted_room_name, CC->room.QRname, sizeof deleted_room_name);
1869                 }
1870
1871                 /* Do the dirty work */
1872                 CtdlScheduleRoomForDeletion(&CC->room);
1873
1874                 /* Return to the Lobby */
1875                 CtdlUserGoto(config.c_baseroom, 0, 0, NULL, NULL);
1876
1877                 /* tell the world what we did */
1878                 snprintf(msg, sizeof msg, "The room \"%s\" has been deleted by %s.\n",
1879                          deleted_room_name,
1880                         (CC->logged_in ? CC->curr_user : "an administrator")
1881                 );
1882                 CtdlAideMessage(msg, "Room Purger Message");
1883                 cprintf("%d '%s' deleted.\n", CIT_OK, deleted_room_name);
1884         } else {
1885                 cprintf("%d ok to delete.\n", CIT_OK);
1886         }
1887 }
1888
1889
1890 /*
1891  * Internal code to create a new room (returns room flags)
1892  *
1893  * Room types:  0=public, 1=hidden, 2=passworded, 3=invitation-only,
1894  *              4=mailbox, 5=mailbox, but caller supplies namespace
1895  */
1896 unsigned CtdlCreateRoom(char *new_room_name,
1897                      int new_room_type,
1898                      char *new_room_pass,
1899                      int new_room_floor,
1900                      int really_create,
1901                      int avoid_access,
1902                      int new_room_view)
1903 {
1904
1905         struct ctdlroom qrbuf;
1906         struct floor flbuf;
1907         visit vbuf;
1908
1909         syslog(LOG_DEBUG, "CtdlCreateRoom(name=%s, type=%d, view=%d)\n",
1910                 new_room_name, new_room_type, new_room_view);
1911
1912         if (CtdlGetRoom(&qrbuf, new_room_name) == 0) {
1913                 syslog(LOG_DEBUG, "%s already exists.\n", new_room_name);
1914                 return(0);
1915         }
1916
1917         memset(&qrbuf, 0, sizeof(struct ctdlroom));
1918         safestrncpy(qrbuf.QRpasswd, new_room_pass, sizeof qrbuf.QRpasswd);
1919         qrbuf.QRflags = QR_INUSE;
1920         if (new_room_type > 0)
1921                 qrbuf.QRflags = (qrbuf.QRflags | QR_PRIVATE);
1922         if (new_room_type == 1)
1923                 qrbuf.QRflags = (qrbuf.QRflags | QR_GUESSNAME);
1924         if (new_room_type == 2)
1925                 qrbuf.QRflags = (qrbuf.QRflags | QR_PASSWORDED);
1926         if ( (new_room_type == 4) || (new_room_type == 5) ) {
1927                 qrbuf.QRflags = (qrbuf.QRflags | QR_MAILBOX);
1928                 /* qrbuf.QRflags2 |= QR2_SUBJECTREQ; */
1929         }
1930
1931         /* If the user is requesting a personal room, set up the room
1932          * name accordingly (prepend the user number)
1933          */
1934         if (new_room_type == 4) {
1935                 CtdlMailboxName(qrbuf.QRname, sizeof qrbuf.QRname, &CC->user, new_room_name);
1936         }
1937         else {
1938                 safestrncpy(qrbuf.QRname, new_room_name, sizeof qrbuf.QRname);
1939         }
1940
1941         /* If the room is private, and the system administrator has elected
1942          * to automatically grant room admin privileges, do so now.
1943          */
1944         if ((qrbuf.QRflags & QR_PRIVATE) && (CREATAIDE == 1)) {
1945                 qrbuf.QRroomaide = CC->user.usernum;
1946         }
1947         /* Blog owners automatically become room admins of their blogs.
1948          * (In the future we will offer a site-wide configuration setting to suppress this behavior.)
1949          */
1950         else if (new_room_view == VIEW_BLOG) {
1951                 qrbuf.QRroomaide = CC->user.usernum;
1952         }
1953         /* Otherwise, set the room admin to undefined.
1954          */
1955         else {
1956                 qrbuf.QRroomaide = (-1L);
1957         }
1958
1959         /* 
1960          * If the caller is only interested in testing whether this will work,
1961          * return now without creating the room.
1962          */
1963         if (!really_create) return (qrbuf.QRflags);
1964
1965         qrbuf.QRnumber = get_new_room_number();
1966         qrbuf.QRhighest = 0L;   /* No messages in this room yet */
1967         time(&qrbuf.QRgen);     /* Use a timestamp as the generation number */
1968         qrbuf.QRfloor = new_room_floor;
1969         qrbuf.QRdefaultview = new_room_view;
1970
1971         /* save what we just did... */
1972         CtdlPutRoom(&qrbuf);
1973
1974         /* bump the reference count on whatever floor the room is on */
1975         lgetfloor(&flbuf, (int) qrbuf.QRfloor);
1976         flbuf.f_ref_count = flbuf.f_ref_count + 1;
1977         lputfloor(&flbuf, (int) qrbuf.QRfloor);
1978
1979         /* Grant the creator access to the room unless the avoid_access
1980          * parameter was specified.
1981          */
1982         if ( (CC->logged_in) && (avoid_access == 0) ) {
1983                 CtdlGetRelationship(&vbuf, &CC->user, &qrbuf);
1984                 vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
1985                 vbuf.v_flags = vbuf.v_flags | V_ACCESS;
1986                 CtdlSetRelationship(&vbuf, &CC->user, &qrbuf);
1987         }
1988
1989         /* resume our happy day */
1990         return (qrbuf.QRflags);
1991 }
1992
1993
1994 /*
1995  * create a new room
1996  */
1997 void cmd_cre8(char *args)
1998 {
1999         int cre8_ok;
2000         char new_room_name[ROOMNAMELEN];
2001         int new_room_type;
2002         char new_room_pass[32];
2003         int new_room_floor;
2004         int new_room_view;
2005         char *notification_message = NULL;
2006         unsigned newflags;
2007         struct floor *fl;
2008         int avoid_access = 0;
2009
2010         cre8_ok = extract_int(args, 0);
2011         extract_token(new_room_name, args, 1, '|', sizeof new_room_name);
2012         new_room_name[ROOMNAMELEN - 1] = 0;
2013         new_room_type = extract_int(args, 2);
2014         extract_token(new_room_pass, args, 3, '|', sizeof new_room_pass);
2015         avoid_access = extract_int(args, 5);
2016         new_room_view = extract_int(args, 6);
2017         new_room_pass[9] = 0;
2018         new_room_floor = 0;
2019
2020         if ((IsEmptyStr(new_room_name)) && (cre8_ok == 1)) {
2021                 cprintf("%d Invalid room name.\n", ERROR + ILLEGAL_VALUE);
2022                 return;
2023         }
2024
2025         if (!strcasecmp(new_room_name, MAILROOM)) {
2026                 cprintf("%d '%s' already exists.\n",
2027                         ERROR + ALREADY_EXISTS, new_room_name);
2028                 return;
2029         }
2030
2031         if (num_parms(args) >= 5) {
2032                 fl = CtdlGetCachedFloor(extract_int(args, 4));
2033                 if (fl == NULL) {
2034                         cprintf("%d Invalid floor number.\n",
2035                                 ERROR + INVALID_FLOOR_OPERATION);
2036                         return;
2037                 }
2038                 else if ((fl->f_flags & F_INUSE) == 0) {
2039                         cprintf("%d Invalid floor number.\n",
2040                                 ERROR + INVALID_FLOOR_OPERATION);
2041                         return;
2042                 } else {
2043                         new_room_floor = extract_int(args, 4);
2044                 }
2045         }
2046
2047         if (CtdlAccessCheck(ac_logged_in)) return;
2048
2049         if (CC->user.axlevel < config.c_createax && !CC->internal_pgm) {
2050                 cprintf("%d You need higher access to create rooms.\n",
2051                         ERROR + HIGHER_ACCESS_REQUIRED);
2052                 return;
2053         }
2054
2055         if ((IsEmptyStr(new_room_name)) && (cre8_ok == 0)) {
2056                 cprintf("%d Ok to create rooms.\n", CIT_OK);
2057                 return;
2058         }
2059
2060         if ((new_room_type < 0) || (new_room_type > 5)) {
2061                 cprintf("%d Invalid room type.\n", ERROR + ILLEGAL_VALUE);
2062                 return;
2063         }
2064
2065         if (new_room_type == 5) {
2066                 if (CC->user.axlevel < AxAideU) {
2067                         cprintf("%d Higher access required\n", 
2068                                 ERROR + HIGHER_ACCESS_REQUIRED);
2069                         return;
2070                 }
2071         }
2072
2073         /* Check to make sure the requested room name doesn't already exist */
2074         newflags = CtdlCreateRoom(new_room_name,
2075                                 new_room_type, new_room_pass, new_room_floor,
2076                                 0, avoid_access, new_room_view);
2077         if (newflags == 0) {
2078                 cprintf("%d '%s' already exists.\n",
2079                         ERROR + ALREADY_EXISTS, new_room_name);
2080                 return;
2081         }
2082
2083         if (cre8_ok == 0) {
2084                 cprintf("%d OK to create '%s'\n", CIT_OK, new_room_name);
2085                 return;
2086         }
2087
2088         /* If we reach this point, the room needs to be created. */
2089
2090         newflags = CtdlCreateRoom(new_room_name,
2091                            new_room_type, new_room_pass, new_room_floor, 1, 0,
2092                            new_room_view);
2093
2094         /* post a message in Aide> describing the new room */
2095         notification_message = malloc(1024);
2096         snprintf(notification_message, 1024,
2097                 "A new room called \"%s\" has been created by %s%s%s%s%s%s\n",
2098                 new_room_name,
2099                 (CC->logged_in ? CC->curr_user : "an administrator"),
2100                 ((newflags & QR_MAILBOX) ? " [personal]" : ""),
2101                 ((newflags & QR_PRIVATE) ? " [private]" : ""),
2102                 ((newflags & QR_GUESSNAME) ? " [hidden]" : ""),
2103                 ((newflags & QR_PASSWORDED) ? " Password: " : ""),
2104                 ((newflags & QR_PASSWORDED) ? new_room_pass : "")
2105         );
2106         CtdlAideMessage(notification_message, "Room Creation Message");
2107         free(notification_message);
2108
2109         cprintf("%d '%s' has been created.\n", CIT_OK, new_room_name);
2110 }
2111
2112
2113
2114 void cmd_einf(char *ok)
2115 {                               /* enter info file for current room */
2116         FILE *fp;
2117         char infofilename[SIZ];
2118         char buf[SIZ];
2119
2120         unbuffer_output();
2121
2122         if (CtdlAccessCheck(ac_room_aide)) return;
2123
2124         if (atoi(ok) == 0) {
2125                 cprintf("%d Ok.\n", CIT_OK);
2126                 return;
2127         }
2128         assoc_file_name(infofilename, sizeof infofilename, &CC->room, ctdl_info_dir);
2129         syslog(LOG_DEBUG, "opening\n");
2130         fp = fopen(infofilename, "w");
2131         syslog(LOG_DEBUG, "checking\n");
2132         if (fp == NULL) {
2133                 cprintf("%d Cannot open %s: %s\n",
2134                   ERROR + INTERNAL_ERROR, infofilename, strerror(errno));
2135                 return;
2136         }
2137         cprintf("%d Send info...\n", SEND_LISTING);
2138
2139         do {
2140                 client_getln(buf, sizeof buf);
2141                 if (strcmp(buf, "000"))
2142                         fprintf(fp, "%s\n", buf);
2143         } while (strcmp(buf, "000"));
2144         fclose(fp);
2145
2146         /* now update the room index so people will see our new info */
2147         CtdlGetRoomLock(&CC->room, CC->room.QRname);            /* lock so no one steps on us */
2148         CC->room.QRinfo = CC->room.QRhighest + 1L;
2149         CtdlPutRoomLock(&CC->room);
2150 }
2151
2152
2153 /* 
2154  * cmd_lflr()   -  List all known floors
2155  */
2156 void cmd_lflr(char *gargs)
2157 {
2158         int a;
2159         struct floor flbuf;
2160
2161         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
2162
2163         cprintf("%d Known floors:\n", LISTING_FOLLOWS);
2164
2165         for (a = 0; a < MAXFLOORS; ++a) {
2166                 CtdlGetFloor(&flbuf, a);
2167                 if (flbuf.f_flags & F_INUSE) {
2168                         cprintf("%d|%s|%d\n",
2169                                 a,
2170                                 flbuf.f_name,
2171                                 flbuf.f_ref_count);
2172                 }
2173         }
2174         cprintf("000\n");
2175 }
2176
2177
2178
2179 /*
2180  * create a new floor
2181  */
2182 void cmd_cflr(char *argbuf)
2183 {
2184         char new_floor_name[256];
2185         struct floor flbuf;
2186         int cflr_ok;
2187         int free_slot = (-1);
2188         int a;
2189
2190         extract_token(new_floor_name, argbuf, 0, '|', sizeof new_floor_name);
2191         cflr_ok = extract_int(argbuf, 1);
2192
2193         if (CtdlAccessCheck(ac_aide)) return;
2194
2195         if (IsEmptyStr(new_floor_name)) {
2196                 cprintf("%d Blank floor name not allowed.\n",
2197                         ERROR + ILLEGAL_VALUE);
2198                 return;
2199         }
2200
2201         for (a = 0; a < MAXFLOORS; ++a) {
2202                 CtdlGetFloor(&flbuf, a);
2203
2204                 /* note any free slots while we're scanning... */
2205                 if (((flbuf.f_flags & F_INUSE) == 0)
2206                     && (free_slot < 0))
2207                         free_slot = a;
2208
2209                 /* check to see if it already exists */
2210                 if ((!strcasecmp(flbuf.f_name, new_floor_name))
2211                     && (flbuf.f_flags & F_INUSE)) {
2212                         cprintf("%d Floor '%s' already exists.\n",
2213                                 ERROR + ALREADY_EXISTS,
2214                                 flbuf.f_name);
2215                         return;
2216                 }
2217         }
2218
2219         if (free_slot < 0) {
2220                 cprintf("%d There is no space available for a new floor.\n",
2221                         ERROR + INVALID_FLOOR_OPERATION);
2222                 return;
2223         }
2224         if (cflr_ok == 0) {
2225                 cprintf("%d ok to create...\n", CIT_OK);
2226                 return;
2227         }
2228         lgetfloor(&flbuf, free_slot);
2229         flbuf.f_flags = F_INUSE;
2230         flbuf.f_ref_count = 0;
2231         safestrncpy(flbuf.f_name, new_floor_name, sizeof flbuf.f_name);
2232         lputfloor(&flbuf, free_slot);
2233         cprintf("%d %d\n", CIT_OK, free_slot);
2234 }
2235
2236
2237
2238 /*
2239  * delete a floor
2240  */
2241 void cmd_kflr(char *argbuf)
2242 {
2243         struct floor flbuf;
2244         int floor_to_delete;
2245         int kflr_ok;
2246         int delete_ok;
2247
2248         floor_to_delete = extract_int(argbuf, 0);
2249         kflr_ok = extract_int(argbuf, 1);
2250
2251         if (CtdlAccessCheck(ac_aide)) return;
2252
2253         lgetfloor(&flbuf, floor_to_delete);
2254
2255         delete_ok = 1;
2256         if ((flbuf.f_flags & F_INUSE) == 0) {
2257                 cprintf("%d Floor %d not in use.\n",
2258                         ERROR + INVALID_FLOOR_OPERATION, floor_to_delete);
2259                 delete_ok = 0;
2260         } else {
2261                 if (flbuf.f_ref_count != 0) {
2262                         cprintf("%d Cannot delete; floor contains %d rooms.\n",
2263                                 ERROR + INVALID_FLOOR_OPERATION,
2264                                 flbuf.f_ref_count);
2265                         delete_ok = 0;
2266                 } else {
2267                         if (kflr_ok == 1) {
2268                                 cprintf("%d Ok\n", CIT_OK);
2269                         } else {
2270                                 cprintf("%d Ok to delete...\n", CIT_OK);
2271                         }
2272
2273                 }
2274
2275         }
2276
2277         if ((delete_ok == 1) && (kflr_ok == 1))
2278                 flbuf.f_flags = 0;
2279         lputfloor(&flbuf, floor_to_delete);
2280 }
2281
2282 /*
2283  * edit a floor
2284  */
2285 void cmd_eflr(char *argbuf)
2286 {
2287         struct floor flbuf;
2288         int floor_num;
2289         int np;
2290
2291         np = num_parms(argbuf);
2292         if (np < 1) {
2293                 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
2294                 return;
2295         }
2296
2297         if (CtdlAccessCheck(ac_aide)) return;
2298
2299         floor_num = extract_int(argbuf, 0);
2300         lgetfloor(&flbuf, floor_num);
2301         if ((flbuf.f_flags & F_INUSE) == 0) {
2302                 lputfloor(&flbuf, floor_num);
2303                 cprintf("%d Floor %d is not in use.\n",
2304                         ERROR + INVALID_FLOOR_OPERATION, floor_num);
2305                 return;
2306         }
2307         if (np >= 2)
2308                 extract_token(flbuf.f_name, argbuf, 1, '|', sizeof flbuf.f_name);
2309         lputfloor(&flbuf, floor_num);
2310
2311         cprintf("%d Ok\n", CIT_OK);
2312 }
2313
2314
2315
2316 /* 
2317  * cmd_stat()  -  return the modification time of the current room (maybe other things in the future)
2318  */
2319 void cmd_stat(char *gargs)
2320 {
2321         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
2322         CtdlGetRoom(&CC->room, CC->room.QRname);
2323         cprintf("%d %s|%ld|\n", CIT_OK, CC->room.QRname, CC->room.QRmtime);
2324 }
2325
2326
2327
2328 /*****************************************************************************/
2329 /*                      MODULE INITIALIZATION STUFF                          */
2330 /*****************************************************************************/
2331
2332 CTDL_MODULE_INIT(room_ops)
2333 {
2334         if (!threading) {
2335                 CtdlRegisterProtoHook(cmd_lrms, "LRMS", "List rooms");
2336                 CtdlRegisterProtoHook(cmd_lkra, "LKRA", "List all known rooms");
2337                 CtdlRegisterProtoHook(cmd_lkrn, "LKRN", "List known rooms with new messages");
2338                 CtdlRegisterProtoHook(cmd_lkro, "LKRO", "List known rooms without new messages");
2339                 CtdlRegisterProtoHook(cmd_lzrm, "LZRM", "List zapped rooms");
2340                 CtdlRegisterProtoHook(cmd_lprm, "LPRM", "List public rooms");
2341                 CtdlRegisterProtoHook(cmd_goto, "GOTO", "Goto a named room");
2342                 CtdlRegisterProtoHook(cmd_stat, "STAT", "Get mtime of the current room");
2343                 CtdlRegisterProtoHook(cmd_whok, "WHOK", "List users who know this room");
2344                 CtdlRegisterProtoHook(cmd_rdir, "RDIR", "List files in room directory");
2345                 CtdlRegisterProtoHook(cmd_getr, "GETR", "Get room parameters");
2346                 CtdlRegisterProtoHook(cmd_setr, "SETR", "Set room parameters");
2347                 CtdlRegisterProtoHook(cmd_geta, "GETA", "Get the room admin name");
2348                 CtdlRegisterProtoHook(cmd_seta, "SETA", "Set the room admin for this room");
2349                 CtdlRegisterProtoHook(cmd_rinf, "RINF", "Fetch room info file");
2350                 CtdlRegisterProtoHook(cmd_kill, "KILL", "Kill (delete) the current room");
2351                 CtdlRegisterProtoHook(cmd_cre8, "CRE8", "Create a new room");
2352                 CtdlRegisterProtoHook(cmd_einf, "EINF", "Enter info file for the current room");
2353                 CtdlRegisterProtoHook(cmd_lflr, "LFLR", "List all known floors");
2354                 CtdlRegisterProtoHook(cmd_cflr, "CFLR", "Create a new floor");
2355                 CtdlRegisterProtoHook(cmd_kflr, "KFLR", "Kill a floor");
2356                 CtdlRegisterProtoHook(cmd_eflr, "EFLR", "Edit a floor");
2357         }
2358         /* return our Subversion id for the Log */
2359         return "room_ops";
2360 }