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