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 multipart/alternative
826 * NOTE: this differs from the standard behavior for a reason. Normally when
827 * displaying multipart/alternative you want to show the _last_ usable
828 * format in the message. Here we show the _first_ one, because it's
829 * usually text/plain. Since this set of functions is designed for text
830 * output to non-MIME-aware clients, this is the desired behavior.
833 void fixed_output_pre(char *name, char *filename, char *partnum, char *disp,
834 void *content, char *cbtype, size_t length, char *encoding,
837 lprintf(9, "fixed_output_pre() type=<%s>\n", cbtype);
838 if (!strcasecmp(cbtype, "multipart/alternative")) {
846 * Post callback function for multipart/alternative
848 void fixed_output_post(char *name, char *filename, char *partnum, char *disp,
849 void *content, char *cbtype, size_t length, char *encoding,
852 lprintf(9, "fixed_output_post() type=<%s>\n", cbtype);
853 if (!strcasecmp(cbtype, "multipart/alternative")) {
861 * Inline callback function for mime parser that wants to display text
863 void fixed_output(char *name, char *filename, char *partnum, char *disp,
864 void *content, char *cbtype, size_t length, char *encoding,
872 lprintf(9, "fixed_output() type=<%s>\n", cbtype);
875 * If we're in the middle of a multipart/alternative scope and
876 * we've already printed another section, skip this one.
878 if ( (ma->is_ma == 1) && (ma->did_print == 1) ) {
879 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
884 if ( (!strcasecmp(cbtype, "text/plain"))
885 || (strlen(cbtype)==0) ) {
891 if (ch==10) cprintf("\r\n");
892 else cprintf("%c", ch);
896 if (ch != '\n') cprintf("\n");
898 else if (!strcasecmp(cbtype, "text/html")) {
899 ptr = html_to_ascii(content, 80, 0);
904 if (ch==10) cprintf("\r\n");
905 else cprintf("%c", ch);
909 else if (strncasecmp(cbtype, "multipart/", 10)) {
910 cprintf("Part %s: %s (%s) (%d bytes)\r\n",
911 partnum, filename, cbtype, length);
917 * Get a message off disk. (returns om_* values found in msgbase.h)
920 int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */
921 int mode, /* how would you like that message? */
922 int headers_only, /* eschew the message body? */
923 int do_proto, /* do Citadel protocol responses? */
924 int crlf /* Use CRLF newlines instead of LF? */
926 struct CtdlMessage *TheMessage;
929 lprintf(7, "CtdlOutputMsg() msgnum=%ld, mode=%d\n",
934 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
935 if (do_proto) cprintf("%d Not logged in.\n",
936 ERROR + NOT_LOGGED_IN);
937 return(om_not_logged_in);
940 /* FIXME ... small security issue
941 * We need to check to make sure the requested message is actually
942 * in the current room, and set msg_ok to 1 only if it is. This
943 * functionality is currently missing because I'm in a hurry to replace
944 * broken production code with nonbroken pre-beta code. :( -- ajc
947 if (do_proto) cprintf("%d Message %ld is not in this room.\n",
949 return(om_no_such_msg);
954 * Fetch the message from disk
956 TheMessage = CtdlFetchMessage(msg_num);
957 if (TheMessage == NULL) {
958 if (do_proto) cprintf("%d Can't locate msg %ld on disk\n",
960 return(om_no_such_msg);
963 retcode = CtdlOutputPreLoadedMsg(
964 TheMessage, msg_num, mode,
965 headers_only, do_proto, crlf);
967 CtdlFreeMessage(TheMessage);
973 * Get a message off disk. (returns om_* values found in msgbase.h)
976 int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage,
978 int mode, /* how would you like that message? */
979 int headers_only, /* eschew the message body? */
980 int do_proto, /* do Citadel protocol responses? */
981 int crlf /* Use CRLF newlines instead of LF? */
987 char display_name[SIZ];
989 char *nl; /* newline string */
991 /* buffers needed for RFC822 translation */
1001 sprintf(mid, "%ld", msg_num);
1002 nl = (crlf ? "\r\n" : "\n");
1004 if (!is_valid_message(TheMessage)) {
1005 lprintf(1, "ERROR: invalid preloaded message for output\n");
1006 return(om_no_such_msg);
1009 /* Are we downloading a MIME component? */
1010 if (mode == MT_DOWNLOAD) {
1011 if (TheMessage->cm_format_type != FMT_RFC822) {
1013 cprintf("%d This is not a MIME message.\n",
1015 } else if (CC->download_fp != NULL) {
1016 if (do_proto) cprintf(
1017 "%d You already have a download open.\n",
1020 /* Parse the message text component */
1021 mptr = TheMessage->cm_fields['M'];
1022 mime_parser(mptr, NULL,
1023 *mime_download, NULL, NULL,
1025 /* If there's no file open by this time, the requested
1026 * section wasn't found, so print an error
1028 if (CC->download_fp == NULL) {
1029 if (do_proto) cprintf(
1030 "%d Section %s not found.\n",
1031 ERROR + FILE_NOT_FOUND,
1035 return((CC->download_fp != NULL) ? om_ok : om_mime_error);
1038 /* now for the user-mode message reading loops */
1039 if (do_proto) cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
1041 /* Tell the client which format type we're using. If this is a
1042 * MIME message, *lie* about it and tell the user it's fixed-format.
1044 if (mode == MT_CITADEL) {
1045 if (TheMessage->cm_format_type == FMT_RFC822) {
1046 if (do_proto) cprintf("type=1\n");
1049 if (do_proto) cprintf("type=%d\n",
1050 TheMessage->cm_format_type);
1054 /* nhdr=yes means that we're only displaying headers, no body */
1055 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
1056 if (do_proto) cprintf("nhdr=yes\n");
1059 /* begin header processing loop for Citadel message format */
1061 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
1063 strcpy(display_name, "<unknown>");
1064 if (TheMessage->cm_fields['A']) {
1065 strcpy(buf, TheMessage->cm_fields['A']);
1066 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
1067 if (TheMessage->cm_anon_type == MES_ANON)
1068 strcpy(display_name, "****");
1069 else if (TheMessage->cm_anon_type == MES_AN2)
1070 strcpy(display_name, "anonymous");
1072 strcpy(display_name, buf);
1073 if ((is_room_aide())
1074 && ((TheMessage->cm_anon_type == MES_ANON)
1075 || (TheMessage->cm_anon_type == MES_AN2))) {
1076 sprintf(&display_name[strlen(display_name)],
1081 strcpy(allkeys, FORDER);
1082 for (i=0; i<strlen(allkeys); ++i) {
1083 k = (int) allkeys[i];
1085 if (TheMessage->cm_fields[k] != NULL) {
1087 if (do_proto) cprintf("%s=%s\n",
1092 if (do_proto) cprintf("%s=%s\n",
1094 TheMessage->cm_fields[k]
1103 /* begin header processing loop for RFC822 transfer format */
1108 strcpy(snode, NODENAME);
1109 strcpy(lnode, HUMANNODE);
1110 if (mode == MT_RFC822) {
1111 cprintf("X-UIDL: %ld%s", msg_num, nl);
1112 for (i = 0; i < 256; ++i) {
1113 if (TheMessage->cm_fields[i]) {
1114 mptr = TheMessage->cm_fields[i];
1117 strcpy(luser, mptr);
1118 strcpy(suser, mptr);
1121 "Path:" removed for now because it confuses brain-dead Microsoft shitware
1122 into thinking that mail messages are newsgroup messages instead. When we
1123 add NNTP support back into Citadel we'll have to add code to only output
1124 this field when appropriate.
1125 else if (i == 'P') {
1126 cprintf("Path: %s%s", mptr, nl);
1130 cprintf("Subject: %s%s", mptr, nl);
1134 strcpy(lnode, mptr);
1136 cprintf("X-Citadel-Room: %s%s",
1139 strcpy(snode, mptr);
1141 cprintf("To: %s%s", mptr, nl);
1142 else if (i == 'T') {
1143 datestring(datestamp, atol(mptr),
1144 DATESTRING_RFC822 );
1145 cprintf("Date: %s%s", datestamp, nl);
1151 for (i=0; i<strlen(suser); ++i) {
1152 suser[i] = tolower(suser[i]);
1153 if (!isalnum(suser[i])) suser[i]='_';
1156 if (mode == MT_RFC822) {
1157 if (!strcasecmp(snode, NODENAME)) {
1158 strcpy(snode, FQDN);
1161 /* Construct a fun message id */
1162 cprintf("Message-ID: <%s", mid);
1163 if (strchr(mid, '@')==NULL) {
1164 cprintf("@%s", snode);
1168 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
1170 if (strlen(fuser) > 0) {
1171 cprintf("From: %s (%s)%s", fuser, luser, nl);
1174 cprintf("From: %s@%s (%s)%s", suser, snode, luser, nl);
1177 cprintf("Organization: %s%s", lnode, nl);
1180 /* end header processing loop ... at this point, we're in the text */
1182 mptr = TheMessage->cm_fields['M'];
1184 /* Tell the client about the MIME parts in this message */
1185 if (TheMessage->cm_format_type == FMT_RFC822) {
1186 if (mode == MT_CITADEL) {
1187 mime_parser(mptr, NULL,
1188 *list_this_part, NULL, NULL,
1191 else if (mode == MT_MIME) { /* list parts only */
1192 mime_parser(mptr, NULL,
1193 *list_this_part, NULL, NULL,
1195 if (do_proto) cprintf("000\n");
1198 else if (mode == MT_RFC822) { /* unparsed RFC822 dump */
1199 /* FIXME ... we have to put some code in here to avoid
1200 * printing duplicate header information when both
1201 * Citadel and RFC822 headers exist. Preference should
1202 * probably be given to the RFC822 headers.
1204 while (ch=*(mptr++), ch!=0) {
1206 else if (ch==10) cprintf("%s", nl);
1207 else cprintf("%c", ch);
1209 if (do_proto) cprintf("000\n");
1215 if (do_proto) cprintf("000\n");
1219 /* signify start of msg text */
1220 if (mode == MT_CITADEL)
1221 if (do_proto) cprintf("text\n");
1222 if (mode == MT_RFC822) {
1223 if (TheMessage->cm_fields['U'] == NULL) {
1224 cprintf("Subject: (no subject)%s", nl);
1229 /* If the format type on disk is 1 (fixed-format), then we want
1230 * everything to be output completely literally ... regardless of
1231 * what message transfer format is in use.
1233 if (TheMessage->cm_format_type == FMT_FIXED) {
1235 while (ch = *mptr++, ch > 0) {
1238 if ((ch == 10) || (strlen(buf) > 250)) {
1239 cprintf("%s%s", buf, nl);
1242 buf[strlen(buf) + 1] = 0;
1243 buf[strlen(buf)] = ch;
1246 if (strlen(buf) > 0)
1247 cprintf("%s%s", buf, nl);
1250 /* If the message on disk is format 0 (Citadel vari-format), we
1251 * output using the formatter at 80 columns. This is the final output
1252 * form if the transfer format is RFC822, but if the transfer format
1253 * is Citadel proprietary, it'll still work, because the indentation
1254 * for new paragraphs is correct and the client will reformat the
1255 * message to the reader's screen width.
1257 if (TheMessage->cm_format_type == FMT_CITADEL) {
1258 memfmout(80, mptr, 0, nl);
1261 /* If the message on disk is format 4 (MIME), we've gotta hand it
1262 * off to the MIME parser. The client has already been told that
1263 * this message is format 1 (fixed format), so the callback function
1264 * we use will display those parts as-is.
1266 if (TheMessage->cm_format_type == FMT_RFC822) {
1267 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
1268 memset(ma, 0, sizeof(struct ma_info));
1269 mime_parser(mptr, NULL,
1270 *fixed_output, *fixed_output_pre, *fixed_output_post,
1274 /* now we're done */
1275 if (do_proto) cprintf("000\n");
1282 * display a message (mode 0 - Citadel proprietary)
1284 void cmd_msg0(char *cmdbuf)
1287 int headers_only = 0;
1289 msgid = extract_long(cmdbuf, 0);
1290 headers_only = extract_int(cmdbuf, 1);
1292 CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0);
1298 * display a message (mode 2 - RFC822)
1300 void cmd_msg2(char *cmdbuf)
1303 int headers_only = 0;
1305 msgid = extract_long(cmdbuf, 0);
1306 headers_only = extract_int(cmdbuf, 1);
1308 CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1);
1314 * display a message (mode 3 - IGnet raw format - internal programs only)
1316 void cmd_msg3(char *cmdbuf)
1319 struct CtdlMessage *msg;
1322 if (CC->internal_pgm == 0) {
1323 cprintf("%d This command is for internal programs only.\n",
1328 msgnum = extract_long(cmdbuf, 0);
1329 msg = CtdlFetchMessage(msgnum);
1331 cprintf("%d Message %ld not found.\n",
1336 serialize_message(&smr, msg);
1337 CtdlFreeMessage(msg);
1340 cprintf("%d Unable to serialize message\n",
1341 ERROR+INTERNAL_ERROR);
1345 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1346 client_write(smr.ser, smr.len);
1353 * display a message (mode 4 - MIME) (FIXME ... still evolving, not complete)
1355 void cmd_msg4(char *cmdbuf)
1359 msgid = extract_long(cmdbuf, 0);
1360 CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0);
1364 * Open a component of a MIME message as a download file
1366 void cmd_opna(char *cmdbuf)
1370 CtdlAllocUserData(SYM_DESIRED_SECTION, SIZ);
1372 msgid = extract_long(cmdbuf, 0);
1373 extract(desired_section, cmdbuf, 1);
1375 CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1);
1380 * Save a message pointer into a specified room
1381 * (Returns 0 for success, nonzero for failure)
1382 * roomname may be NULL to use the current room
1384 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
1386 char hold_rm[ROOMNAMELEN];
1387 struct cdbdata *cdbfr;
1390 long highest_msg = 0L;
1391 struct CtdlMessage *msg = NULL;
1393 lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n",
1394 roomname, msgid, flags);
1396 strcpy(hold_rm, CC->quickroom.QRname);
1398 /* We may need to check to see if this message is real */
1399 if ( (flags & SM_VERIFY_GOODNESS)
1400 || (flags & SM_DO_REPL_CHECK)
1402 msg = CtdlFetchMessage(msgid);
1403 if (msg == NULL) return(ERROR + ILLEGAL_VALUE);
1406 /* Perform replication checks if necessary */
1407 if ( (flags & SM_DO_REPL_CHECK) && (msg != NULL) ) {
1409 if (getroom(&CC->quickroom,
1410 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1412 lprintf(9, "No such room <%s>\n", roomname);
1413 if (msg != NULL) CtdlFreeMessage(msg);
1414 return(ERROR + ROOM_NOT_FOUND);
1417 if (ReplicationChecks(msg) != 0) {
1418 getroom(&CC->quickroom, hold_rm);
1419 if (msg != NULL) CtdlFreeMessage(msg);
1420 lprintf(9, "Did replication, and newer exists\n");
1425 /* Now the regular stuff */
1426 if (lgetroom(&CC->quickroom,
1427 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1429 lprintf(9, "No such room <%s>\n", roomname);
1430 if (msg != NULL) CtdlFreeMessage(msg);
1431 return(ERROR + ROOM_NOT_FOUND);
1434 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
1435 if (cdbfr == NULL) {
1439 msglist = mallok(cdbfr->len);
1440 if (msglist == NULL)
1441 lprintf(3, "ERROR malloc msglist!\n");
1442 num_msgs = cdbfr->len / sizeof(long);
1443 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1448 /* Make sure the message doesn't already exist in this room. It
1449 * is absolutely taboo to have more than one reference to the same
1450 * message in a room.
1452 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1453 if (msglist[i] == msgid) {
1454 lputroom(&CC->quickroom); /* unlock the room */
1455 getroom(&CC->quickroom, hold_rm);
1456 if (msg != NULL) CtdlFreeMessage(msg);
1457 return(ERROR + ALREADY_EXISTS);
1461 /* Now add the new message */
1463 msglist = reallok(msglist,
1464 (num_msgs * sizeof(long)));
1466 if (msglist == NULL) {
1467 lprintf(3, "ERROR: can't realloc message list!\n");
1469 msglist[num_msgs - 1] = msgid;
1471 /* Sort the message list, so all the msgid's are in order */
1472 num_msgs = sort_msglist(msglist, num_msgs);
1474 /* Determine the highest message number */
1475 highest_msg = msglist[num_msgs - 1];
1477 /* Write it back to disk. */
1478 cdb_store(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long),
1479 msglist, num_msgs * sizeof(long));
1481 /* Free up the memory we used. */
1484 /* Update the highest-message pointer and unlock the room. */
1485 CC->quickroom.QRhighest = highest_msg;
1486 lputroom(&CC->quickroom);
1487 getroom(&CC->quickroom, hold_rm);
1489 /* Bump the reference count for this message. */
1490 if ((flags & SM_DONT_BUMP_REF)==0) {
1491 AdjRefCount(msgid, +1);
1494 /* Return success. */
1495 if (msg != NULL) CtdlFreeMessage(msg);
1502 * Message base operation to send a message to the master file
1503 * (returns new message number)
1505 * This is the back end for CtdlSaveMsg() and should not be directly
1506 * called by server-side modules.
1509 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1510 FILE *save_a_copy) /* save a copy to disk? */
1517 /* Get a new message number */
1518 newmsgid = get_new_message_number();
1519 sprintf(msgidbuf, "%ld@%s", newmsgid, config.c_fqdn);
1521 /* Generate an ID if we don't have one already */
1522 if (msg->cm_fields['I']==NULL) {
1523 msg->cm_fields['I'] = strdoop(msgidbuf);
1526 serialize_message(&smr, msg);
1529 cprintf("%d Unable to serialize message\n",
1530 ERROR+INTERNAL_ERROR);
1534 /* Write our little bundle of joy into the message base */
1535 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1536 smr.ser, smr.len) < 0) {
1537 lprintf(2, "Can't store message\n");
1543 /* If the caller specified that a copy should be saved to a particular
1544 * file handle, do that now too.
1546 if (save_a_copy != NULL) {
1547 fwrite(smr.ser, smr.len, 1, save_a_copy);
1550 /* Free the memory we used for the serialized message */
1553 /* Return the *local* message ID to the caller
1554 * (even if we're storing an incoming network message)
1562 * Serialize a struct CtdlMessage into the format used on disk and network.
1564 * This function loads up a "struct ser_ret" (defined in server.h) which
1565 * contains the length of the serialized message and a pointer to the
1566 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1568 void serialize_message(struct ser_ret *ret, /* return values */
1569 struct CtdlMessage *msg) /* unserialized msg */
1573 static char *forder = FORDER;
1575 if (is_valid_message(msg) == 0) return; /* self check */
1578 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1579 ret->len = ret->len +
1580 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1582 lprintf(9, "serialize_message() calling malloc(%d)\n", ret->len);
1583 ret->ser = mallok(ret->len);
1584 if (ret->ser == NULL) {
1590 ret->ser[1] = msg->cm_anon_type;
1591 ret->ser[2] = msg->cm_format_type;
1594 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1595 ret->ser[wlen++] = (char)forder[i];
1596 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1597 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1599 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1608 * Back end for the ReplicationChecks() function
1610 void check_repl(long msgnum, void *userdata) {
1611 struct CtdlMessage *msg;
1612 time_t timestamp = (-1L);
1614 lprintf(9, "check_repl() found message %ld\n", msgnum);
1615 msg = CtdlFetchMessage(msgnum);
1616 if (msg == NULL) return;
1617 if (msg->cm_fields['T'] != NULL) {
1618 timestamp = atol(msg->cm_fields['T']);
1620 CtdlFreeMessage(msg);
1622 if (timestamp > msg_repl->highest) {
1623 msg_repl->highest = timestamp; /* newer! */
1624 lprintf(9, "newer!\n");
1627 lprintf(9, "older!\n");
1629 /* Existing isn't newer? Then delete the old one(s). */
1630 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, "");
1635 * Check to see if any messages already exist which carry the same Extended ID
1639 * -> With older timestamps: delete them and return 0. Message will be saved.
1640 * -> With newer timestamps: return 1. Message save will be aborted.
1642 int ReplicationChecks(struct CtdlMessage *msg) {
1643 struct CtdlMessage *template;
1646 lprintf(9, "ReplicationChecks() started\n");
1647 /* No extended id? Don't do anything. */
1648 if (msg->cm_fields['E'] == NULL) return 0;
1649 if (strlen(msg->cm_fields['E']) == 0) return 0;
1650 lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1652 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1653 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1654 msg_repl->highest = atol(msg->cm_fields['T']);
1656 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1657 memset(template, 0, sizeof(struct CtdlMessage));
1658 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1660 CtdlForEachMessage(MSGS_ALL, 0L, (-127), NULL, template,
1663 /* If a newer message exists with the same Extended ID, abort
1666 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1670 CtdlFreeMessage(template);
1671 lprintf(9, "ReplicationChecks() returning %d\n", abort_this);
1679 * Save a message to disk
1681 long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1682 char *rec, /* Recipient (mail) */
1683 char *force, /* force a particular room? */
1684 int supplied_mailtype) /* local or remote type */
1687 char hold_rm[ROOMNAMELEN];
1688 char actual_rm[ROOMNAMELEN];
1689 char force_room[ROOMNAMELEN];
1690 char content_type[SIZ]; /* We have to learn this */
1691 char recipient[SIZ];
1694 struct usersupp userbuf;
1696 struct SuppMsgInfo smi;
1697 FILE *network_fp = NULL;
1698 static int seqnum = 1;
1699 struct CtdlMessage *imsg;
1703 lprintf(9, "CtdlSaveMsg() called\n");
1704 if (is_valid_message(msg) == 0) return(-1); /* self check */
1705 mailtype = supplied_mailtype;
1707 /* If this message has no timestamp, we take the liberty of
1708 * giving it one, right now.
1710 if (msg->cm_fields['T'] == NULL) {
1711 lprintf(9, "Generating timestamp\n");
1712 sprintf(aaa, "%ld", time(NULL));
1713 msg->cm_fields['T'] = strdoop(aaa);
1716 /* If this message has no path, we generate one.
1718 if (msg->cm_fields['P'] == NULL) {
1719 lprintf(9, "Generating path\n");
1720 if (msg->cm_fields['A'] != NULL) {
1721 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1722 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1723 if (isspace(msg->cm_fields['P'][a])) {
1724 msg->cm_fields['P'][a] = ' ';
1729 msg->cm_fields['P'] = strdoop("unknown");
1733 strcpy(force_room, force);
1735 /* Strip non-printable characters out of the recipient name */
1736 lprintf(9, "Checking recipient (if present)\n");
1737 strcpy(recipient, rec);
1738 for (a = 0; a < strlen(recipient); ++a)
1739 if (!isprint(recipient[a]))
1740 strcpy(&recipient[a], &recipient[a + 1]);
1742 /* Change "user @ xxx" to "user" if xxx is an alias for this host */
1743 for (a=0; a<strlen(recipient); ++a) {
1744 if (recipient[a] == '@') {
1745 if (CtdlHostAlias(&recipient[a+1])
1746 == hostalias_localhost) {
1748 lprintf(7, "Changed to <%s>\n", recipient);
1749 mailtype = MES_LOCAL;
1754 lprintf(9, "Recipient is <%s>\n", recipient);
1756 /* Learn about what's inside, because it's what's inside that counts */
1757 lprintf(9, "Learning what's inside\n");
1758 if (msg->cm_fields['M'] == NULL) {
1759 lprintf(1, "ERROR: attempt to save message with NULL body\n");
1762 switch (msg->cm_format_type) {
1764 strcpy(content_type, "text/x-citadel-variformat");
1767 strcpy(content_type, "text/plain");
1770 strcpy(content_type, "text/plain");
1771 /* advance past header fields */
1772 mptr = msg->cm_fields['M'];
1775 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1776 safestrncpy(content_type, mptr,
1777 sizeof(content_type));
1778 strcpy(content_type, &content_type[14]);
1779 for (a = 0; a < strlen(content_type); ++a)
1780 if ((content_type[a] == ';')
1781 || (content_type[a] == ' ')
1782 || (content_type[a] == 13)
1783 || (content_type[a] == 10))
1784 content_type[a] = 0;
1791 /* Goto the correct room */
1792 lprintf(9, "Switching rooms\n");
1793 strcpy(hold_rm, CC->quickroom.QRname);
1794 strcpy(actual_rm, CC->quickroom.QRname);
1796 /* If the user is a twit, move to the twit room for posting */
1797 lprintf(9, "Handling twit stuff\n");
1799 if (CC->usersupp.axlevel == 2) {
1800 strcpy(hold_rm, actual_rm);
1801 strcpy(actual_rm, config.c_twitroom);
1805 /* ...or if this message is destined for Aide> then go there. */
1806 if (strlen(force_room) > 0) {
1807 strcpy(actual_rm, force_room);
1810 lprintf(9, "Possibly relocating\n");
1811 if (strcasecmp(actual_rm, CC->quickroom.QRname)) {
1812 getroom(&CC->quickroom, actual_rm);
1816 * If this message has no O (room) field, generate one.
1818 if (msg->cm_fields['O'] == NULL) {
1819 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1822 /* Perform "before save" hooks (aborting if any return nonzero) */
1823 lprintf(9, "Performing before-save hooks\n");
1824 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return(-1);
1826 /* If this message has an Extended ID, perform replication checks */
1827 lprintf(9, "Performing replication checks\n");
1828 if (ReplicationChecks(msg) > 0) return(-1);
1830 /* Network mail - send a copy to the network program. */
1831 if ((strlen(recipient) > 0) && (mailtype == MES_BINARY)) {
1832 lprintf(9, "Sending network spool\n");
1833 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1834 (long) getpid(), CC->cs_pid, ++seqnum);
1835 lprintf(9, "Saving a copy to %s\n", aaa);
1836 network_fp = fopen(aaa, "ab+");
1837 if (network_fp == NULL)
1838 lprintf(2, "ERROR: %s\n", strerror(errno));
1841 /* Save it to disk */
1842 lprintf(9, "Saving to disk\n");
1843 newmsgid = send_message(msg, network_fp);
1844 if (network_fp != NULL) {
1846 /* FIXME start a network run here */
1849 if (newmsgid <= 0L) return(-1);
1851 /* Write a supplemental message info record. This doesn't have to
1852 * be a critical section because nobody else knows about this message
1855 lprintf(9, "Creating SuppMsgInfo record\n");
1856 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1857 smi.smi_msgnum = newmsgid;
1858 smi.smi_refcount = 0;
1859 safestrncpy(smi.smi_content_type, content_type, 64);
1860 PutSuppMsgInfo(&smi);
1862 /* Now figure out where to store the pointers */
1863 lprintf(9, "Storing pointers\n");
1865 /* If this is being done by the networker delivering a private
1866 * message, we want to BYPASS saving the sender's copy (because there
1867 * is no local sender; it would otherwise go to the Trashcan).
1869 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1870 if (CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0) != 0) {
1871 lprintf(3, "ERROR saving message pointer!\n");
1872 CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
1876 /* For internet mail, drop a copy in the outbound queue room */
1877 if (mailtype == MES_INTERNET) {
1878 CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0);
1881 /* Bump this user's messages posted counter. */
1882 lprintf(9, "Updating user\n");
1883 lgetuser(&CC->usersupp, CC->curr_user);
1884 CC->usersupp.posted = CC->usersupp.posted + 1;
1885 lputuser(&CC->usersupp);
1887 /* If this is private, local mail, make a copy in the
1888 * recipient's mailbox and bump the reference count.
1890 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1891 if (getuser(&userbuf, recipient) == 0) {
1892 lprintf(9, "Delivering private mail\n");
1893 MailboxName(actual_rm, &userbuf, MAILROOM);
1894 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1897 lprintf(9, "No user <%s>, saving in %s> instead\n",
1898 recipient, AIDEROOM);
1899 CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
1903 /* Perform "after save" hooks */
1904 lprintf(9, "Performing after-save hooks\n");
1905 PerformMessageHooks(msg, EVT_AFTERSAVE);
1908 lprintf(9, "Returning to original room\n");
1909 if (strcasecmp(hold_rm, CC->quickroom.QRname))
1910 getroom(&CC->quickroom, hold_rm);
1912 /* For internet mail, generate delivery instructions
1913 * (Yes, this is recursive! Deal with it!)
1915 if (mailtype == MES_INTERNET) {
1916 lprintf(9, "Generating delivery instructions\n");
1917 instr = mallok(2048);
1919 "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
1922 SPOOLMIME, newmsgid, time(NULL),
1923 msg->cm_fields['A'], msg->cm_fields['N'],
1926 imsg = mallok(sizeof(struct CtdlMessage));
1927 memset(imsg, 0, sizeof(struct CtdlMessage));
1928 imsg->cm_magic = CTDLMESSAGE_MAGIC;
1929 imsg->cm_anon_type = MES_NORMAL;
1930 imsg->cm_format_type = FMT_RFC822;
1931 imsg->cm_fields['A'] = strdoop("Citadel");
1932 imsg->cm_fields['M'] = instr;
1933 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
1934 CtdlFreeMessage(imsg);
1943 * Convenience function for generating small administrative messages.
1945 void quickie_message(char *from, char *to, char *room, char *text)
1947 struct CtdlMessage *msg;
1949 msg = mallok(sizeof(struct CtdlMessage));
1950 memset(msg, 0, sizeof(struct CtdlMessage));
1951 msg->cm_magic = CTDLMESSAGE_MAGIC;
1952 msg->cm_anon_type = MES_NORMAL;
1953 msg->cm_format_type = 0;
1954 msg->cm_fields['A'] = strdoop(from);
1955 msg->cm_fields['O'] = strdoop(room);
1956 msg->cm_fields['N'] = strdoop(NODENAME);
1958 msg->cm_fields['R'] = strdoop(to);
1959 msg->cm_fields['M'] = strdoop(text);
1961 CtdlSaveMsg(msg, "", room, MES_LOCAL);
1962 CtdlFreeMessage(msg);
1963 syslog(LOG_NOTICE, text);
1969 * Back end function used by make_message() and similar functions
1971 char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */
1972 size_t maxlen, /* maximum message length */
1973 char *exist /* if non-null, append to it;
1974 exist is ALWAYS freed */
1978 size_t message_len = 0;
1979 size_t buffer_len = 0;
1983 if (exist == NULL) {
1987 m = reallok(exist, strlen(exist) + 4096);
1988 if (m == NULL) phree(exist);
1991 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) ;;
1998 /* read in the lines of message text one by one */
1999 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) {
2001 /* strip trailing newline type stuff */
2002 if (buf[strlen(buf)-1]==10) buf[strlen(buf)-1]=0;
2003 if (buf[strlen(buf)-1]==13) buf[strlen(buf)-1]=0;
2005 linelen = strlen(buf);
2007 /* augment the buffer if we have to */
2008 if ((message_len + linelen + 2) > buffer_len) {
2009 lprintf(9, "realloking\n");
2010 ptr = reallok(m, (buffer_len * 2) );
2011 if (ptr == NULL) { /* flush if can't allocate */
2012 while ( (client_gets(buf)>0) &&
2013 strcmp(buf, terminator)) ;;
2016 buffer_len = (buffer_len * 2);
2018 lprintf(9, "buffer_len is %d\n", buffer_len);
2022 /* Add the new line to the buffer. We avoid using strcat()
2023 * because that would involve traversing the entire message
2024 * after each line, and this function needs to run fast.
2026 strcpy(&m[message_len], buf);
2027 m[message_len + linelen] = '\n';
2028 m[message_len + linelen + 1] = 0;
2029 message_len = message_len + linelen + 1;
2031 /* if we've hit the max msg length, flush the rest */
2032 if (message_len >= maxlen) {
2033 while ( (client_gets(buf)>0)
2034 && strcmp(buf, terminator)) ;;
2045 * Build a binary message to be saved on disk.
2048 static struct CtdlMessage *make_message(
2049 struct usersupp *author, /* author's usersupp structure */
2050 char *recipient, /* NULL if it's not mail */
2051 char *room, /* room where it's going */
2052 int type, /* see MES_ types in header file */
2053 int net_type, /* see MES_ types in header file */
2054 int format_type, /* local or remote (see citadel.h) */
2055 char *fake_name) /* who we're masquerading as */
2061 struct CtdlMessage *msg;
2063 msg = mallok(sizeof(struct CtdlMessage));
2064 memset(msg, 0, sizeof(struct CtdlMessage));
2065 msg->cm_magic = CTDLMESSAGE_MAGIC;
2066 msg->cm_anon_type = type;
2067 msg->cm_format_type = format_type;
2069 /* Don't confuse the poor folks if it's not routed mail. */
2070 strcpy(dest_node, "");
2072 /* If net_type is MES_BINARY, split out the destination node. */
2073 if (net_type == MES_BINARY) {
2074 strcpy(dest_node, NODENAME);
2075 for (a = 0; a < strlen(recipient); ++a) {
2076 if (recipient[a] == '@') {
2078 strcpy(dest_node, &recipient[a + 1]);
2083 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
2084 if (net_type == MES_INTERNET) {
2085 strcpy(dest_node, "internet");
2088 while (isspace(recipient[strlen(recipient) - 1]))
2089 recipient[strlen(recipient) - 1] = 0;
2091 sprintf(buf, "cit%ld", author->usernum); /* Path */
2092 msg->cm_fields['P'] = strdoop(buf);
2094 sprintf(buf, "%ld", time(NULL)); /* timestamp */
2095 msg->cm_fields['T'] = strdoop(buf);
2097 if (fake_name[0]) /* author */
2098 msg->cm_fields['A'] = strdoop(fake_name);
2100 msg->cm_fields['A'] = strdoop(author->fullname);
2102 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
2103 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
2105 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
2107 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
2108 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
2110 if (recipient[0] != 0)
2111 msg->cm_fields['R'] = strdoop(recipient);
2112 if (dest_node[0] != 0)
2113 msg->cm_fields['D'] = strdoop(dest_node);
2116 msg->cm_fields['M'] = CtdlReadMessageBody("000",
2117 config.c_maxmsglen, NULL);
2125 * Check to see whether we have permission to post a message in the current
2126 * room. Returns a *CITADEL ERROR CODE* and puts a message in errmsgbuf, or
2127 * returns 0 on success.
2129 int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf) {
2131 if (!(CC->logged_in)) {
2132 sprintf(errmsgbuf, "Not logged in.");
2133 return (ERROR + NOT_LOGGED_IN);
2136 if ((CC->usersupp.axlevel < 2)
2137 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
2138 sprintf(errmsgbuf, "Need to be validated to enter "
2139 "(except in %s> to sysop)", MAILROOM);
2140 return (ERROR + HIGHER_ACCESS_REQUIRED);
2143 if ((CC->usersupp.axlevel < 4)
2144 && (CC->quickroom.QRflags & QR_NETWORK)) {
2145 sprintf(errmsgbuf, "Need net privileges to enter here.");
2146 return (ERROR + HIGHER_ACCESS_REQUIRED);
2149 if ((CC->usersupp.axlevel < 6)
2150 && (CC->quickroom.QRflags & QR_READONLY)) {
2151 sprintf(errmsgbuf, "Sorry, this is a read-only room.");
2152 return (ERROR + HIGHER_ACCESS_REQUIRED);
2155 strcpy(errmsgbuf, "Ok");
2163 * message entry - mode 0 (normal)
2165 void cmd_ent0(char *entargs)
2168 char recipient[SIZ];
2170 int format_type = 0;
2171 char newusername[SIZ];
2172 struct CtdlMessage *msg;
2176 struct usersupp tempUS;
2180 post = extract_int(entargs, 0);
2181 extract(recipient, entargs, 1);
2182 anon_flag = extract_int(entargs, 2);
2183 format_type = extract_int(entargs, 3);
2185 /* first check to make sure the request is valid. */
2187 err = CtdlDoIHavePermissionToPostInThisRoom(buf);
2189 cprintf("%d %s\n", err, buf);
2193 /* Check some other permission type things. */
2196 if (CC->usersupp.axlevel < 6) {
2197 cprintf("%d You don't have permission to masquerade.\n",
2198 ERROR + HIGHER_ACCESS_REQUIRED);
2201 extract(newusername, entargs, 4);
2202 memset(CC->fake_postname, 0, 32);
2203 strcpy(CC->fake_postname, newusername);
2204 cprintf("%d Ok\n", OK);
2207 CC->cs_flags |= CS_POSTING;
2210 if (CC->quickroom.QRflags & QR_MAILBOX) {
2211 if (CC->usersupp.axlevel >= 2) {
2212 strcpy(buf, recipient);
2214 strcpy(buf, "sysop");
2215 e = alias(buf); /* alias and mail type */
2216 if ((buf[0] == 0) || (e == MES_ERROR)) {
2217 cprintf("%d Unknown address - cannot send message.\n",
2218 ERROR + NO_SUCH_USER);
2221 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
2222 cprintf("%d Net privileges required for network mail.\n",
2223 ERROR + HIGHER_ACCESS_REQUIRED);
2226 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
2227 && ((CC->usersupp.flags & US_INTERNET) == 0)
2228 && (!CC->internal_pgm)) {
2229 cprintf("%d You don't have access to Internet mail.\n",
2230 ERROR + HIGHER_ACCESS_REQUIRED);
2233 if (!strcasecmp(buf, "sysop")) {
2236 else if (e == MES_LOCAL) { /* don't search local file */
2237 if (!strcasecmp(buf, CC->usersupp.fullname)) {
2238 cprintf("%d Can't send mail to yourself!\n",
2239 ERROR + NO_SUCH_USER);
2242 /* Check to make sure the user exists; also get the correct
2243 * upper/lower casing of the name.
2245 a = getuser(&tempUS, buf);
2247 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
2250 strcpy(buf, tempUS.fullname);
2255 if (CC->quickroom.QRflags & QR_ANONONLY)
2257 if (CC->quickroom.QRflags & QR_ANONOPT) {
2261 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2264 /* If we're only checking the validity of the request, return
2265 * success without creating the message.
2268 cprintf("%d %s\n", OK, buf);
2272 cprintf("%d send message\n", SEND_LISTING);
2274 /* Read in the message from the client. */
2275 if (CC->fake_postname[0])
2276 msg = make_message(&CC->usersupp, buf,
2277 CC->quickroom.QRname, b, e, format_type,
2279 else if (CC->fake_username[0])
2280 msg = make_message(&CC->usersupp, buf,
2281 CC->quickroom.QRname, b, e, format_type,
2284 msg = make_message(&CC->usersupp, buf,
2285 CC->quickroom.QRname, b, e, format_type, "");
2288 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e);
2289 CtdlFreeMessage(msg);
2290 CC->fake_postname[0] = '\0';
2297 * message entry - mode 3 (raw)
2299 void cmd_ent3(char *entargs)
2305 unsigned char ch, which_field;
2306 struct usersupp tempUS;
2308 struct CtdlMessage *msg;
2311 if (CC->internal_pgm == 0) {
2312 cprintf("%d This command is for internal programs only.\n",
2317 /* See if there's a recipient, but make sure it's a real one */
2318 extract(recp, entargs, 1);
2319 for (a = 0; a < strlen(recp); ++a)
2320 if (!isprint(recp[a]))
2321 strcpy(&recp[a], &recp[a + 1]);
2322 while (isspace(recp[0]))
2323 strcpy(recp, &recp[1]);
2324 while (isspace(recp[strlen(recp) - 1]))
2325 recp[strlen(recp) - 1] = 0;
2327 /* If we're in Mail, check the recipient */
2328 if (strlen(recp) > 0) {
2329 e = alias(recp); /* alias and mail type */
2330 if ((recp[0] == 0) || (e == MES_ERROR)) {
2331 cprintf("%d Unknown address - cannot send message.\n",
2332 ERROR + NO_SUCH_USER);
2335 if (e == MES_LOCAL) {
2336 a = getuser(&tempUS, recp);
2338 cprintf("%d No such user.\n",
2339 ERROR + NO_SUCH_USER);
2345 /* At this point, message has been approved. */
2346 if (extract_int(entargs, 0) == 0) {
2347 cprintf("%d OK to send\n", OK);
2351 msglen = extract_long(entargs, 2);
2352 msg = mallok(sizeof(struct CtdlMessage));
2354 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
2358 memset(msg, 0, sizeof(struct CtdlMessage));
2359 tempbuf = mallok(msglen);
2360 if (tempbuf == NULL) {
2361 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
2366 cprintf("%d %ld\n", SEND_BINARY, msglen);
2368 client_read(&ch, 1); /* 0xFF magic number */
2369 msg->cm_magic = CTDLMESSAGE_MAGIC;
2370 client_read(&ch, 1); /* anon type */
2371 msg->cm_anon_type = ch;
2372 client_read(&ch, 1); /* format type */
2373 msg->cm_format_type = ch;
2374 msglen = msglen - 3;
2376 while (msglen > 0) {
2377 client_read(&which_field, 1);
2378 if (!isalpha(which_field)) valid_msg = 0;
2382 client_read(&ch, 1);
2384 a = strlen(tempbuf);
2387 } while ( (ch != 0) && (msglen > 0) );
2389 msg->cm_fields[which_field] = strdoop(tempbuf);
2392 msg->cm_flags = CM_SKIP_HOOKS;
2393 if (valid_msg) CtdlSaveMsg(msg, recp, "", e);
2394 CtdlFreeMessage(msg);
2400 * API function to delete messages which match a set of criteria
2401 * (returns the actual number of messages deleted)
2403 int CtdlDeleteMessages(char *room_name, /* which room */
2404 long dmsgnum, /* or "0" for any */
2405 char *content_type /* or "" for any */
2409 struct quickroom qrbuf;
2410 struct cdbdata *cdbfr;
2411 long *msglist = NULL;
2414 int num_deleted = 0;
2416 struct SuppMsgInfo smi;
2418 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
2419 room_name, dmsgnum, content_type);
2421 /* get room record, obtaining a lock... */
2422 if (lgetroom(&qrbuf, room_name) != 0) {
2423 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
2425 return (0); /* room not found */
2427 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
2429 if (cdbfr != NULL) {
2430 msglist = mallok(cdbfr->len);
2431 memcpy(msglist, cdbfr->ptr, cdbfr->len);
2432 num_msgs = cdbfr->len / sizeof(long);
2436 for (i = 0; i < num_msgs; ++i) {
2439 /* Set/clear a bit for each criterion */
2441 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
2442 delete_this |= 0x01;
2444 if (strlen(content_type) == 0) {
2445 delete_this |= 0x02;
2447 GetSuppMsgInfo(&smi, msglist[i]);
2448 if (!strcasecmp(smi.smi_content_type,
2450 delete_this |= 0x02;
2454 /* Delete message only if all bits are set */
2455 if (delete_this == 0x03) {
2456 AdjRefCount(msglist[i], -1);
2462 num_msgs = sort_msglist(msglist, num_msgs);
2463 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
2464 msglist, (num_msgs * sizeof(long)));
2466 qrbuf.QRhighest = msglist[num_msgs - 1];
2470 lprintf(9, "%d message(s) deleted.\n", num_deleted);
2471 return (num_deleted);
2477 * Check whether the current user has permission to delete messages from
2478 * the current room (returns 1 for yes, 0 for no)
2480 int CtdlDoIHavePermissionToDeleteMessagesFromThisRoom(void) {
2481 getuser(&CC->usersupp, CC->curr_user);
2482 if ((CC->usersupp.axlevel < 6)
2483 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2484 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2485 && (!(CC->internal_pgm))) {
2494 * Delete message from current room
2496 void cmd_dele(char *delstr)
2501 if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom() == 0) {
2502 cprintf("%d Higher access required.\n",
2503 ERROR + HIGHER_ACCESS_REQUIRED);
2506 delnum = extract_long(delstr, 0);
2508 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, "");
2511 cprintf("%d %d message%s deleted.\n", OK,
2512 num_deleted, ((num_deleted != 1) ? "s" : ""));
2514 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2520 * Back end API function for moves and deletes
2522 int CtdlCopyMsgToRoom(long msgnum, char *dest) {
2525 err = CtdlSaveMsgPointerInRoom(dest, msgnum,
2526 (SM_VERIFY_GOODNESS | SM_DO_REPL_CHECK) );
2527 if (err != 0) return(err);
2535 * move or copy a message to another room
2537 void cmd_move(char *args)
2541 struct quickroom qtemp;
2545 num = extract_long(args, 0);
2546 extract(targ, args, 1);
2547 targ[ROOMNAMELEN - 1] = 0;
2548 is_copy = extract_int(args, 2);
2550 getuser(&CC->usersupp, CC->curr_user);
2551 if ((CC->usersupp.axlevel < 6)
2552 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2553 cprintf("%d Higher access required.\n",
2554 ERROR + HIGHER_ACCESS_REQUIRED);
2558 if (getroom(&qtemp, targ) != 0) {
2559 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2563 err = CtdlCopyMsgToRoom(num, targ);
2565 cprintf("%d Cannot store message in %s: error %d\n",
2570 /* Now delete the message from the source room,
2571 * if this is a 'move' rather than a 'copy' operation.
2573 if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, "");
2575 cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") );
2581 * GetSuppMsgInfo() - Get the supplementary record for a message
2583 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2586 struct cdbdata *cdbsmi;
2589 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2590 smibuf->smi_msgnum = msgnum;
2591 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2593 /* Use the negative of the message number for its supp record index */
2594 TheIndex = (0L - msgnum);
2596 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2597 if (cdbsmi == NULL) {
2598 return; /* record not found; go with defaults */
2600 memcpy(smibuf, cdbsmi->ptr,
2601 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2602 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2609 * PutSuppMsgInfo() - (re)write supplementary record for a message
2611 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2615 /* Use the negative of the message number for its supp record index */
2616 TheIndex = (0L - smibuf->smi_msgnum);
2618 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2619 smibuf->smi_msgnum, smibuf->smi_refcount);
2621 cdb_store(CDB_MSGMAIN,
2622 &TheIndex, sizeof(long),
2623 smibuf, sizeof(struct SuppMsgInfo));
2628 * AdjRefCount - change the reference count for a message;
2629 * delete the message if it reaches zero
2631 void AdjRefCount(long msgnum, int incr)
2634 struct SuppMsgInfo smi;
2637 /* This is a *tight* critical section; please keep it that way, as
2638 * it may get called while nested in other critical sections.
2639 * Complicating this any further will surely cause deadlock!
2641 begin_critical_section(S_SUPPMSGMAIN);
2642 GetSuppMsgInfo(&smi, msgnum);
2643 lprintf(9, "Ref count for message <%ld> before write is <%d>\n",
2644 msgnum, smi.smi_refcount);
2645 smi.smi_refcount += incr;
2646 PutSuppMsgInfo(&smi);
2647 end_critical_section(S_SUPPMSGMAIN);
2648 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2649 msgnum, smi.smi_refcount);
2651 /* If the reference count is now zero, delete the message
2652 * (and its supplementary record as well).
2654 if (smi.smi_refcount == 0) {
2655 lprintf(9, "Deleting message <%ld>\n", msgnum);
2657 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2658 delnum = (0L - msgnum);
2659 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2664 * Write a generic object to this room
2666 * Note: this could be much more efficient. Right now we use two temporary
2667 * files, and still pull the message into memory as with all others.
2669 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2670 char *content_type, /* MIME type of this object */
2671 char *tempfilename, /* Where to fetch it from */
2672 struct usersupp *is_mailbox, /* Mailbox room? */
2673 int is_binary, /* Is encoding necessary? */
2674 int is_unique, /* Del others of this type? */
2675 unsigned int flags /* Internal save flags */
2680 char filename[PATH_MAX];
2683 struct quickroom qrbuf;
2684 char roomname[ROOMNAMELEN];
2685 struct CtdlMessage *msg;
2688 if (is_mailbox != NULL)
2689 MailboxName(roomname, is_mailbox, req_room);
2691 safestrncpy(roomname, req_room, sizeof(roomname));
2692 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2694 strcpy(filename, tmpnam(NULL));
2695 fp = fopen(filename, "w");
2699 tempfp = fopen(tempfilename, "r");
2700 if (tempfp == NULL) {
2706 fprintf(fp, "Content-type: %s\n", content_type);
2707 lprintf(9, "Content-type: %s\n", content_type);
2709 if (is_binary == 0) {
2710 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2711 while (ch = getc(tempfp), ch > 0)
2717 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2720 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2721 tempfilename, filename);
2725 lprintf(9, "Allocating\n");
2726 msg = mallok(sizeof(struct CtdlMessage));
2727 memset(msg, 0, sizeof(struct CtdlMessage));
2728 msg->cm_magic = CTDLMESSAGE_MAGIC;
2729 msg->cm_anon_type = MES_NORMAL;
2730 msg->cm_format_type = 4;
2731 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2732 msg->cm_fields['O'] = strdoop(req_room);
2733 msg->cm_fields['N'] = strdoop(config.c_nodename);
2734 msg->cm_fields['H'] = strdoop(config.c_humannode);
2735 msg->cm_flags = flags;
2737 lprintf(9, "Loading\n");
2738 fp = fopen(filename, "rb");
2739 fseek(fp, 0L, SEEK_END);
2742 msg->cm_fields['M'] = mallok(len);
2743 fread(msg->cm_fields['M'], len, 1, fp);
2747 /* Create the requested room if we have to. */
2748 if (getroom(&qrbuf, roomname) != 0) {
2749 create_room(roomname,
2750 ( (is_mailbox != NULL) ? 5 : 3 ),
2753 /* If the caller specified this object as unique, delete all
2754 * other objects of this type that are currently in the room.
2757 lprintf(9, "Deleted %d other msgs of this type\n",
2758 CtdlDeleteMessages(roomname, 0L, content_type));
2760 /* Now write the data */
2761 CtdlSaveMsg(msg, "", roomname, MES_LOCAL);
2762 CtdlFreeMessage(msg);
2770 void CtdlGetSysConfigBackend(long msgnum, void *userdata) {
2771 config_msgnum = msgnum;
2775 char *CtdlGetSysConfig(char *sysconfname) {
2776 char hold_rm[ROOMNAMELEN];
2779 struct CtdlMessage *msg;
2782 strcpy(hold_rm, CC->quickroom.QRname);
2783 if (getroom(&CC->quickroom, SYSCONFIGROOM) != 0) {
2784 getroom(&CC->quickroom, hold_rm);
2789 /* We want the last (and probably only) config in this room */
2790 begin_critical_section(S_CONFIG);
2791 config_msgnum = (-1L);
2792 CtdlForEachMessage(MSGS_LAST, 1, (-127), sysconfname, NULL,
2793 CtdlGetSysConfigBackend, NULL);
2794 msgnum = config_msgnum;
2795 end_critical_section(S_CONFIG);
2801 msg = CtdlFetchMessage(msgnum);
2803 conf = strdoop(msg->cm_fields['M']);
2804 CtdlFreeMessage(msg);
2811 getroom(&CC->quickroom, hold_rm);
2813 if (conf != NULL) do {
2814 extract_token(buf, conf, 0, '\n');
2815 strcpy(conf, &conf[strlen(buf)+1]);
2816 } while ( (strlen(conf)>0) && (strlen(buf)>0) );
2821 void CtdlPutSysConfig(char *sysconfname, char *sysconfdata) {
2822 char temp[PATH_MAX];
2825 strcpy(temp, tmpnam(NULL));
2827 fp = fopen(temp, "w");
2828 if (fp == NULL) return;
2829 fprintf(fp, "%s", sysconfdata);
2832 /* this handy API function does all the work for us */
2833 CtdlWriteObject(SYSCONFIGROOM, sysconfname, temp, NULL, 0, 1, 0);