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