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