4 * Server functions which perform operations on room objects.
15 #include <dirent.h> /* for cmd_rdir to read contents of the directory */
17 #if TIME_WITH_SYS_TIME
18 # include <sys/time.h>
22 # include <sys/time.h>
31 #include <libcitadel.h>
36 #include "sysdep_decls.h"
40 #include "citserver.h"
42 #include "citadel_dirs.h"
45 struct floor
*floorcache
[MAXFLOORS
];
48 * Retrieve access control information for any user/room pair
50 void CtdlRoomAccess(struct ctdlroom
*roombuf
, struct ctdluser
*userbuf
,
51 int *result
, int *view
)
56 /* for internal programs, always do everything */
57 if (((CC
->internal_pgm
)) && (roombuf
->QRflags
& QR_INUSE
)) {
58 retval
= (UA_KNOWN
| UA_GOTOALLOWED
| UA_POSTALLOWED
);
63 /* Locate any applicable user/room relationships */
64 CtdlGetRelationship(&vbuf
, userbuf
, roombuf
);
66 /* Force the properties of the Aide room */
67 if (!strcasecmp(roombuf
->QRname
, config
.c_aideroom
)) {
68 if (userbuf
->axlevel
>= 6) {
69 retval
= UA_KNOWN
| UA_GOTOALLOWED
| UA_POSTALLOWED
| UA_DELETEALLOWED
;
76 /* If this is a public room, it's accessible... */
77 if ( ((roombuf
->QRflags
& QR_PRIVATE
) == 0)
78 && ((roombuf
->QRflags
& QR_MAILBOX
) == 0) ) {
79 retval
= retval
| UA_KNOWN
| UA_GOTOALLOWED
;
82 /* If this is a preferred users only room, check access level */
83 if (roombuf
->QRflags
& QR_PREFONLY
) {
84 if (userbuf
->axlevel
< 5) {
85 retval
= retval
& ~UA_KNOWN
& ~UA_GOTOALLOWED
;
89 /* For private rooms, check the generation number matchups */
90 if ( (roombuf
->QRflags
& QR_PRIVATE
)
91 && ((roombuf
->QRflags
& QR_MAILBOX
) == 0) ) {
93 /* An explicit match means the user belongs in this room */
94 if (vbuf
.v_flags
& V_ACCESS
) {
95 retval
= retval
| UA_KNOWN
| UA_GOTOALLOWED
;
97 /* Otherwise, check if this is a guess-name or passworded
98 * room. If it is, a goto may at least be attempted
100 else if ((roombuf
->QRflags
& QR_PRIVATE
)
101 || (roombuf
->QRflags
& QR_PASSWORDED
)) {
102 retval
= retval
& ~UA_KNOWN
;
103 retval
= retval
| UA_GOTOALLOWED
;
107 /* For mailbox rooms, also check the namespace */
108 /* Also, mailbox owners can delete their messages */
109 if (roombuf
->QRflags
& QR_MAILBOX
) {
110 if (userbuf
->usernum
== atol(roombuf
->QRname
)) {
111 retval
= retval
| UA_KNOWN
| UA_GOTOALLOWED
| UA_POSTALLOWED
| UA_DELETEALLOWED
;
113 /* An explicit match means the user belongs in this room */
114 if (vbuf
.v_flags
& V_ACCESS
) {
115 retval
= retval
| UA_KNOWN
| UA_GOTOALLOWED
| UA_POSTALLOWED
| UA_DELETEALLOWED
;
119 /* For non-mailbox rooms... */
122 /* User is allowed to post in the room unless:
123 * - User is not validated
124 * - User has no net privileges and it is a shared network room
125 * - It is a read-only room
127 int post_allowed
= 1;
128 if (CC
->user
.axlevel
< 2) post_allowed
= 0;
129 if ((CC
->user
.axlevel
< 4) && (CC
->room
.QRflags
& QR_NETWORK
)) post_allowed
= 0;
130 if (roombuf
->QRflags
& QR_READONLY
) post_allowed
= 0;
132 retval
= retval
| UA_POSTALLOWED
;
135 /* If "collaborative deletion" is active for this room, any user who can post
136 * is also allowed to delete
138 if (CC
->room
.QRflags2
& QR2_COLLABDEL
) {
139 if (retval
& UA_POSTALLOWED
) {
140 retval
= retval
| UA_DELETEALLOWED
;
146 /* Check to see if the user has forgotten this room */
147 if (vbuf
.v_flags
& V_FORGET
) {
148 retval
= retval
& ~UA_KNOWN
;
149 if ( ( ((roombuf
->QRflags
& QR_PRIVATE
) == 0)
150 && ((roombuf
->QRflags
& QR_MAILBOX
) == 0) )
151 || ( (roombuf
->QRflags
& QR_MAILBOX
)
152 && (atol(roombuf
->QRname
) == CC
->user
.usernum
))) {
153 retval
= retval
| UA_ZAPPED
;
156 /* If user is explicitly locked out of this room, deny everything */
157 if (vbuf
.v_flags
& V_LOCKOUT
) {
158 retval
= retval
& ~UA_KNOWN
& ~UA_GOTOALLOWED
& ~UA_POSTALLOWED
;
161 /* Aides get access to all private rooms */
162 if ( (userbuf
->axlevel
>= 6)
163 && ((roombuf
->QRflags
& QR_MAILBOX
) == 0) ) {
164 if (vbuf
.v_flags
& V_FORGET
) {
165 retval
= retval
| UA_GOTOALLOWED
| UA_POSTALLOWED
;
168 retval
= retval
| UA_KNOWN
| UA_GOTOALLOWED
| UA_POSTALLOWED
;
172 /* Aides can gain access to mailboxes as well, but they don't show
175 if ( (userbuf
->axlevel
>= 6)
176 && (roombuf
->QRflags
& QR_MAILBOX
) ) {
177 retval
= retval
| UA_GOTOALLOWED
| UA_POSTALLOWED
;
180 /* Aides and Room Aides have admin privileges */
181 if ( (userbuf
->axlevel
>= 6)
182 || (userbuf
->usernum
== roombuf
->QRroomaide
)
184 retval
= retval
| UA_ADMINALLOWED
| UA_DELETEALLOWED
| UA_POSTALLOWED
;
187 NEWMSG
: /* By the way, we also check for the presence of new messages */
188 if (is_msg_in_sequence_set(vbuf
.v_seen
, roombuf
->QRhighest
) == 0) {
189 retval
= retval
| UA_HASNEWMSGS
;
192 /* System rooms never show up in the list. */
193 if (roombuf
->QRflags2
& QR2_SYSTEM
) {
194 retval
= retval
& ~UA_KNOWN
;
198 /* Now give the caller the information it wants. */
199 if (result
!= NULL
) *result
= retval
;
200 if (view
!= NULL
) *view
= vbuf
.v_view
;
204 * Self-checking stuff for a room record read into memory
206 void room_sanity_check(struct ctdlroom
*qrbuf
)
208 /* Mailbox rooms are always on the lowest floor */
209 if (qrbuf
->QRflags
& QR_MAILBOX
) {
212 /* Listing order of 0 is illegal except for base rooms */
213 if (qrbuf
->QRorder
== 0)
214 if (!(qrbuf
->QRflags
& QR_MAILBOX
) &&
215 strncasecmp(qrbuf
->QRname
, config
.c_baseroom
, ROOMNAMELEN
)
217 strncasecmp(qrbuf
->QRname
, config
.c_aideroom
, ROOMNAMELEN
))
223 * getroom() - retrieve room data from disk
225 int getroom(struct ctdlroom
*qrbuf
, char *room_name
)
227 struct cdbdata
*cdbqr
;
228 char lowercase_name
[ROOMNAMELEN
];
229 char personal_lowercase_name
[ROOMNAMELEN
];
230 char *dptr
, *sptr
, *eptr
;
232 dptr
= lowercase_name
;
234 eptr
= (dptr
+ (sizeof lowercase_name
- 1));
235 while (!IsEmptyStr(sptr
) && (dptr
< eptr
)){
236 *dptr
= tolower(*sptr
);
241 memset(qrbuf
, 0, sizeof(struct ctdlroom
));
243 /* First, try the public namespace */
244 cdbqr
= cdb_fetch(CDB_ROOMS
,
245 lowercase_name
, strlen(lowercase_name
));
247 /* If that didn't work, try the user's personal namespace */
249 snprintf(personal_lowercase_name
,
250 sizeof personal_lowercase_name
, "%010ld.%s",
251 CC
->user
.usernum
, lowercase_name
);
252 cdbqr
= cdb_fetch(CDB_ROOMS
,
253 personal_lowercase_name
,
254 strlen(personal_lowercase_name
));
257 memcpy(qrbuf
, cdbqr
->ptr
,
258 ((cdbqr
->len
> sizeof(struct ctdlroom
)) ?
259 sizeof(struct ctdlroom
) : cdbqr
->len
));
262 room_sanity_check(qrbuf
);
271 * lgetroom() - same as getroom() but locks the record (if supported)
273 int lgetroom(struct ctdlroom
*qrbuf
, char *room_name
)
276 retval
= getroom(qrbuf
, room_name
);
277 if (retval
== 0) begin_critical_section(S_ROOMS
);
283 * b_putroom() - back end to putroom() and b_deleteroom()
284 * (if the supplied buffer is NULL, delete the room record)
286 void b_putroom(struct ctdlroom
*qrbuf
, char *room_name
)
288 char lowercase_name
[ROOMNAMELEN
];
293 bptr
= lowercase_name
;
294 while (!IsEmptyStr(aptr
))
296 *bptr
= tolower(*aptr
);
302 len
= bptr
- lowercase_name
;
304 cdb_delete(CDB_ROOMS
,
305 lowercase_name
, len
);
307 time(&qrbuf
->QRmtime
);
310 qrbuf
, sizeof(struct ctdlroom
));
316 * putroom() - store room data to disk
318 void putroom(struct ctdlroom
*qrbuf
) {
319 b_putroom(qrbuf
, qrbuf
->QRname
);
324 * b_deleteroom() - delete a room record from disk
326 void b_deleteroom(char *room_name
) {
327 b_putroom(NULL
, room_name
);
333 * lputroom() - same as putroom() but unlocks the record (if supported)
335 void lputroom(struct ctdlroom
*qrbuf
)
339 end_critical_section(S_ROOMS
);
343 /****************************************************************************/
346 * getfloor() - retrieve floor data from disk
348 void getfloor(struct floor
*flbuf
, int floor_num
)
350 struct cdbdata
*cdbfl
;
352 memset(flbuf
, 0, sizeof(struct floor
));
353 cdbfl
= cdb_fetch(CDB_FLOORTAB
, &floor_num
, sizeof(int));
355 memcpy(flbuf
, cdbfl
->ptr
,
356 ((cdbfl
->len
> sizeof(struct floor
)) ?
357 sizeof(struct floor
) : cdbfl
->len
));
360 if (floor_num
== 0) {
361 safestrncpy(flbuf
->f_name
, "Main Floor",
362 sizeof flbuf
->f_name
);
363 flbuf
->f_flags
= F_INUSE
;
364 flbuf
->f_ref_count
= 3;
371 * lgetfloor() - same as getfloor() but locks the record (if supported)
373 void lgetfloor(struct floor
*flbuf
, int floor_num
)
376 begin_critical_section(S_FLOORTAB
);
377 getfloor(flbuf
, floor_num
);
382 * cgetfloor() - Get floor record from *cache* (loads from disk if needed)
384 * This is strictly a performance hack.
386 struct floor
*cgetfloor(int floor_num
) {
387 static int initialized
= 0;
390 struct floor
*fl
= NULL
;
392 begin_critical_section(S_FLOORCACHE
);
393 if (initialized
== 0) {
394 for (i
=0; i
<MAXFLOORS
; ++i
) {
395 floorcache
[floor_num
] = NULL
;
399 if (floorcache
[floor_num
] == NULL
) {
402 end_critical_section(S_FLOORCACHE
);
405 fl
= malloc(sizeof(struct floor
));
406 getfloor(fl
, floor_num
);
407 begin_critical_section(S_FLOORCACHE
);
408 if (floorcache
[floor_num
] != NULL
) {
409 free(floorcache
[floor_num
]);
411 floorcache
[floor_num
] = fl
;
412 end_critical_section(S_FLOORCACHE
);
415 return(floorcache
[floor_num
]);
421 * putfloor() - store floor data on disk
423 void putfloor(struct floor
*flbuf
, int floor_num
)
425 /* If we've cached this, clear it out, 'cuz it's WRONG now! */
426 begin_critical_section(S_FLOORCACHE
);
427 if (floorcache
[floor_num
] != NULL
) {
428 free(floorcache
[floor_num
]);
429 floorcache
[floor_num
] = malloc(sizeof(struct floor
));
430 memcpy(floorcache
[floor_num
], flbuf
, sizeof(struct floor
));
432 end_critical_section(S_FLOORCACHE
);
434 cdb_store(CDB_FLOORTAB
, &floor_num
, sizeof(int),
435 flbuf
, sizeof(struct floor
));
440 * lputfloor() - same as putfloor() but unlocks the record (if supported)
442 void lputfloor(struct floor
*flbuf
, int floor_num
)
445 putfloor(flbuf
, floor_num
);
446 end_critical_section(S_FLOORTAB
);
452 * Traverse the room file...
454 void ForEachRoom(void (*CallBack
) (struct ctdlroom
*EachRoom
, void *out_data
),
457 struct ctdlroom qrbuf
;
458 struct cdbdata
*cdbqr
;
460 cdb_rewind(CDB_ROOMS
);
462 while (cdbqr
= cdb_next_item(CDB_ROOMS
), cdbqr
!= NULL
) {
463 memset(&qrbuf
, 0, sizeof(struct ctdlroom
));
464 memcpy(&qrbuf
, cdbqr
->ptr
,
465 ((cdbqr
->len
> sizeof(struct ctdlroom
)) ?
466 sizeof(struct ctdlroom
) : cdbqr
->len
));
468 room_sanity_check(&qrbuf
);
469 if (qrbuf
.QRflags
& QR_INUSE
)
470 (*CallBack
)(&qrbuf
, in_data
);
476 * delete_msglist() - delete room message pointers
478 void delete_msglist(struct ctdlroom
*whichroom
)
480 struct cdbdata
*cdbml
;
482 /* Make sure the msglist we're deleting actually exists, otherwise
483 * gdbm will complain when we try to delete an invalid record
485 cdbml
= cdb_fetch(CDB_MSGLISTS
, &whichroom
->QRnumber
, sizeof(long));
489 /* Go ahead and delete it */
490 cdb_delete(CDB_MSGLISTS
, &whichroom
->QRnumber
, sizeof(long));
498 * sort message pointers
499 * (returns new msg count)
501 int sort_msglist(long listptrs
[], int oldcount
)
512 for (a
= numitems
- 2; a
>= 0; --a
) {
513 for (b
= 0; b
<= a
; ++b
) {
514 if (listptrs
[b
] > (listptrs
[b
+ 1])) {
516 hold2
= listptrs
[b
+ 1];
518 listptrs
[b
+ 1] = hold1
;
523 /* and yank any nulls */
524 while ((numitems
> 0) && (listptrs
[0] == 0L)) {
525 memmove(&listptrs
[0], &listptrs
[1],
526 (sizeof(long) * (numitems
- 1)));
535 * Determine whether a given room is non-editable.
537 int is_noneditable(struct ctdlroom
*qrbuf
)
540 /* Mail> rooms are non-editable */
541 if ( (qrbuf
->QRflags
& QR_MAILBOX
)
542 && (!strcasecmp(&qrbuf
->QRname
[11], MAILROOM
)) )
545 /* Everything else is editable */
552 * Back-back-end for all room listing commands
554 void list_roomname(struct ctdlroom
*qrbuf
, int ra
, int current_view
, int default_view
)
556 char truncated_roomname
[ROOMNAMELEN
];
558 /* For my own mailbox rooms, chop off the owner prefix */
559 if ( (qrbuf
->QRflags
& QR_MAILBOX
)
560 && (atol(qrbuf
->QRname
) == CC
->user
.usernum
) ) {
561 safestrncpy(truncated_roomname
, qrbuf
->QRname
, sizeof truncated_roomname
);
562 safestrncpy(truncated_roomname
, &truncated_roomname
[11], sizeof truncated_roomname
);
563 cprintf("%s", truncated_roomname
);
565 /* For all other rooms, just display the name in its entirety */
567 cprintf("%s", qrbuf
->QRname
);
570 /* ...and now the other parameters */
571 cprintf("|%u|%d|%d|%d|%d|%d|%d|%ld|\n",
573 (int) qrbuf
->QRfloor
,
574 (int) qrbuf
->QRorder
,
575 (int) qrbuf
->QRflags2
,
585 * cmd_lrms() - List all accessible rooms, known or forgotten
587 void cmd_lrms_backend(struct ctdlroom
*qrbuf
, void *data
)
589 int FloorBeingSearched
= (-1);
593 FloorBeingSearched
= *(int *)data
;
594 CtdlRoomAccess(qrbuf
, &CC
->user
, &ra
, &view
);
596 if ((( ra
& (UA_KNOWN
| UA_ZAPPED
)))
597 && ((qrbuf
->QRfloor
== (FloorBeingSearched
))
598 || ((FloorBeingSearched
) < 0)))
599 list_roomname(qrbuf
, ra
, view
, qrbuf
->QRdefaultview
);
602 void cmd_lrms(char *argbuf
)
604 int FloorBeingSearched
= (-1);
605 if (!IsEmptyStr(argbuf
))
606 FloorBeingSearched
= extract_int(argbuf
, 0);
608 if (CtdlAccessCheck(ac_logged_in
)) return;
610 if (getuser(&CC
->user
, CC
->curr_user
)) {
611 cprintf("%d Can't locate user!\n", ERROR
+ INTERNAL_ERROR
);
614 cprintf("%d Accessible rooms:\n", LISTING_FOLLOWS
);
616 ForEachRoom(cmd_lrms_backend
, &FloorBeingSearched
);
623 * cmd_lkra() - List all known rooms
625 void cmd_lkra_backend(struct ctdlroom
*qrbuf
, void *data
)
627 int FloorBeingSearched
= (-1);
631 FloorBeingSearched
= *(int *)data
;
632 CtdlRoomAccess(qrbuf
, &CC
->user
, &ra
, &view
);
634 if ((( ra
& (UA_KNOWN
)))
635 && ((qrbuf
->QRfloor
== (FloorBeingSearched
))
636 || ((FloorBeingSearched
) < 0)))
637 list_roomname(qrbuf
, ra
, view
, qrbuf
->QRdefaultview
);
640 void cmd_lkra(char *argbuf
)
642 int FloorBeingSearched
= (-1);
643 if (!IsEmptyStr(argbuf
))
644 FloorBeingSearched
= extract_int(argbuf
, 0);
646 if (CtdlAccessCheck(ac_logged_in
)) return;
648 if (getuser(&CC
->user
, CC
->curr_user
)) {
649 cprintf("%d Can't locate user!\n", ERROR
+ INTERNAL_ERROR
);
652 cprintf("%d Known rooms:\n", LISTING_FOLLOWS
);
654 ForEachRoom(cmd_lkra_backend
, &FloorBeingSearched
);
660 void cmd_lprm_backend(struct ctdlroom
*qrbuf
, void *data
)
662 int FloorBeingSearched
= (-1);
666 FloorBeingSearched
= *(int *)data
;
667 CtdlRoomAccess(qrbuf
, &CC
->user
, &ra
, &view
);
669 if ( ((qrbuf
->QRflags
& QR_PRIVATE
) == 0)
670 && ((qrbuf
->QRflags
& QR_MAILBOX
) == 0)
671 && ((qrbuf
->QRfloor
== (FloorBeingSearched
))
672 || ((FloorBeingSearched
) < 0)))
673 list_roomname(qrbuf
, ra
, view
, qrbuf
->QRdefaultview
);
676 void cmd_lprm(char *argbuf
)
678 int FloorBeingSearched
= (-1);
679 if (!IsEmptyStr(argbuf
))
680 FloorBeingSearched
= extract_int(argbuf
, 0);
682 cprintf("%d Publiic rooms:\n", LISTING_FOLLOWS
);
684 ForEachRoom(cmd_lprm_backend
, &FloorBeingSearched
);
691 * cmd_lkrn() - List all known rooms with new messages
693 void cmd_lkrn_backend(struct ctdlroom
*qrbuf
, void *data
)
695 int FloorBeingSearched
= (-1);
699 FloorBeingSearched
= *(int *)data
;
700 CtdlRoomAccess(qrbuf
, &CC
->user
, &ra
, &view
);
703 && (ra
& UA_HASNEWMSGS
)
704 && ((qrbuf
->QRfloor
== (FloorBeingSearched
))
705 || ((FloorBeingSearched
) < 0)))
706 list_roomname(qrbuf
, ra
, view
, qrbuf
->QRdefaultview
);
709 void cmd_lkrn(char *argbuf
)
711 int FloorBeingSearched
= (-1);
712 if (!IsEmptyStr(argbuf
))
713 FloorBeingSearched
= extract_int(argbuf
, 0);
715 if (CtdlAccessCheck(ac_logged_in
)) return;
717 if (getuser(&CC
->user
, CC
->curr_user
)) {
718 cprintf("%d Can't locate user!\n", ERROR
+ INTERNAL_ERROR
);
721 cprintf("%d Rooms w/ new msgs:\n", LISTING_FOLLOWS
);
723 ForEachRoom(cmd_lkrn_backend
, &FloorBeingSearched
);
730 * cmd_lkro() - List all known rooms
732 void cmd_lkro_backend(struct ctdlroom
*qrbuf
, void *data
)
734 int FloorBeingSearched
= (-1);
738 FloorBeingSearched
= *(int *)data
;
739 CtdlRoomAccess(qrbuf
, &CC
->user
, &ra
, &view
);
742 && ((ra
& UA_HASNEWMSGS
) == 0)
743 && ((qrbuf
->QRfloor
== (FloorBeingSearched
))
744 || ((FloorBeingSearched
) < 0)))
745 list_roomname(qrbuf
, ra
, view
, qrbuf
->QRdefaultview
);
748 void cmd_lkro(char *argbuf
)
750 int FloorBeingSearched
= (-1);
751 if (!IsEmptyStr(argbuf
))
752 FloorBeingSearched
= extract_int(argbuf
, 0);
754 if (CtdlAccessCheck(ac_logged_in
)) return;
756 if (getuser(&CC
->user
, CC
->curr_user
)) {
757 cprintf("%d Can't locate user!\n", ERROR
+ INTERNAL_ERROR
);
760 cprintf("%d Rooms w/o new msgs:\n", LISTING_FOLLOWS
);
762 ForEachRoom(cmd_lkro_backend
, &FloorBeingSearched
);
769 * cmd_lzrm() - List all forgotten rooms
771 void cmd_lzrm_backend(struct ctdlroom
*qrbuf
, void *data
)
773 int FloorBeingSearched
= (-1);
777 FloorBeingSearched
= *(int *)data
;
778 CtdlRoomAccess(qrbuf
, &CC
->user
, &ra
, &view
);
780 if ((ra
& UA_GOTOALLOWED
)
782 && ((qrbuf
->QRfloor
== (FloorBeingSearched
))
783 || ((FloorBeingSearched
) < 0)))
784 list_roomname(qrbuf
, ra
, view
, qrbuf
->QRdefaultview
);
787 void cmd_lzrm(char *argbuf
)
789 int FloorBeingSearched
= (-1);
790 if (!IsEmptyStr(argbuf
))
791 FloorBeingSearched
= extract_int(argbuf
, 0);
793 if (CtdlAccessCheck(ac_logged_in
)) return;
795 if (getuser(&CC
->user
, CC
->curr_user
)) {
796 cprintf("%d Can't locate user!\n", ERROR
+ INTERNAL_ERROR
);
799 cprintf("%d Zapped rooms:\n", LISTING_FOLLOWS
);
801 ForEachRoom(cmd_lzrm_backend
, &FloorBeingSearched
);
807 * Make the specified room the current room for this session. No validation
808 * or access control is done here -- the caller should make sure that the
809 * specified room exists and is ok to access.
811 void usergoto(char *where
, int display_result
, int transiently
,
812 int *retmsgs
, int *retnew
)
815 int new_messages
= 0;
816 int old_messages
= 0;
817 int total_messages
= 0;
821 int newmailcount
= 0;
823 char truncated_roomname
[ROOMNAMELEN
];
824 struct cdbdata
*cdbfr
;
825 long *msglist
= NULL
;
827 unsigned int original_v_flags
;
830 char setstr
[128], lostr
[64], histr
[64];
834 /* If the supplied room name is NULL, the caller wants us to know that
835 * it has already copied the room record into CC->room, so
836 * we can skip the extra database fetch.
839 safestrncpy(CC
->room
.QRname
, where
, sizeof CC
->room
.QRname
);
840 getroom(&CC
->room
, where
);
843 /* Take care of all the formalities. */
845 begin_critical_section(S_USERS
);
846 CtdlGetRelationship(&vbuf
, &CC
->user
, &CC
->room
);
847 original_v_flags
= vbuf
.v_flags
;
849 /* Know the room ... but not if it's the page log room, or if the
850 * caller specified that we're only entering this room transiently.
852 if ((strcasecmp(CC
->room
.QRname
, config
.c_logpages
))
853 && (transiently
== 0) ) {
854 vbuf
.v_flags
= vbuf
.v_flags
& ~V_FORGET
& ~V_LOCKOUT
;
855 vbuf
.v_flags
= vbuf
.v_flags
| V_ACCESS
;
858 /* Only rewrite the database record if we changed something */
859 if (vbuf
.v_flags
!= original_v_flags
) {
860 CtdlSetRelationship(&vbuf
, &CC
->user
, &CC
->room
);
862 end_critical_section(S_USERS
);
864 /* Check for new mail */
865 newmailcount
= NewMailCount();
867 /* set info to 1 if the user needs to read the room's info file */
868 if (CC
->room
.QRinfo
> vbuf
.v_lastseen
) {
872 cdbfr
= cdb_fetch(CDB_MSGLISTS
, &CC
->room
.QRnumber
, sizeof(long));
874 msglist
= (long *) cdbfr
->ptr
;
875 cdbfr
->ptr
= NULL
; /* usergoto() now owns this memory */
876 num_msgs
= cdbfr
->len
/ sizeof(long);
881 for (a
=0; a
<num_msgs
; ++a
) {
882 if (msglist
[a
] > 0L) ++total_messages
;
884 new_messages
= num_msgs
;
885 num_sets
= num_tokens(vbuf
.v_seen
, ',');
886 for (s
=0; s
<num_sets
; ++s
) {
887 extract_token(setstr
, vbuf
.v_seen
, s
, ',', sizeof setstr
);
889 extract_token(lostr
, setstr
, 0, ':', sizeof lostr
);
890 if (num_tokens(setstr
, ':') >= 2) {
891 extract_token(histr
, setstr
, 1, ':', sizeof histr
);
892 if (!strcmp(histr
, "*")) {
893 snprintf(histr
, sizeof histr
, "%ld", LONG_MAX
);
897 strcpy(histr
, lostr
);
902 for (a
=0; a
<num_msgs
; ++a
) if (msglist
[a
] > 0L) {
903 if ((msglist
[a
] >= lo
) && (msglist
[a
] <= hi
)) {
909 new_messages
= total_messages
- old_messages
;
911 if (msglist
!= NULL
) free(msglist
);
913 if (CC
->room
.QRflags
& QR_MAILBOX
)
918 if ((CC
->room
.QRroomaide
== CC
->user
.usernum
)
919 || (CC
->user
.axlevel
>= 6))
924 safestrncpy(truncated_roomname
, CC
->room
.QRname
, sizeof truncated_roomname
);
925 if ( (CC
->room
.QRflags
& QR_MAILBOX
)
926 && (atol(CC
->room
.QRname
) == CC
->user
.usernum
) ) {
927 safestrncpy(truncated_roomname
, &truncated_roomname
[11], sizeof truncated_roomname
);
930 if (!strcasecmp(truncated_roomname
, USERTRASHROOM
)) {
934 if (retmsgs
!= NULL
) *retmsgs
= total_messages
;
935 if (retnew
!= NULL
) *retnew
= new_messages
;
936 CtdlLogPrintf(CTDL_DEBUG
, "<%s> %d new of %d total messages\n",
938 new_messages
, total_messages
941 CC
->curr_view
= (int)vbuf
.v_view
;
943 if (display_result
) {
944 cprintf("%d%c%s|%d|%d|%d|%d|%ld|%ld|%d|%d|%d|%d|%d|%d|%d|%d|\n",
945 CIT_OK
, CtdlCheckExpress(),
950 (int)CC
->room
.QRflags
,
951 (long)CC
->room
.QRhighest
,
952 (long)vbuf
.v_lastseen
,
956 (int)CC
->room
.QRfloor
,
958 (int)CC
->room
.QRdefaultview
,
960 (int)CC
->room
.QRflags2
967 * Handle some of the macro named rooms
969 void convert_room_name_macros(char *towhere
, size_t maxlen
) {
970 if (!strcasecmp(towhere
, "_BASEROOM_")) {
971 safestrncpy(towhere
, config
.c_baseroom
, maxlen
);
973 else if (!strcasecmp(towhere
, "_MAIL_")) {
974 safestrncpy(towhere
, MAILROOM
, maxlen
);
976 else if (!strcasecmp(towhere
, "_TRASH_")) {
977 safestrncpy(towhere
, USERTRASHROOM
, maxlen
);
979 else if (!strcasecmp(towhere
, "_BITBUCKET_")) {
980 safestrncpy(towhere
, config
.c_twitroom
, maxlen
);
982 else if (!strcasecmp(towhere
, "_CALENDAR_")) {
983 safestrncpy(towhere
, USERCALENDARROOM
, maxlen
);
985 else if (!strcasecmp(towhere
, "_TASKS_")) {
986 safestrncpy(towhere
, USERTASKSROOM
, maxlen
);
988 else if (!strcasecmp(towhere
, "_CONTACTS_")) {
989 safestrncpy(towhere
, USERCONTACTSROOM
, maxlen
);
991 else if (!strcasecmp(towhere
, "_NOTES_")) {
992 safestrncpy(towhere
, USERNOTESROOM
, maxlen
);
998 * cmd_goto() - goto a new room
1000 void cmd_goto(char *gargs
)
1002 struct ctdlroom QRscratch
;
1006 char augmented_roomname
[ROOMNAMELEN
];
1007 char towhere
[ROOMNAMELEN
];
1009 int transiently
= 0;
1011 if (CtdlAccessCheck(ac_logged_in
)) return;
1013 extract_token(towhere
, gargs
, 0, '|', sizeof towhere
);
1014 extract_token(password
, gargs
, 1, '|', sizeof password
);
1015 transiently
= extract_int(gargs
, 2);
1017 getuser(&CC
->user
, CC
->curr_user
);
1020 * Handle some of the macro named rooms
1022 convert_room_name_macros(towhere
, sizeof towhere
);
1024 /* First try a regular match */
1025 c
= getroom(&QRscratch
, towhere
);
1027 /* Then try a mailbox name match */
1029 MailboxName(augmented_roomname
, sizeof augmented_roomname
,
1030 &CC
->user
, towhere
);
1031 c
= getroom(&QRscratch
, augmented_roomname
);
1033 safestrncpy(towhere
, augmented_roomname
, sizeof towhere
);
1036 /* And if the room was found... */
1039 /* Let internal programs go directly to any room. */
1040 if (CC
->internal_pgm
) {
1041 memcpy(&CC
->room
, &QRscratch
,
1042 sizeof(struct ctdlroom
));
1043 usergoto(NULL
, 1, transiently
, NULL
, NULL
);
1047 /* See if there is an existing user/room relationship */
1048 CtdlRoomAccess(&QRscratch
, &CC
->user
, &ra
, NULL
);
1050 /* normal clients have to pass through security */
1051 if (ra
& UA_GOTOALLOWED
) {
1056 if ((QRscratch
.QRflags
& QR_MAILBOX
) &&
1057 ((ra
& UA_GOTOALLOWED
))) {
1058 memcpy(&CC
->room
, &QRscratch
,
1059 sizeof(struct ctdlroom
));
1060 usergoto(NULL
, 1, transiently
, NULL
, NULL
);
1062 } else if ((QRscratch
.QRflags
& QR_PASSWORDED
) &&
1063 ((ra
& UA_KNOWN
) == 0) &&
1064 (strcasecmp(QRscratch
.QRpasswd
, password
)) &&
1065 (CC
->user
.axlevel
< 6)
1067 cprintf("%d wrong or missing passwd\n",
1068 ERROR
+ PASSWORD_REQUIRED
);
1070 } else if ((QRscratch
.QRflags
& QR_PRIVATE
) &&
1071 ((QRscratch
.QRflags
& QR_PASSWORDED
) == 0) &&
1072 ((QRscratch
.QRflags
& QR_GUESSNAME
) == 0) &&
1073 ((ra
& UA_KNOWN
) == 0) &&
1074 (CC
->user
.axlevel
< 6)
1076 CtdlLogPrintf(CTDL_DEBUG
, "Failed to acquire private room\n");
1078 memcpy(&CC
->room
, &QRscratch
,
1079 sizeof(struct ctdlroom
));
1080 usergoto(NULL
, 1, transiently
, NULL
, NULL
);
1086 cprintf("%d room '%s' not found\n", ERROR
+ ROOM_NOT_FOUND
, towhere
);
1092 struct ctdluser temp
;
1093 struct cdbdata
*cdbus
;
1096 cprintf("%d Who knows room:\n", LISTING_FOLLOWS
);
1097 cdb_rewind(CDB_USERS
);
1098 while (cdbus
= cdb_next_item(CDB_USERS
), cdbus
!= NULL
) {
1099 memset(&temp
, 0, sizeof temp
);
1100 memcpy(&temp
, cdbus
->ptr
, sizeof temp
);
1103 CtdlRoomAccess(&CC
->room
, &temp
, &ra
, NULL
);
1104 if ((CC
->room
.QRflags
& QR_INUSE
)
1107 cprintf("%s\n", temp
.fullname
);
1114 * RDIR command for room directory
1121 struct stat statbuf
;
1122 DIR *filedir
= NULL
;
1123 struct dirent
*filedir_entry
;
1129 if (CtdlAccessCheck(ac_logged_in
)) return;
1131 getroom(&CC
->room
, CC
->room
.QRname
);
1132 getuser(&CC
->user
, CC
->curr_user
);
1134 if ((CC
->room
.QRflags
& QR_DIRECTORY
) == 0) {
1135 cprintf("%d not here.\n", ERROR
+ NOT_HERE
);
1138 if (((CC
->room
.QRflags
& QR_VISDIR
) == 0)
1139 && (CC
->user
.axlevel
< 6)
1140 && (CC
->user
.usernum
!= CC
->room
.QRroomaide
)) {
1141 cprintf("%d not here.\n", ERROR
+ HIGHER_ACCESS_REQUIRED
);
1145 snprintf(buf
, sizeof buf
, "%s/%s", ctdl_file_dir
, CC
->room
.QRdirname
);
1146 filedir
= opendir (buf
);
1148 if (filedir
== NULL
) {
1149 cprintf("%d not here.\n", ERROR
+ HIGHER_ACCESS_REQUIRED
);
1152 cprintf("%d %s|%s/%s\n", LISTING_FOLLOWS
, config
.c_fqdn
, ctdl_file_dir
, CC
->room
.QRdirname
);
1154 snprintf(buf
, sizeof buf
, "%s/%s/filedir", ctdl_file_dir
, CC
->room
.QRdirname
);
1155 fd
= fopen(buf
, "r");
1157 fd
= fopen("/dev/null", "r");
1158 while ((filedir_entry
= readdir(filedir
)))
1160 if (strcasecmp(filedir_entry
->d_name
, "filedir") && filedir_entry
->d_name
[0] != '.')
1162 #ifdef _DIRENT_HAVE_D_NAMELEN
1163 d_namelen
= filedir_entry
->d_namelen
;
1165 d_namelen
= strlen(filedir_entry
->d_name
);
1167 snprintf(buf
, sizeof buf
, "%s/%s/%s", ctdl_file_dir
, CC
->room
.QRdirname
, filedir_entry
->d_name
);
1168 stat(buf
, &statbuf
); /* stat the file */
1169 if (!(statbuf
.st_mode
& S_IFREG
))
1171 snprintf(buf2
, sizeof buf2
, "Command RDIR found something that is not a useable file. It should be cleaned up.\n RDIR found this non regular file:\n%s\n", buf
);
1172 aide_message(buf2
, "RDIR found bad file");
1173 continue; /* not a useable file type so don't show it */
1175 safestrncpy(comment
, "", sizeof comment
);
1176 fseek(fd
, 0L, 0); /* rewind descriptions file */
1177 /* Get the description from the descriptions file */
1178 while ((fgets(buf
, sizeof buf
, fd
) != NULL
) && (IsEmptyStr(comment
)))
1180 buf
[strlen(buf
) - 1] = 0;
1181 if ((!strncasecmp(buf
, filedir_entry
->d_name
, d_namelen
)) && (buf
[d_namelen
] == ' '))
1182 safestrncpy(comment
, &buf
[d_namelen
+ 1], sizeof comment
);
1184 len
= extract_token (mimebuf
, comment
, 0,' ', 64);
1185 if ((len
<0) || strchr(mimebuf
, '/') == NULL
)
1187 snprintf (mimebuf
, 64, "application/octetstream");
1190 cprintf("%s|%ld|%s|%s\n",
1191 filedir_entry
->d_name
,
1192 (long)statbuf
.st_size
,
1204 * get room parameters (aide or room aide command)
1208 if (CtdlAccessCheck(ac_room_aide
)) return;
1210 getroom(&CC
->room
, CC
->room
.QRname
);
1211 cprintf("%d%c%s|%s|%s|%d|%d|%d|%d|%d|\n",
1215 ((CC
->room
.QRflags
& QR_MAILBOX
) ?
1216 &CC
->room
.QRname
[11] : CC
->room
.QRname
),
1218 ((CC
->room
.QRflags
& QR_PASSWORDED
) ?
1219 CC
->room
.QRpasswd
: ""),
1221 ((CC
->room
.QRflags
& QR_DIRECTORY
) ?
1222 CC
->room
.QRdirname
: ""),
1225 (int) CC
->room
.QRfloor
,
1226 (int) CC
->room
.QRorder
,
1228 CC
->room
.QRdefaultview
,
1235 * Back end function to rename a room.
1236 * You can also specify which floor to move the room to, or specify -1 to
1237 * keep the room on the same floor it was on.
1239 * If you are renaming a mailbox room, you must supply the namespace prefix
1240 * in *at least* the old name!
1242 int CtdlRenameRoom(char *old_name
, char *new_name
, int new_floor
) {
1244 struct ctdlroom qrbuf
;
1245 struct ctdlroom qrtmp
;
1250 char actual_old_name
[ROOMNAMELEN
];
1252 CtdlLogPrintf(CTDL_DEBUG
, "CtdlRenameRoom(%s, %s, %d)\n",
1253 old_name
, new_name
, new_floor
);
1255 if (new_floor
>= 0) {
1256 fl
= cgetfloor(new_floor
);
1257 if ((fl
->f_flags
& F_INUSE
) == 0) {
1258 return(crr_invalid_floor
);
1262 begin_critical_section(S_ROOMS
);
1264 if ( (getroom(&qrtmp
, new_name
) == 0)
1265 && (strcasecmp(new_name
, old_name
)) ) {
1266 ret
= crr_already_exists
;
1269 else if (getroom(&qrbuf
, old_name
) != 0) {
1270 ret
= crr_room_not_found
;
1273 else if ( (CC
->user
.axlevel
< 6) && (!CC
->internal_pgm
)
1274 && (CC
->user
.usernum
!= qrbuf
.QRroomaide
)
1275 && ( (((qrbuf
.QRflags
& QR_MAILBOX
) == 0) || (atol(qrbuf
.QRname
) != CC
->user
.usernum
))) ) {
1276 ret
= crr_access_denied
;
1279 else if (is_noneditable(&qrbuf
)) {
1280 ret
= crr_noneditable
;
1285 safestrncpy(actual_old_name
, qrbuf
.QRname
, sizeof actual_old_name
);
1286 if (qrbuf
.QRflags
& QR_MAILBOX
) {
1287 owner
= atol(qrbuf
.QRname
);
1289 if ( (owner
> 0L) && (atol(new_name
) == 0L) ) {
1290 snprintf(qrbuf
.QRname
, sizeof(qrbuf
.QRname
),
1291 "%010ld.%s", owner
, new_name
);
1294 safestrncpy(qrbuf
.QRname
, new_name
,
1295 sizeof(qrbuf
.QRname
));
1298 /* Reject change of floor for baseroom/aideroom */
1299 if (!strncasecmp(old_name
, config
.c_baseroom
, ROOMNAMELEN
) ||
1300 !strncasecmp(old_name
, config
.c_aideroom
, ROOMNAMELEN
)) {
1304 /* Take care of floor stuff */
1305 old_floor
= qrbuf
.QRfloor
;
1306 if (new_floor
< 0) {
1307 new_floor
= old_floor
;
1309 qrbuf
.QRfloor
= new_floor
;
1312 begin_critical_section(S_CONFIG
);
1314 /* If baseroom/aideroom name changes, update config */
1315 if (!strncasecmp(old_name
, config
.c_baseroom
, ROOMNAMELEN
)) {
1316 safestrncpy(config
.c_baseroom
, new_name
, ROOMNAMELEN
);
1319 if (!strncasecmp(old_name
, config
.c_aideroom
, ROOMNAMELEN
)) {
1320 safestrncpy(config
.c_aideroom
, new_name
, ROOMNAMELEN
);
1324 end_critical_section(S_CONFIG
);
1326 /* If the room name changed, then there are now two room
1327 * records, so we have to delete the old one.
1329 if (strcasecmp(new_name
, old_name
)) {
1330 b_deleteroom(actual_old_name
);
1336 end_critical_section(S_ROOMS
);
1338 /* Adjust the floor reference counts if necessary */
1339 if (new_floor
!= old_floor
) {
1340 lgetfloor(&flbuf
, old_floor
);
1341 --flbuf
.f_ref_count
;
1342 lputfloor(&flbuf
, old_floor
);
1343 CtdlLogPrintf(CTDL_DEBUG
, "Reference count for floor %d is now %d\n", old_floor
, flbuf
.f_ref_count
);
1344 lgetfloor(&flbuf
, new_floor
);
1345 ++flbuf
.f_ref_count
;
1346 lputfloor(&flbuf
, new_floor
);
1347 CtdlLogPrintf(CTDL_DEBUG
, "Reference count for floor %d is now %d\n", new_floor
, flbuf
.f_ref_count
);
1350 /* ...and everybody say "YATTA!" */
1356 * set room parameters (aide or room aide command)
1358 void cmd_setr(char *args
)
1364 char new_name
[ROOMNAMELEN
];
1366 if (CtdlAccessCheck(ac_logged_in
)) return;
1368 if (num_parms(args
) >= 6) {
1369 new_floor
= extract_int(args
, 5);
1371 new_floor
= (-1); /* don't change the floor */
1374 /* When is a new name more than just a new name? When the old name
1375 * has a namespace prefix.
1377 if (CC
->room
.QRflags
& QR_MAILBOX
) {
1378 sprintf(new_name
, "%010ld.", atol(CC
->room
.QRname
) );
1380 safestrncpy(new_name
, "", sizeof new_name
);
1382 extract_token(&new_name
[strlen(new_name
)], args
, 0, '|', (sizeof new_name
- strlen(new_name
)));
1384 r
= CtdlRenameRoom(CC
->room
.QRname
, new_name
, new_floor
);
1386 if (r
== crr_room_not_found
) {
1387 cprintf("%d Internal error - room not found?\n", ERROR
+ INTERNAL_ERROR
);
1388 } else if (r
== crr_already_exists
) {
1389 cprintf("%d '%s' already exists.\n",
1390 ERROR
+ ALREADY_EXISTS
, new_name
);
1391 } else if (r
== crr_noneditable
) {
1392 cprintf("%d Cannot edit this room.\n", ERROR
+ NOT_HERE
);
1393 } else if (r
== crr_invalid_floor
) {
1394 cprintf("%d Target floor does not exist.\n",
1395 ERROR
+ INVALID_FLOOR_OPERATION
);
1396 } else if (r
== crr_access_denied
) {
1397 cprintf("%d You do not have permission to edit '%s'\n",
1398 ERROR
+ HIGHER_ACCESS_REQUIRED
,
1400 } else if (r
!= crr_ok
) {
1401 cprintf("%d Error: CtdlRenameRoom() returned %d\n",
1402 ERROR
+ INTERNAL_ERROR
, r
);
1409 getroom(&CC
->room
, new_name
);
1411 /* Now we have to do a bunch of other stuff */
1413 if (num_parms(args
) >= 7) {
1414 new_order
= extract_int(args
, 6);
1417 if (new_order
> 127)
1421 lgetroom(&CC
->room
, CC
->room
.QRname
);
1423 /* Directory room */
1424 extract_token(buf
, args
, 2, '|', sizeof buf
);
1426 safestrncpy(CC
->room
.QRdirname
, buf
,
1427 sizeof CC
->room
.QRdirname
);
1430 if (num_parms(args
) >= 8) {
1431 CC
->room
.QRdefaultview
= extract_int(args
, 7);
1434 /* Second set of flags */
1435 if (num_parms(args
) >= 9) {
1436 CC
->room
.QRflags2
= extract_int(args
, 8);
1440 CC
->room
.QRflags
= (extract_int(args
, 3) | QR_INUSE
);
1441 /* Clean up a client boo-boo: if the client set the room to
1442 * guess-name or passworded, ensure that the private flag is
1445 if ((CC
->room
.QRflags
& QR_GUESSNAME
)
1446 || (CC
->room
.QRflags
& QR_PASSWORDED
))
1447 CC
->room
.QRflags
|= QR_PRIVATE
;
1449 /* Some changes can't apply to BASEROOM */
1450 if (!strncasecmp(CC
->room
.QRname
, config
.c_baseroom
,
1452 CC
->room
.QRorder
= 0;
1453 CC
->room
.QRpasswd
[0] = '\0';
1454 CC
->room
.QRflags
&= ~(QR_PRIVATE
& QR_PASSWORDED
&
1455 QR_GUESSNAME
& QR_PREFONLY
& QR_MAILBOX
);
1456 CC
->room
.QRflags
|= QR_PERMANENT
;
1458 /* March order (doesn't apply to AIDEROOM) */
1459 if (num_parms(args
) >= 7)
1460 CC
->room
.QRorder
= (char) new_order
;
1462 extract_token(buf
, args
, 1, '|', sizeof buf
);
1464 safestrncpy(CC
->room
.QRpasswd
, buf
,
1465 sizeof CC
->room
.QRpasswd
);
1466 /* Kick everyone out if the client requested it
1467 * (by changing the room's generation number)
1469 if (extract_int(args
, 4)) {
1470 time(&CC
->room
.QRgen
);
1473 /* Some changes can't apply to AIDEROOM */
1474 if (!strncasecmp(CC
->room
.QRname
, config
.c_baseroom
,
1476 CC
->room
.QRorder
= 0;
1477 CC
->room
.QRflags
&= ~QR_MAILBOX
;
1478 CC
->room
.QRflags
|= QR_PERMANENT
;
1481 /* Write the room record back to disk */
1482 lputroom(&CC
->room
);
1484 /* Create a room directory if necessary */
1485 if (CC
->room
.QRflags
& QR_DIRECTORY
) {
1486 snprintf(buf
, sizeof buf
,"%s/%s",
1488 CC
->room
.QRdirname
);
1491 snprintf(buf
, sizeof buf
, "The room \"%s\" has been edited by %s.\n",
1492 CC
->room
.QRname
, CC
->curr_user
);
1493 aide_message(buf
, "Room modification Message");
1494 cprintf("%d Ok\n", CIT_OK
);
1500 * get the name of the room aide for this room
1504 struct ctdluser usbuf
;
1506 if (CtdlAccessCheck(ac_logged_in
)) return;
1508 if (getuserbynumber(&usbuf
, CC
->room
.QRroomaide
) == 0) {
1509 cprintf("%d %s\n", CIT_OK
, usbuf
.fullname
);
1511 cprintf("%d \n", CIT_OK
);
1517 * set the room aide for this room
1519 void cmd_seta(char *new_ra
)
1521 struct ctdluser usbuf
;
1526 if (CtdlAccessCheck(ac_room_aide
)) return;
1528 if (getuser(&usbuf
, new_ra
) != 0) {
1531 newu
= usbuf
.usernum
;
1534 lgetroom(&CC
->room
, CC
->room
.QRname
);
1536 if (CC
->room
.QRroomaide
!= newu
) {
1539 CC
->room
.QRroomaide
= newu
;
1540 lputroom(&CC
->room
);
1543 * We have to post the change notice _after_ writing changes to
1544 * the room table, otherwise it would deadlock!
1546 if (post_notice
== 1) {
1547 if (!IsEmptyStr(usbuf
.fullname
))
1548 snprintf(buf
, sizeof buf
,
1549 "%s is now the room aide for \"%s\".\n",
1550 usbuf
.fullname
, CC
->room
.QRname
);
1552 snprintf(buf
, sizeof buf
,
1553 "There is now no room aide for \"%s\".\n",
1555 aide_message(buf
, "Aide Room Modification");
1557 cprintf("%d Ok\n", CIT_OK
);
1561 * retrieve info file for this room
1569 assoc_file_name(filename
, sizeof filename
, &CC
->room
, ctdl_info_dir
);
1570 info_fp
= fopen(filename
, "r");
1572 if (info_fp
== NULL
) {
1573 cprintf("%d No info file.\n", ERROR
+ FILE_NOT_FOUND
);
1576 cprintf("%d Info:\n", LISTING_FOLLOWS
);
1577 while (fgets(buf
, sizeof buf
, info_fp
) != NULL
) {
1578 if (!IsEmptyStr(buf
))
1579 buf
[strlen(buf
) - 1] = 0;
1580 cprintf("%s\n", buf
);
1587 * Asynchronously schedule a room for deletion. The room will appear
1588 * deleted to the user(s), but it won't actually get purged from the
1589 * database until THE DREADED AUTO-PURGER makes its next run.
1591 void schedule_room_for_deletion(struct ctdlroom
*qrbuf
)
1593 char old_name
[ROOMNAMELEN
];
1596 CtdlLogPrintf(CTDL_NOTICE
, "Scheduling room <%s> for deletion\n",
1599 safestrncpy(old_name
, qrbuf
->QRname
, sizeof old_name
);
1601 getroom(qrbuf
, qrbuf
->QRname
);
1603 /* Turn the room into a private mailbox owned by a user who doesn't
1604 * exist. This will immediately make the room invisible to everyone,
1605 * and qualify the room for purging.
1607 snprintf(qrbuf
->QRname
, sizeof qrbuf
->QRname
, "9999999999.%08lx.%04d.%s",
1612 qrbuf
->QRflags
|= QR_MAILBOX
;
1613 time(&qrbuf
->QRgen
); /* Use a timestamp as the new generation number */
1617 b_deleteroom(old_name
);
1623 * Back end processing to delete a room and everything associated with it
1624 * (This one is synchronous and should only get called by THE DREADED
1625 * AUTO-PURGER in serv_expire.c. All user-facing code should call
1626 * the asynchronous schedule_room_for_deletion() instead.)
1628 void delete_room(struct ctdlroom
*qrbuf
)
1632 /* TODO: filename magic? does this realy work? */
1634 CtdlLogPrintf(CTDL_NOTICE
, "Deleting room <%s>\n", qrbuf
->QRname
);
1636 /* Delete the info file */
1637 assoc_file_name(filename
, sizeof filename
, qrbuf
, ctdl_info_dir
);
1640 /* Delete the image file */
1641 assoc_file_name(filename
, sizeof filename
, qrbuf
, ctdl_image_dir
);
1644 /* Delete the room's network config file */
1645 assoc_file_name(filename
, sizeof filename
, qrbuf
, ctdl_netcfg_dir
);
1648 /* Delete the messages in the room
1649 * (Careful: this opens an S_ROOMS critical section!)
1651 CtdlDeleteMessages(qrbuf
->QRname
, NULL
, 0, "");
1653 /* Flag the room record as not in use */
1654 lgetroom(qrbuf
, qrbuf
->QRname
);
1658 /* then decrement the reference count for the floor */
1659 lgetfloor(&flbuf
, (int) (qrbuf
->QRfloor
));
1660 flbuf
.f_ref_count
= flbuf
.f_ref_count
- 1;
1661 lputfloor(&flbuf
, (int) (qrbuf
->QRfloor
));
1663 /* Delete the room record from the database! */
1664 b_deleteroom(qrbuf
->QRname
);
1670 * Check access control for deleting a room
1672 int CtdlDoIHavePermissionToDeleteThisRoom(struct ctdlroom
*qr
) {
1674 if ((!(CC
->logged_in
)) && (!(CC
->internal_pgm
))) {
1678 if (is_noneditable(qr
)) {
1683 * For mailboxes, check stuff
1685 if (qr
->QRflags
& QR_MAILBOX
) {
1687 if (strlen(qr
->QRname
) < 12) return(0); /* bad name */
1689 if (atol(qr
->QRname
) != CC
->user
.usernum
) {
1690 return(0); /* not my room */
1693 /* Can't delete your Mail> room */
1694 if (!strcasecmp(&qr
->QRname
[11], MAILROOM
)) return(0);
1696 /* Otherwise it's ok */
1701 * For normal rooms, just check for aide or room aide status.
1703 return(is_room_aide());
1707 * aide command: kill the current room
1709 void cmd_kill(char *argbuf
)
1711 char deleted_room_name
[ROOMNAMELEN
];
1715 kill_ok
= extract_int(argbuf
, 0);
1717 if (CtdlDoIHavePermissionToDeleteThisRoom(&CC
->room
) == 0) {
1718 cprintf("%d Can't delete this room.\n", ERROR
+ NOT_HERE
);
1722 if (CC
->room
.QRflags
& QR_MAILBOX
) {
1723 safestrncpy(deleted_room_name
, &CC
->room
.QRname
[11], sizeof deleted_room_name
);
1726 safestrncpy(deleted_room_name
, CC
->room
.QRname
, sizeof deleted_room_name
);
1729 /* Do the dirty work */
1730 schedule_room_for_deletion(&CC
->room
);
1732 /* Return to the Lobby */
1733 usergoto(config
.c_baseroom
, 0, 0, NULL
, NULL
);
1735 /* tell the world what we did */
1736 snprintf(msg
, sizeof msg
, "The room \"%s\" has been deleted by %s.\n",
1737 deleted_room_name
, CC
->curr_user
);
1738 aide_message(msg
, "Room Purger Message");
1739 cprintf("%d '%s' deleted.\n", CIT_OK
, deleted_room_name
);
1741 cprintf("%d ok to delete.\n", CIT_OK
);
1747 * Internal code to create a new room (returns room flags)
1749 * Room types: 0=public, 1=guessname, 2=passworded, 3=inv-only,
1750 * 4=mailbox, 5=mailbox, but caller supplies namespace
1752 unsigned create_room(char *new_room_name
,
1754 char *new_room_pass
,
1761 struct ctdlroom qrbuf
;
1765 CtdlLogPrintf(CTDL_DEBUG
, "create_room(name=%s, type=%d, view=%d)\n",
1766 new_room_name
, new_room_type
, new_room_view
);
1768 if (getroom(&qrbuf
, new_room_name
) == 0) {
1769 CtdlLogPrintf(CTDL_DEBUG
, "%s already exists.\n", new_room_name
);
1773 memset(&qrbuf
, 0, sizeof(struct ctdlroom
));
1774 safestrncpy(qrbuf
.QRpasswd
, new_room_pass
, sizeof qrbuf
.QRpasswd
);
1775 qrbuf
.QRflags
= QR_INUSE
;
1776 if (new_room_type
> 0)
1777 qrbuf
.QRflags
= (qrbuf
.QRflags
| QR_PRIVATE
);
1778 if (new_room_type
== 1)
1779 qrbuf
.QRflags
= (qrbuf
.QRflags
| QR_GUESSNAME
);
1780 if (new_room_type
== 2)
1781 qrbuf
.QRflags
= (qrbuf
.QRflags
| QR_PASSWORDED
);
1782 if ( (new_room_type
== 4) || (new_room_type
== 5) ) {
1783 qrbuf
.QRflags
= (qrbuf
.QRflags
| QR_MAILBOX
);
1784 /* qrbuf.QRflags2 |= QR2_SUBJECTREQ; */
1787 /* If the user is requesting a personal room, set up the room
1788 * name accordingly (prepend the user number)
1790 if (new_room_type
== 4) {
1791 MailboxName(qrbuf
.QRname
, sizeof qrbuf
.QRname
, &CC
->user
, new_room_name
);
1794 safestrncpy(qrbuf
.QRname
, new_room_name
, sizeof qrbuf
.QRname
);
1797 /* If the room is private, and the system administrator has elected
1798 * to automatically grant room aide privileges, do so now; otherwise,
1799 * set the room aide to undefined.
1801 if ((qrbuf
.QRflags
& QR_PRIVATE
) && (CREATAIDE
== 1)) {
1802 qrbuf
.QRroomaide
= CC
->user
.usernum
;
1804 qrbuf
.QRroomaide
= (-1L);
1808 * If the caller is only interested in testing whether this will work,
1809 * return now without creating the room.
1811 if (!really_create
) return (qrbuf
.QRflags
);
1813 qrbuf
.QRnumber
= get_new_room_number();
1814 qrbuf
.QRhighest
= 0L; /* No messages in this room yet */
1815 time(&qrbuf
.QRgen
); /* Use a timestamp as the generation number */
1816 qrbuf
.QRfloor
= new_room_floor
;
1817 qrbuf
.QRdefaultview
= new_room_view
;
1819 /* save what we just did... */
1822 /* bump the reference count on whatever floor the room is on */
1823 lgetfloor(&flbuf
, (int) qrbuf
.QRfloor
);
1824 flbuf
.f_ref_count
= flbuf
.f_ref_count
+ 1;
1825 lputfloor(&flbuf
, (int) qrbuf
.QRfloor
);
1827 /* Grant the creator access to the room unless the avoid_access
1828 * parameter was specified.
1830 if ( (CC
->logged_in
) && (avoid_access
== 0) ) {
1831 CtdlGetRelationship(&vbuf
, &CC
->user
, &qrbuf
);
1832 vbuf
.v_flags
= vbuf
.v_flags
& ~V_FORGET
& ~V_LOCKOUT
;
1833 vbuf
.v_flags
= vbuf
.v_flags
| V_ACCESS
;
1834 CtdlSetRelationship(&vbuf
, &CC
->user
, &qrbuf
);
1837 /* resume our happy day */
1838 return (qrbuf
.QRflags
);
1845 void cmd_cre8(char *args
)
1848 char new_room_name
[ROOMNAMELEN
];
1850 char new_room_pass
[32];
1853 char *notification_message
= NULL
;
1856 int avoid_access
= 0;
1858 cre8_ok
= extract_int(args
, 0);
1859 extract_token(new_room_name
, args
, 1, '|', sizeof new_room_name
);
1860 new_room_name
[ROOMNAMELEN
- 1] = 0;
1861 new_room_type
= extract_int(args
, 2);
1862 extract_token(new_room_pass
, args
, 3, '|', sizeof new_room_pass
);
1863 avoid_access
= extract_int(args
, 5);
1864 new_room_view
= extract_int(args
, 6);
1865 new_room_pass
[9] = 0;
1868 if ((IsEmptyStr(new_room_name
)) && (cre8_ok
== 1)) {
1869 cprintf("%d Invalid room name.\n", ERROR
+ ILLEGAL_VALUE
);
1873 if (!strcasecmp(new_room_name
, MAILROOM
)) {
1874 cprintf("%d '%s' already exists.\n",
1875 ERROR
+ ALREADY_EXISTS
, new_room_name
);
1879 if (num_parms(args
) >= 5) {
1880 fl
= cgetfloor(extract_int(args
, 4));
1882 cprintf("%d Invalid floor number.\n",
1883 ERROR
+ INVALID_FLOOR_OPERATION
);
1886 else if ((fl
->f_flags
& F_INUSE
) == 0) {
1887 cprintf("%d Invalid floor number.\n",
1888 ERROR
+ INVALID_FLOOR_OPERATION
);
1891 new_room_floor
= extract_int(args
, 4);
1895 if (CtdlAccessCheck(ac_logged_in
)) return;
1897 if (CC
->user
.axlevel
< config
.c_createax
&& !CC
->internal_pgm
) {
1898 cprintf("%d You need higher access to create rooms.\n",
1899 ERROR
+ HIGHER_ACCESS_REQUIRED
);
1903 if ((IsEmptyStr(new_room_name
)) && (cre8_ok
== 0)) {
1904 cprintf("%d Ok to create rooms.\n", CIT_OK
);
1908 if ((new_room_type
< 0) || (new_room_type
> 5)) {
1909 cprintf("%d Invalid room type.\n", ERROR
+ ILLEGAL_VALUE
);
1913 if (new_room_type
== 5) {
1914 if (CC
->user
.axlevel
< 6) {
1915 cprintf("%d Higher access required\n",
1916 ERROR
+ HIGHER_ACCESS_REQUIRED
);
1921 /* Check to make sure the requested room name doesn't already exist */
1922 newflags
= create_room(new_room_name
,
1923 new_room_type
, new_room_pass
, new_room_floor
,
1924 0, avoid_access
, new_room_view
);
1925 if (newflags
== 0) {
1926 cprintf("%d '%s' already exists.\n",
1927 ERROR
+ ALREADY_EXISTS
, new_room_name
);
1932 cprintf("%d OK to create '%s'\n", CIT_OK
, new_room_name
);
1936 /* If we reach this point, the room needs to be created. */
1938 newflags
= create_room(new_room_name
,
1939 new_room_type
, new_room_pass
, new_room_floor
, 1, 0,
1942 /* post a message in Aide> describing the new room */
1943 notification_message
= malloc(1024);
1944 snprintf(notification_message
, 1024,
1945 "A new room called \"%s\" has been created by %s%s%s%s%s%s\n",
1948 ((newflags
& QR_MAILBOX
) ? " [personal]" : ""),
1949 ((newflags
& QR_PRIVATE
) ? " [private]" : ""),
1950 ((newflags
& QR_GUESSNAME
) ? " [hidden]" : ""),
1951 ((newflags
& QR_PASSWORDED
) ? " Password: " : ""),
1952 ((newflags
& QR_PASSWORDED
) ? new_room_pass
: "")
1954 aide_message(notification_message
, "Room Creation Message");
1955 free(notification_message
);
1957 cprintf("%d '%s' has been created.\n", CIT_OK
, new_room_name
);
1962 void cmd_einf(char *ok
)
1963 { /* enter info file for current room */
1965 char infofilename
[SIZ
];
1970 if (CtdlAccessCheck(ac_room_aide
)) return;
1972 if (atoi(ok
) == 0) {
1973 cprintf("%d Ok.\n", CIT_OK
);
1976 assoc_file_name(infofilename
, sizeof infofilename
, &CC
->room
, ctdl_info_dir
);
1977 CtdlLogPrintf(CTDL_DEBUG
, "opening\n");
1978 fp
= fopen(infofilename
, "w");
1979 CtdlLogPrintf(CTDL_DEBUG
, "checking\n");
1981 cprintf("%d Cannot open %s: %s\n",
1982 ERROR
+ INTERNAL_ERROR
, infofilename
, strerror(errno
));
1985 cprintf("%d Send info...\n", SEND_LISTING
);
1988 client_getln(buf
, sizeof buf
);
1989 if (strcmp(buf
, "000"))
1990 fprintf(fp
, "%s\n", buf
);
1991 } while (strcmp(buf
, "000"));
1994 /* now update the room index so people will see our new info */
1995 lgetroom(&CC
->room
, CC
->room
.QRname
); /* lock so no one steps on us */
1996 CC
->room
.QRinfo
= CC
->room
.QRhighest
+ 1L;
1997 lputroom(&CC
->room
);
2002 * cmd_lflr() - List all known floors
2009 if (CtdlAccessCheck(ac_logged_in
)) return;
2011 cprintf("%d Known floors:\n", LISTING_FOLLOWS
);
2013 for (a
= 0; a
< MAXFLOORS
; ++a
) {
2014 getfloor(&flbuf
, a
);
2015 if (flbuf
.f_flags
& F_INUSE
) {
2016 cprintf("%d|%s|%d\n",
2028 * create a new floor
2030 void cmd_cflr(char *argbuf
)
2032 char new_floor_name
[256];
2035 int free_slot
= (-1);
2038 extract_token(new_floor_name
, argbuf
, 0, '|', sizeof new_floor_name
);
2039 cflr_ok
= extract_int(argbuf
, 1);
2041 if (CtdlAccessCheck(ac_aide
)) return;
2043 if (IsEmptyStr(new_floor_name
)) {
2044 cprintf("%d Blank floor name not allowed.\n",
2045 ERROR
+ ILLEGAL_VALUE
);
2049 for (a
= 0; a
< MAXFLOORS
; ++a
) {
2050 getfloor(&flbuf
, a
);
2052 /* note any free slots while we're scanning... */
2053 if (((flbuf
.f_flags
& F_INUSE
) == 0)
2057 /* check to see if it already exists */
2058 if ((!strcasecmp(flbuf
.f_name
, new_floor_name
))
2059 && (flbuf
.f_flags
& F_INUSE
)) {
2060 cprintf("%d Floor '%s' already exists.\n",
2061 ERROR
+ ALREADY_EXISTS
,
2067 if (free_slot
< 0) {
2068 cprintf("%d There is no space available for a new floor.\n",
2069 ERROR
+ INVALID_FLOOR_OPERATION
);
2073 cprintf("%d ok to create...\n", CIT_OK
);
2076 lgetfloor(&flbuf
, free_slot
);
2077 flbuf
.f_flags
= F_INUSE
;
2078 flbuf
.f_ref_count
= 0;
2079 safestrncpy(flbuf
.f_name
, new_floor_name
, sizeof flbuf
.f_name
);
2080 lputfloor(&flbuf
, free_slot
);
2081 cprintf("%d %d\n", CIT_OK
, free_slot
);
2089 void cmd_kflr(char *argbuf
)
2092 int floor_to_delete
;
2096 floor_to_delete
= extract_int(argbuf
, 0);
2097 kflr_ok
= extract_int(argbuf
, 1);
2099 if (CtdlAccessCheck(ac_aide
)) return;
2101 lgetfloor(&flbuf
, floor_to_delete
);
2104 if ((flbuf
.f_flags
& F_INUSE
) == 0) {
2105 cprintf("%d Floor %d not in use.\n",
2106 ERROR
+ INVALID_FLOOR_OPERATION
, floor_to_delete
);
2109 if (flbuf
.f_ref_count
!= 0) {
2110 cprintf("%d Cannot delete; floor contains %d rooms.\n",
2111 ERROR
+ INVALID_FLOOR_OPERATION
,
2116 cprintf("%d Ok\n", CIT_OK
);
2118 cprintf("%d Ok to delete...\n", CIT_OK
);
2125 if ((delete_ok
== 1) && (kflr_ok
== 1))
2127 lputfloor(&flbuf
, floor_to_delete
);
2133 void cmd_eflr(char *argbuf
)
2139 np
= num_parms(argbuf
);
2141 cprintf("%d Usage error.\n", ERROR
+ ILLEGAL_VALUE
);
2145 if (CtdlAccessCheck(ac_aide
)) return;
2147 floor_num
= extract_int(argbuf
, 0);
2148 lgetfloor(&flbuf
, floor_num
);
2149 if ((flbuf
.f_flags
& F_INUSE
) == 0) {
2150 lputfloor(&flbuf
, floor_num
);
2151 cprintf("%d Floor %d is not in use.\n",
2152 ERROR
+ INVALID_FLOOR_OPERATION
, floor_num
);
2156 extract_token(flbuf
.f_name
, argbuf
, 1, '|', sizeof flbuf
.f_name
);
2157 lputfloor(&flbuf
, floor_num
);
2159 cprintf("%d Ok\n", CIT_OK
);