4 * Implements the message store.
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
23 # include <sys/time.h>
38 #include "dynloader.h"
42 #include "sysdep_decls.h"
43 #include "citserver.h"
49 #include "mime_parser.h"
52 #include "internet_addressing.h"
54 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
55 #define ma ((struct ma_info *)CtdlGetUserData(SYM_MA_INFO))
56 #define msg_repl ((struct repl *)CtdlGetUserData(SYM_REPL))
58 extern struct config config;
62 "", "", "", "", "", "", "", "",
63 "", "", "", "", "", "", "", "",
64 "", "", "", "", "", "", "", "",
65 "", "", "", "", "", "", "", "",
66 "", "", "", "", "", "", "", "",
67 "", "", "", "", "", "", "", "",
68 "", "", "", "", "", "", "", "",
69 "", "", "", "", "", "", "", "",
96 * This function is self explanatory.
97 * (What can I say, I'm in a weird mood today...)
99 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
103 for (i = 0; i < strlen(name); ++i) {
104 if (name[i] == '@') {
105 while (isspace(name[i - 1]) && i > 0) {
106 strcpy(&name[i - 1], &name[i]);
109 while (isspace(name[i + 1])) {
110 strcpy(&name[i + 1], &name[i + 2]);
118 * Aliasing for network mail.
119 * (Error messages have been commented out, because this is a server.)
121 int alias(char *name)
122 { /* process alias and routing info for mail */
125 char aaa[300], bbb[300];
127 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
129 fp = fopen("network/mail.aliases", "r");
131 fp = fopen("/dev/null", "r");
136 while (fgets(aaa, sizeof aaa, fp) != NULL) {
137 while (isspace(name[0]))
138 strcpy(name, &name[1]);
139 aaa[strlen(aaa) - 1] = 0;
141 for (a = 0; a < strlen(aaa); ++a) {
143 strcpy(bbb, &aaa[a + 1]);
147 if (!strcasecmp(name, aaa))
151 lprintf(7, "Mail is being forwarded to %s\n", name);
153 /* Change "user @ xxx" to "user" if xxx is an alias for this host */
154 for (a=0; a<strlen(name); ++a) {
155 if (name[a] == '@') {
156 if (CtdlHostAlias(&name[a+1]) == hostalias_localhost) {
158 lprintf(7, "Changed to <%s>\n", name);
163 /* determine local or remote type, see citadel.h */
164 for (a = 0; a < strlen(name); ++a)
166 return (MES_INTERNET);
167 for (a = 0; a < strlen(name); ++a)
169 for (b = a; b < strlen(name); ++b)
171 return (MES_INTERNET);
173 for (a = 0; a < strlen(name); ++a)
177 lprintf(7, "Too many @'s in address\n");
181 for (a = 0; a < strlen(name); ++a)
183 strcpy(bbb, &name[a + 1]);
185 strcpy(bbb, &bbb[1]);
186 fp = fopen("network/mail.sysinfo", "r");
190 a = getstring(fp, aaa);
191 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
192 a = getstring(fp, aaa);
193 if (!strncmp(aaa, "use ", 4)) {
194 strcpy(bbb, &aaa[4]);
199 if (!strncmp(aaa, "uum", 3)) {
201 for (a = 0; a < strlen(bbb); ++a) {
207 while (bbb[strlen(bbb) - 1] == '_')
208 bbb[strlen(bbb) - 1] = 0;
209 sprintf(name, &aaa[4], bbb);
210 lprintf(9, "returning MES_INTERNET\n");
211 return (MES_INTERNET);
213 if (!strncmp(aaa, "bin", 3)) {
216 while (aaa[strlen(aaa) - 1] != '@')
217 aaa[strlen(aaa) - 1] = 0;
218 aaa[strlen(aaa) - 1] = 0;
219 while (aaa[strlen(aaa) - 1] == ' ')
220 aaa[strlen(aaa) - 1] = 0;
221 while (bbb[0] != '@')
222 strcpy(bbb, &bbb[1]);
223 strcpy(bbb, &bbb[1]);
224 while (bbb[0] == ' ')
225 strcpy(bbb, &bbb[1]);
226 sprintf(name, "%s @%s", aaa, bbb);
227 lprintf(9, "returning MES_BINARY\n");
232 lprintf(9, "returning MES_LOCAL\n");
241 fp = fopen("citadel.control", "r");
242 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
248 void simple_listing(long msgnum, void *userdata)
250 cprintf("%ld\n", msgnum);
255 /* Determine if a given message matches the fields in a message template.
256 * Return 0 for a successful match.
258 int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
261 /* If there aren't any fields in the template, all messages will
264 if (template == NULL) return(0);
266 /* Null messages are bogus. */
267 if (msg == NULL) return(1);
269 for (i='A'; i<='Z'; ++i) {
270 if (template->cm_fields[i] != NULL) {
271 if (msg->cm_fields[i] == NULL) {
274 if (strcasecmp(msg->cm_fields[i],
275 template->cm_fields[i])) return 1;
279 /* All compares succeeded: we have a match! */
285 * Manipulate the "seen msgs" string.
287 void CtdlSetSeen(long target_msgnum, int target_setting) {
289 struct cdbdata *cdbfr;
299 /* Learn about the user and room in question */
301 getuser(&CC->usersupp, CC->curr_user);
302 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
304 /* Load the message list */
305 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
307 msglist = mallok(cdbfr->len);
308 memcpy(msglist, cdbfr->ptr, cdbfr->len);
309 num_msgs = cdbfr->len / sizeof(long);
312 return; /* No messages at all? No further action. */
315 lprintf(9, "before optimize: %s\n", vbuf.v_seen);
318 for (i=0; i<num_msgs; ++i) {
321 if (msglist[i] == target_msgnum) {
322 is_seen = target_setting;
325 if (is_msg_in_mset(vbuf.v_seen, msglist[i])) {
331 if (lo < 0L) lo = msglist[i];
334 if ( ((is_seen == 0) && (was_seen == 1))
335 || ((is_seen == 1) && (i == num_msgs-1)) ) {
336 if ( (strlen(newseen) + 20) > SIZ) {
337 strcpy(newseen, &newseen[20]);
340 if (strlen(newseen) > 0) strcat(newseen, ",");
342 sprintf(&newseen[strlen(newseen)], "%ld", lo);
345 sprintf(&newseen[strlen(newseen)], "%ld:%ld",
354 safestrncpy(vbuf.v_seen, newseen, SIZ);
355 lprintf(9, " after optimize: %s\n", vbuf.v_seen);
357 CtdlSetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
362 * API function to perform an operation for each qualifying message in the
363 * current room. (Returns the number of messages processed.)
365 int CtdlForEachMessage(int mode, long ref,
366 int moderation_level,
368 struct CtdlMessage *compare,
369 void (*CallBack) (long, void *),
375 struct cdbdata *cdbfr;
376 long *msglist = NULL;
378 int num_processed = 0;
380 struct SuppMsgInfo smi;
381 struct CtdlMessage *msg;
384 int printed_lastold = 0;
386 /* Learn about the user and room in question */
388 getuser(&CC->usersupp, CC->curr_user);
389 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
391 /* Load the message list */
392 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
394 msglist = mallok(cdbfr->len);
395 memcpy(msglist, cdbfr->ptr, cdbfr->len);
396 num_msgs = cdbfr->len / sizeof(long);
399 return 0; /* No messages at all? No further action. */
404 * Now begin the traversal.
406 if (num_msgs > 0) for (a = 0; a < num_msgs; ++a) {
407 GetSuppMsgInfo(&smi, msglist[a]);
409 /* Filter out messages that are moderated below the level
410 * currently being viewed at.
412 if (smi.smi_mod < moderation_level) {
416 /* If the caller is looking for a specific MIME type, filter
417 * out all messages which are not of the type requested.
419 if (content_type != NULL) if (strlen(content_type) > 0) {
420 if (strcasecmp(smi.smi_content_type, content_type)) {
426 num_msgs = sort_msglist(msglist, num_msgs);
428 /* If a template was supplied, filter out the messages which
429 * don't match. (This could induce some delays!)
432 if (compare != NULL) {
433 for (a = 0; a < num_msgs; ++a) {
434 msg = CtdlFetchMessage(msglist[a]);
436 if (CtdlMsgCmp(msg, compare)) {
439 CtdlFreeMessage(msg);
447 * Now iterate through the message list, according to the
448 * criteria supplied by the caller.
451 for (a = 0; a < num_msgs; ++a) {
452 thismsg = msglist[a];
453 is_seen = is_msg_in_mset(vbuf.v_seen, thismsg);
454 if (is_seen) lastold = thismsg;
459 || ((mode == MSGS_OLD) && (is_seen))
460 || ((mode == MSGS_NEW) && (!is_seen))
461 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
462 || ((mode == MSGS_FIRST) && (a < ref))
463 || ((mode == MSGS_GT) && (thismsg > ref))
464 || ((mode == MSGS_EQ) && (thismsg == ref))
467 if ((mode == MSGS_NEW) && (CC->usersupp.flags & US_LASTOLD) && (lastold > 0L) && (printed_lastold == 0) && (!is_seen)) {
469 CallBack(lastold, userdata);
473 if (CallBack) CallBack(thismsg, userdata);
477 phree(msglist); /* Clean up */
478 return num_processed;
484 * cmd_msgs() - get list of message #'s in this room
485 * implements the MSGS server command using CtdlForEachMessage()
487 void cmd_msgs(char *cmdbuf)
496 int with_template = 0;
497 struct CtdlMessage *template = NULL;
499 extract(which, cmdbuf, 0);
500 cm_ref = extract_int(cmdbuf, 1);
501 with_template = extract_int(cmdbuf, 2);
505 if (!strncasecmp(which, "OLD", 3))
507 else if (!strncasecmp(which, "NEW", 3))
509 else if (!strncasecmp(which, "FIRST", 5))
511 else if (!strncasecmp(which, "LAST", 4))
513 else if (!strncasecmp(which, "GT", 2))
516 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
517 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
522 cprintf("%d Send template then receive message list\n",
524 template = (struct CtdlMessage *)
525 mallok(sizeof(struct CtdlMessage));
526 memset(template, 0, sizeof(struct CtdlMessage));
527 while(client_gets(buf), strcmp(buf,"000")) {
528 extract(tfield, buf, 0);
529 extract(tvalue, buf, 1);
530 for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
531 if (!strcasecmp(tfield, msgkeys[i])) {
532 template->cm_fields[i] =
539 cprintf("%d Message list...\n", LISTING_FOLLOWS);
542 CtdlForEachMessage(mode, cm_ref,
543 CC->usersupp.moderation_filter,
544 NULL, template, simple_listing, NULL);
545 if (template != NULL) CtdlFreeMessage(template);
553 * help_subst() - support routine for help file viewer
555 void help_subst(char *strbuf, char *source, char *dest)
560 while (p = pattern2(strbuf, source), (p >= 0)) {
561 strcpy(workbuf, &strbuf[p + strlen(source)]);
562 strcpy(&strbuf[p], dest);
563 strcat(strbuf, workbuf);
568 void do_help_subst(char *buffer)
572 help_subst(buffer, "^nodename", config.c_nodename);
573 help_subst(buffer, "^humannode", config.c_humannode);
574 help_subst(buffer, "^fqdn", config.c_fqdn);
575 help_subst(buffer, "^username", CC->usersupp.fullname);
576 sprintf(buf2, "%ld", CC->usersupp.usernum);
577 help_subst(buffer, "^usernum", buf2);
578 help_subst(buffer, "^sysadm", config.c_sysadm);
579 help_subst(buffer, "^variantname", CITADEL);
580 sprintf(buf2, "%d", config.c_maxsessions);
581 help_subst(buffer, "^maxsessions", buf2);
587 * memfmout() - Citadel text formatter and paginator.
588 * Although the original purpose of this routine was to format
589 * text to the reader's screen width, all we're really using it
590 * for here is to format text out to 80 columns before sending it
591 * to the client. The client software may reformat it again.
594 int width, /* screen width to use */
595 char *mptr, /* where are we going to get our text from? */
596 char subst, /* nonzero if we should do substitutions */
597 char *nl) /* string to terminate lines with */
609 c = 1; /* c is the current pos */
613 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
615 buffer[strlen(buffer) + 1] = 0;
616 buffer[strlen(buffer)] = ch;
619 if (buffer[0] == '^')
620 do_help_subst(buffer);
622 buffer[strlen(buffer) + 1] = 0;
624 strcpy(buffer, &buffer[1]);
632 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
634 if (((old == 13) || (old == 10)) && (isspace(real))) {
642 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
643 cprintf("%s%s", nl, aaa);
652 if ((strlen(aaa) + c) > (width - 5)) {
661 if ((ch == 13) || (ch == 10)) {
662 cprintf("%s%s", aaa, nl);
669 cprintf("%s%s", aaa, nl);
675 * Callback function for mime parser that simply lists the part
677 void list_this_part(char *name, char *filename, char *partnum, char *disp,
678 void *content, char *cbtype, size_t length, char *encoding,
682 cprintf("part=%s|%s|%s|%s|%s|%d\n",
683 name, filename, partnum, disp, cbtype, length);
688 * Callback function for mime parser that opens a section for downloading
690 void mime_download(char *name, char *filename, char *partnum, char *disp,
691 void *content, char *cbtype, size_t length, char *encoding,
695 /* Silently go away if there's already a download open... */
696 if (CC->download_fp != NULL)
699 /* ...or if this is not the desired section */
700 if (strcasecmp(desired_section, partnum))
703 CC->download_fp = tmpfile();
704 if (CC->download_fp == NULL)
707 fwrite(content, length, 1, CC->download_fp);
708 fflush(CC->download_fp);
709 rewind(CC->download_fp);
711 OpenCmdResult(filename, cbtype);
717 * Load a message from disk into memory.
718 * This is used by CtdlOutputMsg() and other fetch functions.
720 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
721 * using the CtdlMessageFree() function.
723 struct CtdlMessage *CtdlFetchMessage(long msgnum)
725 struct cdbdata *dmsgtext;
726 struct CtdlMessage *ret = NULL;
729 CIT_UBYTE field_header;
732 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
733 if (dmsgtext == NULL) {
736 mptr = dmsgtext->ptr;
738 /* Parse the three bytes that begin EVERY message on disk.
739 * The first is always 0xFF, the on-disk magic number.
740 * The second is the anonymous/public type byte.
741 * The third is the format type byte (vari, fixed, or MIME).
745 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
749 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
750 memset(ret, 0, sizeof(struct CtdlMessage));
752 ret->cm_magic = CTDLMESSAGE_MAGIC;
753 ret->cm_anon_type = *mptr++; /* Anon type byte */
754 ret->cm_format_type = *mptr++; /* Format type byte */
757 * The rest is zero or more arbitrary fields. Load them in.
758 * We're done when we encounter either a zero-length field or
759 * have just processed the 'M' (message text) field.
762 field_length = strlen(mptr);
763 if (field_length == 0)
765 field_header = *mptr++;
766 ret->cm_fields[field_header] = mallok(field_length);
767 strcpy(ret->cm_fields[field_header], mptr);
769 while (*mptr++ != 0); /* advance to next field */
771 } while ((field_length > 0) && (field_header != 'M'));
775 /* Always make sure there's something in the msg text field */
776 if (ret->cm_fields['M'] == NULL)
777 ret->cm_fields['M'] = strdoop("<no text>\n");
779 /* Perform "before read" hooks (aborting if any return nonzero) */
780 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
781 CtdlFreeMessage(ret);
790 * Returns 1 if the supplied pointer points to a valid Citadel message.
791 * If the pointer is NULL or the magic number check fails, returns 0.
793 int is_valid_message(struct CtdlMessage *msg) {
796 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
797 lprintf(3, "is_valid_message() -- self-check failed\n");
805 * 'Destructor' for struct CtdlMessage
807 void CtdlFreeMessage(struct CtdlMessage *msg)
811 if (is_valid_message(msg) == 0) return;
813 for (i = 0; i < 256; ++i)
814 if (msg->cm_fields[i] != NULL) {
815 phree(msg->cm_fields[i]);
818 msg->cm_magic = 0; /* just in case */
824 * Pre callback function for mime parser that wants to display text
826 void fixed_output_pre(char *name, char *filename, char *partnum, char *disp,
827 void *content, char *cbtype, size_t length, char *encoding,
830 lprintf(9, "fixed_output_pre() type=<%s>\n", cbtype);
831 if (!strcasecmp(cbtype, "multipart/alternative")) {
834 lprintf(9, "multipart/alternative: <%s>\n", ma->prefix);
840 * Pre callback function for mime parser that wants to display text
842 void fixed_output_post(char *name, char *filename, char *partnum, char *disp,
843 void *content, char *cbtype, size_t length, char *encoding,
846 lprintf(9, "fixed_output_post() type=<%s>\n", cbtype);
847 if (!strcasecmp(cbtype, "multipart/alternative")) {
850 lprintf(9, "multipart/alternative: <%s>\n", ma->prefix);
856 * Inline callback function for mime parser that wants to display text
858 void fixed_output(char *name, char *filename, char *partnum, char *disp,
859 void *content, char *cbtype, size_t length, char *encoding,
867 lprintf(9, "fixed_output() type=<%s>\n", cbtype);
869 if ( (ma->is_ma == 1) && (ma->did_print == 1) ) {
870 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
876 if ( (!strcasecmp(cbtype, "text/plain"))
877 || (strlen(cbtype)==0) ) {
883 if (ch==10) cprintf("\r\n");
884 else cprintf("%c", ch);
888 if (ch != '\n') cprintf("\n");
890 else if (!strcasecmp(cbtype, "text/html")) {
891 ptr = html_to_ascii(content, 80, 0);
896 if (ch==10) cprintf("\r\n");
897 else cprintf("%c", ch);
901 else if (strncasecmp(cbtype, "multipart/", 10)) {
902 cprintf("Part %s: %s (%s) (%d bytes)\r\n",
903 partnum, filename, cbtype, length);
909 * Get a message off disk. (returns om_* values found in msgbase.h)
912 int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */
913 int mode, /* how would you like that message? */
914 int headers_only, /* eschew the message body? */
915 int do_proto, /* do Citadel protocol responses? */
916 int crlf /* Use CRLF newlines instead of LF? */
918 struct CtdlMessage *TheMessage;
921 lprintf(7, "CtdlOutputMsg() msgnum=%ld, mode=%d\n",
926 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
927 if (do_proto) cprintf("%d Not logged in.\n",
928 ERROR + NOT_LOGGED_IN);
929 return(om_not_logged_in);
932 /* FIXME ... small security issue
933 * We need to check to make sure the requested message is actually
934 * in the current room, and set msg_ok to 1 only if it is. This
935 * functionality is currently missing because I'm in a hurry to replace
936 * broken production code with nonbroken pre-beta code. :( -- ajc
939 if (do_proto) cprintf("%d Message %ld is not in this room.\n",
941 return(om_no_such_msg);
946 * Fetch the message from disk
948 TheMessage = CtdlFetchMessage(msg_num);
949 if (TheMessage == NULL) {
950 if (do_proto) cprintf("%d Can't locate msg %ld on disk\n",
952 return(om_no_such_msg);
955 retcode = CtdlOutputPreLoadedMsg(
956 TheMessage, msg_num, mode,
957 headers_only, do_proto, crlf);
959 CtdlFreeMessage(TheMessage);
965 * Get a message off disk. (returns om_* values found in msgbase.h)
968 int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage,
970 int mode, /* how would you like that message? */
971 int headers_only, /* eschew the message body? */
972 int do_proto, /* do Citadel protocol responses? */
973 int crlf /* Use CRLF newlines instead of LF? */
979 char display_name[SIZ];
981 char *nl; /* newline string */
983 /* buffers needed for RFC822 translation */
993 sprintf(mid, "%ld", msg_num);
994 nl = (crlf ? "\r\n" : "\n");
996 if (!is_valid_message(TheMessage)) {
997 lprintf(1, "ERROR: invalid preloaded message for output\n");
998 return(om_no_such_msg);
1001 /* Are we downloading a MIME component? */
1002 if (mode == MT_DOWNLOAD) {
1003 if (TheMessage->cm_format_type != FMT_RFC822) {
1005 cprintf("%d This is not a MIME message.\n",
1007 } else if (CC->download_fp != NULL) {
1008 if (do_proto) cprintf(
1009 "%d You already have a download open.\n",
1012 /* Parse the message text component */
1013 mptr = TheMessage->cm_fields['M'];
1014 mime_parser(mptr, NULL,
1015 *mime_download, NULL, NULL,
1017 /* If there's no file open by this time, the requested
1018 * section wasn't found, so print an error
1020 if (CC->download_fp == NULL) {
1021 if (do_proto) cprintf(
1022 "%d Section %s not found.\n",
1023 ERROR + FILE_NOT_FOUND,
1027 return((CC->download_fp != NULL) ? om_ok : om_mime_error);
1030 /* now for the user-mode message reading loops */
1031 if (do_proto) cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
1033 /* Tell the client which format type we're using. If this is a
1034 * MIME message, *lie* about it and tell the user it's fixed-format.
1036 if (mode == MT_CITADEL) {
1037 if (TheMessage->cm_format_type == FMT_RFC822) {
1038 if (do_proto) cprintf("type=1\n");
1041 if (do_proto) cprintf("type=%d\n",
1042 TheMessage->cm_format_type);
1046 /* nhdr=yes means that we're only displaying headers, no body */
1047 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
1048 if (do_proto) cprintf("nhdr=yes\n");
1051 /* begin header processing loop for Citadel message format */
1053 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
1055 strcpy(display_name, "<unknown>");
1056 if (TheMessage->cm_fields['A']) {
1057 strcpy(buf, TheMessage->cm_fields['A']);
1058 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
1059 if (TheMessage->cm_anon_type == MES_ANON)
1060 strcpy(display_name, "****");
1061 else if (TheMessage->cm_anon_type == MES_AN2)
1062 strcpy(display_name, "anonymous");
1064 strcpy(display_name, buf);
1065 if ((is_room_aide())
1066 && ((TheMessage->cm_anon_type == MES_ANON)
1067 || (TheMessage->cm_anon_type == MES_AN2))) {
1068 sprintf(&display_name[strlen(display_name)],
1073 strcpy(allkeys, FORDER);
1074 for (i=0; i<strlen(allkeys); ++i) {
1075 k = (int) allkeys[i];
1077 if (TheMessage->cm_fields[k] != NULL) {
1079 if (do_proto) cprintf("%s=%s\n",
1084 if (do_proto) cprintf("%s=%s\n",
1086 TheMessage->cm_fields[k]
1095 /* begin header processing loop for RFC822 transfer format */
1100 strcpy(snode, NODENAME);
1101 strcpy(lnode, HUMANNODE);
1102 if (mode == MT_RFC822) {
1103 cprintf("X-UIDL: %ld%s", msg_num, nl);
1104 for (i = 0; i < 256; ++i) {
1105 if (TheMessage->cm_fields[i]) {
1106 mptr = TheMessage->cm_fields[i];
1109 strcpy(luser, mptr);
1110 strcpy(suser, mptr);
1113 "Path:" removed for now because it confuses brain-dead Microsoft shitware
1114 into thinking that mail messages are newsgroup messages instead. When we
1115 add NNTP support back into Citadel we'll have to add code to only output
1116 this field when appropriate.
1117 else if (i == 'P') {
1118 cprintf("Path: %s%s", mptr, nl);
1122 cprintf("Subject: %s%s", mptr, nl);
1126 strcpy(lnode, mptr);
1128 cprintf("X-Citadel-Room: %s%s",
1131 strcpy(snode, mptr);
1133 cprintf("To: %s%s", mptr, nl);
1134 else if (i == 'T') {
1135 datestring(datestamp, atol(mptr),
1136 DATESTRING_RFC822 );
1137 cprintf("Date: %s%s", datestamp, nl);
1143 for (i=0; i<strlen(suser); ++i) {
1144 suser[i] = tolower(suser[i]);
1145 if (!isalnum(suser[i])) suser[i]='_';
1148 if (mode == MT_RFC822) {
1149 if (!strcasecmp(snode, NODENAME)) {
1150 strcpy(snode, FQDN);
1153 /* Construct a fun message id */
1154 cprintf("Message-ID: <%s", mid);
1155 if (strchr(mid, '@')==NULL) {
1156 cprintf("@%s", snode);
1160 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
1162 if (strlen(fuser) > 0) {
1163 cprintf("From: %s (%s)%s", fuser, luser, nl);
1166 cprintf("From: %s@%s (%s)%s", suser, snode, luser, nl);
1169 cprintf("Organization: %s%s", lnode, nl);
1172 /* end header processing loop ... at this point, we're in the text */
1174 mptr = TheMessage->cm_fields['M'];
1176 /* Tell the client about the MIME parts in this message */
1177 if (TheMessage->cm_format_type == FMT_RFC822) {
1178 if (mode == MT_CITADEL) {
1179 mime_parser(mptr, NULL,
1180 *list_this_part, NULL, NULL,
1183 else if (mode == MT_MIME) { /* list parts only */
1184 mime_parser(mptr, NULL,
1185 *list_this_part, NULL, NULL,
1187 if (do_proto) cprintf("000\n");
1190 else if (mode == MT_RFC822) { /* unparsed RFC822 dump */
1191 /* FIXME ... we have to put some code in here to avoid
1192 * printing duplicate header information when both
1193 * Citadel and RFC822 headers exist. Preference should
1194 * probably be given to the RFC822 headers.
1196 while (ch=*(mptr++), ch!=0) {
1198 else if (ch==10) cprintf("%s", nl);
1199 else cprintf("%c", ch);
1201 if (do_proto) cprintf("000\n");
1207 if (do_proto) cprintf("000\n");
1211 /* signify start of msg text */
1212 if (mode == MT_CITADEL)
1213 if (do_proto) cprintf("text\n");
1214 if (mode == MT_RFC822) {
1215 if (TheMessage->cm_fields['U'] == NULL) {
1216 cprintf("Subject: (no subject)%s", nl);
1221 /* If the format type on disk is 1 (fixed-format), then we want
1222 * everything to be output completely literally ... regardless of
1223 * what message transfer format is in use.
1225 if (TheMessage->cm_format_type == FMT_FIXED) {
1227 while (ch = *mptr++, ch > 0) {
1230 if ((ch == 10) || (strlen(buf) > 250)) {
1231 cprintf("%s%s", buf, nl);
1234 buf[strlen(buf) + 1] = 0;
1235 buf[strlen(buf)] = ch;
1238 if (strlen(buf) > 0)
1239 cprintf("%s%s", buf, nl);
1242 /* If the message on disk is format 0 (Citadel vari-format), we
1243 * output using the formatter at 80 columns. This is the final output
1244 * form if the transfer format is RFC822, but if the transfer format
1245 * is Citadel proprietary, it'll still work, because the indentation
1246 * for new paragraphs is correct and the client will reformat the
1247 * message to the reader's screen width.
1249 if (TheMessage->cm_format_type == FMT_CITADEL) {
1250 memfmout(80, mptr, 0, nl);
1253 /* If the message on disk is format 4 (MIME), we've gotta hand it
1254 * off to the MIME parser. The client has already been told that
1255 * this message is format 1 (fixed format), so the callback function
1256 * we use will display those parts as-is.
1258 if (TheMessage->cm_format_type == FMT_RFC822) {
1259 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
1260 memset(ma, 0, sizeof(struct ma_info));
1261 mime_parser(mptr, NULL,
1262 *fixed_output, *fixed_output_pre, *fixed_output_post,
1266 /* now we're done */
1267 if (do_proto) cprintf("000\n");
1274 * display a message (mode 0 - Citadel proprietary)
1276 void cmd_msg0(char *cmdbuf)
1279 int headers_only = 0;
1281 msgid = extract_long(cmdbuf, 0);
1282 headers_only = extract_int(cmdbuf, 1);
1284 CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0);
1290 * display a message (mode 2 - RFC822)
1292 void cmd_msg2(char *cmdbuf)
1295 int headers_only = 0;
1297 msgid = extract_long(cmdbuf, 0);
1298 headers_only = extract_int(cmdbuf, 1);
1300 CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1);
1306 * display a message (mode 3 - IGnet raw format - internal programs only)
1308 void cmd_msg3(char *cmdbuf)
1311 struct CtdlMessage *msg;
1314 if (CC->internal_pgm == 0) {
1315 cprintf("%d This command is for internal programs only.\n",
1320 msgnum = extract_long(cmdbuf, 0);
1321 msg = CtdlFetchMessage(msgnum);
1323 cprintf("%d Message %ld not found.\n",
1328 serialize_message(&smr, msg);
1329 CtdlFreeMessage(msg);
1332 cprintf("%d Unable to serialize message\n",
1333 ERROR+INTERNAL_ERROR);
1337 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1338 client_write(smr.ser, smr.len);
1345 * display a message (mode 4 - MIME) (FIXME ... still evolving, not complete)
1347 void cmd_msg4(char *cmdbuf)
1351 msgid = extract_long(cmdbuf, 0);
1352 CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0);
1356 * Open a component of a MIME message as a download file
1358 void cmd_opna(char *cmdbuf)
1362 CtdlAllocUserData(SYM_DESIRED_SECTION, SIZ);
1364 msgid = extract_long(cmdbuf, 0);
1365 extract(desired_section, cmdbuf, 1);
1367 CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1);
1372 * Save a message pointer into a specified room
1373 * (Returns 0 for success, nonzero for failure)
1374 * roomname may be NULL to use the current room
1376 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
1378 char hold_rm[ROOMNAMELEN];
1379 struct cdbdata *cdbfr;
1382 long highest_msg = 0L;
1383 struct CtdlMessage *msg = NULL;
1385 lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n",
1386 roomname, msgid, flags);
1388 strcpy(hold_rm, CC->quickroom.QRname);
1390 /* We may need to check to see if this message is real */
1391 if ( (flags & SM_VERIFY_GOODNESS)
1392 || (flags & SM_DO_REPL_CHECK)
1394 msg = CtdlFetchMessage(msgid);
1395 if (msg == NULL) return(ERROR + ILLEGAL_VALUE);
1398 /* Perform replication checks if necessary */
1399 if ( (flags & SM_DO_REPL_CHECK) && (msg != NULL) ) {
1401 if (getroom(&CC->quickroom,
1402 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1404 lprintf(9, "No such room <%s>\n", roomname);
1405 if (msg != NULL) CtdlFreeMessage(msg);
1406 return(ERROR + ROOM_NOT_FOUND);
1409 if (ReplicationChecks(msg) != 0) {
1410 getroom(&CC->quickroom, hold_rm);
1411 if (msg != NULL) CtdlFreeMessage(msg);
1412 lprintf(9, "Did replication, and newer exists\n");
1417 /* Now the regular stuff */
1418 if (lgetroom(&CC->quickroom,
1419 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1421 lprintf(9, "No such room <%s>\n", roomname);
1422 if (msg != NULL) CtdlFreeMessage(msg);
1423 return(ERROR + ROOM_NOT_FOUND);
1426 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
1427 if (cdbfr == NULL) {
1431 msglist = mallok(cdbfr->len);
1432 if (msglist == NULL)
1433 lprintf(3, "ERROR malloc msglist!\n");
1434 num_msgs = cdbfr->len / sizeof(long);
1435 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1440 /* Make sure the message doesn't already exist in this room. It
1441 * is absolutely taboo to have more than one reference to the same
1442 * message in a room.
1444 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1445 if (msglist[i] == msgid) {
1446 lputroom(&CC->quickroom); /* unlock the room */
1447 getroom(&CC->quickroom, hold_rm);
1448 if (msg != NULL) CtdlFreeMessage(msg);
1449 return(ERROR + ALREADY_EXISTS);
1453 /* Now add the new message */
1455 msglist = reallok(msglist,
1456 (num_msgs * sizeof(long)));
1458 if (msglist == NULL) {
1459 lprintf(3, "ERROR: can't realloc message list!\n");
1461 msglist[num_msgs - 1] = msgid;
1463 /* Sort the message list, so all the msgid's are in order */
1464 num_msgs = sort_msglist(msglist, num_msgs);
1466 /* Determine the highest message number */
1467 highest_msg = msglist[num_msgs - 1];
1469 /* Write it back to disk. */
1470 cdb_store(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long),
1471 msglist, num_msgs * sizeof(long));
1473 /* Free up the memory we used. */
1476 /* Update the highest-message pointer and unlock the room. */
1477 CC->quickroom.QRhighest = highest_msg;
1478 lputroom(&CC->quickroom);
1479 getroom(&CC->quickroom, hold_rm);
1481 /* Bump the reference count for this message. */
1482 if ((flags & SM_DONT_BUMP_REF)==0) {
1483 AdjRefCount(msgid, +1);
1486 /* Return success. */
1487 if (msg != NULL) CtdlFreeMessage(msg);
1494 * Message base operation to send a message to the master file
1495 * (returns new message number)
1497 * This is the back end for CtdlSaveMsg() and should not be directly
1498 * called by server-side modules.
1501 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1502 FILE *save_a_copy) /* save a copy to disk? */
1509 /* Get a new message number */
1510 newmsgid = get_new_message_number();
1511 sprintf(msgidbuf, "%ld@%s", newmsgid, config.c_fqdn);
1513 /* Generate an ID if we don't have one already */
1514 if (msg->cm_fields['I']==NULL) {
1515 msg->cm_fields['I'] = strdoop(msgidbuf);
1518 serialize_message(&smr, msg);
1521 cprintf("%d Unable to serialize message\n",
1522 ERROR+INTERNAL_ERROR);
1526 /* Write our little bundle of joy into the message base */
1527 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1528 smr.ser, smr.len) < 0) {
1529 lprintf(2, "Can't store message\n");
1535 /* If the caller specified that a copy should be saved to a particular
1536 * file handle, do that now too.
1538 if (save_a_copy != NULL) {
1539 fwrite(smr.ser, smr.len, 1, save_a_copy);
1542 /* Free the memory we used for the serialized message */
1545 /* Return the *local* message ID to the caller
1546 * (even if we're storing an incoming network message)
1554 * Serialize a struct CtdlMessage into the format used on disk and network.
1556 * This function loads up a "struct ser_ret" (defined in server.h) which
1557 * contains the length of the serialized message and a pointer to the
1558 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1560 void serialize_message(struct ser_ret *ret, /* return values */
1561 struct CtdlMessage *msg) /* unserialized msg */
1565 static char *forder = FORDER;
1567 if (is_valid_message(msg) == 0) return; /* self check */
1570 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1571 ret->len = ret->len +
1572 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1574 lprintf(9, "serialize_message() calling malloc(%d)\n", ret->len);
1575 ret->ser = mallok(ret->len);
1576 if (ret->ser == NULL) {
1582 ret->ser[1] = msg->cm_anon_type;
1583 ret->ser[2] = msg->cm_format_type;
1586 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1587 ret->ser[wlen++] = (char)forder[i];
1588 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1589 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1591 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1600 * Back end for the ReplicationChecks() function
1602 void check_repl(long msgnum, void *userdata) {
1603 struct CtdlMessage *msg;
1604 time_t timestamp = (-1L);
1606 lprintf(9, "check_repl() found message %ld\n", msgnum);
1607 msg = CtdlFetchMessage(msgnum);
1608 if (msg == NULL) return;
1609 if (msg->cm_fields['T'] != NULL) {
1610 timestamp = atol(msg->cm_fields['T']);
1612 CtdlFreeMessage(msg);
1614 if (timestamp > msg_repl->highest) {
1615 msg_repl->highest = timestamp; /* newer! */
1616 lprintf(9, "newer!\n");
1619 lprintf(9, "older!\n");
1621 /* Existing isn't newer? Then delete the old one(s). */
1622 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, "");
1627 * Check to see if any messages already exist which carry the same Extended ID
1631 * -> With older timestamps: delete them and return 0. Message will be saved.
1632 * -> With newer timestamps: return 1. Message save will be aborted.
1634 int ReplicationChecks(struct CtdlMessage *msg) {
1635 struct CtdlMessage *template;
1638 lprintf(9, "ReplicationChecks() started\n");
1639 /* No extended id? Don't do anything. */
1640 if (msg->cm_fields['E'] == NULL) return 0;
1641 if (strlen(msg->cm_fields['E']) == 0) return 0;
1642 lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1644 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1645 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1646 msg_repl->highest = atol(msg->cm_fields['T']);
1648 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1649 memset(template, 0, sizeof(struct CtdlMessage));
1650 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1652 CtdlForEachMessage(MSGS_ALL, 0L, (-127), NULL, template,
1655 /* If a newer message exists with the same Extended ID, abort
1658 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1662 CtdlFreeMessage(template);
1663 lprintf(9, "ReplicationChecks() returning %d\n", abort_this);
1671 * Save a message to disk
1673 long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1674 char *rec, /* Recipient (mail) */
1675 char *force, /* force a particular room? */
1676 int supplied_mailtype) /* local or remote type */
1679 char hold_rm[ROOMNAMELEN];
1680 char actual_rm[ROOMNAMELEN];
1681 char force_room[ROOMNAMELEN];
1682 char content_type[SIZ]; /* We have to learn this */
1683 char recipient[SIZ];
1686 struct usersupp userbuf;
1688 struct SuppMsgInfo smi;
1689 FILE *network_fp = NULL;
1690 static int seqnum = 1;
1691 struct CtdlMessage *imsg;
1695 lprintf(9, "CtdlSaveMsg() called\n");
1696 if (is_valid_message(msg) == 0) return(-1); /* self check */
1697 mailtype = supplied_mailtype;
1699 /* If this message has no timestamp, we take the liberty of
1700 * giving it one, right now.
1702 if (msg->cm_fields['T'] == NULL) {
1703 lprintf(9, "Generating timestamp\n");
1704 sprintf(aaa, "%ld", time(NULL));
1705 msg->cm_fields['T'] = strdoop(aaa);
1708 /* If this message has no path, we generate one.
1710 if (msg->cm_fields['P'] == NULL) {
1711 lprintf(9, "Generating path\n");
1712 if (msg->cm_fields['A'] != NULL) {
1713 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1714 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1715 if (isspace(msg->cm_fields['P'][a])) {
1716 msg->cm_fields['P'][a] = ' ';
1721 msg->cm_fields['P'] = strdoop("unknown");
1725 strcpy(force_room, force);
1727 /* Strip non-printable characters out of the recipient name */
1728 lprintf(9, "Checking recipient (if present)\n");
1729 strcpy(recipient, rec);
1730 for (a = 0; a < strlen(recipient); ++a)
1731 if (!isprint(recipient[a]))
1732 strcpy(&recipient[a], &recipient[a + 1]);
1734 /* Change "user @ xxx" to "user" if xxx is an alias for this host */
1735 for (a=0; a<strlen(recipient); ++a) {
1736 if (recipient[a] == '@') {
1737 if (CtdlHostAlias(&recipient[a+1])
1738 == hostalias_localhost) {
1740 lprintf(7, "Changed to <%s>\n", recipient);
1741 mailtype = MES_LOCAL;
1746 lprintf(9, "Recipient is <%s>\n", recipient);
1748 /* Learn about what's inside, because it's what's inside that counts */
1749 lprintf(9, "Learning what's inside\n");
1750 if (msg->cm_fields['M'] == NULL) {
1751 lprintf(1, "ERROR: attempt to save message with NULL body\n");
1754 switch (msg->cm_format_type) {
1756 strcpy(content_type, "text/x-citadel-variformat");
1759 strcpy(content_type, "text/plain");
1762 strcpy(content_type, "text/plain");
1763 /* advance past header fields */
1764 mptr = msg->cm_fields['M'];
1767 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1768 safestrncpy(content_type, mptr,
1769 sizeof(content_type));
1770 strcpy(content_type, &content_type[14]);
1771 for (a = 0; a < strlen(content_type); ++a)
1772 if ((content_type[a] == ';')
1773 || (content_type[a] == ' ')
1774 || (content_type[a] == 13)
1775 || (content_type[a] == 10))
1776 content_type[a] = 0;
1783 /* Goto the correct room */
1784 lprintf(9, "Switching rooms\n");
1785 strcpy(hold_rm, CC->quickroom.QRname);
1786 strcpy(actual_rm, CC->quickroom.QRname);
1788 /* If the user is a twit, move to the twit room for posting */
1789 lprintf(9, "Handling twit stuff\n");
1791 if (CC->usersupp.axlevel == 2) {
1792 strcpy(hold_rm, actual_rm);
1793 strcpy(actual_rm, config.c_twitroom);
1797 /* ...or if this message is destined for Aide> then go there. */
1798 if (strlen(force_room) > 0) {
1799 strcpy(actual_rm, force_room);
1802 lprintf(9, "Possibly relocating\n");
1803 if (strcasecmp(actual_rm, CC->quickroom.QRname)) {
1804 getroom(&CC->quickroom, actual_rm);
1808 * If this message has no O (room) field, generate one.
1810 if (msg->cm_fields['O'] == NULL) {
1811 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1814 /* Perform "before save" hooks (aborting if any return nonzero) */
1815 lprintf(9, "Performing before-save hooks\n");
1816 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return(-1);
1818 /* If this message has an Extended ID, perform replication checks */
1819 lprintf(9, "Performing replication checks\n");
1820 if (ReplicationChecks(msg) > 0) return(-1);
1822 /* Network mail - send a copy to the network program. */
1823 if ((strlen(recipient) > 0) && (mailtype == MES_BINARY)) {
1824 lprintf(9, "Sending network spool\n");
1825 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1826 (long) getpid(), CC->cs_pid, ++seqnum);
1827 lprintf(9, "Saving a copy to %s\n", aaa);
1828 network_fp = fopen(aaa, "ab+");
1829 if (network_fp == NULL)
1830 lprintf(2, "ERROR: %s\n", strerror(errno));
1833 /* Save it to disk */
1834 lprintf(9, "Saving to disk\n");
1835 newmsgid = send_message(msg, network_fp);
1836 if (network_fp != NULL) {
1838 /* FIXME start a network run here */
1841 if (newmsgid <= 0L) return(-1);
1843 /* Write a supplemental message info record. This doesn't have to
1844 * be a critical section because nobody else knows about this message
1847 lprintf(9, "Creating SuppMsgInfo record\n");
1848 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1849 smi.smi_msgnum = newmsgid;
1850 smi.smi_refcount = 0;
1851 safestrncpy(smi.smi_content_type, content_type, 64);
1852 PutSuppMsgInfo(&smi);
1854 /* Now figure out where to store the pointers */
1855 lprintf(9, "Storing pointers\n");
1857 /* If this is being done by the networker delivering a private
1858 * message, we want to BYPASS saving the sender's copy (because there
1859 * is no local sender; it would otherwise go to the Trashcan).
1861 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1862 if (CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0) != 0) {
1863 lprintf(3, "ERROR saving message pointer!\n");
1864 CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
1868 /* For internet mail, drop a copy in the outbound queue room */
1869 if (mailtype == MES_INTERNET) {
1870 CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0);
1873 /* Bump this user's messages posted counter. */
1874 lprintf(9, "Updating user\n");
1875 lgetuser(&CC->usersupp, CC->curr_user);
1876 CC->usersupp.posted = CC->usersupp.posted + 1;
1877 lputuser(&CC->usersupp);
1879 /* If this is private, local mail, make a copy in the
1880 * recipient's mailbox and bump the reference count.
1882 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1883 if (getuser(&userbuf, recipient) == 0) {
1884 lprintf(9, "Delivering private mail\n");
1885 MailboxName(actual_rm, &userbuf, MAILROOM);
1886 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1889 lprintf(9, "No user <%s>, saving in %s> instead\n",
1890 recipient, AIDEROOM);
1891 CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
1895 /* Perform "after save" hooks */
1896 lprintf(9, "Performing after-save hooks\n");
1897 PerformMessageHooks(msg, EVT_AFTERSAVE);
1900 lprintf(9, "Returning to original room\n");
1901 if (strcasecmp(hold_rm, CC->quickroom.QRname))
1902 getroom(&CC->quickroom, hold_rm);
1904 /* For internet mail, generate delivery instructions
1905 * (Yes, this is recursive! Deal with it!)
1907 if (mailtype == MES_INTERNET) {
1908 lprintf(9, "Generating delivery instructions\n");
1909 instr = mallok(2048);
1911 "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
1914 SPOOLMIME, newmsgid, time(NULL),
1915 msg->cm_fields['A'], msg->cm_fields['N'],
1918 imsg = mallok(sizeof(struct CtdlMessage));
1919 memset(imsg, 0, sizeof(struct CtdlMessage));
1920 imsg->cm_magic = CTDLMESSAGE_MAGIC;
1921 imsg->cm_anon_type = MES_NORMAL;
1922 imsg->cm_format_type = FMT_RFC822;
1923 imsg->cm_fields['A'] = strdoop("Citadel");
1924 imsg->cm_fields['M'] = instr;
1925 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
1926 CtdlFreeMessage(imsg);
1935 * Convenience function for generating small administrative messages.
1937 void quickie_message(char *from, char *to, char *room, char *text)
1939 struct CtdlMessage *msg;
1941 msg = mallok(sizeof(struct CtdlMessage));
1942 memset(msg, 0, sizeof(struct CtdlMessage));
1943 msg->cm_magic = CTDLMESSAGE_MAGIC;
1944 msg->cm_anon_type = MES_NORMAL;
1945 msg->cm_format_type = 0;
1946 msg->cm_fields['A'] = strdoop(from);
1947 msg->cm_fields['O'] = strdoop(room);
1948 msg->cm_fields['N'] = strdoop(NODENAME);
1950 msg->cm_fields['R'] = strdoop(to);
1951 msg->cm_fields['M'] = strdoop(text);
1953 CtdlSaveMsg(msg, "", room, MES_LOCAL);
1954 CtdlFreeMessage(msg);
1955 syslog(LOG_NOTICE, text);
1961 * Back end function used by make_message() and similar functions
1963 char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */
1964 size_t maxlen, /* maximum message length */
1965 char *exist /* if non-null, append to it;
1966 exist is ALWAYS freed */
1970 size_t message_len = 0;
1971 size_t buffer_len = 0;
1975 if (exist == NULL) {
1979 m = reallok(exist, strlen(exist) + 4096);
1980 if (m == NULL) phree(exist);
1983 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) ;;
1990 /* read in the lines of message text one by one */
1991 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) {
1993 /* strip trailing newline type stuff */
1994 if (buf[strlen(buf)-1]==10) buf[strlen(buf)-1]=0;
1995 if (buf[strlen(buf)-1]==13) buf[strlen(buf)-1]=0;
1997 linelen = strlen(buf);
1999 /* augment the buffer if we have to */
2000 if ((message_len + linelen + 2) > buffer_len) {
2001 lprintf(9, "realloking\n");
2002 ptr = reallok(m, (buffer_len * 2) );
2003 if (ptr == NULL) { /* flush if can't allocate */
2004 while ( (client_gets(buf)>0) &&
2005 strcmp(buf, terminator)) ;;
2008 buffer_len = (buffer_len * 2);
2010 lprintf(9, "buffer_len is %d\n", buffer_len);
2014 /* Add the new line to the buffer. We avoid using strcat()
2015 * because that would involve traversing the entire message
2016 * after each line, and this function needs to run fast.
2018 strcpy(&m[message_len], buf);
2019 m[message_len + linelen] = '\n';
2020 m[message_len + linelen + 1] = 0;
2021 message_len = message_len + linelen + 1;
2023 /* if we've hit the max msg length, flush the rest */
2024 if (message_len >= maxlen) {
2025 while ( (client_gets(buf)>0)
2026 && strcmp(buf, terminator)) ;;
2037 * Build a binary message to be saved on disk.
2040 struct CtdlMessage *make_message(
2041 struct usersupp *author, /* author's usersupp structure */
2042 char *recipient, /* NULL if it's not mail */
2043 char *room, /* room where it's going */
2044 int type, /* see MES_ types in header file */
2045 int net_type, /* see MES_ types in header file */
2046 int format_type, /* local or remote (see citadel.h) */
2047 char *fake_name) /* who we're masquerading as */
2053 struct CtdlMessage *msg;
2055 msg = mallok(sizeof(struct CtdlMessage));
2056 memset(msg, 0, sizeof(struct CtdlMessage));
2057 msg->cm_magic = CTDLMESSAGE_MAGIC;
2058 msg->cm_anon_type = type;
2059 msg->cm_format_type = format_type;
2061 /* Don't confuse the poor folks if it's not routed mail. */
2062 strcpy(dest_node, "");
2064 /* If net_type is MES_BINARY, split out the destination node. */
2065 if (net_type == MES_BINARY) {
2066 strcpy(dest_node, NODENAME);
2067 for (a = 0; a < strlen(recipient); ++a) {
2068 if (recipient[a] == '@') {
2070 strcpy(dest_node, &recipient[a + 1]);
2075 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
2076 if (net_type == MES_INTERNET) {
2077 strcpy(dest_node, "internet");
2080 while (isspace(recipient[strlen(recipient) - 1]))
2081 recipient[strlen(recipient) - 1] = 0;
2083 sprintf(buf, "cit%ld", author->usernum); /* Path */
2084 msg->cm_fields['P'] = strdoop(buf);
2086 sprintf(buf, "%ld", time(NULL)); /* timestamp */
2087 msg->cm_fields['T'] = strdoop(buf);
2089 if (fake_name[0]) /* author */
2090 msg->cm_fields['A'] = strdoop(fake_name);
2092 msg->cm_fields['A'] = strdoop(author->fullname);
2094 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
2095 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
2097 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
2099 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
2100 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
2102 if (recipient[0] != 0)
2103 msg->cm_fields['R'] = strdoop(recipient);
2104 if (dest_node[0] != 0)
2105 msg->cm_fields['D'] = strdoop(dest_node);
2108 msg->cm_fields['M'] = CtdlReadMessageBody("000",
2109 config.c_maxmsglen, NULL);
2117 * Check to see whether we have permission to post a message in the current
2118 * room. Returns a *CITADEL ERROR CODE* and puts a message in errmsgbuf, or
2119 * returns 0 on success.
2121 int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf) {
2123 if (!(CC->logged_in)) {
2124 sprintf(errmsgbuf, "Not logged in.");
2125 return (ERROR + NOT_LOGGED_IN);
2128 if ((CC->usersupp.axlevel < 2)
2129 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
2130 sprintf(errmsgbuf, "Need to be validated to enter "
2131 "(except in %s> to sysop)", MAILROOM);
2132 return (ERROR + HIGHER_ACCESS_REQUIRED);
2135 if ((CC->usersupp.axlevel < 4)
2136 && (CC->quickroom.QRflags & QR_NETWORK)) {
2137 sprintf(errmsgbuf, "Need net privileges to enter here.");
2138 return (ERROR + HIGHER_ACCESS_REQUIRED);
2141 if ((CC->usersupp.axlevel < 6)
2142 && (CC->quickroom.QRflags & QR_READONLY)) {
2143 sprintf(errmsgbuf, "Sorry, this is a read-only room.");
2144 return (ERROR + HIGHER_ACCESS_REQUIRED);
2147 strcpy(errmsgbuf, "Ok");
2155 * message entry - mode 0 (normal)
2157 void cmd_ent0(char *entargs)
2160 char recipient[SIZ];
2162 int format_type = 0;
2163 char newusername[SIZ];
2164 struct CtdlMessage *msg;
2168 struct usersupp tempUS;
2172 post = extract_int(entargs, 0);
2173 extract(recipient, entargs, 1);
2174 anon_flag = extract_int(entargs, 2);
2175 format_type = extract_int(entargs, 3);
2177 /* first check to make sure the request is valid. */
2179 err = CtdlDoIHavePermissionToPostInThisRoom(buf);
2181 cprintf("%d %s\n", err, buf);
2185 /* Check some other permission type things. */
2188 if (CC->usersupp.axlevel < 6) {
2189 cprintf("%d You don't have permission to masquerade.\n",
2190 ERROR + HIGHER_ACCESS_REQUIRED);
2193 extract(newusername, entargs, 4);
2194 memset(CC->fake_postname, 0, 32);
2195 strcpy(CC->fake_postname, newusername);
2196 cprintf("%d Ok\n", OK);
2199 CC->cs_flags |= CS_POSTING;
2202 if (CC->quickroom.QRflags & QR_MAILBOX) {
2203 if (CC->usersupp.axlevel >= 2) {
2204 strcpy(buf, recipient);
2206 strcpy(buf, "sysop");
2207 e = alias(buf); /* alias and mail type */
2208 if ((buf[0] == 0) || (e == MES_ERROR)) {
2209 cprintf("%d Unknown address - cannot send message.\n",
2210 ERROR + NO_SUCH_USER);
2213 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
2214 cprintf("%d Net privileges required for network mail.\n",
2215 ERROR + HIGHER_ACCESS_REQUIRED);
2218 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
2219 && ((CC->usersupp.flags & US_INTERNET) == 0)
2220 && (!CC->internal_pgm)) {
2221 cprintf("%d You don't have access to Internet mail.\n",
2222 ERROR + HIGHER_ACCESS_REQUIRED);
2225 if (!strcasecmp(buf, "sysop")) {
2228 else if (e == MES_LOCAL) { /* don't search local file */
2229 if (!strcasecmp(buf, CC->usersupp.fullname)) {
2230 cprintf("%d Can't send mail to yourself!\n",
2231 ERROR + NO_SUCH_USER);
2234 /* Check to make sure the user exists; also get the correct
2235 * upper/lower casing of the name.
2237 a = getuser(&tempUS, buf);
2239 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
2242 strcpy(buf, tempUS.fullname);
2247 if (CC->quickroom.QRflags & QR_ANONONLY)
2249 if (CC->quickroom.QRflags & QR_ANONOPT) {
2253 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2256 /* If we're only checking the validity of the request, return
2257 * success without creating the message.
2260 cprintf("%d %s\n", OK, buf);
2264 cprintf("%d send message\n", SEND_LISTING);
2266 /* Read in the message from the client. */
2267 if (CC->fake_postname[0])
2268 msg = make_message(&CC->usersupp, buf,
2269 CC->quickroom.QRname, b, e, format_type,
2271 else if (CC->fake_username[0])
2272 msg = make_message(&CC->usersupp, buf,
2273 CC->quickroom.QRname, b, e, format_type,
2276 msg = make_message(&CC->usersupp, buf,
2277 CC->quickroom.QRname, b, e, format_type, "");
2280 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e);
2281 CtdlFreeMessage(msg);
2282 CC->fake_postname[0] = '\0';
2289 * message entry - mode 3 (raw)
2291 void cmd_ent3(char *entargs)
2297 unsigned char ch, which_field;
2298 struct usersupp tempUS;
2300 struct CtdlMessage *msg;
2303 if (CC->internal_pgm == 0) {
2304 cprintf("%d This command is for internal programs only.\n",
2309 /* See if there's a recipient, but make sure it's a real one */
2310 extract(recp, entargs, 1);
2311 for (a = 0; a < strlen(recp); ++a)
2312 if (!isprint(recp[a]))
2313 strcpy(&recp[a], &recp[a + 1]);
2314 while (isspace(recp[0]))
2315 strcpy(recp, &recp[1]);
2316 while (isspace(recp[strlen(recp) - 1]))
2317 recp[strlen(recp) - 1] = 0;
2319 /* If we're in Mail, check the recipient */
2320 if (strlen(recp) > 0) {
2321 e = alias(recp); /* alias and mail type */
2322 if ((recp[0] == 0) || (e == MES_ERROR)) {
2323 cprintf("%d Unknown address - cannot send message.\n",
2324 ERROR + NO_SUCH_USER);
2327 if (e == MES_LOCAL) {
2328 a = getuser(&tempUS, recp);
2330 cprintf("%d No such user.\n",
2331 ERROR + NO_SUCH_USER);
2337 /* At this point, message has been approved. */
2338 if (extract_int(entargs, 0) == 0) {
2339 cprintf("%d OK to send\n", OK);
2343 msglen = extract_long(entargs, 2);
2344 msg = mallok(sizeof(struct CtdlMessage));
2346 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
2350 memset(msg, 0, sizeof(struct CtdlMessage));
2351 tempbuf = mallok(msglen);
2352 if (tempbuf == NULL) {
2353 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
2358 cprintf("%d %ld\n", SEND_BINARY, msglen);
2360 client_read(&ch, 1); /* 0xFF magic number */
2361 msg->cm_magic = CTDLMESSAGE_MAGIC;
2362 client_read(&ch, 1); /* anon type */
2363 msg->cm_anon_type = ch;
2364 client_read(&ch, 1); /* format type */
2365 msg->cm_format_type = ch;
2366 msglen = msglen - 3;
2368 while (msglen > 0) {
2369 client_read(&which_field, 1);
2370 if (!isalpha(which_field)) valid_msg = 0;
2374 client_read(&ch, 1);
2376 a = strlen(tempbuf);
2379 } while ( (ch != 0) && (msglen > 0) );
2381 msg->cm_fields[which_field] = strdoop(tempbuf);
2384 msg->cm_flags = CM_SKIP_HOOKS;
2385 if (valid_msg) CtdlSaveMsg(msg, recp, "", e);
2386 CtdlFreeMessage(msg);
2392 * API function to delete messages which match a set of criteria
2393 * (returns the actual number of messages deleted)
2395 int CtdlDeleteMessages(char *room_name, /* which room */
2396 long dmsgnum, /* or "0" for any */
2397 char *content_type /* or "" for any */
2401 struct quickroom qrbuf;
2402 struct cdbdata *cdbfr;
2403 long *msglist = NULL;
2406 int num_deleted = 0;
2408 struct SuppMsgInfo smi;
2410 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
2411 room_name, dmsgnum, content_type);
2413 /* get room record, obtaining a lock... */
2414 if (lgetroom(&qrbuf, room_name) != 0) {
2415 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
2417 return (0); /* room not found */
2419 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
2421 if (cdbfr != NULL) {
2422 msglist = mallok(cdbfr->len);
2423 memcpy(msglist, cdbfr->ptr, cdbfr->len);
2424 num_msgs = cdbfr->len / sizeof(long);
2428 for (i = 0; i < num_msgs; ++i) {
2431 /* Set/clear a bit for each criterion */
2433 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
2434 delete_this |= 0x01;
2436 if (strlen(content_type) == 0) {
2437 delete_this |= 0x02;
2439 GetSuppMsgInfo(&smi, msglist[i]);
2440 if (!strcasecmp(smi.smi_content_type,
2442 delete_this |= 0x02;
2446 /* Delete message only if all bits are set */
2447 if (delete_this == 0x03) {
2448 AdjRefCount(msglist[i], -1);
2454 num_msgs = sort_msglist(msglist, num_msgs);
2455 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
2456 msglist, (num_msgs * sizeof(long)));
2458 qrbuf.QRhighest = msglist[num_msgs - 1];
2462 lprintf(9, "%d message(s) deleted.\n", num_deleted);
2463 return (num_deleted);
2469 * Check whether the current user has permission to delete messages from
2470 * the current room (returns 1 for yes, 0 for no)
2472 int CtdlDoIHavePermissionToDeleteMessagesFromThisRoom(void) {
2473 getuser(&CC->usersupp, CC->curr_user);
2474 if ((CC->usersupp.axlevel < 6)
2475 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2476 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2477 && (!(CC->internal_pgm))) {
2486 * Delete message from current room
2488 void cmd_dele(char *delstr)
2493 if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom() == 0) {
2494 cprintf("%d Higher access required.\n",
2495 ERROR + HIGHER_ACCESS_REQUIRED);
2498 delnum = extract_long(delstr, 0);
2500 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, "");
2503 cprintf("%d %d message%s deleted.\n", OK,
2504 num_deleted, ((num_deleted != 1) ? "s" : ""));
2506 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2512 * Back end API function for moves and deletes
2514 int CtdlCopyMsgToRoom(long msgnum, char *dest) {
2517 err = CtdlSaveMsgPointerInRoom(dest, msgnum,
2518 (SM_VERIFY_GOODNESS | SM_DO_REPL_CHECK) );
2519 if (err != 0) return(err);
2527 * move or copy a message to another room
2529 void cmd_move(char *args)
2533 struct quickroom qtemp;
2537 num = extract_long(args, 0);
2538 extract(targ, args, 1);
2539 targ[ROOMNAMELEN - 1] = 0;
2540 is_copy = extract_int(args, 2);
2542 getuser(&CC->usersupp, CC->curr_user);
2543 if ((CC->usersupp.axlevel < 6)
2544 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2545 cprintf("%d Higher access required.\n",
2546 ERROR + HIGHER_ACCESS_REQUIRED);
2550 if (getroom(&qtemp, targ) != 0) {
2551 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2555 err = CtdlCopyMsgToRoom(num, targ);
2557 cprintf("%d Cannot store message in %s: error %d\n",
2562 /* Now delete the message from the source room,
2563 * if this is a 'move' rather than a 'copy' operation.
2565 if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, "");
2567 cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") );
2573 * GetSuppMsgInfo() - Get the supplementary record for a message
2575 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2578 struct cdbdata *cdbsmi;
2581 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2582 smibuf->smi_msgnum = msgnum;
2583 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2585 /* Use the negative of the message number for its supp record index */
2586 TheIndex = (0L - msgnum);
2588 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2589 if (cdbsmi == NULL) {
2590 return; /* record not found; go with defaults */
2592 memcpy(smibuf, cdbsmi->ptr,
2593 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2594 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2601 * PutSuppMsgInfo() - (re)write supplementary record for a message
2603 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2607 /* Use the negative of the message number for its supp record index */
2608 TheIndex = (0L - smibuf->smi_msgnum);
2610 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2611 smibuf->smi_msgnum, smibuf->smi_refcount);
2613 cdb_store(CDB_MSGMAIN,
2614 &TheIndex, sizeof(long),
2615 smibuf, sizeof(struct SuppMsgInfo));
2620 * AdjRefCount - change the reference count for a message;
2621 * delete the message if it reaches zero
2623 void AdjRefCount(long msgnum, int incr)
2626 struct SuppMsgInfo smi;
2629 /* This is a *tight* critical section; please keep it that way, as
2630 * it may get called while nested in other critical sections.
2631 * Complicating this any further will surely cause deadlock!
2633 begin_critical_section(S_SUPPMSGMAIN);
2634 GetSuppMsgInfo(&smi, msgnum);
2635 lprintf(9, "Ref count for message <%ld> before write is <%d>\n",
2636 msgnum, smi.smi_refcount);
2637 smi.smi_refcount += incr;
2638 PutSuppMsgInfo(&smi);
2639 end_critical_section(S_SUPPMSGMAIN);
2640 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2641 msgnum, smi.smi_refcount);
2643 /* If the reference count is now zero, delete the message
2644 * (and its supplementary record as well).
2646 if (smi.smi_refcount == 0) {
2647 lprintf(9, "Deleting message <%ld>\n", msgnum);
2649 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2650 delnum = (0L - msgnum);
2651 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2656 * Write a generic object to this room
2658 * Note: this could be much more efficient. Right now we use two temporary
2659 * files, and still pull the message into memory as with all others.
2661 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2662 char *content_type, /* MIME type of this object */
2663 char *tempfilename, /* Where to fetch it from */
2664 struct usersupp *is_mailbox, /* Mailbox room? */
2665 int is_binary, /* Is encoding necessary? */
2666 int is_unique, /* Del others of this type? */
2667 unsigned int flags /* Internal save flags */
2672 char filename[PATH_MAX];
2675 struct quickroom qrbuf;
2676 char roomname[ROOMNAMELEN];
2677 struct CtdlMessage *msg;
2680 if (is_mailbox != NULL)
2681 MailboxName(roomname, is_mailbox, req_room);
2683 safestrncpy(roomname, req_room, sizeof(roomname));
2684 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2686 strcpy(filename, tmpnam(NULL));
2687 fp = fopen(filename, "w");
2691 tempfp = fopen(tempfilename, "r");
2692 if (tempfp == NULL) {
2698 fprintf(fp, "Content-type: %s\n", content_type);
2699 lprintf(9, "Content-type: %s\n", content_type);
2701 if (is_binary == 0) {
2702 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2703 while (ch = getc(tempfp), ch > 0)
2709 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2712 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2713 tempfilename, filename);
2717 lprintf(9, "Allocating\n");
2718 msg = mallok(sizeof(struct CtdlMessage));
2719 memset(msg, 0, sizeof(struct CtdlMessage));
2720 msg->cm_magic = CTDLMESSAGE_MAGIC;
2721 msg->cm_anon_type = MES_NORMAL;
2722 msg->cm_format_type = 4;
2723 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2724 msg->cm_fields['O'] = strdoop(req_room);
2725 msg->cm_fields['N'] = strdoop(config.c_nodename);
2726 msg->cm_fields['H'] = strdoop(config.c_humannode);
2727 msg->cm_flags = flags;
2729 lprintf(9, "Loading\n");
2730 fp = fopen(filename, "rb");
2731 fseek(fp, 0L, SEEK_END);
2734 msg->cm_fields['M'] = mallok(len);
2735 fread(msg->cm_fields['M'], len, 1, fp);
2739 /* Create the requested room if we have to. */
2740 if (getroom(&qrbuf, roomname) != 0) {
2741 create_room(roomname,
2742 ( (is_mailbox != NULL) ? 5 : 3 ),
2745 /* If the caller specified this object as unique, delete all
2746 * other objects of this type that are currently in the room.
2749 lprintf(9, "Deleted %d other msgs of this type\n",
2750 CtdlDeleteMessages(roomname, 0L, content_type));
2752 /* Now write the data */
2753 CtdlSaveMsg(msg, "", roomname, MES_LOCAL);
2754 CtdlFreeMessage(msg);
2762 void CtdlGetSysConfigBackend(long msgnum, void *userdata) {
2763 config_msgnum = msgnum;
2767 char *CtdlGetSysConfig(char *sysconfname) {
2768 char hold_rm[ROOMNAMELEN];
2771 struct CtdlMessage *msg;
2774 strcpy(hold_rm, CC->quickroom.QRname);
2775 if (getroom(&CC->quickroom, SYSCONFIGROOM) != 0) {
2776 getroom(&CC->quickroom, hold_rm);
2781 /* We want the last (and probably only) config in this room */
2782 begin_critical_section(S_CONFIG);
2783 config_msgnum = (-1L);
2784 CtdlForEachMessage(MSGS_LAST, 1, (-127), sysconfname, NULL,
2785 CtdlGetSysConfigBackend, NULL);
2786 msgnum = config_msgnum;
2787 end_critical_section(S_CONFIG);
2793 msg = CtdlFetchMessage(msgnum);
2795 conf = strdoop(msg->cm_fields['M']);
2796 CtdlFreeMessage(msg);
2803 getroom(&CC->quickroom, hold_rm);
2805 if (conf != NULL) do {
2806 extract_token(buf, conf, 0, '\n');
2807 strcpy(conf, &conf[strlen(buf)+1]);
2808 } while ( (strlen(conf)>0) && (strlen(buf)>0) );
2813 void CtdlPutSysConfig(char *sysconfname, char *sysconfdata) {
2814 char temp[PATH_MAX];
2817 strcpy(temp, tmpnam(NULL));
2819 fp = fopen(temp, "w");
2820 if (fp == NULL) return;
2821 fprintf(fp, "%s", sysconfdata);
2824 /* this handy API function does all the work for us */
2825 CtdlWriteObject(SYSCONFIGROOM, sysconfname, temp, NULL, 0, 1, 0);