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