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