style
[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|\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                 );
1060         }
1061 }
1062
1063
1064 /*
1065  * Handle some of the macro named rooms
1066  */
1067 void convert_room_name_macros(char *towhere, size_t maxlen) {
1068         if (!strcasecmp(towhere, "_BASEROOM_")) {
1069                 safestrncpy(towhere, config.c_baseroom, maxlen);
1070         }
1071         else if (!strcasecmp(towhere, "_MAIL_")) {
1072                 safestrncpy(towhere, MAILROOM, maxlen);
1073         }
1074         else if (!strcasecmp(towhere, "_TRASH_")) {
1075                 safestrncpy(towhere, USERTRASHROOM, maxlen);
1076         }
1077         else if (!strcasecmp(towhere, "_DRAFTS_")) {
1078                 safestrncpy(towhere, USERDRAFTROOM, maxlen);
1079         }
1080         else if (!strcasecmp(towhere, "_BITBUCKET_")) {
1081                 safestrncpy(towhere, config.c_twitroom, maxlen);
1082         }
1083         else if (!strcasecmp(towhere, "_CALENDAR_")) {
1084                 safestrncpy(towhere, USERCALENDARROOM, maxlen);
1085         }
1086         else if (!strcasecmp(towhere, "_TASKS_")) {
1087                 safestrncpy(towhere, USERTASKSROOM, maxlen);
1088         }
1089         else if (!strcasecmp(towhere, "_CONTACTS_")) {
1090                 safestrncpy(towhere, USERCONTACTSROOM, maxlen);
1091         }
1092         else if (!strcasecmp(towhere, "_NOTES_")) {
1093                 safestrncpy(towhere, USERNOTESROOM, maxlen);
1094         }
1095 }
1096
1097
1098 /* 
1099  * cmd_goto()  -  goto a new room
1100  */
1101 void cmd_goto(char *gargs)
1102 {
1103         struct ctdlroom QRscratch;
1104         int c;
1105         int ok = 0;
1106         int ra;
1107         char augmented_roomname[ROOMNAMELEN];
1108         char towhere[ROOMNAMELEN];
1109         char password[32];
1110         int transiently = 0;
1111
1112         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
1113
1114         extract_token(towhere, gargs, 0, '|', sizeof towhere);
1115         extract_token(password, gargs, 1, '|', sizeof password);
1116         transiently = extract_int(gargs, 2);
1117
1118         CtdlGetUser(&CC->user, CC->curr_user);
1119
1120         /*
1121          * Handle some of the macro named rooms
1122          */
1123         convert_room_name_macros(towhere, sizeof towhere);
1124
1125         /* First try a regular match */
1126         c = CtdlGetRoom(&QRscratch, towhere);
1127
1128         /* Then try a mailbox name match */
1129         if (c != 0) {
1130                 CtdlMailboxName(augmented_roomname, sizeof augmented_roomname,
1131                             &CC->user, towhere);
1132                 c = CtdlGetRoom(&QRscratch, augmented_roomname);
1133                 if (c == 0)
1134                         safestrncpy(towhere, augmented_roomname, sizeof towhere);
1135         }
1136
1137         /* And if the room was found... */
1138         if (c == 0) {
1139
1140                 /* Let internal programs go directly to any room. */
1141                 if (CC->internal_pgm) {
1142                         memcpy(&CC->room, &QRscratch,
1143                                 sizeof(struct ctdlroom));
1144                         CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
1145                         return;
1146                 }
1147
1148                 /* See if there is an existing user/room relationship */
1149                 CtdlRoomAccess(&QRscratch, &CC->user, &ra, NULL);
1150
1151                 /* normal clients have to pass through security */
1152                 if (ra & UA_GOTOALLOWED) {
1153                         ok = 1;
1154                 }
1155
1156                 if (ok == 1) {
1157                         if ((QRscratch.QRflags & QR_MAILBOX) &&
1158                             ((ra & UA_GOTOALLOWED))) {
1159                                 memcpy(&CC->room, &QRscratch,
1160                                         sizeof(struct ctdlroom));
1161                                 CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
1162                                 return;
1163                         } else if ((QRscratch.QRflags & QR_PASSWORDED) &&
1164                             ((ra & UA_KNOWN) == 0) &&
1165                             (strcasecmp(QRscratch.QRpasswd, password)) &&
1166                             (CC->user.axlevel < AxAideU)
1167                             ) {
1168                                 cprintf("%d wrong or missing passwd\n",
1169                                         ERROR + PASSWORD_REQUIRED);
1170                                 return;
1171                         } else if ((QRscratch.QRflags & QR_PRIVATE) &&
1172                                    ((QRscratch.QRflags & QR_PASSWORDED) == 0) &&
1173                                    ((QRscratch.QRflags & QR_GUESSNAME) == 0) &&
1174                                    ((ra & UA_KNOWN) == 0) &&
1175                                    (CC->user.axlevel < AxAideU)
1176                                   ) {
1177                                 syslog(LOG_DEBUG, "Failed to acquire private room\n");
1178                         } else {
1179                                 memcpy(&CC->room, &QRscratch,
1180                                         sizeof(struct ctdlroom));
1181                                 CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
1182                                 return;
1183                         }
1184                 }
1185         }
1186
1187         cprintf("%d room '%s' not found\n", ERROR + ROOM_NOT_FOUND, towhere);
1188 }
1189
1190
1191 void cmd_whok(char *cmdbuf)
1192 {
1193         struct ctdluser temp;
1194         struct cdbdata *cdbus;
1195         int ra;
1196
1197         cprintf("%d Who knows room:\n", LISTING_FOLLOWS);
1198         cdb_rewind(CDB_USERS);
1199         while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
1200                 memset(&temp, 0, sizeof temp);
1201                 memcpy(&temp, cdbus->ptr, sizeof temp);
1202                 cdb_free(cdbus);
1203
1204                 CtdlRoomAccess(&CC->room, &temp, &ra, NULL);
1205                 if ((!IsEmptyStr(temp.fullname)) && 
1206                     (CC->room.QRflags & QR_INUSE) &&
1207                     (ra & UA_KNOWN)
1208                         )
1209                         cprintf("%s\n", temp.fullname);
1210         }
1211         cprintf("000\n");
1212 }
1213
1214
1215 /*
1216  * RDIR command for room directory
1217  */
1218 void cmd_rdir(char *cmdbuf)
1219 {
1220         char buf[256];
1221         char comment[256];
1222         FILE *fd;
1223         struct stat statbuf;
1224         DIR *filedir = NULL;
1225         struct dirent *filedir_entry;
1226         int d_namelen;
1227         char buf2[SIZ];
1228         char mimebuf[64];
1229         long len;
1230         
1231         if (CtdlAccessCheck(ac_logged_in)) return;
1232         
1233         CtdlGetRoom(&CC->room, CC->room.QRname);
1234         CtdlGetUser(&CC->user, CC->curr_user);
1235
1236         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
1237                 cprintf("%d not here.\n", ERROR + NOT_HERE);
1238                 return;
1239         }
1240         if (((CC->room.QRflags & QR_VISDIR) == 0)
1241             && (CC->user.axlevel < AxAideU)
1242             && (CC->user.usernum != CC->room.QRroomaide)) {
1243                 cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
1244                 return;
1245         }
1246
1247         snprintf(buf, sizeof buf, "%s/%s", ctdl_file_dir, CC->room.QRdirname);
1248         filedir = opendir (buf);
1249         
1250         if (filedir == NULL) {
1251                 cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
1252                 return;
1253         }
1254         cprintf("%d %s|%s/%s\n", LISTING_FOLLOWS, config.c_fqdn, ctdl_file_dir, CC->room.QRdirname);
1255         
1256         snprintf(buf, sizeof buf, "%s/%s/filedir", ctdl_file_dir, CC->room.QRdirname);
1257         fd = fopen(buf, "r");
1258         if (fd == NULL)
1259                 fd = fopen("/dev/null", "r");
1260         while ((filedir_entry = readdir(filedir)))
1261         {
1262                 if (strcasecmp(filedir_entry->d_name, "filedir") && filedir_entry->d_name[0] != '.')
1263                 {
1264 #ifdef _DIRENT_HAVE_D_NAMELEN
1265                         d_namelen = filedir_entry->d_namelen;
1266 #else
1267                         d_namelen = strlen(filedir_entry->d_name);
1268 #endif
1269                         snprintf(buf, sizeof buf, "%s/%s/%s", ctdl_file_dir, CC->room.QRdirname, filedir_entry->d_name);
1270                         stat(buf, &statbuf);    /* stat the file */
1271                         if (!(statbuf.st_mode & S_IFREG))
1272                         {
1273                                 snprintf(buf2, sizeof buf2,
1274                                         "\"%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",
1275                                         buf, CC->room.QRname
1276                                 );
1277                                 CtdlAideMessage(buf2, "Unusable data found in room directory");
1278                                 continue;       /* not a useable file type so don't show it */
1279                         }
1280                         safestrncpy(comment, "", sizeof comment);
1281                         fseek(fd, 0L, 0);       /* rewind descriptions file */
1282                         /* Get the description from the descriptions file */
1283                         while ((fgets(buf, sizeof buf, fd) != NULL) && (IsEmptyStr(comment))) 
1284                         {
1285                                 buf[strlen(buf) - 1] = 0;
1286                                 if ((!strncasecmp(buf, filedir_entry->d_name, d_namelen)) && (buf[d_namelen] == ' '))
1287                                         safestrncpy(comment, &buf[d_namelen + 1], sizeof comment);
1288                         }
1289                         len = extract_token (mimebuf, comment, 0,' ', 64);
1290                         if ((len <0) || strchr(mimebuf, '/') == NULL)
1291                         {
1292                                 snprintf (mimebuf, 64, "application/octetstream");
1293                                 len = 0;
1294                         }
1295                         cprintf("%s|%ld|%s|%s\n", 
1296                                 filedir_entry->d_name, 
1297                                 (long)statbuf.st_size, 
1298                                 mimebuf, 
1299                                 &comment[len]);
1300                 }
1301         }
1302         fclose(fd);
1303         closedir(filedir);
1304         
1305         cprintf("000\n");
1306 }
1307
1308 /*
1309  * get room parameters (aide or room aide command)
1310  */
1311 void cmd_getr(char *cmdbuf)
1312 {
1313         if (CtdlAccessCheck(ac_room_aide)) return;
1314
1315         CtdlGetRoom(&CC->room, CC->room.QRname);
1316         cprintf("%d%c%s|%s|%s|%d|%d|%d|%d|%d|\n",
1317                 CIT_OK,
1318                 CtdlCheckExpress(),
1319
1320                 ((CC->room.QRflags & QR_MAILBOX) ?
1321                         &CC->room.QRname[11] : CC->room.QRname),
1322
1323                 ((CC->room.QRflags & QR_PASSWORDED) ?
1324                         CC->room.QRpasswd : ""),
1325
1326                 ((CC->room.QRflags & QR_DIRECTORY) ?
1327                         CC->room.QRdirname : ""),
1328
1329                 CC->room.QRflags,
1330                 (int) CC->room.QRfloor,
1331                 (int) CC->room.QRorder,
1332
1333                 CC->room.QRdefaultview,
1334                 CC->room.QRflags2
1335                 );
1336 }
1337
1338
1339 /*
1340  * Back end function to rename a room.
1341  * You can also specify which floor to move the room to, or specify -1 to
1342  * keep the room on the same floor it was on.
1343  *
1344  * If you are renaming a mailbox room, you must supply the namespace prefix
1345  * in *at least* the old name!
1346  */
1347 int CtdlRenameRoom(char *old_name, char *new_name, int new_floor) {
1348         int old_floor = 0;
1349         struct ctdlroom qrbuf;
1350         struct ctdlroom qrtmp;
1351         int ret = 0;
1352         struct floor *fl;
1353         struct floor flbuf;
1354         long owner = 0L;
1355         char actual_old_name[ROOMNAMELEN];
1356
1357         syslog(LOG_DEBUG, "CtdlRenameRoom(%s, %s, %d)\n",
1358                 old_name, new_name, new_floor);
1359
1360         if (new_floor >= 0) {
1361                 fl = CtdlGetCachedFloor(new_floor);
1362                 if ((fl->f_flags & F_INUSE) == 0) {
1363                         return(crr_invalid_floor);
1364                 }
1365         }
1366
1367         begin_critical_section(S_ROOMS);
1368
1369         if ( (CtdlGetRoom(&qrtmp, new_name) == 0) 
1370            && (strcasecmp(new_name, old_name)) ) {
1371                 ret = crr_already_exists;
1372         }
1373
1374         else if (CtdlGetRoom(&qrbuf, old_name) != 0) {
1375                 ret = crr_room_not_found;
1376         }
1377
1378         else if ( (CC->user.axlevel < AxAideU) && (!CC->internal_pgm)
1379                   && (CC->user.usernum != qrbuf.QRroomaide)
1380                   && ( (((qrbuf.QRflags & QR_MAILBOX) == 0) || (atol(qrbuf.QRname) != CC->user.usernum))) )  {
1381                 ret = crr_access_denied;
1382         }
1383
1384         else if (CtdlIsNonEditable(&qrbuf)) {
1385                 ret = crr_noneditable;
1386         }
1387
1388         else {
1389                 /* Rename it */
1390                 safestrncpy(actual_old_name, qrbuf.QRname, sizeof actual_old_name);
1391                 if (qrbuf.QRflags & QR_MAILBOX) {
1392                         owner = atol(qrbuf.QRname);
1393                 }
1394                 if ( (owner > 0L) && (atol(new_name) == 0L) ) {
1395                         snprintf(qrbuf.QRname, sizeof(qrbuf.QRname),
1396                                         "%010ld.%s", owner, new_name);
1397                 }
1398                 else {
1399                         safestrncpy(qrbuf.QRname, new_name,
1400                                                 sizeof(qrbuf.QRname));
1401                 }
1402
1403                 /* Reject change of floor for baseroom/aideroom */
1404                 if (!strncasecmp(old_name, config.c_baseroom, ROOMNAMELEN) ||
1405                     !strncasecmp(old_name, config.c_aideroom, ROOMNAMELEN)) {
1406                         new_floor = 0;
1407                 }
1408
1409                 /* Take care of floor stuff */
1410                 old_floor = qrbuf.QRfloor;
1411                 if (new_floor < 0) {
1412                         new_floor = old_floor;
1413                 }
1414                 qrbuf.QRfloor = new_floor;
1415                 CtdlPutRoom(&qrbuf);
1416
1417                 begin_critical_section(S_CONFIG);
1418         
1419                 /* If baseroom/aideroom name changes, update config */
1420                 if (!strncasecmp(old_name, config.c_baseroom, ROOMNAMELEN)) {
1421                         safestrncpy(config.c_baseroom, new_name, ROOMNAMELEN);
1422                         put_config();
1423                 }
1424                 if (!strncasecmp(old_name, config.c_aideroom, ROOMNAMELEN)) {
1425                         safestrncpy(config.c_aideroom, new_name, ROOMNAMELEN);
1426                         put_config();
1427                 }
1428         
1429                 end_critical_section(S_CONFIG);
1430         
1431                 /* If the room name changed, then there are now two room
1432                  * records, so we have to delete the old one.
1433                  */
1434                 if (strcasecmp(new_name, old_name)) {
1435                         b_deleteroom(actual_old_name);
1436                 }
1437
1438                 ret = crr_ok;
1439         }
1440
1441         end_critical_section(S_ROOMS);
1442
1443         /* Adjust the floor reference counts if necessary */
1444         if (new_floor != old_floor) {
1445                 lgetfloor(&flbuf, old_floor);
1446                 --flbuf.f_ref_count;
1447                 lputfloor(&flbuf, old_floor);
1448                 syslog(LOG_DEBUG, "Reference count for floor %d is now %d\n", old_floor, flbuf.f_ref_count);
1449                 lgetfloor(&flbuf, new_floor);
1450                 ++flbuf.f_ref_count;
1451                 lputfloor(&flbuf, new_floor);
1452                 syslog(LOG_DEBUG, "Reference count for floor %d is now %d\n", new_floor, flbuf.f_ref_count);
1453         }
1454
1455         /* ...and everybody say "YATTA!" */     
1456         return(ret);
1457 }
1458
1459
1460 /*
1461  * set room parameters (aide or room aide command)
1462  */
1463 void cmd_setr(char *args)
1464 {
1465         char buf[256];
1466         int new_order = 0;
1467         int r;
1468         int new_floor;
1469         char new_name[ROOMNAMELEN];
1470
1471         if (CtdlAccessCheck(ac_logged_in)) return;
1472
1473         if (num_parms(args) >= 6) {
1474                 new_floor = extract_int(args, 5);
1475         } else {
1476                 new_floor = (-1);       /* don't change the floor */
1477         }
1478
1479         /* When is a new name more than just a new name?  When the old name
1480          * has a namespace prefix.
1481          */
1482         if (CC->room.QRflags & QR_MAILBOX) {
1483                 sprintf(new_name, "%010ld.", atol(CC->room.QRname) );
1484         } else {
1485                 safestrncpy(new_name, "", sizeof new_name);
1486         }
1487         extract_token(&new_name[strlen(new_name)], args, 0, '|', (sizeof new_name - strlen(new_name)));
1488
1489         r = CtdlRenameRoom(CC->room.QRname, new_name, new_floor);
1490
1491         if (r == crr_room_not_found) {
1492                 cprintf("%d Internal error - room not found?\n", ERROR + INTERNAL_ERROR);
1493         } else if (r == crr_already_exists) {
1494                 cprintf("%d '%s' already exists.\n",
1495                         ERROR + ALREADY_EXISTS, new_name);
1496         } else if (r == crr_noneditable) {
1497                 cprintf("%d Cannot edit this room.\n", ERROR + NOT_HERE);
1498         } else if (r == crr_invalid_floor) {
1499                 cprintf("%d Target floor does not exist.\n",
1500                         ERROR + INVALID_FLOOR_OPERATION);
1501         } else if (r == crr_access_denied) {
1502                 cprintf("%d You do not have permission to edit '%s'\n",
1503                         ERROR + HIGHER_ACCESS_REQUIRED,
1504                         CC->room.QRname);
1505         } else if (r != crr_ok) {
1506                 cprintf("%d Error: CtdlRenameRoom() returned %d\n",
1507                         ERROR + INTERNAL_ERROR, r);
1508         }
1509
1510         if (r != crr_ok) {
1511                 return;
1512         }
1513
1514         CtdlGetRoom(&CC->room, new_name);
1515
1516         /* Now we have to do a bunch of other stuff */
1517
1518         if (num_parms(args) >= 7) {
1519                 new_order = extract_int(args, 6);
1520                 if (new_order < 1)
1521                         new_order = 1;
1522                 if (new_order > 127)
1523                         new_order = 127;
1524         }
1525
1526         CtdlGetRoomLock(&CC->room, CC->room.QRname);
1527
1528         /* Directory room */
1529         extract_token(buf, args, 2, '|', sizeof buf);
1530         buf[15] = 0;
1531         safestrncpy(CC->room.QRdirname, buf,
1532                 sizeof CC->room.QRdirname);
1533
1534         /* Default view */
1535         if (num_parms(args) >= 8) {
1536                 CC->room.QRdefaultview = extract_int(args, 7);
1537         }
1538
1539         /* Second set of flags */
1540         if (num_parms(args) >= 9) {
1541                 CC->room.QRflags2 = extract_int(args, 8);
1542         }
1543
1544         /* Misc. flags */
1545         CC->room.QRflags = (extract_int(args, 3) | QR_INUSE);
1546         /* Clean up a client boo-boo: if the client set the room to
1547          * guess-name or passworded, ensure that the private flag is
1548          * also set.
1549          */
1550         if ((CC->room.QRflags & QR_GUESSNAME)
1551             || (CC->room.QRflags & QR_PASSWORDED))
1552                 CC->room.QRflags |= QR_PRIVATE;
1553
1554         /* Some changes can't apply to BASEROOM */
1555         if (!strncasecmp(CC->room.QRname, config.c_baseroom,
1556                          ROOMNAMELEN)) {
1557                 CC->room.QRorder = 0;
1558                 CC->room.QRpasswd[0] = '\0';
1559                 CC->room.QRflags &= ~(QR_PRIVATE & QR_PASSWORDED &
1560                         QR_GUESSNAME & QR_PREFONLY & QR_MAILBOX);
1561                 CC->room.QRflags |= QR_PERMANENT;
1562         } else {        
1563                 /* March order (doesn't apply to AIDEROOM) */
1564                 if (num_parms(args) >= 7)
1565                         CC->room.QRorder = (char) new_order;
1566                 /* Room password */
1567                 extract_token(buf, args, 1, '|', sizeof buf);
1568                 buf[10] = 0;
1569                 safestrncpy(CC->room.QRpasswd, buf,
1570                             sizeof CC->room.QRpasswd);
1571                 /* Kick everyone out if the client requested it
1572                  * (by changing the room's generation number)
1573                  */
1574                 if (extract_int(args, 4)) {
1575                         time(&CC->room.QRgen);
1576                 }
1577         }
1578         /* Some changes can't apply to AIDEROOM */
1579         if (!strncasecmp(CC->room.QRname, config.c_baseroom,
1580                          ROOMNAMELEN)) {
1581                 CC->room.QRorder = 0;
1582                 CC->room.QRflags &= ~QR_MAILBOX;
1583                 CC->room.QRflags |= QR_PERMANENT;
1584         }
1585
1586         /* Write the room record back to disk */
1587         CtdlPutRoomLock(&CC->room);
1588
1589         /* Create a room directory if necessary */
1590         if (CC->room.QRflags & QR_DIRECTORY) {
1591                 snprintf(buf, sizeof buf,"%s/%s",
1592                                  ctdl_file_dir,
1593                                  CC->room.QRdirname);
1594                 mkdir(buf, 0755);
1595         }
1596         snprintf(buf, sizeof buf, "The room \"%s\" has been edited by %s.\n",
1597                 CC->room.QRname,
1598                 (CC->logged_in ? CC->curr_user : "an administrator")
1599         );
1600         CtdlAideMessage(buf, "Room modification Message");
1601         cprintf("%d Ok\n", CIT_OK);
1602 }
1603
1604
1605
1606 /* 
1607  * get the name of the room aide for this room
1608  */
1609 void cmd_geta(char *cmdbuf)
1610 {
1611         struct ctdluser usbuf;
1612
1613         if (CtdlAccessCheck(ac_logged_in)) return;
1614
1615         if (CtdlGetUserByNumber(&usbuf, CC->room.QRroomaide) == 0) {
1616                 cprintf("%d %s\n", CIT_OK, usbuf.fullname);
1617         } else {
1618                 cprintf("%d \n", CIT_OK);
1619         }
1620 }
1621
1622
1623 /* 
1624  * set the room aide for this room
1625  */
1626 void cmd_seta(char *new_ra)
1627 {
1628         struct ctdluser usbuf;
1629         long newu;
1630         char buf[SIZ];
1631         int post_notice;
1632
1633         if (CtdlAccessCheck(ac_room_aide)) return;
1634
1635         if (CtdlGetUser(&usbuf, new_ra) != 0) {
1636                 newu = (-1L);
1637         } else {
1638                 newu = usbuf.usernum;
1639         }
1640
1641         CtdlGetRoomLock(&CC->room, CC->room.QRname);
1642         post_notice = 0;
1643         if (CC->room.QRroomaide != newu) {
1644                 post_notice = 1;
1645         }
1646         CC->room.QRroomaide = newu;
1647         CtdlPutRoomLock(&CC->room);
1648
1649         /*
1650          * We have to post the change notice _after_ writing changes to 
1651          * the room table, otherwise it would deadlock!
1652          */
1653         if (post_notice == 1) {
1654                 if (!IsEmptyStr(usbuf.fullname))
1655                         snprintf(buf, sizeof buf,
1656                                 "%s is now the room aide for \"%s\".\n",
1657                                 usbuf.fullname, CC->room.QRname);
1658                 else
1659                         snprintf(buf, sizeof buf,
1660                                 "There is now no room aide for \"%s\".\n",
1661                                 CC->room.QRname);
1662                 CtdlAideMessage(buf, "Aide Room Modification");
1663         }
1664         cprintf("%d Ok\n", CIT_OK);
1665 }
1666
1667 /* 
1668  * retrieve info file for this room
1669  */
1670 void cmd_rinf(char *gargs)
1671 {
1672         char filename[128];
1673         char buf[SIZ];
1674         FILE *info_fp;
1675
1676         assoc_file_name(filename, sizeof filename, &CC->room, ctdl_info_dir);
1677         info_fp = fopen(filename, "r");
1678
1679         if (info_fp == NULL) {
1680                 cprintf("%d No info file.\n", ERROR + FILE_NOT_FOUND);
1681                 return;
1682         }
1683         cprintf("%d Info:\n", LISTING_FOLLOWS);
1684         while (fgets(buf, sizeof buf, info_fp) != NULL) {
1685                 if (!IsEmptyStr(buf))
1686                         buf[strlen(buf) - 1] = 0;
1687                 cprintf("%s\n", buf);
1688         }
1689         cprintf("000\n");
1690         fclose(info_fp);
1691 }
1692
1693 /*
1694  * Asynchronously schedule a room for deletion.  The room will appear
1695  * deleted to the user(s), but it won't actually get purged from the
1696  * database until THE DREADED AUTO-PURGER makes its next run.
1697  */
1698 void CtdlScheduleRoomForDeletion(struct ctdlroom *qrbuf)
1699 {
1700         char old_name[ROOMNAMELEN];
1701         static int seq = 0;
1702
1703         syslog(LOG_NOTICE, "Scheduling room <%s> for deletion\n",
1704                 qrbuf->QRname);
1705
1706         safestrncpy(old_name, qrbuf->QRname, sizeof old_name);
1707
1708         CtdlGetRoom(qrbuf, qrbuf->QRname);
1709
1710         /* Turn the room into a private mailbox owned by a user who doesn't
1711          * exist.  This will immediately make the room invisible to everyone,
1712          * and qualify the room for purging.
1713          */
1714         snprintf(qrbuf->QRname, sizeof qrbuf->QRname, "9999999999.%08lx.%04d.%s",
1715                 time(NULL),
1716                 ++seq,
1717                 old_name
1718         );
1719         qrbuf->QRflags |= QR_MAILBOX;
1720         time(&qrbuf->QRgen);    /* Use a timestamp as the new generation number  */
1721
1722         CtdlPutRoom(qrbuf);
1723
1724         b_deleteroom(old_name);
1725 }
1726
1727
1728
1729 /*
1730  * Back end processing to delete a room and everything associated with it
1731  * (This one is synchronous and should only get called by THE DREADED
1732  * AUTO-PURGER in serv_expire.c.  All user-facing code should call
1733  * the asynchronous schedule_room_for_deletion() instead.)
1734  */
1735 void CtdlDeleteRoom(struct ctdlroom *qrbuf)
1736 {
1737         struct floor flbuf;
1738         char filename[100];
1739         /* TODO: filename magic? does this realy work? */
1740
1741         syslog(LOG_NOTICE, "Deleting room <%s>\n", qrbuf->QRname);
1742
1743         /* Delete the info file */
1744         assoc_file_name(filename, sizeof filename, qrbuf, ctdl_info_dir);
1745         unlink(filename);
1746
1747         /* Delete the image file */
1748         assoc_file_name(filename, sizeof filename, qrbuf, ctdl_image_dir);
1749         unlink(filename);
1750
1751         /* Delete the room's network config file */
1752         assoc_file_name(filename, sizeof filename, qrbuf, ctdl_netcfg_dir);
1753         unlink(filename);
1754
1755         /* Delete the messages in the room
1756          * (Careful: this opens an S_ROOMS critical section!)
1757          */
1758         CtdlDeleteMessages(qrbuf->QRname, NULL, 0, "");
1759
1760         /* Flag the room record as not in use */
1761         CtdlGetRoomLock(qrbuf, qrbuf->QRname);
1762         qrbuf->QRflags = 0;
1763         CtdlPutRoomLock(qrbuf);
1764
1765         /* then decrement the reference count for the floor */
1766         lgetfloor(&flbuf, (int) (qrbuf->QRfloor));
1767         flbuf.f_ref_count = flbuf.f_ref_count - 1;
1768         lputfloor(&flbuf, (int) (qrbuf->QRfloor));
1769
1770         /* Delete the room record from the database! */
1771         b_deleteroom(qrbuf->QRname);
1772 }
1773
1774
1775
1776 /*
1777  * Check access control for deleting a room
1778  */
1779 int CtdlDoIHavePermissionToDeleteThisRoom(struct ctdlroom *qr) {
1780
1781         if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
1782                 return(0);
1783         }
1784
1785         if (CtdlIsNonEditable(qr)) {
1786                 return(0);
1787         }
1788
1789         /*
1790          * For mailboxes, check stuff
1791          */
1792         if (qr->QRflags & QR_MAILBOX) {
1793
1794                 if (strlen(qr->QRname) < 12) return(0); /* bad name */
1795
1796                 if (atol(qr->QRname) != CC->user.usernum) {
1797                         return(0);      /* not my room */
1798                 }
1799
1800                 /* Can't delete your Mail> room */
1801                 if (!strcasecmp(&qr->QRname[11], MAILROOM)) return(0);
1802
1803                 /* Otherwise it's ok */
1804                 return(1);
1805         }
1806
1807         /*
1808          * For normal rooms, just check for aide or room aide status.
1809          */
1810         return(is_room_aide());
1811 }
1812
1813 /*
1814  * aide command: kill the current room
1815  */
1816 void cmd_kill(char *argbuf)
1817 {
1818         char deleted_room_name[ROOMNAMELEN];
1819         char msg[SIZ];
1820         int kill_ok;
1821
1822         kill_ok = extract_int(argbuf, 0);
1823
1824         if (CtdlDoIHavePermissionToDeleteThisRoom(&CC->room) == 0) {
1825                 cprintf("%d Can't delete this room.\n", ERROR + NOT_HERE);
1826                 return;
1827         }
1828         if (kill_ok) {
1829                 if (CC->room.QRflags & QR_MAILBOX) {
1830                         safestrncpy(deleted_room_name, &CC->room.QRname[11], sizeof deleted_room_name);
1831                 }
1832                 else {
1833                         safestrncpy(deleted_room_name, CC->room.QRname, sizeof deleted_room_name);
1834                 }
1835
1836                 /* Do the dirty work */
1837                 CtdlScheduleRoomForDeletion(&CC->room);
1838
1839                 /* Return to the Lobby */
1840                 CtdlUserGoto(config.c_baseroom, 0, 0, NULL, NULL);
1841
1842                 /* tell the world what we did */
1843                 snprintf(msg, sizeof msg, "The room \"%s\" has been deleted by %s.\n",
1844                          deleted_room_name,
1845                         (CC->logged_in ? CC->curr_user : "an administrator")
1846                 );
1847                 CtdlAideMessage(msg, "Room Purger Message");
1848                 cprintf("%d '%s' deleted.\n", CIT_OK, deleted_room_name);
1849         } else {
1850                 cprintf("%d ok to delete.\n", CIT_OK);
1851         }
1852 }
1853
1854
1855 /*
1856  * Internal code to create a new room (returns room flags)
1857  *
1858  * Room types:  0=public, 1=hidden, 2=passworded, 3=invitation-only,
1859  *              4=mailbox, 5=mailbox, but caller supplies namespace
1860  */
1861 unsigned CtdlCreateRoom(char *new_room_name,
1862                      int new_room_type,
1863                      char *new_room_pass,
1864                      int new_room_floor,
1865                      int really_create,
1866                      int avoid_access,
1867                      int new_room_view)
1868 {
1869
1870         struct ctdlroom qrbuf;
1871         struct floor flbuf;
1872         visit vbuf;
1873
1874         syslog(LOG_DEBUG, "CtdlCreateRoom(name=%s, type=%d, view=%d)\n",
1875                 new_room_name, new_room_type, new_room_view);
1876
1877         if (CtdlGetRoom(&qrbuf, new_room_name) == 0) {
1878                 syslog(LOG_DEBUG, "%s already exists.\n", new_room_name);
1879                 return(0);
1880         }
1881
1882         memset(&qrbuf, 0, sizeof(struct ctdlroom));
1883         safestrncpy(qrbuf.QRpasswd, new_room_pass, sizeof qrbuf.QRpasswd);
1884         qrbuf.QRflags = QR_INUSE;
1885         if (new_room_type > 0)
1886                 qrbuf.QRflags = (qrbuf.QRflags | QR_PRIVATE);
1887         if (new_room_type == 1)
1888                 qrbuf.QRflags = (qrbuf.QRflags | QR_GUESSNAME);
1889         if (new_room_type == 2)
1890                 qrbuf.QRflags = (qrbuf.QRflags | QR_PASSWORDED);
1891         if ( (new_room_type == 4) || (new_room_type == 5) ) {
1892                 qrbuf.QRflags = (qrbuf.QRflags | QR_MAILBOX);
1893                 /* qrbuf.QRflags2 |= QR2_SUBJECTREQ; */
1894         }
1895
1896         /* If the user is requesting a personal room, set up the room
1897          * name accordingly (prepend the user number)
1898          */
1899         if (new_room_type == 4) {
1900                 CtdlMailboxName(qrbuf.QRname, sizeof qrbuf.QRname, &CC->user, new_room_name);
1901         }
1902         else {
1903                 safestrncpy(qrbuf.QRname, new_room_name, sizeof qrbuf.QRname);
1904         }
1905
1906         /* If the room is private, and the system administrator has elected
1907          * to automatically grant room aide privileges, do so now.
1908          */
1909         if ((qrbuf.QRflags & QR_PRIVATE) && (CREATAIDE == 1)) {
1910                 qrbuf.QRroomaide = CC->user.usernum;
1911         }
1912         /* Blog owners automatically become room aides of their blogs.
1913          * (In the future we will offer a site-wide configuration setting to suppress this behavior.)
1914          */
1915         else if (new_room_view == VIEW_BLOG) {
1916                 qrbuf.QRroomaide = CC->user.usernum;
1917         }
1918         /* Otherwise, set the room aide to undefined.
1919          */
1920         else {
1921                 qrbuf.QRroomaide = (-1L);
1922         }
1923
1924         /* 
1925          * If the caller is only interested in testing whether this will work,
1926          * return now without creating the room.
1927          */
1928         if (!really_create) return (qrbuf.QRflags);
1929
1930         qrbuf.QRnumber = get_new_room_number();
1931         qrbuf.QRhighest = 0L;   /* No messages in this room yet */
1932         time(&qrbuf.QRgen);     /* Use a timestamp as the generation number */
1933         qrbuf.QRfloor = new_room_floor;
1934         qrbuf.QRdefaultview = new_room_view;
1935
1936         /* save what we just did... */
1937         CtdlPutRoom(&qrbuf);
1938
1939         /* bump the reference count on whatever floor the room is on */
1940         lgetfloor(&flbuf, (int) qrbuf.QRfloor);
1941         flbuf.f_ref_count = flbuf.f_ref_count + 1;
1942         lputfloor(&flbuf, (int) qrbuf.QRfloor);
1943
1944         /* Grant the creator access to the room unless the avoid_access
1945          * parameter was specified.
1946          */
1947         if ( (CC->logged_in) && (avoid_access == 0) ) {
1948                 CtdlGetRelationship(&vbuf, &CC->user, &qrbuf);
1949                 vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
1950                 vbuf.v_flags = vbuf.v_flags | V_ACCESS;
1951                 CtdlSetRelationship(&vbuf, &CC->user, &qrbuf);
1952         }
1953
1954         /* resume our happy day */
1955         return (qrbuf.QRflags);
1956 }
1957
1958
1959 /*
1960  * create a new room
1961  */
1962 void cmd_cre8(char *args)
1963 {
1964         int cre8_ok;
1965         char new_room_name[ROOMNAMELEN];
1966         int new_room_type;
1967         char new_room_pass[32];
1968         int new_room_floor;
1969         int new_room_view;
1970         char *notification_message = NULL;
1971         unsigned newflags;
1972         struct floor *fl;
1973         int avoid_access = 0;
1974
1975         cre8_ok = extract_int(args, 0);
1976         extract_token(new_room_name, args, 1, '|', sizeof new_room_name);
1977         new_room_name[ROOMNAMELEN - 1] = 0;
1978         new_room_type = extract_int(args, 2);
1979         extract_token(new_room_pass, args, 3, '|', sizeof new_room_pass);
1980         avoid_access = extract_int(args, 5);
1981         new_room_view = extract_int(args, 6);
1982         new_room_pass[9] = 0;
1983         new_room_floor = 0;
1984
1985         if ((IsEmptyStr(new_room_name)) && (cre8_ok == 1)) {
1986                 cprintf("%d Invalid room name.\n", ERROR + ILLEGAL_VALUE);
1987                 return;
1988         }
1989
1990         if (!strcasecmp(new_room_name, MAILROOM)) {
1991                 cprintf("%d '%s' already exists.\n",
1992                         ERROR + ALREADY_EXISTS, new_room_name);
1993                 return;
1994         }
1995
1996         if (num_parms(args) >= 5) {
1997                 fl = CtdlGetCachedFloor(extract_int(args, 4));
1998                 if (fl == NULL) {
1999                         cprintf("%d Invalid floor number.\n",
2000                                 ERROR + INVALID_FLOOR_OPERATION);
2001                         return;
2002                 }
2003                 else if ((fl->f_flags & F_INUSE) == 0) {
2004                         cprintf("%d Invalid floor number.\n",
2005                                 ERROR + INVALID_FLOOR_OPERATION);
2006                         return;
2007                 } else {
2008                         new_room_floor = extract_int(args, 4);
2009                 }
2010         }
2011
2012         if (CtdlAccessCheck(ac_logged_in)) return;
2013
2014         if (CC->user.axlevel < config.c_createax && !CC->internal_pgm) {
2015                 cprintf("%d You need higher access to create rooms.\n",
2016                         ERROR + HIGHER_ACCESS_REQUIRED);
2017                 return;
2018         }
2019
2020         if ((IsEmptyStr(new_room_name)) && (cre8_ok == 0)) {
2021                 cprintf("%d Ok to create rooms.\n", CIT_OK);
2022                 return;
2023         }
2024
2025         if ((new_room_type < 0) || (new_room_type > 5)) {
2026                 cprintf("%d Invalid room type.\n", ERROR + ILLEGAL_VALUE);
2027                 return;
2028         }
2029
2030         if (new_room_type == 5) {
2031                 if (CC->user.axlevel < AxAideU) {
2032                         cprintf("%d Higher access required\n", 
2033                                 ERROR + HIGHER_ACCESS_REQUIRED);
2034                         return;
2035                 }
2036         }
2037
2038         /* Check to make sure the requested room name doesn't already exist */
2039         newflags = CtdlCreateRoom(new_room_name,
2040                                 new_room_type, new_room_pass, new_room_floor,
2041                                 0, avoid_access, new_room_view);
2042         if (newflags == 0) {
2043                 cprintf("%d '%s' already exists.\n",
2044                         ERROR + ALREADY_EXISTS, new_room_name);
2045                 return;
2046         }
2047
2048         if (cre8_ok == 0) {
2049                 cprintf("%d OK to create '%s'\n", CIT_OK, new_room_name);
2050                 return;
2051         }
2052
2053         /* If we reach this point, the room needs to be created. */
2054
2055         newflags = CtdlCreateRoom(new_room_name,
2056                            new_room_type, new_room_pass, new_room_floor, 1, 0,
2057                            new_room_view);
2058
2059         /* post a message in Aide> describing the new room */
2060         notification_message = malloc(1024);
2061         snprintf(notification_message, 1024,
2062                 "A new room called \"%s\" has been created by %s%s%s%s%s%s\n",
2063                 new_room_name,
2064                 (CC->logged_in ? CC->curr_user : "an administrator"),
2065                 ((newflags & QR_MAILBOX) ? " [personal]" : ""),
2066                 ((newflags & QR_PRIVATE) ? " [private]" : ""),
2067                 ((newflags & QR_GUESSNAME) ? " [hidden]" : ""),
2068                 ((newflags & QR_PASSWORDED) ? " Password: " : ""),
2069                 ((newflags & QR_PASSWORDED) ? new_room_pass : "")
2070         );
2071         CtdlAideMessage(notification_message, "Room Creation Message");
2072         free(notification_message);
2073
2074         cprintf("%d '%s' has been created.\n", CIT_OK, new_room_name);
2075 }
2076
2077
2078
2079 void cmd_einf(char *ok)
2080 {                               /* enter info file for current room */
2081         FILE *fp;
2082         char infofilename[SIZ];
2083         char buf[SIZ];
2084
2085         unbuffer_output();
2086
2087         if (CtdlAccessCheck(ac_room_aide)) return;
2088
2089         if (atoi(ok) == 0) {
2090                 cprintf("%d Ok.\n", CIT_OK);
2091                 return;
2092         }
2093         assoc_file_name(infofilename, sizeof infofilename, &CC->room, ctdl_info_dir);
2094         syslog(LOG_DEBUG, "opening\n");
2095         fp = fopen(infofilename, "w");
2096         syslog(LOG_DEBUG, "checking\n");
2097         if (fp == NULL) {
2098                 cprintf("%d Cannot open %s: %s\n",
2099                   ERROR + INTERNAL_ERROR, infofilename, strerror(errno));
2100                 return;
2101         }
2102         cprintf("%d Send info...\n", SEND_LISTING);
2103
2104         do {
2105                 client_getln(buf, sizeof buf);
2106                 if (strcmp(buf, "000"))
2107                         fprintf(fp, "%s\n", buf);
2108         } while (strcmp(buf, "000"));
2109         fclose(fp);
2110
2111         /* now update the room index so people will see our new info */
2112         CtdlGetRoomLock(&CC->room, CC->room.QRname);            /* lock so no one steps on us */
2113         CC->room.QRinfo = CC->room.QRhighest + 1L;
2114         CtdlPutRoomLock(&CC->room);
2115 }
2116
2117
2118 /* 
2119  * cmd_lflr()   -  List all known floors
2120  */
2121 void cmd_lflr(char *gargs)
2122 {
2123         int a;
2124         struct floor flbuf;
2125
2126         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
2127
2128         cprintf("%d Known floors:\n", LISTING_FOLLOWS);
2129
2130         for (a = 0; a < MAXFLOORS; ++a) {
2131                 CtdlGetFloor(&flbuf, a);
2132                 if (flbuf.f_flags & F_INUSE) {
2133                         cprintf("%d|%s|%d\n",
2134                                 a,
2135                                 flbuf.f_name,
2136                                 flbuf.f_ref_count);
2137                 }
2138         }
2139         cprintf("000\n");
2140 }
2141
2142
2143
2144 /*
2145  * create a new floor
2146  */
2147 void cmd_cflr(char *argbuf)
2148 {
2149         char new_floor_name[256];
2150         struct floor flbuf;
2151         int cflr_ok;
2152         int free_slot = (-1);
2153         int a;
2154
2155         extract_token(new_floor_name, argbuf, 0, '|', sizeof new_floor_name);
2156         cflr_ok = extract_int(argbuf, 1);
2157
2158         if (CtdlAccessCheck(ac_aide)) return;
2159
2160         if (IsEmptyStr(new_floor_name)) {
2161                 cprintf("%d Blank floor name not allowed.\n",
2162                         ERROR + ILLEGAL_VALUE);
2163                 return;
2164         }
2165
2166         for (a = 0; a < MAXFLOORS; ++a) {
2167                 CtdlGetFloor(&flbuf, a);
2168
2169                 /* note any free slots while we're scanning... */
2170                 if (((flbuf.f_flags & F_INUSE) == 0)
2171                     && (free_slot < 0))
2172                         free_slot = a;
2173
2174                 /* check to see if it already exists */
2175                 if ((!strcasecmp(flbuf.f_name, new_floor_name))
2176                     && (flbuf.f_flags & F_INUSE)) {
2177                         cprintf("%d Floor '%s' already exists.\n",
2178                                 ERROR + ALREADY_EXISTS,
2179                                 flbuf.f_name);
2180                         return;
2181                 }
2182         }
2183
2184         if (free_slot < 0) {
2185                 cprintf("%d There is no space available for a new floor.\n",
2186                         ERROR + INVALID_FLOOR_OPERATION);
2187                 return;
2188         }
2189         if (cflr_ok == 0) {
2190                 cprintf("%d ok to create...\n", CIT_OK);
2191                 return;
2192         }
2193         lgetfloor(&flbuf, free_slot);
2194         flbuf.f_flags = F_INUSE;
2195         flbuf.f_ref_count = 0;
2196         safestrncpy(flbuf.f_name, new_floor_name, sizeof flbuf.f_name);
2197         lputfloor(&flbuf, free_slot);
2198         cprintf("%d %d\n", CIT_OK, free_slot);
2199 }
2200
2201
2202
2203 /*
2204  * delete a floor
2205  */
2206 void cmd_kflr(char *argbuf)
2207 {
2208         struct floor flbuf;
2209         int floor_to_delete;
2210         int kflr_ok;
2211         int delete_ok;
2212
2213         floor_to_delete = extract_int(argbuf, 0);
2214         kflr_ok = extract_int(argbuf, 1);
2215
2216         if (CtdlAccessCheck(ac_aide)) return;
2217
2218         lgetfloor(&flbuf, floor_to_delete);
2219
2220         delete_ok = 1;
2221         if ((flbuf.f_flags & F_INUSE) == 0) {
2222                 cprintf("%d Floor %d not in use.\n",
2223                         ERROR + INVALID_FLOOR_OPERATION, floor_to_delete);
2224                 delete_ok = 0;
2225         } else {
2226                 if (flbuf.f_ref_count != 0) {
2227                         cprintf("%d Cannot delete; floor contains %d rooms.\n",
2228                                 ERROR + INVALID_FLOOR_OPERATION,
2229                                 flbuf.f_ref_count);
2230                         delete_ok = 0;
2231                 } else {
2232                         if (kflr_ok == 1) {
2233                                 cprintf("%d Ok\n", CIT_OK);
2234                         } else {
2235                                 cprintf("%d Ok to delete...\n", CIT_OK);
2236                         }
2237
2238                 }
2239
2240         }
2241
2242         if ((delete_ok == 1) && (kflr_ok == 1))
2243                 flbuf.f_flags = 0;
2244         lputfloor(&flbuf, floor_to_delete);
2245 }
2246
2247 /*
2248  * edit a floor
2249  */
2250 void cmd_eflr(char *argbuf)
2251 {
2252         struct floor flbuf;
2253         int floor_num;
2254         int np;
2255
2256         np = num_parms(argbuf);
2257         if (np < 1) {
2258                 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
2259                 return;
2260         }
2261
2262         if (CtdlAccessCheck(ac_aide)) return;
2263
2264         floor_num = extract_int(argbuf, 0);
2265         lgetfloor(&flbuf, floor_num);
2266         if ((flbuf.f_flags & F_INUSE) == 0) {
2267                 lputfloor(&flbuf, floor_num);
2268                 cprintf("%d Floor %d is not in use.\n",
2269                         ERROR + INVALID_FLOOR_OPERATION, floor_num);
2270                 return;
2271         }
2272         if (np >= 2)
2273                 extract_token(flbuf.f_name, argbuf, 1, '|', sizeof flbuf.f_name);
2274         lputfloor(&flbuf, floor_num);
2275
2276         cprintf("%d Ok\n", CIT_OK);
2277 }
2278
2279
2280 /*****************************************************************************/
2281 /*                      MODULE INITIALIZATION STUFF                          */
2282 /*****************************************************************************/
2283
2284 CTDL_MODULE_INIT(room_ops)
2285 {
2286         if (!threading) {
2287                 CtdlRegisterProtoHook(cmd_lrms, "LRMS", "List rooms");
2288                 CtdlRegisterProtoHook(cmd_lkra, "LKRA", "List all known rooms");
2289                 CtdlRegisterProtoHook(cmd_lkrn, "LKRN", "List known rooms with new messages");
2290                 CtdlRegisterProtoHook(cmd_lkro, "LKRO", "List known rooms without new messages");
2291                 CtdlRegisterProtoHook(cmd_lzrm, "LZRM", "List zapped rooms");
2292                 CtdlRegisterProtoHook(cmd_lprm, "LPRM", "List public rooms");
2293                 CtdlRegisterProtoHook(cmd_goto, "GOTO", "Goto a named room");
2294                 CtdlRegisterProtoHook(cmd_whok, "WHOK", "List users who know this room");
2295                 CtdlRegisterProtoHook(cmd_rdir, "RDIR", "List files in room directory");
2296                 CtdlRegisterProtoHook(cmd_getr, "GETR", "Get room parameters");
2297                 CtdlRegisterProtoHook(cmd_setr, "SETR", "Set room parameters");
2298                 CtdlRegisterProtoHook(cmd_geta, "GETA", "Get the room aide name");
2299                 CtdlRegisterProtoHook(cmd_seta, "SETA", "Set the room aide for this room");
2300                 CtdlRegisterProtoHook(cmd_rinf, "RINF", "Fetch room info file");
2301                 CtdlRegisterProtoHook(cmd_kill, "KILL", "Kill (delete) the current room");
2302                 CtdlRegisterProtoHook(cmd_cre8, "CRE8", "Create a new room");
2303                 CtdlRegisterProtoHook(cmd_einf, "EINF", "Enter info file for the current room");
2304                 CtdlRegisterProtoHook(cmd_lflr, "LFLR", "List all known floors");
2305                 CtdlRegisterProtoHook(cmd_cflr, "CFLR", "Create a new floor");
2306                 CtdlRegisterProtoHook(cmd_kflr, "KFLR", "Kill a floor");
2307                 CtdlRegisterProtoHook(cmd_eflr, "EFLR", "Edit a floor");
2308         }
2309         /* return our Subversion id for the Log */
2310         return "room_ops";
2311 }