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