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