]> code.citadel.org Git - citadel.git/blob - citadel/room_ops.c
Continued replacing references to [get_put]_msglist() with better code.
[citadel.git] / citadel / room_ops.c
1 /* $Id$ */
2 #include "sysdep.h"
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <stdio.h>
6 #include <sys/stat.h>
7 #include <string.h>
8 #ifdef HAVE_PTHREAD_H
9 #include <pthread.h>
10 #endif
11 #include <time.h>
12 #include <limits.h>
13 #include <errno.h>
14 #include "citadel.h"
15 #include "server.h"
16 #include "database.h"
17 #include "config.h"
18 #include "room_ops.h"
19 #include "sysdep_decls.h"
20 #include "support.h"
21 #include "user_ops.h"
22 #include "msgbase.h"
23 #include "serv_chat.h"
24 #include "citserver.h"
25 #include "control.h"
26 #include "tools.h"
27
28 /*
29  * Generic routine for determining user access to rooms
30  */
31 int CtdlRoomAccess(struct quickroom *roombuf, struct usersupp *userbuf)
32 {
33         int retval = 0;
34         struct visit vbuf;
35
36         /* for internal programs, always do everything */
37         if (((CC->internal_pgm)) && (roombuf->QRflags & QR_INUSE)) {
38                 return (UA_KNOWN | UA_GOTOALLOWED);
39         }
40         /* For mailbox rooms, only allow access to the owner */
41         if (roombuf->QRflags & QR_MAILBOX) {
42                 if (userbuf->usernum != atol(roombuf->QRname)) {
43                         return (retval);
44                 }
45         }
46         /* Locate any applicable user/room relationships */
47         CtdlGetRelationship(&vbuf, userbuf, roombuf);
48
49         /* Force the properties of the Aide room */
50         if (!strcasecmp(roombuf->QRname, AIDEROOM)) {
51                 if (userbuf->axlevel >= 6) {
52                         retval = UA_KNOWN | UA_GOTOALLOWED;
53                 } else {
54                         retval = 0;
55                 }
56                 goto NEWMSG;
57         }
58         /* For mailboxes, we skip all the access stuff (and we've
59          * already checked by this point that the mailbox belongs
60          * to the user)
61          */
62         if (roombuf->QRflags & QR_MAILBOX) {
63                 retval = UA_KNOWN | UA_GOTOALLOWED;
64                 goto NEWMSG;
65         }
66         /* If this is a public room, it's accessible... */
67         if ((roombuf->QRflags & QR_PRIVATE) == 0) {
68                 retval = retval | UA_KNOWN | UA_GOTOALLOWED;
69         }
70         /* If this is a preferred users only room, check access level */
71         if (roombuf->QRflags & QR_PREFONLY) {
72                 if (userbuf->axlevel < 5) {
73                         retval = retval & ~UA_KNOWN & ~UA_GOTOALLOWED;
74                 }
75         }
76         /* For private rooms, check the generation number matchups */
77         if (roombuf->QRflags & QR_PRIVATE) {
78
79                 /* An explicit match means the user belongs in this room */
80                 if (vbuf.v_flags & V_ACCESS) {
81                         retval = retval | UA_KNOWN | UA_GOTOALLOWED;
82                 }
83                 /* Otherwise, check if this is a guess-name or passworded
84                  * room.  If it is, a goto may at least be attempted
85                  */
86                 else if ((roombuf->QRflags & QR_PRIVATE)
87                          || (roombuf->QRflags & QR_PASSWORDED)) {
88                         retval = retval & ~UA_KNOWN;
89                         retval = retval | UA_GOTOALLOWED;
90                 }
91         }
92         /* Check to see if the user has forgotten this room */
93         if (vbuf.v_flags & V_FORGET) {
94                 retval = retval & ~UA_KNOWN;
95                 retval = retval | UA_ZAPPED;
96         }
97         /* If user is explicitly locked out of this room, deny everything */
98         if (vbuf.v_flags & V_LOCKOUT) {
99                 retval = retval & ~UA_KNOWN & ~UA_GOTOALLOWED;
100         }
101         /* Aides get access to everything */
102         if (userbuf->axlevel >= 6) {
103                 retval = retval | UA_KNOWN | UA_GOTOALLOWED;
104                 retval = retval & ~UA_ZAPPED;
105         }
106       NEWMSG:                   /* By the way, we also check for the presence of new messages */
107         if ((roombuf->QRhighest) > (vbuf.v_lastseen)) {
108                 retval = retval | UA_HASNEWMSGS;
109         }
110         return (retval);
111 }
112
113 /*
114  * Self-checking stuff for a room record read into memory
115  */
116 void room_sanity_check(struct quickroom *qrbuf)
117 {
118         /* Mailbox rooms are always on the lowest floor */
119         if (qrbuf->QRflags & QR_MAILBOX) {
120                 qrbuf->QRfloor = 0;
121         }
122         /* Listing order of 0 is illegal except for base rooms */
123         if (qrbuf->QRorder == 0)
124                 if (!is_noneditable(qrbuf))
125                         qrbuf->QRorder = 64;
126 }
127
128
129 /*
130  * getroom()  -  retrieve room data from disk
131  */
132 int getroom(struct quickroom *qrbuf, char *room_name)
133 {
134         struct cdbdata *cdbqr;
135         char lowercase_name[ROOMNAMELEN];
136         char personal_lowercase_name[ROOMNAMELEN];
137         int a;
138
139         for (a = 0; room_name[a] && a < sizeof lowercase_name - 1; ++a) {
140                 lowercase_name[a] = tolower(room_name[a]);
141         }
142         lowercase_name[a] = 0;
143
144         memset(qrbuf, 0, sizeof(struct quickroom));
145
146         /* First, try the public namespace */
147         cdbqr = cdb_fetch(CDB_QUICKROOM,
148                           lowercase_name, strlen(lowercase_name));
149
150         /* If that didn't work, try the user's personal namespace */
151         if (cdbqr == NULL) {
152                 sprintf(personal_lowercase_name, "%010ld.%s",
153                         CC->usersupp.usernum, lowercase_name);
154                 cdbqr = cdb_fetch(CDB_QUICKROOM,
155                                   personal_lowercase_name,
156                                   strlen(personal_lowercase_name));
157         }
158         if (cdbqr != NULL) {
159                 memcpy(qrbuf, cdbqr->ptr,
160                        ((cdbqr->len > sizeof(struct quickroom)) ?
161                         sizeof(struct quickroom) : cdbqr->len));
162                 cdb_free(cdbqr);
163
164                 room_sanity_check(qrbuf);
165
166                 return (0);
167         } else {
168                 return (1);
169         }
170 }
171
172 /*
173  * lgetroom()  -  same as getroom() but locks the record (if supported)
174  */
175 int lgetroom(struct quickroom *qrbuf, char *room_name)
176 {
177         begin_critical_section(S_QUICKROOM);
178         return (getroom(qrbuf, room_name));
179 }
180
181
182 /*
183  * b_putroom()  -  back end to putroom() and b_deleteroom()
184  *              (if the supplied buffer is NULL, delete the room record)
185  */
186 void b_putroom(struct quickroom *qrbuf, char *room_name)
187 {
188         char lowercase_name[ROOMNAMELEN];
189         int a;
190
191         for (a = 0; a <= strlen(room_name); ++a) {
192                 lowercase_name[a] = tolower(room_name[a]);
193         }
194
195         if (qrbuf == NULL) {
196                 cdb_delete(CDB_QUICKROOM,
197                            lowercase_name, strlen(lowercase_name));
198         } else {
199                 time(&qrbuf->QRmtime);
200                 cdb_store(CDB_QUICKROOM,
201                           lowercase_name, strlen(lowercase_name),
202                           qrbuf, sizeof(struct quickroom));
203         }
204 }
205
206
207 /* 
208  * putroom()  -  store room data to disk
209  */
210 void putroom(struct quickroom *qrbuf) {
211         b_putroom(qrbuf, qrbuf->QRname);
212 }
213
214
215 /*
216  * b_deleteroom()  -  delete a room record from disk
217  */
218 void b_deleteroom(char *room_name) {
219         b_putroom(NULL, room_name);
220 }
221
222
223
224 /*
225  * lputroom()  -  same as putroom() but unlocks the record (if supported)
226  */
227 void lputroom(struct quickroom *qrbuf)
228 {
229
230         putroom(qrbuf);
231         end_critical_section(S_QUICKROOM);
232
233 }
234
235 /****************************************************************************/
236
237 /*
238  * getfloor()  -  retrieve floor data from disk
239  */
240 void getfloor(struct floor *flbuf, int floor_num)
241 {
242         struct cdbdata *cdbfl;
243
244         memset(flbuf, 0, sizeof(struct floor));
245         cdbfl = cdb_fetch(CDB_FLOORTAB, &floor_num, sizeof(int));
246         if (cdbfl != NULL) {
247                 memcpy(flbuf, cdbfl->ptr,
248                        ((cdbfl->len > sizeof(struct floor)) ?
249                         sizeof(struct floor) : cdbfl->len));
250                 cdb_free(cdbfl);
251         } else {
252                 if (floor_num == 0) {
253                         strcpy(flbuf->f_name, "Main Floor");
254                         flbuf->f_flags = F_INUSE;
255                         flbuf->f_ref_count = 3;
256                 }
257         }
258
259 }
260
261 /*
262  * lgetfloor()  -  same as getfloor() but locks the record (if supported)
263  */
264 void lgetfloor(struct floor *flbuf, int floor_num)
265 {
266
267         begin_critical_section(S_FLOORTAB);
268         getfloor(flbuf, floor_num);
269 }
270
271
272 /*
273  * putfloor()  -  store floor data on disk
274  */
275 void putfloor(struct floor *flbuf, int floor_num)
276 {
277         cdb_store(CDB_FLOORTAB, &floor_num, sizeof(int),
278                   flbuf, sizeof(struct floor));
279 }
280
281
282 /*
283  * lputfloor()  -  same as putfloor() but unlocks the record (if supported)
284  */
285 void lputfloor(struct floor *flbuf, int floor_num)
286 {
287
288         putfloor(flbuf, floor_num);
289         end_critical_section(S_FLOORTAB);
290
291 }
292
293
294 /* 
295  *  Traverse the room file...
296  */
297 void ForEachRoom(void (*CallBack) (struct quickroom * EachRoom))
298 {
299         struct quickroom qrbuf;
300         struct cdbdata *cdbqr;
301
302         cdb_rewind(CDB_QUICKROOM);
303
304         while (cdbqr = cdb_next_item(CDB_QUICKROOM), cdbqr != NULL) {
305                 memset(&qrbuf, 0, sizeof(struct quickroom));
306                 memcpy(&qrbuf, cdbqr->ptr,
307                        ((cdbqr->len > sizeof(struct quickroom)) ?
308                         sizeof(struct quickroom) : cdbqr->len));
309                 cdb_free(cdbqr);
310                 room_sanity_check(&qrbuf);
311                 if (qrbuf.QRflags & QR_INUSE)
312                         (*CallBack) (&qrbuf);
313         }
314 }
315
316
317
318 /*
319  * get_msglist()  -  retrieve room message pointers
320  */
321 void get_msglist(struct quickroom *whichroom)
322 {
323         struct cdbdata *cdbfr;
324
325         if (CC->msglist != NULL) {
326                 phree(CC->msglist);
327         }
328         CC->msglist = NULL;
329         CC->num_msgs = 0;
330
331         cdbfr = cdb_fetch(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
332         if (cdbfr == NULL) {
333                 return;
334         }
335         CC->msglist = mallok(cdbfr->len);
336         memcpy(CC->msglist, cdbfr->ptr, cdbfr->len);
337         CC->num_msgs = cdbfr->len / sizeof(long);
338         cdb_free(cdbfr);
339 }
340
341
342 /*
343  * put_msglist()  -  retrieve room message pointers
344  */
345 void put_msglist(struct quickroom *whichroom)
346 {
347
348         if (CC->msglist != NULL)
349                 cdb_store(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long),
350                           CC->msglist, CC->num_msgs * sizeof(long));
351 }
352
353
354 /*
355  * delete_msglist()  -  delete room message pointers
356  * FIX - this really should check first to make sure there's actually a
357  *       msglist to delete.  As things stand now, calling this function on
358  *       a room which has never been posted in will result in a message
359  *       like "gdbm: illegal data" (no big deal, but could use fixing).
360  */
361 void delete_msglist(struct quickroom *whichroom)
362 {
363
364         cdb_delete(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
365 }
366
367
368 /* 
369  * Add a message number to a room's message list.  
370  *
371  * This function returns the highest message number present in the room after
372  * the add operation is performed - which is not necessarily the message
373  * being added.
374  */
375 long AddMessageToRoom(struct quickroom *whichroom, long newmsgid)
376 {
377         struct cdbdata *cdbfr;
378         int num_msgs;
379         long *msglist;
380         long highest_msg = 0L;
381
382         lprintf(9, "AddMessageToRoom(%s, %ld)\n", whichroom->QRname, newmsgid);
383         cdbfr = cdb_fetch(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long));
384         if (cdbfr == NULL) {
385                 msglist = NULL;
386                 num_msgs = 0;
387         } else {
388                 msglist = mallok(cdbfr->len);
389                 if (msglist == NULL)
390                         lprintf(3, "ERROR malloc msglist!\n");
391                 num_msgs = cdbfr->len / sizeof(long);
392                 memcpy(msglist, cdbfr->ptr, cdbfr->len);
393                 cdb_free(cdbfr);
394         }
395
396         /* Now add the new message */
397         ++num_msgs;
398         msglist = reallok(msglist,
399                           (num_msgs * sizeof(long)));
400
401         if (msglist == NULL) {
402                 lprintf(3, "ERROR: can't realloc message list!\n");
403         }
404         msglist[num_msgs - 1] = newmsgid;
405
406         /* Sort the message list, so all the msgid's are in order */
407         num_msgs = sort_msglist(msglist, num_msgs);
408
409         /* Determine the highest message number */
410         highest_msg = msglist[num_msgs - 1];
411
412         /* Write it back to disk. */
413         cdb_store(CDB_MSGLISTS, &whichroom->QRnumber, sizeof(long),
414                   msglist, num_msgs * sizeof(long));
415
416         /* And finally, free up the memory we used. */
417         phree(msglist);
418         return (highest_msg);
419 }
420
421
422 /*
423  * MessageFromList()  -  get a message number from the list currently in memory
424  */
425 long MessageFromList(int whichpos)
426 {
427
428         /* Return zero if the position is invalid */
429         if (whichpos >= CC->num_msgs)
430                 return 0L;
431
432         return (CC->msglist[whichpos]);
433 }
434
435 /* 
436  * SetMessageInList()  -  set a message number in the list currently in memory
437  */
438 void SetMessageInList(int whichpos, long newmsgnum)
439 {
440
441         /* Return zero if the position is invalid */
442         if (whichpos >= CC->num_msgs)
443                 return;
444
445         CC->msglist[whichpos] = newmsgnum;
446 }
447
448
449
450 /*
451  * sort message pointers
452  * (returns new msg count)
453  */
454 int sort_msglist(long listptrs[], int oldcount)
455 {
456         int a, b;
457         long hold1, hold2;
458         int numitems;
459
460         numitems = oldcount;
461         if (numitems < 2)
462                 return (oldcount);
463
464         /* do the sort */
465         for (a = numitems - 2; a >= 0; --a) {
466                 for (b = 0; b <= a; ++b) {
467                         if (listptrs[b] > (listptrs[b + 1])) {
468                                 hold1 = listptrs[b];
469                                 hold2 = listptrs[b + 1];
470                                 listptrs[b] = hold2;
471                                 listptrs[b + 1] = hold1;
472                         }
473                 }
474         }
475
476         /* and yank any nulls */
477         while ((numitems > 0) && (listptrs[0] == 0L)) {
478                 memcpy(&listptrs[0], &listptrs[1],
479                        (sizeof(long) * (CC->num_msgs - 1)));
480                 --numitems;
481         }
482
483         return (numitems);
484 }
485
486
487 /*
488  * Determine whether a given room is non-editable.
489  */
490 int is_noneditable(struct quickroom *qrbuf)
491 {
492
493         /* Lobby> and Aide> are non-editable */
494         if (!strcasecmp(qrbuf->QRname, BASEROOM))
495                 return (1);
496         else if (!strcasecmp(qrbuf->QRname, AIDEROOM))
497                 return (1);
498
499         /* Mail> rooms are also non-editable */
500         else if ( (qrbuf->QRflags & QR_MAILBOX)
501              && (!strcasecmp(&qrbuf->QRname[11], MAILROOM)) )
502                 return (1);
503
504         /* Everything else is editable */
505         else
506                 return (0);
507 }
508
509
510
511 /*
512  * Back-back-end for all room listing commands
513  */
514 void list_roomname(struct quickroom *qrbuf)
515 {
516         char truncated_roomname[ROOMNAMELEN];
517
518         /* For mailbox rooms, chop off the owner prefix */
519         if (qrbuf->QRflags & QR_MAILBOX) {
520                 strcpy(truncated_roomname, qrbuf->QRname);
521                 strcpy(truncated_roomname, &truncated_roomname[11]);
522                 cprintf("%s", truncated_roomname);
523         }
524         /* For all other rooms, just display the name in its entirety */
525         else {
526                 cprintf("%s", qrbuf->QRname);
527         }
528
529         /* ...and now the other parameters */
530         cprintf("|%u|%d|%d\n",
531                 qrbuf->QRflags,
532                 (int) qrbuf->QRfloor,
533                 (int) qrbuf->QRorder);
534 }
535
536
537 /* 
538  * cmd_lrms()   -  List all accessible rooms, known or forgotten
539  */
540 void cmd_lrms_backend(struct quickroom *qrbuf)
541 {
542         if (((CtdlRoomAccess(qrbuf, &CC->usersupp)
543               & (UA_KNOWN | UA_ZAPPED)))
544             && ((qrbuf->QRfloor == (CC->FloorBeingSearched))
545                 || ((CC->FloorBeingSearched) < 0)))
546                 list_roomname(qrbuf);
547 }
548
549 void cmd_lrms(char *argbuf)
550 {
551         CC->FloorBeingSearched = (-1);
552         if (strlen(argbuf) > 0)
553                 CC->FloorBeingSearched = extract_int(argbuf, 0);
554
555         if (!(CC->logged_in)) {
556                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
557                 return;
558         }
559         if (getuser(&CC->usersupp, CC->curr_user)) {
560                 cprintf("%d Can't locate user!\n", ERROR + INTERNAL_ERROR);
561                 return;
562         }
563         cprintf("%d Accessible rooms:\n", LISTING_FOLLOWS);
564
565         ForEachRoom(cmd_lrms_backend);
566         cprintf("000\n");
567 }
568
569
570
571 /* 
572  * cmd_lkra()   -  List all known rooms
573  */
574 void cmd_lkra_backend(struct quickroom *qrbuf)
575 {
576         if (((CtdlRoomAccess(qrbuf, &CC->usersupp)
577               & (UA_KNOWN)))
578             && ((qrbuf->QRfloor == (CC->FloorBeingSearched))
579                 || ((CC->FloorBeingSearched) < 0)))
580                 list_roomname(qrbuf);
581 }
582
583 void cmd_lkra(char *argbuf)
584 {
585         CC->FloorBeingSearched = (-1);
586         if (strlen(argbuf) > 0)
587                 CC->FloorBeingSearched = extract_int(argbuf, 0);
588
589         if (!(CC->logged_in)) {
590                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
591                 return;
592         }
593         if (getuser(&CC->usersupp, CC->curr_user)) {
594                 cprintf("%d Can't locate user!\n", ERROR + INTERNAL_ERROR);
595                 return;
596         }
597         cprintf("%d Known rooms:\n", LISTING_FOLLOWS);
598
599         ForEachRoom(cmd_lkra_backend);
600         cprintf("000\n");
601 }
602
603
604
605 /* 
606  * cmd_lkrn()   -  List all known rooms with new messages
607  */
608 void cmd_lkrn_backend(struct quickroom *qrbuf)
609 {
610         int ra;
611
612         ra = CtdlRoomAccess(qrbuf, &CC->usersupp);
613         if ((ra & UA_KNOWN)
614             && (ra & UA_HASNEWMSGS)
615             && ((qrbuf->QRfloor == (CC->FloorBeingSearched))
616                 || ((CC->FloorBeingSearched) < 0)))
617                 list_roomname(qrbuf);
618 }
619
620 void cmd_lkrn(char *argbuf)
621 {
622         CC->FloorBeingSearched = (-1);
623         if (strlen(argbuf) > 0)
624                 CC->FloorBeingSearched = extract_int(argbuf, 0);
625
626         if (!(CC->logged_in)) {
627                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
628                 return;
629         }
630         if (getuser(&CC->usersupp, CC->curr_user)) {
631                 cprintf("%d Can't locate user!\n", ERROR + INTERNAL_ERROR);
632                 return;
633         }
634         cprintf("%d Rooms w/ new msgs:\n", LISTING_FOLLOWS);
635
636         ForEachRoom(cmd_lkrn_backend);
637         cprintf("000\n");
638 }
639
640
641
642 /* 
643  * cmd_lkro()   -  List all known rooms
644  */
645 void cmd_lkro_backend(struct quickroom *qrbuf)
646 {
647         int ra;
648
649         ra = CtdlRoomAccess(qrbuf, &CC->usersupp);
650         if ((ra & UA_KNOWN)
651             && ((ra & UA_HASNEWMSGS) == 0)
652             && ((qrbuf->QRfloor == (CC->FloorBeingSearched))
653                 || ((CC->FloorBeingSearched) < 0)))
654                 list_roomname(qrbuf);
655 }
656
657 void cmd_lkro(char *argbuf)
658 {
659         CC->FloorBeingSearched = (-1);
660         if (strlen(argbuf) > 0)
661                 CC->FloorBeingSearched = extract_int(argbuf, 0);
662
663         if (!(CC->logged_in)) {
664                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
665                 return;
666         }
667         if (getuser(&CC->usersupp, CC->curr_user)) {
668                 cprintf("%d Can't locate user!\n", ERROR + INTERNAL_ERROR);
669                 return;
670         }
671         cprintf("%d Rooms w/o new msgs:\n", LISTING_FOLLOWS);
672
673         ForEachRoom(cmd_lkro_backend);
674         cprintf("000\n");
675 }
676
677
678
679 /* 
680  * cmd_lzrm()   -  List all forgotten rooms
681  */
682 void cmd_lzrm_backend(struct quickroom *qrbuf)
683 {
684         int ra;
685
686         ra = CtdlRoomAccess(qrbuf, &CC->usersupp);
687         if ((ra & UA_GOTOALLOWED)
688             && (ra & UA_ZAPPED)
689             && ((qrbuf->QRfloor == (CC->FloorBeingSearched))
690                 || ((CC->FloorBeingSearched) < 0)))
691                 list_roomname(qrbuf);
692 }
693
694 void cmd_lzrm(char *argbuf)
695 {
696         CC->FloorBeingSearched = (-1);
697         if (strlen(argbuf) > 0)
698                 CC->FloorBeingSearched = extract_int(argbuf, 0);
699
700         if (!(CC->logged_in)) {
701                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
702                 return;
703         }
704         if (getuser(&CC->usersupp, CC->curr_user)) {
705                 cprintf("%d Can't locate user!\n", ERROR + INTERNAL_ERROR);
706                 return;
707         }
708         cprintf("%d Zapped rooms:\n", LISTING_FOLLOWS);
709
710         ForEachRoom(cmd_lzrm_backend);
711         cprintf("000\n");
712 }
713
714
715
716 void usergoto(char *where, int display_result)
717 {
718         int a;
719         int new_messages = 0;
720         int total_messages = 0;
721         int info = 0;
722         int rmailflag;
723         int raideflag;
724         int newmailcount = 0;
725         struct visit vbuf;
726         char truncated_roomname[ROOMNAMELEN];
727
728         strcpy(CC->quickroom.QRname, where);
729         getroom(&CC->quickroom, where);
730
731         lgetuser(&CC->usersupp, CC->curr_user);
732         CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
733
734         /* Know the room ... but not if it's the page log room */
735         if (strcasecmp(where, config.c_logpages)) {
736                 vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
737                 vbuf.v_flags = vbuf.v_flags | V_ACCESS;
738         }
739         CtdlSetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
740         lputuser(&CC->usersupp);
741
742         /* check for new mail */
743         newmailcount = NewMailCount();
744
745         /* set info to 1 if the user needs to read the room's info file */
746         if (CC->quickroom.QRinfo > vbuf.v_lastseen)
747                 info = 1;
748
749         get_mm();
750         get_msglist(&CC->quickroom);
751         for (a = 0; a < CC->num_msgs; ++a) {
752                 if (MessageFromList(a) > 0L) {
753                         ++total_messages;
754                         if (MessageFromList(a) > vbuf.v_lastseen) {
755                                 ++new_messages;
756                         }
757                 }
758         }
759
760         if (CC->quickroom.QRflags & QR_MAILBOX)
761                 rmailflag = 1;
762         else
763                 rmailflag = 0;
764
765         if ((CC->quickroom.QRroomaide == CC->usersupp.usernum)
766             || (CC->usersupp.axlevel >= 6))
767                 raideflag = 1;
768         else
769                 raideflag = 0;
770
771         strcpy(truncated_roomname, CC->quickroom.QRname);
772         if (CC->quickroom.QRflags & QR_MAILBOX) {
773                 strcpy(truncated_roomname, &truncated_roomname[11]);
774         }
775
776         if (display_result)
777                 cprintf("%d%c%s|%d|%d|%d|%d|%ld|%ld|%d|%d|%d|%d\n",
778                         OK, check_express(),
779                         truncated_roomname,
780                         new_messages, total_messages,
781                         info, CC->quickroom.QRflags,
782                         CC->quickroom.QRhighest,
783                         vbuf.v_lastseen,
784                         rmailflag, raideflag, newmailcount,
785                         CC->quickroom.QRfloor);
786
787         set_wtmpsupp_to_current_room();
788 }
789
790
791 /* 
792  * cmd_goto()  -  goto a new room
793  */
794 void cmd_goto(char *gargs)
795 {
796         struct quickroom QRscratch;
797         int c;
798         int ok = 0;
799         int ra;
800         char augmented_roomname[256];
801         char towhere[256];
802         char password[256];
803
804         if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
805                 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
806                 return;
807         }
808
809         extract(towhere, gargs, 0);
810         extract(password, gargs, 1);
811
812         getuser(&CC->usersupp, CC->curr_user);
813
814         if (!strcasecmp(towhere, "_BASEROOM_"))
815                 strcpy(towhere, BASEROOM);
816
817         if (!strcasecmp(towhere, "_MAIL_"))
818                 strcpy(towhere, MAILROOM);
819
820         if (!strcasecmp(towhere, "_BITBUCKET_"))
821                 strcpy(towhere, config.c_twitroom);
822
823
824         /* First try a regular match */
825         c = getroom(&QRscratch, towhere);
826
827         /* Then try a mailbox name match */
828         if (c != 0) {
829                 MailboxName(augmented_roomname, &CC->usersupp, towhere);
830                 c = getroom(&QRscratch, augmented_roomname);
831                 if (c == 0)
832                         strcpy(towhere, augmented_roomname);
833         }
834
835         /* And if the room was found... */
836         if (c == 0) {
837
838                 /* let internal programs go directly to any room */
839                 if (CC->internal_pgm) {
840                         usergoto(towhere, 1);
841                         return;
842                 }
843
844                 /* See if there is an existing user/room relationship */
845                 ra = CtdlRoomAccess(&QRscratch, &CC->usersupp);
846
847                 /* normal clients have to pass through security */
848                 if (ra & UA_GOTOALLOWED)
849                         ok = 1;
850
851                 if (ok == 1) {
852                         if ((QRscratch.QRflags & QR_PASSWORDED) &&
853                             ((ra & UA_KNOWN) == 0) &&
854                             (strcasecmp(QRscratch.QRpasswd, password))
855                             ) {
856                                 cprintf("%d wrong or missing passwd\n",
857                                         ERROR + PASSWORD_REQUIRED);
858                                 return;
859                         } else if ((QRscratch.QRflags & QR_PRIVATE) &&
860                                    ((QRscratch.QRflags & QR_PASSWORDED) == 0) &&
861                                    ((QRscratch.QRflags & QR_GUESSNAME) == 0) &&
862                                    ((ra & UA_KNOWN) == 0)) {
863                                 goto NOPE;
864                         } else {
865                                 usergoto(towhere, 1);
866                                 return;
867                         }
868                 }
869         }
870
871 NOPE:   cprintf("%d room '%s' not found\n", ERROR + ROOM_NOT_FOUND, towhere);
872 }
873
874
875 void cmd_whok(void)
876 {
877         struct usersupp temp;
878         struct cdbdata *cdbus;
879
880         if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
881                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
882                 return;
883         }
884         getuser(&CC->usersupp, CC->curr_user);
885
886         if ((!is_room_aide()) && (!(CC->internal_pgm))) {
887                 cprintf("%d Higher access required.\n",
888                         ERROR + HIGHER_ACCESS_REQUIRED);
889                 return;
890         }
891         cprintf("%d Who knows room:\n", LISTING_FOLLOWS);
892         cdb_rewind(CDB_USERSUPP);
893         while (cdbus = cdb_next_item(CDB_USERSUPP), cdbus != NULL) {
894                 memset(&temp, 0, sizeof(struct usersupp));
895                 memcpy(&temp, cdbus->ptr, cdbus->len);
896                 cdb_free(cdbus);
897
898                 if ((CC->quickroom.QRflags & QR_INUSE)
899                     && (CtdlRoomAccess(&CC->quickroom, &temp) & UA_KNOWN)
900                     )
901                         cprintf("%s\n", temp.fullname);
902         }
903         cprintf("000\n");
904 }
905
906
907 /*
908  * RDIR command for room directory
909  */
910 void cmd_rdir(void)
911 {
912         char buf[256];
913         char flnm[256];
914         char comment[256];
915         FILE *ls, *fd;
916         struct stat statbuf;
917
918         if (!(CC->logged_in)) {
919                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
920                 return;
921         }
922         getroom(&CC->quickroom, CC->quickroom.QRname);
923         getuser(&CC->usersupp, CC->curr_user);
924
925         if ((CC->quickroom.QRflags & QR_DIRECTORY) == 0) {
926                 cprintf("%d not here.\n", ERROR + NOT_HERE);
927                 return;
928         }
929         if (((CC->quickroom.QRflags & QR_VISDIR) == 0)
930             && (CC->usersupp.axlevel < 6)
931             && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
932                 cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
933                 return;
934         }
935         cprintf("%d %s|%s/files/%s\n",
936         LISTING_FOLLOWS, config.c_fqdn, BBSDIR, CC->quickroom.QRdirname);
937
938         sprintf(buf, "cd %s/files/%s; ls >%s 2>/dev/null",
939                 BBSDIR, CC->quickroom.QRdirname, CC->temp);
940         system(buf);
941
942         sprintf(buf, "%s/files/%s/filedir", BBSDIR, CC->quickroom.QRdirname);
943         fd = fopen(buf, "r");
944         if (fd == NULL)
945                 fd = fopen("/dev/null", "r");
946
947         ls = fopen(CC->temp, "r");
948         while (fgets(flnm, 256, ls) != NULL) {
949                 flnm[strlen(flnm) - 1] = 0;
950                 if (strcasecmp(flnm, "filedir")) {
951                         sprintf(buf, "%s/files/%s/%s",
952                                 BBSDIR, CC->quickroom.QRdirname, flnm);
953                         stat(buf, &statbuf);
954                         strcpy(comment, "");
955                         fseek(fd, 0L, 0);
956                         while ((fgets(buf, 256, fd) != NULL)
957                                && (strlen(comment) == 0)) {
958                                 buf[strlen(buf) - 1] = 0;
959                                 if ((!strncasecmp(buf, flnm, strlen(flnm)))
960                                     && (buf[strlen(flnm)] == ' '))
961                                         strncpy(comment,
962                                             &buf[strlen(flnm) + 1], 255);
963                         }
964                         cprintf("%s|%ld|%s\n", flnm, statbuf.st_size, comment);
965                 }
966         }
967         fclose(ls);
968         fclose(fd);
969         unlink(CC->temp);
970
971         cprintf("000\n");
972 }
973
974 /*
975  * get room parameters (aide or room aide command)
976  */
977 void cmd_getr(void)
978 {
979         if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
980                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
981                 return;
982         }
983         if ((!is_room_aide()) && (!(CC->internal_pgm))) {
984                 cprintf("%d Higher access required.\n",
985                         ERROR + HIGHER_ACCESS_REQUIRED);
986                 return;
987         }
988         if (is_noneditable(&CC->quickroom)) {
989                 cprintf("%d Can't edit this room.\n", ERROR + NOT_HERE);
990                 return;
991         }
992         getroom(&CC->quickroom, CC->quickroom.QRname);
993         cprintf("%d%c%s|%s|%s|%d|%d|%d\n",
994                 OK, check_express(),
995                 CC->quickroom.QRname,
996                 ((CC->quickroom.QRflags & QR_PASSWORDED) ? CC->quickroom.QRpasswd : ""),
997                 ((CC->quickroom.QRflags & QR_DIRECTORY) ? CC->quickroom.QRdirname : ""),
998                 CC->quickroom.QRflags,
999                 (int) CC->quickroom.QRfloor,
1000                 (int) CC->quickroom.QRorder);
1001 }
1002
1003
1004 /*
1005  * set room parameters (aide or room aide command)
1006  */
1007 void cmd_setr(char *args)
1008 {
1009         char buf[256];
1010         struct floor flbuf;
1011         char old_name[ROOMNAMELEN];
1012         int old_floor;
1013         int new_order = 0;
1014
1015         if (!(CC->logged_in)) {
1016                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1017                 return;
1018         }
1019         if (!is_room_aide()) {
1020                 cprintf("%d Higher access required.\n",
1021                         ERROR + HIGHER_ACCESS_REQUIRED);
1022                 return;
1023         }
1024         if (is_noneditable(&CC->quickroom)) {
1025                 cprintf("%d Can't edit this room.\n", ERROR + NOT_HERE);
1026                 return;
1027         }
1028         if (num_parms(args) >= 6) {
1029                 getfloor(&flbuf, extract_int(args, 5));
1030                 if ((flbuf.f_flags & F_INUSE) == 0) {
1031                         cprintf("%d Invalid floor number.\n",
1032                                 ERROR + INVALID_FLOOR_OPERATION);
1033                         return;
1034                 }
1035         }
1036         if (num_parms(args) >= 7) {
1037                 new_order = extract_int(args, 6);
1038                 if (new_order < 1)
1039                         new_order = 1;
1040                 if (new_order > 127)
1041                         new_order = 127;
1042         }
1043         lgetroom(&CC->quickroom, CC->quickroom.QRname);
1044         strcpy(old_name, CC->quickroom.QRname);
1045         extract(buf, args, 0);
1046         buf[ROOMNAMELEN] = 0;
1047         strncpy(CC->quickroom.QRname, buf, ROOMNAMELEN - 1);
1048         extract(buf, args, 1);
1049         buf[10] = 0;
1050         strncpy(CC->quickroom.QRpasswd, buf, 9);
1051         extract(buf, args, 2);
1052         buf[15] = 0;
1053         strncpy(CC->quickroom.QRdirname, buf, 19);
1054         CC->quickroom.QRflags = (extract_int(args, 3) | QR_INUSE);
1055         if (num_parms(args) >= 7)
1056                 CC->quickroom.QRorder = (char) new_order;
1057
1058         /* Clean up a client boo-boo: if the client set the room to
1059          * guess-name or passworded, ensure that the private flag is
1060          * also set.
1061          */
1062         if ((CC->quickroom.QRflags & QR_GUESSNAME)
1063             || (CC->quickroom.QRflags & QR_PASSWORDED))
1064                 CC->quickroom.QRflags |= QR_PRIVATE;
1065
1066         /* Kick everyone out if the client requested it (by changing the
1067          * room's generation number)
1068          */
1069         if (extract_int(args, 4)) {
1070                 time(&CC->quickroom.QRgen);
1071         }
1072         old_floor = CC->quickroom.QRfloor;
1073         if (num_parms(args) >= 6) {
1074                 CC->quickroom.QRfloor = extract_int(args, 5);
1075         }
1076         /* Write the room record back to disk */
1077         lputroom(&CC->quickroom);
1078
1079         /* If the room name changed, then there are now two room records,
1080          * so we have to delete the old one.
1081          */
1082         if (strcasecmp(CC->quickroom.QRname, old_name)) {
1083                 b_deleteroom(old_name);
1084         }
1085         /* adjust the floor reference counts */
1086         lgetfloor(&flbuf, old_floor);
1087         --flbuf.f_ref_count;
1088         lputfloor(&flbuf, old_floor);
1089         lgetfloor(&flbuf, CC->quickroom.QRfloor);
1090         ++flbuf.f_ref_count;
1091         lputfloor(&flbuf, CC->quickroom.QRfloor);
1092
1093         /* create a room directory if necessary */
1094         if (CC->quickroom.QRflags & QR_DIRECTORY) {
1095                 sprintf(buf,
1096                     "mkdir ./files/%s </dev/null >/dev/null 2>/dev/null",
1097                         CC->quickroom.QRdirname);
1098                 system(buf);
1099         }
1100         sprintf(buf, "%s> edited by %s", CC->quickroom.QRname, CC->curr_user);
1101         aide_message(buf);
1102         cprintf("%d Ok\n", OK);
1103 }
1104
1105
1106
1107 /* 
1108  * get the name of the room aide for this room
1109  */
1110 void cmd_geta(void)
1111 {
1112         struct usersupp usbuf;
1113
1114         if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
1115                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1116                 return;
1117         }
1118         if (is_noneditable(&CC->quickroom)) {
1119                 cprintf("%d Can't edit this room.\n", ERROR + NOT_HERE);
1120                 return;
1121         }
1122         if (getuserbynumber(&usbuf, CC->quickroom.QRroomaide) == 0) {
1123                 cprintf("%d %s\n", OK, usbuf.fullname);
1124         } else {
1125                 cprintf("%d \n", OK);
1126         }
1127 }
1128
1129
1130 /* 
1131  * set the room aide for this room
1132  */
1133 void cmd_seta(char *new_ra)
1134 {
1135         struct usersupp usbuf;
1136         long newu;
1137         char buf[256];
1138         int post_notice;
1139
1140         if (!(CC->logged_in)) {
1141                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1142                 return;
1143         }
1144         if (!is_room_aide()) {
1145                 cprintf("%d Higher access required.\n",
1146                         ERROR + HIGHER_ACCESS_REQUIRED);
1147                 return;
1148         }
1149         if (getuser(&usbuf, new_ra) != 0) {
1150                 newu = (-1L);
1151         } else {
1152                 newu = usbuf.usernum;
1153         }
1154
1155         lgetroom(&CC->quickroom, CC->quickroom.QRname);
1156         post_notice = 0;
1157         if (CC->quickroom.QRroomaide != newu) {
1158                 post_notice = 1;
1159         }
1160         CC->quickroom.QRroomaide = newu;
1161         lputroom(&CC->quickroom);
1162
1163         /*
1164          * We have to post the change notice _after_ writing changes to 
1165          * the room table, otherwise it would deadlock!
1166          */
1167         if (post_notice == 1) {
1168                 sprintf(buf, "%s is now room aide for %s>",
1169                         usbuf.fullname, CC->quickroom.QRname);
1170                 aide_message(buf);
1171         }
1172         cprintf("%d Ok\n", OK);
1173 }
1174
1175 /*
1176  * Generate an associated file name for a room
1177  */
1178 void assoc_file_name(char *buf, struct quickroom *qrbuf, char *prefix)
1179 {
1180         sprintf(buf, "./%s/%ld", prefix, qrbuf->QRnumber);
1181 }
1182
1183 /* 
1184  * retrieve info file for this room
1185  */
1186 void cmd_rinf(void)
1187 {
1188         char filename[128];
1189         char buf[256];
1190         FILE *info_fp;
1191
1192         assoc_file_name(filename, &CC->quickroom, "info");
1193         info_fp = fopen(filename, "r");
1194
1195         if (info_fp == NULL) {
1196                 cprintf("%d No info file.\n", ERROR);
1197                 return;
1198         }
1199         cprintf("%d Info:\n", LISTING_FOLLOWS);
1200         while (fgets(buf, 256, info_fp) != NULL) {
1201                 if (strlen(buf) > 0)
1202                         buf[strlen(buf) - 1] = 0;
1203                 cprintf("%s\n", buf);
1204         }
1205         cprintf("000\n");
1206         fclose(info_fp);
1207 }
1208
1209 /*
1210  * Back end processing to delete a room and everything associated with it
1211  */
1212 void delete_room(struct quickroom *qrbuf)
1213 {
1214         struct floor flbuf;
1215         char filename[100];
1216
1217         lprintf(9, "Deleting room <%s>\n", qrbuf->QRname);
1218
1219         /* Delete the info file */
1220         assoc_file_name(filename, qrbuf, "info");
1221         unlink(filename);
1222
1223         /* Delete the image file */
1224         assoc_file_name(filename, qrbuf, "images");
1225         unlink(filename);
1226
1227         /* Delete the messages in the room
1228          * (Careful: this opens an S_QUICKROOM critical section!)
1229          */
1230         CtdlDeleteMessages(qrbuf->QRname, 0L, NULL);
1231
1232         /* Flag the room record as not in use */
1233         lgetroom(qrbuf, qrbuf->QRname);
1234         qrbuf->QRflags = 0;
1235         lputroom(qrbuf);
1236
1237         /* then decrement the reference count for the floor */
1238         lgetfloor(&flbuf, (int) (qrbuf->QRfloor));
1239         flbuf.f_ref_count = flbuf.f_ref_count - 1;
1240         lputfloor(&flbuf, (int) (qrbuf->QRfloor));
1241
1242         /* Delete the room record from the database! */
1243         b_deleteroom(qrbuf->QRname);
1244 }
1245
1246
1247 /*
1248  * aide command: kill the current room
1249  */
1250 void cmd_kill(char *argbuf)
1251 {
1252         char aaa[100];
1253         char deleted_room_name[ROOMNAMELEN];
1254         int kill_ok;
1255
1256         kill_ok = extract_int(argbuf, 0);
1257
1258         if (!(CC->logged_in)) {
1259                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1260                 return;
1261         }
1262         if (!is_room_aide()) {
1263                 cprintf("%d Higher access required.\n",
1264                         ERROR + HIGHER_ACCESS_REQUIRED);
1265                 return;
1266         }
1267         if (is_noneditable(&CC->quickroom)) {
1268                 cprintf("%d Can't edit this room.\n", ERROR + NOT_HERE);
1269                 return;
1270         }
1271         if (kill_ok) {
1272                 strcpy(deleted_room_name, CC->quickroom.QRname);
1273                 delete_room(&CC->quickroom);    /* Do the dirty work */
1274                 usergoto(BASEROOM, 0);  /* Return to the Lobby */
1275
1276                 /* tell the world what we did */
1277                 sprintf(aaa, "%s> killed by %s",
1278                         deleted_room_name, CC->curr_user);
1279                 aide_message(aaa);
1280                 cprintf("%d '%s' deleted.\n", OK, deleted_room_name);
1281         } else {
1282                 cprintf("%d ok to delete.\n", OK);
1283         }
1284 }
1285
1286
1287 /*
1288  * Internal code to create a new room (returns room flags)
1289  *
1290  * Room types:  0=public, 1=guessname, 2=passworded, 3=inv-only, 4=mailbox
1291  */
1292 unsigned create_room(char *new_room_name,
1293                      int new_room_type,
1294                      char *new_room_pass,
1295                      int new_room_floor)
1296 {
1297
1298         struct quickroom qrbuf;
1299         struct floor flbuf;
1300         struct visit vbuf;
1301
1302         if (getroom(&qrbuf, new_room_name) == 0)
1303                 return (0);     /* already exists */
1304
1305         memset(&qrbuf, 0, sizeof(struct quickroom));
1306         strncpy(qrbuf.QRname, new_room_name, ROOMNAMELEN);
1307         strncpy(qrbuf.QRpasswd, new_room_pass, 9);
1308         qrbuf.QRflags = QR_INUSE;
1309         qrbuf.QRnumber = get_new_room_number();
1310         if (new_room_type > 0)
1311                 qrbuf.QRflags = (qrbuf.QRflags | QR_PRIVATE);
1312         if (new_room_type == 1)
1313                 qrbuf.QRflags = (qrbuf.QRflags | QR_GUESSNAME);
1314         if (new_room_type == 2)
1315                 qrbuf.QRflags = (qrbuf.QRflags | QR_PASSWORDED);
1316         if (new_room_type == 4)
1317                 qrbuf.QRflags = (qrbuf.QRflags | QR_MAILBOX);
1318
1319         /* If the room is private, and the system administrator has elected
1320          * to automatically grant room aide privileges, do so now; otherwise,
1321          * set the room aide to undefined.
1322          */
1323         if ((qrbuf.QRflags & QR_PRIVATE) && (CREATAIDE == 1)) {
1324                 qrbuf.QRroomaide = CC->usersupp.usernum;
1325         } else {
1326                 qrbuf.QRroomaide = (-1L);
1327         }
1328
1329         qrbuf.QRhighest = 0L;   /* No messages in this room yet */
1330         time(&qrbuf.QRgen);     /* Use a timestamp as the generation number */
1331         qrbuf.QRfloor = new_room_floor;
1332
1333         /* save what we just did... */
1334         putroom(&qrbuf);
1335
1336         /* bump the reference count on whatever floor the room is on */
1337         lgetfloor(&flbuf, (int) qrbuf.QRfloor);
1338         flbuf.f_ref_count = flbuf.f_ref_count + 1;
1339         lputfloor(&flbuf, (int) qrbuf.QRfloor);
1340
1341         /* be sure not to kick the creator out of the room! */
1342         lgetuser(&CC->usersupp, CC->curr_user);
1343         CtdlGetRelationship(&vbuf, &CC->usersupp, &qrbuf);
1344         vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
1345         vbuf.v_flags = vbuf.v_flags | V_ACCESS;
1346         CtdlSetRelationship(&vbuf, &CC->usersupp, &qrbuf);
1347         lputuser(&CC->usersupp);
1348
1349         /* resume our happy day */
1350         return (qrbuf.QRflags);
1351 }
1352
1353
1354 /*
1355  * create a new room
1356  */
1357 void cmd_cre8(char *args)
1358 {
1359         int cre8_ok;
1360         char new_room_name[256];
1361         int new_room_type;
1362         char new_room_pass[256];
1363         int new_room_floor;
1364         char aaa[256];
1365         unsigned newflags;
1366         struct quickroom qrbuf;
1367         struct floor flbuf;
1368
1369         cre8_ok = extract_int(args, 0);
1370         extract(new_room_name, args, 1);
1371         new_room_name[ROOMNAMELEN - 1] = 0;
1372         new_room_type = extract_int(args, 2);
1373         extract(new_room_pass, args, 3);
1374         new_room_pass[9] = 0;
1375         new_room_floor = 0;
1376
1377         if ((strlen(new_room_name) == 0) && (cre8_ok == 1)) {
1378                 cprintf("%d Invalid room name.\n", ERROR);
1379                 return;
1380         }
1381
1382         if (!strcasecmp(new_room_name, MAILROOM)) {
1383                 cprintf("%d '%s' already exists.\n",
1384                         ERROR + ALREADY_EXISTS, new_room_name);
1385                 return;
1386         }
1387
1388         if (num_parms(args) >= 5) {
1389                 getfloor(&flbuf, extract_int(args, 4));
1390                 if ((flbuf.f_flags & F_INUSE) == 0) {
1391                         cprintf("%d Invalid floor number.\n",
1392                                 ERROR + INVALID_FLOOR_OPERATION);
1393                         return;
1394                 } else {
1395                         new_room_floor = extract_int(args, 4);
1396                 }
1397         }
1398
1399         if (!(CC->logged_in)) {
1400                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1401                 return;
1402         }
1403
1404         if (CC->usersupp.axlevel < config.c_createax) {
1405                 cprintf("%d You need higher access to create rooms.\n",
1406                         ERROR + HIGHER_ACCESS_REQUIRED);
1407                 return;
1408         }
1409
1410         if ((strlen(new_room_name) == 0) && (cre8_ok == 0)) {
1411                 cprintf("%d Ok to create rooms.\n", OK);
1412                 return;
1413         }
1414
1415         if ((new_room_type < 0) || (new_room_type > 4)) {
1416                 cprintf("%d Invalid room type.\n", ERROR);
1417                 return;
1418         }
1419
1420         /* If the user is requesting a personal room, set up the room
1421          * name accordingly (prepend the user number)
1422          */
1423         if (new_room_type == 4) {
1424                 sprintf(aaa, "%010ld.%s",
1425                         CC->usersupp.usernum, new_room_name);
1426                 strcpy(new_room_name, aaa);
1427         }
1428
1429         /* Check to make sure the requested room name doesn't already exist */
1430         if (getroom(&qrbuf, new_room_name) == 0) {
1431                 cprintf("%d '%s' already exists.\n",
1432                         ERROR + ALREADY_EXISTS, qrbuf.QRname);
1433                 return;
1434         }
1435
1436         if (cre8_ok == 0) {
1437                 cprintf("%d OK to create '%s'\n", OK, new_room_name);
1438                 return;
1439         }
1440
1441         newflags = create_room(new_room_name,
1442                            new_room_type, new_room_pass, new_room_floor);
1443
1444         /* post a message in Aide> describing the new room */
1445         strncpy(aaa, new_room_name, 255);
1446         strcat(aaa, "> created by ");
1447         strcat(aaa, CC->usersupp.fullname);
1448         if (newflags & QR_MAILBOX)
1449                 strcat(aaa, " [personal]");
1450         else if (newflags & QR_PRIVATE)
1451                 strcat(aaa, " [private]");
1452         if (newflags & QR_GUESSNAME)
1453                 strcat(aaa, "[guessname] ");
1454         if (newflags & QR_PASSWORDED) {
1455                 strcat(aaa, "\n Password: ");
1456                 strcat(aaa, new_room_pass);
1457         }
1458         aide_message(aaa);
1459
1460         cprintf("%d '%s' has been created.\n", OK, qrbuf.QRname);
1461 }
1462
1463
1464
1465 void cmd_einf(char *ok)
1466 {                               /* enter info file for current room */
1467         FILE *fp;
1468         char infofilename[256];
1469         char buf[256];
1470
1471         if (!(CC->logged_in)) {
1472                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1473                 return;
1474         }
1475         if (!is_room_aide()) {
1476                 cprintf("%d Higher access required.\n",
1477                         ERROR + HIGHER_ACCESS_REQUIRED);
1478                 return;
1479         }
1480         if (atoi(ok) == 0) {
1481                 cprintf("%d Ok.\n", OK);
1482                 return;
1483         }
1484         assoc_file_name(infofilename, &CC->quickroom, "info");
1485         lprintf(9, "opening\n");
1486         fp = fopen(infofilename, "w");
1487         lprintf(9, "checking\n");
1488         if (fp == NULL) {
1489                 cprintf("%d Cannot open %s: %s\n",
1490                   ERROR + INTERNAL_ERROR, infofilename, strerror(errno));
1491                 return;
1492         }
1493         cprintf("%d Send info...\n", SEND_LISTING);
1494
1495         do {
1496                 client_gets(buf);
1497                 if (strcmp(buf, "000"))
1498                         fprintf(fp, "%s\n", buf);
1499         } while (strcmp(buf, "000"));
1500         fclose(fp);
1501
1502         /* now update the room index so people will see our new info */
1503         lgetroom(&CC->quickroom, CC->quickroom.QRname);         /* lock so no one steps on us */
1504         CC->quickroom.QRinfo = CC->quickroom.QRhighest + 1L;
1505         lputroom(&CC->quickroom);
1506 }
1507
1508
1509 /* 
1510  * cmd_lflr()   -  List all known floors
1511  */
1512 void cmd_lflr(void)
1513 {
1514         int a;
1515         struct floor flbuf;
1516
1517         if (!(CC->logged_in)) {
1518                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1519                 return;
1520         }
1521         /* if (getuser(&CC->usersupp,CC->curr_user)) {
1522            cprintf("%d Can't locate user!\n",ERROR+INTERNAL_ERROR);
1523            return;
1524            }
1525          */
1526
1527         cprintf("%d Known floors:\n", LISTING_FOLLOWS);
1528
1529         for (a = 0; a < MAXFLOORS; ++a) {
1530                 getfloor(&flbuf, a);
1531                 if (flbuf.f_flags & F_INUSE) {
1532                         cprintf("%d|%s|%d\n",
1533                                 a,
1534                                 flbuf.f_name,
1535                                 flbuf.f_ref_count);
1536                 }
1537         }
1538         cprintf("000\n");
1539 }
1540
1541
1542
1543 /*
1544  * create a new floor
1545  */
1546 void cmd_cflr(char *argbuf)
1547 {
1548         char new_floor_name[256];
1549         struct floor flbuf;
1550         int cflr_ok;
1551         int free_slot = (-1);
1552         int a;
1553
1554         extract(new_floor_name, argbuf, 0);
1555         cflr_ok = extract_int(argbuf, 1);
1556
1557
1558         if (!(CC->logged_in)) {
1559                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1560                 return;
1561         }
1562         if (CC->usersupp.axlevel < 6) {
1563                 cprintf("%d You need higher access to create rooms.\n",
1564                         ERROR + HIGHER_ACCESS_REQUIRED);
1565                 return;
1566         }
1567         for (a = 0; a < MAXFLOORS; ++a) {
1568                 getfloor(&flbuf, a);
1569
1570                 /* note any free slots while we're scanning... */
1571                 if (((flbuf.f_flags & F_INUSE) == 0)
1572                     && (free_slot < 0))
1573                         free_slot = a;
1574
1575                 /* check to see if it already exists */
1576                 if ((!strcasecmp(flbuf.f_name, new_floor_name))
1577                     && (flbuf.f_flags & F_INUSE)) {
1578                         cprintf("%d Floor '%s' already exists.\n",
1579                                 ERROR + ALREADY_EXISTS,
1580                                 flbuf.f_name);
1581                         return;
1582                 }
1583         }
1584
1585         if (free_slot < 0) {
1586                 cprintf("%d There is no space available for a new floor.\n",
1587                         ERROR + INVALID_FLOOR_OPERATION);
1588                 return;
1589         }
1590         if (cflr_ok == 0) {
1591                 cprintf("%d ok to create...\n", OK);
1592                 return;
1593         }
1594         lgetfloor(&flbuf, free_slot);
1595         flbuf.f_flags = F_INUSE;
1596         flbuf.f_ref_count = 0;
1597         strncpy(flbuf.f_name, new_floor_name, 255);
1598         lputfloor(&flbuf, free_slot);
1599         cprintf("%d %d\n", OK, free_slot);
1600 }
1601
1602
1603
1604 /*
1605  * delete a floor
1606  */
1607 void cmd_kflr(char *argbuf)
1608 {
1609         struct floor flbuf;
1610         int floor_to_delete;
1611         int kflr_ok;
1612         int delete_ok;
1613
1614         floor_to_delete = extract_int(argbuf, 0);
1615         kflr_ok = extract_int(argbuf, 1);
1616
1617
1618         if (!(CC->logged_in)) {
1619                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1620                 return;
1621         }
1622         if (CC->usersupp.axlevel < 6) {
1623                 cprintf("%d You need higher access to delete floors.\n",
1624                         ERROR + HIGHER_ACCESS_REQUIRED);
1625                 return;
1626         }
1627         lgetfloor(&flbuf, floor_to_delete);
1628
1629         delete_ok = 1;
1630         if ((flbuf.f_flags & F_INUSE) == 0) {
1631                 cprintf("%d Floor %d not in use.\n",
1632                         ERROR + INVALID_FLOOR_OPERATION, floor_to_delete);
1633                 delete_ok = 0;
1634         } else {
1635                 if (flbuf.f_ref_count != 0) {
1636                         cprintf("%d Cannot delete; floor contains %d rooms.\n",
1637                                 ERROR + INVALID_FLOOR_OPERATION,
1638                                 flbuf.f_ref_count);
1639                         delete_ok = 0;
1640                 } else {
1641                         if (kflr_ok == 1) {
1642                                 cprintf("%d Ok\n", OK);
1643                         } else {
1644                                 cprintf("%d Ok to delete...\n", OK);
1645                         }
1646
1647                 }
1648
1649         }
1650
1651         if ((delete_ok == 1) && (kflr_ok == 1))
1652                 flbuf.f_flags = 0;
1653         lputfloor(&flbuf, floor_to_delete);
1654 }
1655
1656 /*
1657  * edit a floor
1658  */
1659 void cmd_eflr(char *argbuf)
1660 {
1661         struct floor flbuf;
1662         int floor_num;
1663         int np;
1664
1665         np = num_parms(argbuf);
1666         if (np < 1) {
1667                 cprintf("%d Usage error.\n", ERROR);
1668                 return;
1669         }
1670         if (!(CC->logged_in)) {
1671                 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1672                 return;
1673         }
1674         if (CC->usersupp.axlevel < 6) {
1675                 cprintf("%d You need higher access to edit floors.\n",
1676                         ERROR + HIGHER_ACCESS_REQUIRED);
1677                 return;
1678         }
1679         floor_num = extract_int(argbuf, 0);
1680         lgetfloor(&flbuf, floor_num);
1681         if ((flbuf.f_flags & F_INUSE) == 0) {
1682                 lputfloor(&flbuf, floor_num);
1683                 cprintf("%d Floor %d is not in use.\n",
1684                         ERROR + INVALID_FLOOR_OPERATION, floor_num);
1685                 return;
1686         }
1687         if (np >= 2)
1688                 extract(flbuf.f_name, argbuf, 1);
1689         lputfloor(&flbuf, floor_num);
1690
1691         cprintf("%d Ok\n", OK);
1692 }