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