cac7758f4ad255b20b996697334a4dee9fb8ff44
[citadel.git] / citadel / modules / ctdlproto / serv_rooms.c
1 /* 
2  * Server functions which perform operations on room objects.
3  *
4  * Copyright (c) 1987-2012 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include "sysdep.h"
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <sys/stat.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include <dirent.h>     /* for cmd_rdir to read contents of the directory */
23
24 #if TIME_WITH_SYS_TIME
25 # include <sys/time.h>
26 # include <time.h>
27 #else
28 # if HAVE_SYS_TIME_H
29 #  include <sys/time.h>
30 # else
31 #  include <time.h>
32 # endif
33 #endif
34
35 #include <limits.h>
36 #include <errno.h>
37 #include "citadel.h"
38 #include <libcitadel.h>
39 #include "server.h"
40 #include "database.h"
41 #include "config.h"
42 #include "room_ops.h"
43 #include "sysdep_decls.h"
44 #include "support.h"
45 #include "msgbase.h"
46 #include "citserver.h"
47 #include "control.h"
48 #include "citadel_dirs.h"
49 #include "threads.h"
50
51 #include "ctdl_module.h"
52 #include "user_ops.h"
53
54 /*
55  * Back-back-end for all room listing commands
56  */
57 void list_roomname(struct ctdlroom *qrbuf, int ra, int current_view, int default_view)
58 {
59         char truncated_roomname[ROOMNAMELEN];
60
61         /* For my own mailbox rooms, chop off the owner prefix */
62         if ( (qrbuf->QRflags & QR_MAILBOX)
63              && (atol(qrbuf->QRname) == CC->user.usernum) ) {
64                 safestrncpy(truncated_roomname, qrbuf->QRname, sizeof truncated_roomname);
65                 safestrncpy(truncated_roomname, &truncated_roomname[11], sizeof truncated_roomname);
66                 cprintf("%s", truncated_roomname);
67         }
68         /* For all other rooms, just display the name in its entirety */
69         else {
70                 cprintf("%s", qrbuf->QRname);
71         }
72
73         /* ...and now the other parameters */
74         cprintf("|%u|%d|%d|%d|%d|%d|%d|%ld|\n",
75                 qrbuf->QRflags,
76                 (int) qrbuf->QRfloor,
77                 (int) qrbuf->QRorder,
78                 (int) qrbuf->QRflags2,
79                 ra,
80                 current_view,
81                 default_view,
82                 qrbuf->QRmtime
83         );
84 }
85
86
87 /* 
88  * cmd_lrms()   -  List all accessible rooms, known or forgotten
89  */
90 void cmd_lrms_backend(struct ctdlroom *qrbuf, void *data)
91 {
92         int FloorBeingSearched = (-1);
93         int ra;
94         int view;
95
96         FloorBeingSearched = *(int *)data;
97         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
98
99         if ((( ra & (UA_KNOWN | UA_ZAPPED)))
100             && ((qrbuf->QRfloor == (FloorBeingSearched))
101                 || ((FloorBeingSearched) < 0)))
102                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
103 }
104
105 void cmd_lrms(char *argbuf)
106 {
107         int FloorBeingSearched = (-1);
108         if (!IsEmptyStr(argbuf))
109                 FloorBeingSearched = extract_int(argbuf, 0);
110
111         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
112
113         CtdlGetUser(&CC->user, CC->curr_user);
114         cprintf("%d Accessible rooms:\n", LISTING_FOLLOWS);
115
116         CtdlForEachRoom(cmd_lrms_backend, &FloorBeingSearched);
117         cprintf("000\n");
118 }
119
120
121
122 /* 
123  * cmd_lkra()   -  List all known rooms
124  */
125 void cmd_lkra_backend(struct ctdlroom *qrbuf, void *data)
126 {
127         int FloorBeingSearched = (-1);
128         int ra;
129         int view;
130
131         FloorBeingSearched = *(int *)data;
132         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
133
134         if ((( ra & (UA_KNOWN)))
135             && ((qrbuf->QRfloor == (FloorBeingSearched))
136                 || ((FloorBeingSearched) < 0)))
137                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
138 }
139
140 void cmd_lkra(char *argbuf)
141 {
142         int FloorBeingSearched = (-1);
143         if (!IsEmptyStr(argbuf))
144                 FloorBeingSearched = extract_int(argbuf, 0);
145
146         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
147         
148         CtdlGetUser(&CC->user, CC->curr_user);
149         cprintf("%d Known rooms:\n", LISTING_FOLLOWS);
150
151         CtdlForEachRoom(cmd_lkra_backend, &FloorBeingSearched);
152         cprintf("000\n");
153 }
154
155
156
157 void cmd_lprm_backend(struct ctdlroom *qrbuf, void *data)
158 {
159         int FloorBeingSearched = (-1);
160         int ra;
161         int view;
162
163         FloorBeingSearched = *(int *)data;
164         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
165
166         if (   ((qrbuf->QRflags & QR_PRIVATE) == 0)
167                 && ((qrbuf->QRflags & QR_MAILBOX) == 0)
168             && ((qrbuf->QRfloor == (FloorBeingSearched))
169                 || ((FloorBeingSearched) < 0)))
170                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
171 }
172
173 void cmd_lprm(char *argbuf)
174 {
175         int FloorBeingSearched = (-1);
176         if (!IsEmptyStr(argbuf))
177                 FloorBeingSearched = extract_int(argbuf, 0);
178
179         cprintf("%d Public rooms:\n", LISTING_FOLLOWS);
180
181         CtdlForEachRoom(cmd_lprm_backend, &FloorBeingSearched);
182         cprintf("000\n");
183 }
184
185
186
187 /* 
188  * cmd_lkrn()   -  List all known rooms with new messages
189  */
190 void cmd_lkrn_backend(struct ctdlroom *qrbuf, void *data)
191 {
192         int FloorBeingSearched = (-1);
193         int ra;
194         int view;
195
196         FloorBeingSearched = *(int *)data;
197         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
198
199         if ((ra & UA_KNOWN)
200             && (ra & UA_HASNEWMSGS)
201             && ((qrbuf->QRfloor == (FloorBeingSearched))
202                 || ((FloorBeingSearched) < 0)))
203                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
204 }
205
206 void cmd_lkrn(char *argbuf)
207 {
208         int FloorBeingSearched = (-1);
209         if (!IsEmptyStr(argbuf))
210                 FloorBeingSearched = extract_int(argbuf, 0);
211
212         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
213         
214         CtdlGetUser(&CC->user, CC->curr_user);
215         cprintf("%d Rooms w/ new msgs:\n", LISTING_FOLLOWS);
216
217         CtdlForEachRoom(cmd_lkrn_backend, &FloorBeingSearched);
218         cprintf("000\n");
219 }
220
221
222
223 /* 
224  * cmd_lkro()   -  List all known rooms
225  */
226 void cmd_lkro_backend(struct ctdlroom *qrbuf, void *data)
227 {
228         int FloorBeingSearched = (-1);
229         int ra;
230         int view;
231
232         FloorBeingSearched = *(int *)data;
233         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
234
235         if ((ra & UA_KNOWN)
236             && ((ra & UA_HASNEWMSGS) == 0)
237             && ((qrbuf->QRfloor == (FloorBeingSearched))
238                 || ((FloorBeingSearched) < 0)))
239                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
240 }
241
242 void cmd_lkro(char *argbuf)
243 {
244         int FloorBeingSearched = (-1);
245         if (!IsEmptyStr(argbuf))
246                 FloorBeingSearched = extract_int(argbuf, 0);
247
248         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
249         
250         CtdlGetUser(&CC->user, CC->curr_user);
251         cprintf("%d Rooms w/o new msgs:\n", LISTING_FOLLOWS);
252
253         CtdlForEachRoom(cmd_lkro_backend, &FloorBeingSearched);
254         cprintf("000\n");
255 }
256
257
258
259 /* 
260  * cmd_lzrm()   -  List all forgotten rooms
261  */
262 void cmd_lzrm_backend(struct ctdlroom *qrbuf, void *data)
263 {
264         int FloorBeingSearched = (-1);
265         int ra;
266         int view;
267
268         FloorBeingSearched = *(int *)data;
269         CtdlRoomAccess(qrbuf, &CC->user, &ra, &view);
270
271         if ((ra & UA_GOTOALLOWED)
272             && (ra & UA_ZAPPED)
273             && ((qrbuf->QRfloor == (FloorBeingSearched))
274                 || ((FloorBeingSearched) < 0)))
275                 list_roomname(qrbuf, ra, view, qrbuf->QRdefaultview);
276 }
277
278 void cmd_lzrm(char *argbuf)
279 {
280         int FloorBeingSearched = (-1);
281         if (!IsEmptyStr(argbuf))
282                 FloorBeingSearched = extract_int(argbuf, 0);
283
284         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
285         
286         CtdlGetUser(&CC->user, CC->curr_user);
287         cprintf("%d Zapped rooms:\n", LISTING_FOLLOWS);
288
289         CtdlForEachRoom(cmd_lzrm_backend, &FloorBeingSearched);
290         cprintf("000\n");
291 }
292
293
294 /* 
295  * cmd_goto()  -  goto a new room
296  */
297 void cmd_goto(char *gargs)
298 {
299         struct ctdlroom QRscratch;
300         int c;
301         int ok = 0;
302         int ra;
303         char augmented_roomname[ROOMNAMELEN];
304         char towhere[ROOMNAMELEN];
305         char password[32];
306         int transiently = 0;
307
308         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
309
310         extract_token(towhere, gargs, 0, '|', sizeof towhere);
311         extract_token(password, gargs, 1, '|', sizeof password);
312         transiently = extract_int(gargs, 2);
313
314         CtdlGetUser(&CC->user, CC->curr_user);
315
316         /*
317          * Handle some of the macro named rooms
318          */
319         convert_room_name_macros(towhere, sizeof towhere);
320
321         /* First try a regular match */
322         c = CtdlGetRoom(&QRscratch, towhere);
323
324         /* Then try a mailbox name match */
325         if (c != 0) {
326                 CtdlMailboxName(augmented_roomname, sizeof augmented_roomname,
327                             &CC->user, towhere);
328                 c = CtdlGetRoom(&QRscratch, augmented_roomname);
329                 if (c == 0)
330                         safestrncpy(towhere, augmented_roomname, sizeof towhere);
331         }
332
333         /* And if the room was found... */
334         if (c == 0) {
335
336                 /* Let internal programs go directly to any room. */
337                 if (CC->internal_pgm) {
338                         memcpy(&CC->room, &QRscratch,
339                                 sizeof(struct ctdlroom));
340                         CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
341                         return;
342                 }
343
344                 /* See if there is an existing user/room relationship */
345                 CtdlRoomAccess(&QRscratch, &CC->user, &ra, NULL);
346
347                 /* normal clients have to pass through security */
348                 if (ra & UA_GOTOALLOWED) {
349                         ok = 1;
350                 }
351
352                 if (ok == 1) {
353                         if ((QRscratch.QRflags & QR_MAILBOX) &&
354                             ((ra & UA_GOTOALLOWED))) {
355                                 memcpy(&CC->room, &QRscratch,
356                                         sizeof(struct ctdlroom));
357                                 CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
358                                 return;
359                         } else if ((QRscratch.QRflags & QR_PASSWORDED) &&
360                             ((ra & UA_KNOWN) == 0) &&
361                             (strcasecmp(QRscratch.QRpasswd, password)) &&
362                             (CC->user.axlevel < AxAideU)
363                             ) {
364                                 cprintf("%d wrong or missing passwd\n",
365                                         ERROR + PASSWORD_REQUIRED);
366                                 return;
367                         } else if ((QRscratch.QRflags & QR_PRIVATE) &&
368                                    ((QRscratch.QRflags & QR_PASSWORDED) == 0) &&
369                                    ((QRscratch.QRflags & QR_GUESSNAME) == 0) &&
370                                    ((ra & UA_KNOWN) == 0) &&
371                                    (CC->user.axlevel < AxAideU)
372                                   ) {
373                                 syslog(LOG_DEBUG, "Failed to acquire private room\n");
374                         } else {
375                                 memcpy(&CC->room, &QRscratch,
376                                         sizeof(struct ctdlroom));
377                                 CtdlUserGoto(NULL, 1, transiently, NULL, NULL);
378                                 return;
379                         }
380                 }
381         }
382
383         cprintf("%d room '%s' not found\n", ERROR + ROOM_NOT_FOUND, towhere);
384 }
385
386
387 void cmd_whok(char *cmdbuf)
388 {
389         struct ctdluser temp;
390         struct cdbdata *cdbus;
391         int ra;
392
393         cprintf("%d Who knows room:\n", LISTING_FOLLOWS);
394         cdb_rewind(CDB_USERS);
395         while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
396                 memset(&temp, 0, sizeof temp);
397                 memcpy(&temp, cdbus->ptr, sizeof temp);
398                 cdb_free(cdbus);
399
400                 CtdlRoomAccess(&CC->room, &temp, &ra, NULL);
401                 if ((!IsEmptyStr(temp.fullname)) && 
402                     (CC->room.QRflags & QR_INUSE) &&
403                     (ra & UA_KNOWN)
404                         )
405                         cprintf("%s\n", temp.fullname);
406         }
407         cprintf("000\n");
408 }
409
410
411 /*
412  * RDIR command for room directory
413  */
414 void cmd_rdir(char *cmdbuf)
415 {
416         char buf[256];
417         char comment[256];
418         FILE *fd;
419         struct stat statbuf;
420         DIR *filedir = NULL;
421         struct dirent *filedir_entry;
422         int d_namelen;
423         char buf2[SIZ];
424         char mimebuf[64];
425         long len;
426         
427         if (CtdlAccessCheck(ac_logged_in)) return;
428         
429         CtdlGetRoom(&CC->room, CC->room.QRname);
430         CtdlGetUser(&CC->user, CC->curr_user);
431
432         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
433                 cprintf("%d not here.\n", ERROR + NOT_HERE);
434                 return;
435         }
436         if (((CC->room.QRflags & QR_VISDIR) == 0)
437             && (CC->user.axlevel < AxAideU)
438             && (CC->user.usernum != CC->room.QRroomaide)) {
439                 cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
440                 return;
441         }
442
443         snprintf(buf, sizeof buf, "%s/%s", ctdl_file_dir, CC->room.QRdirname);
444         filedir = opendir (buf);
445         
446         if (filedir == NULL) {
447                 cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
448                 return;
449         }
450         cprintf("%d %s|%s/%s\n", LISTING_FOLLOWS, config.c_fqdn, ctdl_file_dir, CC->room.QRdirname);
451         
452         snprintf(buf, sizeof buf, "%s/%s/filedir", ctdl_file_dir, CC->room.QRdirname);
453         fd = fopen(buf, "r");
454         if (fd == NULL)
455                 fd = fopen("/dev/null", "r");
456         while ((filedir_entry = readdir(filedir)))
457         {
458                 if (strcasecmp(filedir_entry->d_name, "filedir") && filedir_entry->d_name[0] != '.')
459                 {
460 #ifdef _DIRENT_HAVE_D_NAMELEN
461                         d_namelen = filedir_entry->d_namelen;
462 #else
463                         d_namelen = strlen(filedir_entry->d_name);
464 #endif
465                         snprintf(buf, sizeof buf, "%s/%s/%s", ctdl_file_dir, CC->room.QRdirname, filedir_entry->d_name);
466                         stat(buf, &statbuf);    /* stat the file */
467                         if (!(statbuf.st_mode & S_IFREG))
468                         {
469                                 snprintf(buf2, sizeof buf2,
470                                         "\"%s\" appears in the file directory for room \"%s\" but is not a regular file.  Directories, named pipes, sockets, etc. are not usable in Citadel room directories.\n",
471                                         buf, CC->room.QRname
472                                 );
473                                 CtdlAideMessage(buf2, "Unusable data found in room directory");
474                                 continue;       /* not a useable file type so don't show it */
475                         }
476                         safestrncpy(comment, "", sizeof comment);
477                         fseek(fd, 0L, 0);       /* rewind descriptions file */
478                         /* Get the description from the descriptions file */
479                         while ((fgets(buf, sizeof buf, fd) != NULL) && (IsEmptyStr(comment))) 
480                         {
481                                 buf[strlen(buf) - 1] = 0;
482                                 if ((!strncasecmp(buf, filedir_entry->d_name, d_namelen)) && (buf[d_namelen] == ' '))
483                                         safestrncpy(comment, &buf[d_namelen + 1], sizeof comment);
484                         }
485                         len = extract_token (mimebuf, comment, 0,' ', 64);
486                         if ((len <0) || strchr(mimebuf, '/') == NULL)
487                         {
488                                 snprintf (mimebuf, 64, "application/octetstream");
489                                 len = 0;
490                         }
491                         cprintf("%s|%ld|%s|%s\n", 
492                                 filedir_entry->d_name, 
493                                 (long)statbuf.st_size, 
494                                 mimebuf, 
495                                 &comment[len]);
496                 }
497         }
498         fclose(fd);
499         closedir(filedir);
500         
501         cprintf("000\n");
502 }
503
504 /*
505  * get room parameters (admin or room admin command)
506  */
507 void cmd_getr(char *cmdbuf)
508 {
509         if (CtdlAccessCheck(ac_room_aide)) return;
510
511         CtdlGetRoom(&CC->room, CC->room.QRname);
512         cprintf("%d%c%s|%s|%s|%d|%d|%d|%d|%d|\n",
513                 CIT_OK,
514                 CtdlCheckExpress(),
515
516                 ((CC->room.QRflags & QR_MAILBOX) ?
517                         &CC->room.QRname[11] : CC->room.QRname),
518
519                 ((CC->room.QRflags & QR_PASSWORDED) ?
520                         CC->room.QRpasswd : ""),
521
522                 ((CC->room.QRflags & QR_DIRECTORY) ?
523                         CC->room.QRdirname : ""),
524
525                 CC->room.QRflags,
526                 (int) CC->room.QRfloor,
527                 (int) CC->room.QRorder,
528
529                 CC->room.QRdefaultview,
530                 CC->room.QRflags2
531                 );
532 }
533
534 /*
535  * set room parameters (admin or room admin command)
536  */
537 void cmd_setr(char *args)
538 {
539         char buf[256];
540         int new_order = 0;
541         int r;
542         int new_floor;
543         char new_name[ROOMNAMELEN];
544
545         if (CtdlAccessCheck(ac_logged_in)) return;
546
547         if (num_parms(args) >= 6) {
548                 new_floor = extract_int(args, 5);
549         } else {
550                 new_floor = (-1);       /* don't change the floor */
551         }
552
553         /* When is a new name more than just a new name?  When the old name
554          * has a namespace prefix.
555          */
556         if (CC->room.QRflags & QR_MAILBOX) {
557                 sprintf(new_name, "%010ld.", atol(CC->room.QRname) );
558         } else {
559                 safestrncpy(new_name, "", sizeof new_name);
560         }
561         extract_token(&new_name[strlen(new_name)], args, 0, '|', (sizeof new_name - strlen(new_name)));
562
563         r = CtdlRenameRoom(CC->room.QRname, new_name, new_floor);
564
565         if (r == crr_room_not_found) {
566                 cprintf("%d Internal error - room not found?\n", ERROR + INTERNAL_ERROR);
567         } else if (r == crr_already_exists) {
568                 cprintf("%d '%s' already exists.\n",
569                         ERROR + ALREADY_EXISTS, new_name);
570         } else if (r == crr_noneditable) {
571                 cprintf("%d Cannot edit this room.\n", ERROR + NOT_HERE);
572         } else if (r == crr_invalid_floor) {
573                 cprintf("%d Target floor does not exist.\n",
574                         ERROR + INVALID_FLOOR_OPERATION);
575         } else if (r == crr_access_denied) {
576                 cprintf("%d You do not have permission to edit '%s'\n",
577                         ERROR + HIGHER_ACCESS_REQUIRED,
578                         CC->room.QRname);
579         } else if (r != crr_ok) {
580                 cprintf("%d Error: CtdlRenameRoom() returned %d\n",
581                         ERROR + INTERNAL_ERROR, r);
582         }
583
584         if (r != crr_ok) {
585                 return;
586         }
587
588         CtdlGetRoom(&CC->room, new_name);
589
590         /* Now we have to do a bunch of other stuff */
591
592         if (num_parms(args) >= 7) {
593                 new_order = extract_int(args, 6);
594                 if (new_order < 1)
595                         new_order = 1;
596                 if (new_order > 127)
597                         new_order = 127;
598         }
599
600         CtdlGetRoomLock(&CC->room, CC->room.QRname);
601
602         /* Directory room */
603         extract_token(buf, args, 2, '|', sizeof buf);
604         buf[15] = 0;
605         safestrncpy(CC->room.QRdirname, buf,
606                 sizeof CC->room.QRdirname);
607
608         /* Default view */
609         if (num_parms(args) >= 8) {
610                 CC->room.QRdefaultview = extract_int(args, 7);
611         }
612
613         /* Second set of flags */
614         if (num_parms(args) >= 9) {
615                 CC->room.QRflags2 = extract_int(args, 8);
616         }
617
618         /* Misc. flags */
619         CC->room.QRflags = (extract_int(args, 3) | QR_INUSE);
620         /* Clean up a client boo-boo: if the client set the room to
621          * guess-name or passworded, ensure that the private flag is
622          * also set.
623          */
624         if ((CC->room.QRflags & QR_GUESSNAME)
625             || (CC->room.QRflags & QR_PASSWORDED))
626                 CC->room.QRflags |= QR_PRIVATE;
627
628         /* Some changes can't apply to BASEROOM */
629         if (!strncasecmp(CC->room.QRname, config.c_baseroom,
630                          ROOMNAMELEN)) {
631                 CC->room.QRorder = 0;
632                 CC->room.QRpasswd[0] = '\0';
633                 CC->room.QRflags &= ~(QR_PRIVATE & QR_PASSWORDED &
634                         QR_GUESSNAME & QR_PREFONLY & QR_MAILBOX);
635                 CC->room.QRflags |= QR_PERMANENT;
636         } else {        
637                 /* March order (doesn't apply to AIDEROOM) */
638                 if (num_parms(args) >= 7)
639                         CC->room.QRorder = (char) new_order;
640                 /* Room password */
641                 extract_token(buf, args, 1, '|', sizeof buf);
642                 buf[10] = 0;
643                 safestrncpy(CC->room.QRpasswd, buf,
644                             sizeof CC->room.QRpasswd);
645                 /* Kick everyone out if the client requested it
646                  * (by changing the room's generation number)
647                  */
648                 if (extract_int(args, 4)) {
649                         time(&CC->room.QRgen);
650                 }
651         }
652         /* Some changes can't apply to AIDEROOM */
653         if (!strncasecmp(CC->room.QRname, config.c_baseroom,
654                          ROOMNAMELEN)) {
655                 CC->room.QRorder = 0;
656                 CC->room.QRflags &= ~QR_MAILBOX;
657                 CC->room.QRflags |= QR_PERMANENT;
658         }
659
660         /* Write the room record back to disk */
661         CtdlPutRoomLock(&CC->room);
662
663         /* Create a room directory if necessary */
664         if (CC->room.QRflags & QR_DIRECTORY) {
665                 snprintf(buf, sizeof buf,"%s/%s",
666                                  ctdl_file_dir,
667                                  CC->room.QRdirname);
668                 mkdir(buf, 0755);
669         }
670         snprintf(buf, sizeof buf, "The room \"%s\" has been edited by %s.\n",
671                 CC->room.QRname,
672                 (CC->logged_in ? CC->curr_user : "an administrator")
673         );
674         CtdlAideMessage(buf, "Room modification Message");
675         cprintf("%d Ok\n", CIT_OK);
676 }
677
678
679
680 /* 
681  * get the name of the room admin for this room
682  */
683 void cmd_geta(char *cmdbuf)
684 {
685         struct ctdluser usbuf;
686
687         if (CtdlAccessCheck(ac_logged_in)) return;
688
689         if (CtdlGetUserByNumber(&usbuf, CC->room.QRroomaide) == 0) {
690                 cprintf("%d %s\n", CIT_OK, usbuf.fullname);
691         } else {
692                 cprintf("%d \n", CIT_OK);
693         }
694 }
695
696
697 /* 
698  * set the room admin for this room
699  */
700 void cmd_seta(char *new_ra)
701 {
702         struct ctdluser usbuf;
703         long newu;
704         char buf[SIZ];
705         int post_notice;
706
707         if (CtdlAccessCheck(ac_room_aide)) return;
708
709         if (CtdlGetUser(&usbuf, new_ra) != 0) {
710                 newu = (-1L);
711         } else {
712                 newu = usbuf.usernum;
713         }
714
715         CtdlGetRoomLock(&CC->room, CC->room.QRname);
716         post_notice = 0;
717         if (CC->room.QRroomaide != newu) {
718                 post_notice = 1;
719         }
720         CC->room.QRroomaide = newu;
721         CtdlPutRoomLock(&CC->room);
722
723         /*
724          * We have to post the change notice _after_ writing changes to 
725          * the room table, otherwise it would deadlock!
726          */
727         if (post_notice == 1) {
728                 if (!IsEmptyStr(usbuf.fullname))
729                         snprintf(buf, sizeof buf,
730                                 "%s is now the room admin for \"%s\".\n",
731                                 usbuf.fullname, CC->room.QRname);
732                 else
733                         snprintf(buf, sizeof buf,
734                                 "There is now no room admin for \"%s\".\n",
735                                 CC->room.QRname);
736                 CtdlAideMessage(buf, "Admin Room Modification");
737         }
738         cprintf("%d Ok\n", CIT_OK);
739 }
740
741 /* 
742  * retrieve info file for this room
743  */
744 void cmd_rinf(char *gargs)
745 {
746         char filename[PATH_MAX];
747         char buf[SIZ];
748         FILE *info_fp;
749
750         assoc_file_name(filename, sizeof filename, &CC->room, ctdl_info_dir);
751         info_fp = fopen(filename, "r");
752
753         if (info_fp == NULL) {
754                 cprintf("%d No info file.\n", ERROR + FILE_NOT_FOUND);
755                 return;
756         }
757         cprintf("%d Info:\n", LISTING_FOLLOWS);
758         while (fgets(buf, sizeof buf, info_fp) != NULL) {
759                 if (!IsEmptyStr(buf))
760                         buf[strlen(buf) - 1] = 0;
761                 cprintf("%s\n", buf);
762         }
763         cprintf("000\n");
764         fclose(info_fp);
765 }
766
767
768 /*
769  * admin command: kill the current room
770  */
771 void cmd_kill(char *argbuf)
772 {
773         char deleted_room_name[ROOMNAMELEN];
774         char msg[SIZ];
775         int kill_ok;
776
777         kill_ok = extract_int(argbuf, 0);
778
779         if (CtdlDoIHavePermissionToDeleteThisRoom(&CC->room) == 0) {
780                 cprintf("%d Can't delete this room.\n", ERROR + NOT_HERE);
781                 return;
782         }
783         if (kill_ok) {
784                 if (CC->room.QRflags & QR_MAILBOX) {
785                         safestrncpy(deleted_room_name, &CC->room.QRname[11], sizeof deleted_room_name);
786                 }
787                 else {
788                         safestrncpy(deleted_room_name, CC->room.QRname, sizeof deleted_room_name);
789                 }
790
791                 /* Do the dirty work */
792                 CtdlScheduleRoomForDeletion(&CC->room);
793
794                 /* Return to the Lobby */
795                 CtdlUserGoto(config.c_baseroom, 0, 0, NULL, NULL);
796
797                 /* tell the world what we did */
798                 snprintf(msg, sizeof msg, "The room \"%s\" has been deleted by %s.\n",
799                          deleted_room_name,
800                         (CC->logged_in ? CC->curr_user : "an administrator")
801                 );
802                 CtdlAideMessage(msg, "Room Purger Message");
803                 cprintf("%d '%s' deleted.\n", CIT_OK, deleted_room_name);
804         } else {
805                 cprintf("%d ok to delete.\n", CIT_OK);
806         }
807 }
808
809
810 /*
811  * create a new room
812  */
813 void cmd_cre8(char *args)
814 {
815         int cre8_ok;
816         char new_room_name[ROOMNAMELEN];
817         int new_room_type;
818         char new_room_pass[32];
819         int new_room_floor;
820         int new_room_view;
821         char *notification_message = NULL;
822         unsigned newflags;
823         struct floor *fl;
824         int avoid_access = 0;
825
826         cre8_ok = extract_int(args, 0);
827         extract_token(new_room_name, args, 1, '|', sizeof new_room_name);
828         new_room_name[ROOMNAMELEN - 1] = 0;
829         new_room_type = extract_int(args, 2);
830         extract_token(new_room_pass, args, 3, '|', sizeof new_room_pass);
831         avoid_access = extract_int(args, 5);
832         new_room_view = extract_int(args, 6);
833         new_room_pass[9] = 0;
834         new_room_floor = 0;
835
836         if ((IsEmptyStr(new_room_name)) && (cre8_ok == 1)) {
837                 cprintf("%d Invalid room name.\n", ERROR + ILLEGAL_VALUE);
838                 return;
839         }
840
841         if (!strcasecmp(new_room_name, MAILROOM)) {
842                 cprintf("%d '%s' already exists.\n",
843                         ERROR + ALREADY_EXISTS, new_room_name);
844                 return;
845         }
846
847         if (num_parms(args) >= 5) {
848                 fl = CtdlGetCachedFloor(extract_int(args, 4));
849                 if (fl == NULL) {
850                         cprintf("%d Invalid floor number.\n",
851                                 ERROR + INVALID_FLOOR_OPERATION);
852                         return;
853                 }
854                 else if ((fl->f_flags & F_INUSE) == 0) {
855                         cprintf("%d Invalid floor number.\n",
856                                 ERROR + INVALID_FLOOR_OPERATION);
857                         return;
858                 } else {
859                         new_room_floor = extract_int(args, 4);
860                 }
861         }
862
863         if (CtdlAccessCheck(ac_logged_in)) return;
864
865         if (CC->user.axlevel < config.c_createax && !CC->internal_pgm) {
866                 cprintf("%d You need higher access to create rooms.\n",
867                         ERROR + HIGHER_ACCESS_REQUIRED);
868                 return;
869         }
870
871         if ((IsEmptyStr(new_room_name)) && (cre8_ok == 0)) {
872                 cprintf("%d Ok to create rooms.\n", CIT_OK);
873                 return;
874         }
875
876         if ((new_room_type < 0) || (new_room_type > 5)) {
877                 cprintf("%d Invalid room type.\n", ERROR + ILLEGAL_VALUE);
878                 return;
879         }
880
881         if (new_room_type == 5) {
882                 if (CC->user.axlevel < AxAideU) {
883                         cprintf("%d Higher access required\n", 
884                                 ERROR + HIGHER_ACCESS_REQUIRED);
885                         return;
886                 }
887         }
888
889         /* Check to make sure the requested room name doesn't already exist */
890         newflags = CtdlCreateRoom(new_room_name,
891                                 new_room_type, new_room_pass, new_room_floor,
892                                 0, avoid_access, new_room_view);
893         if (newflags == 0) {
894                 cprintf("%d '%s' already exists.\n",
895                         ERROR + ALREADY_EXISTS, new_room_name);
896                 return;
897         }
898
899         if (cre8_ok == 0) {
900                 cprintf("%d OK to create '%s'\n", CIT_OK, new_room_name);
901                 return;
902         }
903
904         /* If we reach this point, the room needs to be created. */
905
906         newflags = CtdlCreateRoom(new_room_name,
907                            new_room_type, new_room_pass, new_room_floor, 1, 0,
908                            new_room_view);
909
910         /* post a message in Aide> describing the new room */
911         notification_message = malloc(1024);
912         snprintf(notification_message, 1024,
913                 "A new room called \"%s\" has been created by %s%s%s%s%s%s\n",
914                 new_room_name,
915                 (CC->logged_in ? CC->curr_user : "an administrator"),
916                 ((newflags & QR_MAILBOX) ? " [personal]" : ""),
917                 ((newflags & QR_PRIVATE) ? " [private]" : ""),
918                 ((newflags & QR_GUESSNAME) ? " [hidden]" : ""),
919                 ((newflags & QR_PASSWORDED) ? " Password: " : ""),
920                 ((newflags & QR_PASSWORDED) ? new_room_pass : "")
921         );
922         CtdlAideMessage(notification_message, "Room Creation Message");
923         free(notification_message);
924
925         cprintf("%d '%s' has been created.\n", CIT_OK, new_room_name);
926 }
927
928
929
930 void cmd_einf(char *ok)
931 {                               /* enter info file for current room */
932         FILE *fp;
933         char infofilename[SIZ];
934         char buf[SIZ];
935
936         unbuffer_output();
937
938         if (CtdlAccessCheck(ac_room_aide)) return;
939
940         if (atoi(ok) == 0) {
941                 cprintf("%d Ok.\n", CIT_OK);
942                 return;
943         }
944         assoc_file_name(infofilename, sizeof infofilename, &CC->room, ctdl_info_dir);
945         syslog(LOG_DEBUG, "opening\n");
946         fp = fopen(infofilename, "w");
947         syslog(LOG_DEBUG, "checking\n");
948         if (fp == NULL) {
949                 cprintf("%d Cannot open %s: %s\n",
950                   ERROR + INTERNAL_ERROR, infofilename, strerror(errno));
951                 return;
952         }
953         cprintf("%d Send info...\n", SEND_LISTING);
954
955         do {
956                 client_getln(buf, sizeof buf);
957                 if (strcmp(buf, "000"))
958                         fprintf(fp, "%s\n", buf);
959         } while (strcmp(buf, "000"));
960         fclose(fp);
961
962         /* now update the room index so people will see our new info */
963         CtdlGetRoomLock(&CC->room, CC->room.QRname);            /* lock so no one steps on us */
964         CC->room.QRinfo = CC->room.QRhighest + 1L;
965         CtdlPutRoomLock(&CC->room);
966 }
967
968
969 /* 
970  * cmd_lflr()   -  List all known floors
971  */
972 void cmd_lflr(char *gargs)
973 {
974         int a;
975         struct floor flbuf;
976
977         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
978
979         cprintf("%d Known floors:\n", LISTING_FOLLOWS);
980
981         for (a = 0; a < MAXFLOORS; ++a) {
982                 CtdlGetFloor(&flbuf, a);
983                 if (flbuf.f_flags & F_INUSE) {
984                         cprintf("%d|%s|%d\n",
985                                 a,
986                                 flbuf.f_name,
987                                 flbuf.f_ref_count);
988                 }
989         }
990         cprintf("000\n");
991 }
992
993
994
995 /*
996  * create a new floor
997  */
998 void cmd_cflr(char *argbuf)
999 {
1000         char new_floor_name[256];
1001         struct floor flbuf;
1002         int cflr_ok;
1003         int free_slot = (-1);
1004         int a;
1005
1006         extract_token(new_floor_name, argbuf, 0, '|', sizeof new_floor_name);
1007         cflr_ok = extract_int(argbuf, 1);
1008
1009         if (CtdlAccessCheck(ac_aide)) return;
1010
1011         if (IsEmptyStr(new_floor_name)) {
1012                 cprintf("%d Blank floor name not allowed.\n",
1013                         ERROR + ILLEGAL_VALUE);
1014                 return;
1015         }
1016
1017         for (a = 0; a < MAXFLOORS; ++a) {
1018                 CtdlGetFloor(&flbuf, a);
1019
1020                 /* note any free slots while we're scanning... */
1021                 if (((flbuf.f_flags & F_INUSE) == 0)
1022                     && (free_slot < 0))
1023                         free_slot = a;
1024
1025                 /* check to see if it already exists */
1026                 if ((!strcasecmp(flbuf.f_name, new_floor_name))
1027                     && (flbuf.f_flags & F_INUSE)) {
1028                         cprintf("%d Floor '%s' already exists.\n",
1029                                 ERROR + ALREADY_EXISTS,
1030                                 flbuf.f_name);
1031                         return;
1032                 }
1033         }
1034
1035         if (free_slot < 0) {
1036                 cprintf("%d There is no space available for a new floor.\n",
1037                         ERROR + INVALID_FLOOR_OPERATION);
1038                 return;
1039         }
1040         if (cflr_ok == 0) {
1041                 cprintf("%d ok to create...\n", CIT_OK);
1042                 return;
1043         }
1044         lgetfloor(&flbuf, free_slot);
1045         flbuf.f_flags = F_INUSE;
1046         flbuf.f_ref_count = 0;
1047         safestrncpy(flbuf.f_name, new_floor_name, sizeof flbuf.f_name);
1048         lputfloor(&flbuf, free_slot);
1049         cprintf("%d %d\n", CIT_OK, free_slot);
1050 }
1051
1052
1053
1054 /*
1055  * delete a floor
1056  */
1057 void cmd_kflr(char *argbuf)
1058 {
1059         struct floor flbuf;
1060         int floor_to_delete;
1061         int kflr_ok;
1062         int delete_ok;
1063
1064         floor_to_delete = extract_int(argbuf, 0);
1065         kflr_ok = extract_int(argbuf, 1);
1066
1067         if (CtdlAccessCheck(ac_aide)) return;
1068
1069         lgetfloor(&flbuf, floor_to_delete);
1070
1071         delete_ok = 1;
1072         if ((flbuf.f_flags & F_INUSE) == 0) {
1073                 cprintf("%d Floor %d not in use.\n",
1074                         ERROR + INVALID_FLOOR_OPERATION, floor_to_delete);
1075                 delete_ok = 0;
1076         } else {
1077                 if (flbuf.f_ref_count != 0) {
1078                         cprintf("%d Cannot delete; floor contains %d rooms.\n",
1079                                 ERROR + INVALID_FLOOR_OPERATION,
1080                                 flbuf.f_ref_count);
1081                         delete_ok = 0;
1082                 } else {
1083                         if (kflr_ok == 1) {
1084                                 cprintf("%d Ok\n", CIT_OK);
1085                         } else {
1086                                 cprintf("%d Ok to delete...\n", CIT_OK);
1087                         }
1088
1089                 }
1090
1091         }
1092
1093         if ((delete_ok == 1) && (kflr_ok == 1))
1094                 flbuf.f_flags = 0;
1095         lputfloor(&flbuf, floor_to_delete);
1096 }
1097
1098 /*
1099  * edit a floor
1100  */
1101 void cmd_eflr(char *argbuf)
1102 {
1103         struct floor flbuf;
1104         int floor_num;
1105         int np;
1106
1107         np = num_parms(argbuf);
1108         if (np < 1) {
1109                 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
1110                 return;
1111         }
1112
1113         if (CtdlAccessCheck(ac_aide)) return;
1114
1115         floor_num = extract_int(argbuf, 0);
1116         lgetfloor(&flbuf, floor_num);
1117         if ((flbuf.f_flags & F_INUSE) == 0) {
1118                 lputfloor(&flbuf, floor_num);
1119                 cprintf("%d Floor %d is not in use.\n",
1120                         ERROR + INVALID_FLOOR_OPERATION, floor_num);
1121                 return;
1122         }
1123         if (np >= 2)
1124                 extract_token(flbuf.f_name, argbuf, 1, '|', sizeof flbuf.f_name);
1125         lputfloor(&flbuf, floor_num);
1126
1127         cprintf("%d Ok\n", CIT_OK);
1128 }
1129
1130
1131
1132 /* 
1133  * cmd_stat()  -  return the modification time of the current room (maybe other things in the future)
1134  */
1135 void cmd_stat(char *gargs)
1136 {
1137         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
1138         CtdlGetRoom(&CC->room, CC->room.QRname);
1139         cprintf("%d %s|%ld|\n", CIT_OK, CC->room.QRname, CC->room.QRmtime);
1140 }
1141
1142
1143
1144
1145 /*****************************************************************************/
1146 /*                      MODULE INITIALIZATION STUFF                          */
1147 /*****************************************************************************/
1148
1149 CTDL_MODULE_INIT(rooms)
1150 {
1151         if (!threading) {
1152                 CtdlRegisterProtoHook(cmd_lrms, "LRMS", "List rooms");
1153                 CtdlRegisterProtoHook(cmd_lkra, "LKRA", "List all known rooms");
1154                 CtdlRegisterProtoHook(cmd_lkrn, "LKRN", "List known rooms with new messages");
1155                 CtdlRegisterProtoHook(cmd_lkro, "LKRO", "List known rooms without new messages");
1156                 CtdlRegisterProtoHook(cmd_lzrm, "LZRM", "List zapped rooms");
1157                 CtdlRegisterProtoHook(cmd_lprm, "LPRM", "List public rooms");
1158                 CtdlRegisterProtoHook(cmd_goto, "GOTO", "Goto a named room");
1159                 CtdlRegisterProtoHook(cmd_stat, "STAT", "Get mtime of the current room");
1160                 CtdlRegisterProtoHook(cmd_whok, "WHOK", "List users who know this room");
1161                 CtdlRegisterProtoHook(cmd_rdir, "RDIR", "List files in room directory");
1162                 CtdlRegisterProtoHook(cmd_getr, "GETR", "Get room parameters");
1163                 CtdlRegisterProtoHook(cmd_setr, "SETR", "Set room parameters");
1164                 CtdlRegisterProtoHook(cmd_geta, "GETA", "Get the room admin name");
1165                 CtdlRegisterProtoHook(cmd_seta, "SETA", "Set the room admin for this room");
1166                 CtdlRegisterProtoHook(cmd_rinf, "RINF", "Fetch room info file");
1167                 CtdlRegisterProtoHook(cmd_kill, "KILL", "Kill (delete) the current room");
1168                 CtdlRegisterProtoHook(cmd_cre8, "CRE8", "Create a new room");
1169                 CtdlRegisterProtoHook(cmd_einf, "EINF", "Enter info file for the current room");
1170                 CtdlRegisterProtoHook(cmd_lflr, "LFLR", "List all known floors");
1171                 CtdlRegisterProtoHook(cmd_cflr, "CFLR", "Create a new floor");
1172                 CtdlRegisterProtoHook(cmd_kflr, "KFLR", "Kill a floor");
1173                 CtdlRegisterProtoHook(cmd_eflr, "EFLR", "Edit a floor");
1174         }
1175         /* return our Subversion id for the Log */
1176         return "rooms";
1177 }