4 * Implements the message store.
26 #include "sysdep_decls.h"
27 #include "citserver.h"
32 #include "dynloader.h"
34 #include "mime_parser.h"
37 #include "internet_addressing.h"
39 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
40 #define ma ((struct ma_info *)CtdlGetUserData(SYM_MA_INFO))
41 #define msg_repl ((struct repl *)CtdlGetUserData(SYM_REPL))
43 extern struct config config;
47 "", "", "", "", "", "", "", "",
48 "", "", "", "", "", "", "", "",
49 "", "", "", "", "", "", "", "",
50 "", "", "", "", "", "", "", "",
51 "", "", "", "", "", "", "", "",
52 "", "", "", "", "", "", "", "",
53 "", "", "", "", "", "", "", "",
54 "", "", "", "", "", "", "", "",
81 * This function is self explanatory.
82 * (What can I say, I'm in a weird mood today...)
84 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
88 for (i = 0; i < strlen(name); ++i) {
90 while (isspace(name[i - 1]) && i > 0) {
91 strcpy(&name[i - 1], &name[i]);
94 while (isspace(name[i + 1])) {
95 strcpy(&name[i + 1], &name[i + 2]);
103 * Aliasing for network mail.
104 * (Error messages have been commented out, because this is a server.)
106 int alias(char *name)
107 { /* process alias and routing info for mail */
110 char aaa[300], bbb[300];
112 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
114 fp = fopen("network/mail.aliases", "r");
116 fp = fopen("/dev/null", "r");
121 while (fgets(aaa, sizeof aaa, fp) != NULL) {
122 while (isspace(name[0]))
123 strcpy(name, &name[1]);
124 aaa[strlen(aaa) - 1] = 0;
126 for (a = 0; a < strlen(aaa); ++a) {
128 strcpy(bbb, &aaa[a + 1]);
132 if (!strcasecmp(name, aaa))
136 lprintf(7, "Mail is being forwarded to %s\n", name);
138 /* Change "user @ xxx" to "user" if xxx is an alias for this host */
139 for (a=0; a<strlen(name); ++a) {
140 if (name[a] == '@') {
141 if (CtdlHostAlias(&name[a+1]) == hostalias_localhost) {
143 lprintf(7, "Changed to <%s>\n", name);
148 /* determine local or remote type, see citadel.h */
149 for (a = 0; a < strlen(name); ++a)
151 return (MES_INTERNET);
152 for (a = 0; a < strlen(name); ++a)
154 for (b = a; b < strlen(name); ++b)
156 return (MES_INTERNET);
158 for (a = 0; a < strlen(name); ++a)
162 lprintf(7, "Too many @'s in address\n");
166 for (a = 0; a < strlen(name); ++a)
168 strcpy(bbb, &name[a + 1]);
170 strcpy(bbb, &bbb[1]);
171 fp = fopen("network/mail.sysinfo", "r");
175 a = getstring(fp, aaa);
176 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
177 a = getstring(fp, aaa);
178 if (!strncmp(aaa, "use ", 4)) {
179 strcpy(bbb, &aaa[4]);
184 if (!strncmp(aaa, "uum", 3)) {
186 for (a = 0; a < strlen(bbb); ++a) {
192 while (bbb[strlen(bbb) - 1] == '_')
193 bbb[strlen(bbb) - 1] = 0;
194 sprintf(name, &aaa[4], bbb);
195 lprintf(9, "returning MES_INTERNET\n");
196 return (MES_INTERNET);
198 if (!strncmp(aaa, "bin", 3)) {
201 while (aaa[strlen(aaa) - 1] != '@')
202 aaa[strlen(aaa) - 1] = 0;
203 aaa[strlen(aaa) - 1] = 0;
204 while (aaa[strlen(aaa) - 1] == ' ')
205 aaa[strlen(aaa) - 1] = 0;
206 while (bbb[0] != '@')
207 strcpy(bbb, &bbb[1]);
208 strcpy(bbb, &bbb[1]);
209 while (bbb[0] == ' ')
210 strcpy(bbb, &bbb[1]);
211 sprintf(name, "%s @%s", aaa, bbb);
212 lprintf(9, "returning MES_BINARY\n");
217 lprintf(9, "returning MES_LOCAL\n");
226 fp = fopen("citadel.control", "r");
227 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
233 void simple_listing(long msgnum, void *userdata)
235 cprintf("%ld\n", msgnum);
240 /* Determine if a given message matches the fields in a message template.
241 * Return 0 for a successful match.
243 int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
246 /* If there aren't any fields in the template, all messages will
249 if (template == NULL) return(0);
251 /* Null messages are bogus. */
252 if (msg == NULL) return(1);
254 for (i='A'; i<='Z'; ++i) {
255 if (template->cm_fields[i] != NULL) {
256 if (msg->cm_fields[i] == NULL) {
259 if (strcasecmp(msg->cm_fields[i],
260 template->cm_fields[i])) return 1;
264 /* All compares succeeded: we have a match! */
270 * Manipulate the "seen msgs" string.
272 void CtdlSetSeen(long target_msgnum, int target_setting) {
274 struct cdbdata *cdbfr;
284 /* Learn about the user and room in question */
286 getuser(&CC->usersupp, CC->curr_user);
287 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
289 /* Load the message list */
290 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
292 msglist = mallok(cdbfr->len);
293 memcpy(msglist, cdbfr->ptr, cdbfr->len);
294 num_msgs = cdbfr->len / sizeof(long);
297 return; /* No messages at all? No further action. */
300 lprintf(9, "before optimize: %s\n", vbuf.v_seen);
303 for (i=0; i<num_msgs; ++i) {
306 if (msglist[i] == target_msgnum) {
307 is_seen = target_setting;
310 if (is_msg_in_mset(vbuf.v_seen, msglist[i])) {
316 if (lo < 0L) lo = msglist[i];
319 if ( ((is_seen == 0) && (was_seen == 1))
320 || ((is_seen == 1) && (i == num_msgs-1)) ) {
321 if ( (strlen(newseen) + 20) > SIZ) {
322 strcpy(newseen, &newseen[20]);
325 if (strlen(newseen) > 0) strcat(newseen, ",");
327 sprintf(&newseen[strlen(newseen)], "%ld", lo);
330 sprintf(&newseen[strlen(newseen)], "%ld:%ld",
339 safestrncpy(vbuf.v_seen, newseen, SIZ);
340 lprintf(9, " after optimize: %s\n", vbuf.v_seen);
342 CtdlSetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
347 * API function to perform an operation for each qualifying message in the
348 * current room. (Returns the number of messages processed.)
350 int CtdlForEachMessage(int mode, long ref,
351 int moderation_level,
353 struct CtdlMessage *compare,
354 void (*CallBack) (long, void *),
360 struct cdbdata *cdbfr;
361 long *msglist = NULL;
363 int num_processed = 0;
365 struct SuppMsgInfo smi;
366 struct CtdlMessage *msg;
369 int printed_lastold = 0;
371 /* Learn about the user and room in question */
373 getuser(&CC->usersupp, CC->curr_user);
374 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
376 /* Load the message list */
377 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
379 msglist = mallok(cdbfr->len);
380 memcpy(msglist, cdbfr->ptr, cdbfr->len);
381 num_msgs = cdbfr->len / sizeof(long);
384 return 0; /* No messages at all? No further action. */
389 * Now begin the traversal.
391 if (num_msgs > 0) for (a = 0; a < num_msgs; ++a) {
392 GetSuppMsgInfo(&smi, msglist[a]);
394 /* Filter out messages that are moderated below the level
395 * currently being viewed at.
397 if (smi.smi_mod < moderation_level) {
401 /* If the caller is looking for a specific MIME type, filter
402 * out all messages which are not of the type requested.
404 if (content_type != NULL) if (strlen(content_type) > 0) {
405 if (strcasecmp(smi.smi_content_type, content_type)) {
411 num_msgs = sort_msglist(msglist, num_msgs);
413 /* If a template was supplied, filter out the messages which
414 * don't match. (This could induce some delays!)
417 if (compare != NULL) {
418 for (a = 0; a < num_msgs; ++a) {
419 msg = CtdlFetchMessage(msglist[a]);
421 if (CtdlMsgCmp(msg, compare)) {
424 CtdlFreeMessage(msg);
432 * Now iterate through the message list, according to the
433 * criteria supplied by the caller.
436 for (a = 0; a < num_msgs; ++a) {
437 thismsg = msglist[a];
438 is_seen = is_msg_in_mset(vbuf.v_seen, thismsg);
439 if (is_seen) lastold = thismsg;
444 || ((mode == MSGS_OLD) && (is_seen))
445 || ((mode == MSGS_NEW) && (!is_seen))
446 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
447 || ((mode == MSGS_FIRST) && (a < ref))
448 || ((mode == MSGS_GT) && (thismsg > ref))
449 || ((mode == MSGS_EQ) && (thismsg == ref))
452 if ((mode == MSGS_NEW) && (CC->usersupp.flags & US_LASTOLD) && (lastold > 0L) && (printed_lastold == 0) && (!is_seen)) {
454 CallBack(lastold, userdata);
458 if (CallBack) CallBack(thismsg, userdata);
462 phree(msglist); /* Clean up */
463 return num_processed;
469 * cmd_msgs() - get list of message #'s in this room
470 * implements the MSGS server command using CtdlForEachMessage()
472 void cmd_msgs(char *cmdbuf)
481 int with_template = 0;
482 struct CtdlMessage *template = NULL;
484 extract(which, cmdbuf, 0);
485 cm_ref = extract_int(cmdbuf, 1);
486 with_template = extract_int(cmdbuf, 2);
490 if (!strncasecmp(which, "OLD", 3))
492 else if (!strncasecmp(which, "NEW", 3))
494 else if (!strncasecmp(which, "FIRST", 5))
496 else if (!strncasecmp(which, "LAST", 4))
498 else if (!strncasecmp(which, "GT", 2))
501 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
502 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
507 cprintf("%d Send template then receive message list\n",
509 template = (struct CtdlMessage *)
510 mallok(sizeof(struct CtdlMessage));
511 memset(template, 0, sizeof(struct CtdlMessage));
512 while(client_gets(buf), strcmp(buf,"000")) {
513 extract(tfield, buf, 0);
514 extract(tvalue, buf, 1);
515 for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
516 if (!strcasecmp(tfield, msgkeys[i])) {
517 template->cm_fields[i] =
524 cprintf("%d Message list...\n", LISTING_FOLLOWS);
527 CtdlForEachMessage(mode, cm_ref,
528 CC->usersupp.moderation_filter,
529 NULL, template, simple_listing, NULL);
530 if (template != NULL) CtdlFreeMessage(template);
538 * help_subst() - support routine for help file viewer
540 void help_subst(char *strbuf, char *source, char *dest)
545 while (p = pattern2(strbuf, source), (p >= 0)) {
546 strcpy(workbuf, &strbuf[p + strlen(source)]);
547 strcpy(&strbuf[p], dest);
548 strcat(strbuf, workbuf);
553 void do_help_subst(char *buffer)
557 help_subst(buffer, "^nodename", config.c_nodename);
558 help_subst(buffer, "^humannode", config.c_humannode);
559 help_subst(buffer, "^fqdn", config.c_fqdn);
560 help_subst(buffer, "^username", CC->usersupp.fullname);
561 sprintf(buf2, "%ld", CC->usersupp.usernum);
562 help_subst(buffer, "^usernum", buf2);
563 help_subst(buffer, "^sysadm", config.c_sysadm);
564 help_subst(buffer, "^variantname", CITADEL);
565 sprintf(buf2, "%d", config.c_maxsessions);
566 help_subst(buffer, "^maxsessions", buf2);
572 * memfmout() - Citadel text formatter and paginator.
573 * Although the original purpose of this routine was to format
574 * text to the reader's screen width, all we're really using it
575 * for here is to format text out to 80 columns before sending it
576 * to the client. The client software may reformat it again.
579 int width, /* screen width to use */
580 char *mptr, /* where are we going to get our text from? */
581 char subst, /* nonzero if we should do substitutions */
582 char *nl) /* string to terminate lines with */
594 c = 1; /* c is the current pos */
598 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
600 buffer[strlen(buffer) + 1] = 0;
601 buffer[strlen(buffer)] = ch;
604 if (buffer[0] == '^')
605 do_help_subst(buffer);
607 buffer[strlen(buffer) + 1] = 0;
609 strcpy(buffer, &buffer[1]);
617 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
619 if (((old == 13) || (old == 10)) && (isspace(real))) {
627 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
628 cprintf("%s%s", nl, aaa);
637 if ((strlen(aaa) + c) > (width - 5)) {
646 if ((ch == 13) || (ch == 10)) {
647 cprintf("%s%s", aaa, nl);
654 cprintf("%s%s", aaa, nl);
660 * Callback function for mime parser that simply lists the part
662 void list_this_part(char *name, char *filename, char *partnum, char *disp,
663 void *content, char *cbtype, size_t length, char *encoding,
667 cprintf("part=%s|%s|%s|%s|%s|%d\n",
668 name, filename, partnum, disp, cbtype, length);
673 * Callback function for mime parser that opens a section for downloading
675 void mime_download(char *name, char *filename, char *partnum, char *disp,
676 void *content, char *cbtype, size_t length, char *encoding,
680 /* Silently go away if there's already a download open... */
681 if (CC->download_fp != NULL)
684 /* ...or if this is not the desired section */
685 if (strcasecmp(desired_section, partnum))
688 CC->download_fp = tmpfile();
689 if (CC->download_fp == NULL)
692 fwrite(content, length, 1, CC->download_fp);
693 fflush(CC->download_fp);
694 rewind(CC->download_fp);
696 OpenCmdResult(filename, cbtype);
702 * Load a message from disk into memory.
703 * This is used by CtdlOutputMsg() and other fetch functions.
705 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
706 * using the CtdlMessageFree() function.
708 struct CtdlMessage *CtdlFetchMessage(long msgnum)
710 struct cdbdata *dmsgtext;
711 struct CtdlMessage *ret = NULL;
714 CIT_UBYTE field_header;
717 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
718 if (dmsgtext == NULL) {
721 mptr = dmsgtext->ptr;
723 /* Parse the three bytes that begin EVERY message on disk.
724 * The first is always 0xFF, the on-disk magic number.
725 * The second is the anonymous/public type byte.
726 * The third is the format type byte (vari, fixed, or MIME).
730 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
734 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
735 memset(ret, 0, sizeof(struct CtdlMessage));
737 ret->cm_magic = CTDLMESSAGE_MAGIC;
738 ret->cm_anon_type = *mptr++; /* Anon type byte */
739 ret->cm_format_type = *mptr++; /* Format type byte */
742 * The rest is zero or more arbitrary fields. Load them in.
743 * We're done when we encounter either a zero-length field or
744 * have just processed the 'M' (message text) field.
747 field_length = strlen(mptr);
748 if (field_length == 0)
750 field_header = *mptr++;
751 ret->cm_fields[field_header] = mallok(field_length);
752 strcpy(ret->cm_fields[field_header], mptr);
754 while (*mptr++ != 0); /* advance to next field */
756 } while ((field_length > 0) && (field_header != 'M'));
760 /* Always make sure there's something in the msg text field */
761 if (ret->cm_fields['M'] == NULL)
762 ret->cm_fields['M'] = strdoop("<no text>\n");
764 /* Perform "before read" hooks (aborting if any return nonzero) */
765 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
766 CtdlFreeMessage(ret);
775 * Returns 1 if the supplied pointer points to a valid Citadel message.
776 * If the pointer is NULL or the magic number check fails, returns 0.
778 int is_valid_message(struct CtdlMessage *msg) {
781 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
782 lprintf(3, "is_valid_message() -- self-check failed\n");
790 * 'Destructor' for struct CtdlMessage
792 void CtdlFreeMessage(struct CtdlMessage *msg)
796 if (is_valid_message(msg) == 0) return;
798 for (i = 0; i < 256; ++i)
799 if (msg->cm_fields[i] != NULL) {
800 phree(msg->cm_fields[i]);
803 msg->cm_magic = 0; /* just in case */
809 * Callback function for mime parser that wants to display text
811 void fixed_output(char *name, char *filename, char *partnum, char *disp,
812 void *content, char *cbtype, size_t length, char *encoding,
820 if (!strcasecmp(cbtype, "multipart/alternative")) {
821 strcpy(ma->prefix, partnum);
822 strcat(ma->prefix, ".");
828 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
830 && (ma->did_print == 1) ) {
831 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
837 if ( (!strcasecmp(cbtype, "text/plain"))
838 || (strlen(cbtype)==0) ) {
843 if (ch==10) cprintf("\r\n");
844 else cprintf("%c", ch);
847 else if (!strcasecmp(cbtype, "text/html")) {
848 ptr = html_to_ascii(content, 80, 0);
853 if (ch==10) cprintf("\r\n");
854 else cprintf("%c", ch);
858 else if (strncasecmp(cbtype, "multipart/", 10)) {
859 cprintf("Part %s: %s (%s) (%d bytes)\r\n",
860 partnum, filename, cbtype, length);
866 * Get a message off disk. (returns om_* values found in msgbase.h)
869 int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */
870 int mode, /* how would you like that message? */
871 int headers_only, /* eschew the message body? */
872 int do_proto, /* do Citadel protocol responses? */
873 int crlf /* Use CRLF newlines instead of LF? */
875 struct CtdlMessage *TheMessage;
878 lprintf(7, "CtdlOutputMsg() msgnum=%ld, mode=%d\n",
883 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
884 if (do_proto) cprintf("%d Not logged in.\n",
885 ERROR + NOT_LOGGED_IN);
886 return(om_not_logged_in);
889 /* FIXME ... small security issue
890 * We need to check to make sure the requested message is actually
891 * in the current room, and set msg_ok to 1 only if it is. This
892 * functionality is currently missing because I'm in a hurry to replace
893 * broken production code with nonbroken pre-beta code. :( -- ajc
896 if (do_proto) cprintf("%d Message %ld is not in this room.\n",
898 return(om_no_such_msg);
903 * Fetch the message from disk
905 TheMessage = CtdlFetchMessage(msg_num);
906 if (TheMessage == NULL) {
907 if (do_proto) cprintf("%d Can't locate msg %ld on disk\n",
909 return(om_no_such_msg);
912 retcode = CtdlOutputPreLoadedMsg(
913 TheMessage, msg_num, mode,
914 headers_only, do_proto, crlf);
916 CtdlFreeMessage(TheMessage);
922 * Get a message off disk. (returns om_* values found in msgbase.h)
925 int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage,
927 int mode, /* how would you like that message? */
928 int headers_only, /* eschew the message body? */
929 int do_proto, /* do Citadel protocol responses? */
930 int crlf /* Use CRLF newlines instead of LF? */
936 char display_name[SIZ];
938 char *nl; /* newline string */
940 /* buffers needed for RFC822 translation */
950 sprintf(mid, "%ld", msg_num);
951 nl = (crlf ? "\r\n" : "\n");
953 if (!is_valid_message(TheMessage)) {
954 lprintf(1, "ERROR: invalid preloaded message for output\n");
955 return(om_no_such_msg);
958 /* Are we downloading a MIME component? */
959 if (mode == MT_DOWNLOAD) {
960 if (TheMessage->cm_format_type != FMT_RFC822) {
962 cprintf("%d This is not a MIME message.\n",
964 } else if (CC->download_fp != NULL) {
965 if (do_proto) cprintf(
966 "%d You already have a download open.\n",
969 /* Parse the message text component */
970 mptr = TheMessage->cm_fields['M'];
971 mime_parser(mptr, NULL,
972 *mime_download, NULL, NULL,
974 /* If there's no file open by this time, the requested
975 * section wasn't found, so print an error
977 if (CC->download_fp == NULL) {
978 if (do_proto) cprintf(
979 "%d Section %s not found.\n",
980 ERROR + FILE_NOT_FOUND,
984 return((CC->download_fp != NULL) ? om_ok : om_mime_error);
987 /* now for the user-mode message reading loops */
988 if (do_proto) cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
990 /* Tell the client which format type we're using. If this is a
991 * MIME message, *lie* about it and tell the user it's fixed-format.
993 if (mode == MT_CITADEL) {
994 if (TheMessage->cm_format_type == FMT_RFC822) {
995 if (do_proto) cprintf("type=1\n");
998 if (do_proto) cprintf("type=%d\n",
999 TheMessage->cm_format_type);
1003 /* nhdr=yes means that we're only displaying headers, no body */
1004 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
1005 if (do_proto) cprintf("nhdr=yes\n");
1008 /* begin header processing loop for Citadel message format */
1010 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
1012 strcpy(display_name, "<unknown>");
1013 if (TheMessage->cm_fields['A']) {
1014 strcpy(buf, TheMessage->cm_fields['A']);
1015 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
1016 if (TheMessage->cm_anon_type == MES_ANON)
1017 strcpy(display_name, "****");
1018 else if (TheMessage->cm_anon_type == MES_AN2)
1019 strcpy(display_name, "anonymous");
1021 strcpy(display_name, buf);
1022 if ((is_room_aide())
1023 && ((TheMessage->cm_anon_type == MES_ANON)
1024 || (TheMessage->cm_anon_type == MES_AN2))) {
1025 sprintf(&display_name[strlen(display_name)],
1030 strcpy(allkeys, FORDER);
1031 for (i=0; i<strlen(allkeys); ++i) {
1032 k = (int) allkeys[i];
1034 if (TheMessage->cm_fields[k] != NULL) {
1036 if (do_proto) cprintf("%s=%s\n",
1041 if (do_proto) cprintf("%s=%s\n",
1043 TheMessage->cm_fields[k]
1052 /* begin header processing loop for RFC822 transfer format */
1057 strcpy(snode, NODENAME);
1058 strcpy(lnode, HUMANNODE);
1059 if (mode == MT_RFC822) {
1060 cprintf("X-UIDL: %ld%s", msg_num, nl);
1061 for (i = 0; i < 256; ++i) {
1062 if (TheMessage->cm_fields[i]) {
1063 mptr = TheMessage->cm_fields[i];
1066 strcpy(luser, mptr);
1067 strcpy(suser, mptr);
1070 "Path:" removed for now because it confuses brain-dead Microsoft shitware
1071 into thinking that mail messages are newsgroup messages instead. When we
1072 add NNTP support back into Citadel we'll have to add code to only output
1073 this field when appropriate.
1074 else if (i == 'P') {
1075 cprintf("Path: %s%s", mptr, nl);
1079 cprintf("Subject: %s%s", mptr, nl);
1083 strcpy(lnode, mptr);
1085 cprintf("X-Citadel-Room: %s%s",
1088 strcpy(snode, mptr);
1090 cprintf("To: %s%s", mptr, nl);
1091 else if (i == 'T') {
1092 datestring(datestamp, atol(mptr),
1093 DATESTRING_RFC822 );
1094 cprintf("Date: %s%s", datestamp, nl);
1100 for (i=0; i<strlen(suser); ++i) {
1101 suser[i] = tolower(suser[i]);
1102 if (!isalnum(suser[i])) suser[i]='_';
1105 if (mode == MT_RFC822) {
1106 if (!strcasecmp(snode, NODENAME)) {
1107 strcpy(snode, FQDN);
1110 /* Construct a fun message id */
1111 cprintf("Message-ID: <%s", mid);
1112 if (strchr(mid, '@')==NULL) {
1113 cprintf("@%s", snode);
1117 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
1119 if (strlen(fuser) > 0) {
1120 cprintf("From: %s (%s)%s", fuser, luser, nl);
1123 cprintf("From: %s@%s (%s)%s", suser, snode, luser, nl);
1126 cprintf("Organization: %s%s", lnode, nl);
1129 /* end header processing loop ... at this point, we're in the text */
1131 mptr = TheMessage->cm_fields['M'];
1133 /* Tell the client about the MIME parts in this message */
1134 if (TheMessage->cm_format_type == FMT_RFC822) {
1135 if (mode == MT_CITADEL) {
1136 mime_parser(mptr, NULL,
1137 *list_this_part, NULL, NULL,
1140 else if (mode == MT_MIME) { /* list parts only */
1141 mime_parser(mptr, NULL,
1142 *list_this_part, NULL, NULL,
1144 if (do_proto) cprintf("000\n");
1147 else if (mode == MT_RFC822) { /* unparsed RFC822 dump */
1148 /* FIXME ... we have to put some code in here to avoid
1149 * printing duplicate header information when both
1150 * Citadel and RFC822 headers exist. Preference should
1151 * probably be given to the RFC822 headers.
1153 while (ch=*(mptr++), ch!=0) {
1155 else if (ch==10) cprintf("%s", nl);
1156 else cprintf("%c", ch);
1158 if (do_proto) cprintf("000\n");
1164 if (do_proto) cprintf("000\n");
1168 /* signify start of msg text */
1169 if (mode == MT_CITADEL)
1170 if (do_proto) cprintf("text\n");
1171 if (mode == MT_RFC822) {
1172 if (TheMessage->cm_fields['U'] == NULL) {
1173 cprintf("Subject: (no subject)%s", nl);
1178 /* If the format type on disk is 1 (fixed-format), then we want
1179 * everything to be output completely literally ... regardless of
1180 * what message transfer format is in use.
1182 if (TheMessage->cm_format_type == FMT_FIXED) {
1184 while (ch = *mptr++, ch > 0) {
1187 if ((ch == 10) || (strlen(buf) > 250)) {
1188 cprintf("%s%s", buf, nl);
1191 buf[strlen(buf) + 1] = 0;
1192 buf[strlen(buf)] = ch;
1195 if (strlen(buf) > 0)
1196 cprintf("%s%s", buf, nl);
1199 /* If the message on disk is format 0 (Citadel vari-format), we
1200 * output using the formatter at 80 columns. This is the final output
1201 * form if the transfer format is RFC822, but if the transfer format
1202 * is Citadel proprietary, it'll still work, because the indentation
1203 * for new paragraphs is correct and the client will reformat the
1204 * message to the reader's screen width.
1206 if (TheMessage->cm_format_type == FMT_CITADEL) {
1207 memfmout(80, mptr, 0, nl);
1210 /* If the message on disk is format 4 (MIME), we've gotta hand it
1211 * off to the MIME parser. The client has already been told that
1212 * this message is format 1 (fixed format), so the callback function
1213 * we use will display those parts as-is.
1215 if (TheMessage->cm_format_type == FMT_RFC822) {
1216 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
1217 memset(ma, 0, sizeof(struct ma_info));
1218 mime_parser(mptr, NULL,
1219 *fixed_output, NULL, NULL,
1223 /* now we're done */
1224 if (do_proto) cprintf("000\n");
1231 * display a message (mode 0 - Citadel proprietary)
1233 void cmd_msg0(char *cmdbuf)
1236 int headers_only = 0;
1238 msgid = extract_long(cmdbuf, 0);
1239 headers_only = extract_int(cmdbuf, 1);
1241 CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0);
1247 * display a message (mode 2 - RFC822)
1249 void cmd_msg2(char *cmdbuf)
1252 int headers_only = 0;
1254 msgid = extract_long(cmdbuf, 0);
1255 headers_only = extract_int(cmdbuf, 1);
1257 CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1);
1263 * display a message (mode 3 - IGnet raw format - internal programs only)
1265 void cmd_msg3(char *cmdbuf)
1268 struct CtdlMessage *msg;
1271 if (CC->internal_pgm == 0) {
1272 cprintf("%d This command is for internal programs only.\n",
1277 msgnum = extract_long(cmdbuf, 0);
1278 msg = CtdlFetchMessage(msgnum);
1280 cprintf("%d Message %ld not found.\n",
1285 serialize_message(&smr, msg);
1286 CtdlFreeMessage(msg);
1289 cprintf("%d Unable to serialize message\n",
1290 ERROR+INTERNAL_ERROR);
1294 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1295 client_write(smr.ser, smr.len);
1302 * display a message (mode 4 - MIME) (FIXME ... still evolving, not complete)
1304 void cmd_msg4(char *cmdbuf)
1308 msgid = extract_long(cmdbuf, 0);
1309 CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0);
1313 * Open a component of a MIME message as a download file
1315 void cmd_opna(char *cmdbuf)
1319 CtdlAllocUserData(SYM_DESIRED_SECTION, SIZ);
1321 msgid = extract_long(cmdbuf, 0);
1322 extract(desired_section, cmdbuf, 1);
1324 CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1);
1329 * Save a message pointer into a specified room
1330 * (Returns 0 for success, nonzero for failure)
1331 * roomname may be NULL to use the current room
1333 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
1335 char hold_rm[ROOMNAMELEN];
1336 struct cdbdata *cdbfr;
1339 long highest_msg = 0L;
1340 struct CtdlMessage *msg = NULL;
1342 lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n",
1343 roomname, msgid, flags);
1345 strcpy(hold_rm, CC->quickroom.QRname);
1347 /* We may need to check to see if this message is real */
1348 if ( (flags & SM_VERIFY_GOODNESS)
1349 || (flags & SM_DO_REPL_CHECK)
1351 msg = CtdlFetchMessage(msgid);
1352 if (msg == NULL) return(ERROR + ILLEGAL_VALUE);
1355 /* Perform replication checks if necessary */
1356 if ( (flags & SM_DO_REPL_CHECK) && (msg != NULL) ) {
1358 if (getroom(&CC->quickroom,
1359 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1361 lprintf(9, "No such room <%s>\n", roomname);
1362 if (msg != NULL) CtdlFreeMessage(msg);
1363 return(ERROR + ROOM_NOT_FOUND);
1366 if (ReplicationChecks(msg) != 0) {
1367 getroom(&CC->quickroom, hold_rm);
1368 if (msg != NULL) CtdlFreeMessage(msg);
1369 lprintf(9, "Did replication, and newer exists\n");
1374 /* Now the regular stuff */
1375 if (lgetroom(&CC->quickroom,
1376 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1378 lprintf(9, "No such room <%s>\n", roomname);
1379 if (msg != NULL) CtdlFreeMessage(msg);
1380 return(ERROR + ROOM_NOT_FOUND);
1383 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
1384 if (cdbfr == NULL) {
1388 msglist = mallok(cdbfr->len);
1389 if (msglist == NULL)
1390 lprintf(3, "ERROR malloc msglist!\n");
1391 num_msgs = cdbfr->len / sizeof(long);
1392 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1397 /* Make sure the message doesn't already exist in this room. It
1398 * is absolutely taboo to have more than one reference to the same
1399 * message in a room.
1401 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1402 if (msglist[i] == msgid) {
1403 lputroom(&CC->quickroom); /* unlock the room */
1404 getroom(&CC->quickroom, hold_rm);
1405 if (msg != NULL) CtdlFreeMessage(msg);
1406 return(ERROR + ALREADY_EXISTS);
1410 /* Now add the new message */
1412 msglist = reallok(msglist,
1413 (num_msgs * sizeof(long)));
1415 if (msglist == NULL) {
1416 lprintf(3, "ERROR: can't realloc message list!\n");
1418 msglist[num_msgs - 1] = msgid;
1420 /* Sort the message list, so all the msgid's are in order */
1421 num_msgs = sort_msglist(msglist, num_msgs);
1423 /* Determine the highest message number */
1424 highest_msg = msglist[num_msgs - 1];
1426 /* Write it back to disk. */
1427 cdb_store(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long),
1428 msglist, num_msgs * sizeof(long));
1430 /* Free up the memory we used. */
1433 /* Update the highest-message pointer and unlock the room. */
1434 CC->quickroom.QRhighest = highest_msg;
1435 lputroom(&CC->quickroom);
1436 getroom(&CC->quickroom, hold_rm);
1438 /* Bump the reference count for this message. */
1439 if ((flags & SM_DONT_BUMP_REF)==0) {
1440 AdjRefCount(msgid, +1);
1443 /* Return success. */
1444 if (msg != NULL) CtdlFreeMessage(msg);
1451 * Message base operation to send a message to the master file
1452 * (returns new message number)
1454 * This is the back end for CtdlSaveMsg() and should not be directly
1455 * called by server-side modules.
1458 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1459 FILE *save_a_copy) /* save a copy to disk? */
1466 /* Get a new message number */
1467 newmsgid = get_new_message_number();
1468 sprintf(msgidbuf, "%ld@%s", newmsgid, config.c_fqdn);
1470 /* Generate an ID if we don't have one already */
1471 if (msg->cm_fields['I']==NULL) {
1472 msg->cm_fields['I'] = strdoop(msgidbuf);
1475 serialize_message(&smr, msg);
1478 cprintf("%d Unable to serialize message\n",
1479 ERROR+INTERNAL_ERROR);
1483 /* Write our little bundle of joy into the message base */
1484 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1485 smr.ser, smr.len) < 0) {
1486 lprintf(2, "Can't store message\n");
1492 /* If the caller specified that a copy should be saved to a particular
1493 * file handle, do that now too.
1495 if (save_a_copy != NULL) {
1496 fwrite(smr.ser, smr.len, 1, save_a_copy);
1499 /* Free the memory we used for the serialized message */
1502 /* Return the *local* message ID to the caller
1503 * (even if we're storing an incoming network message)
1511 * Serialize a struct CtdlMessage into the format used on disk and network.
1513 * This function loads up a "struct ser_ret" (defined in server.h) which
1514 * contains the length of the serialized message and a pointer to the
1515 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1517 void serialize_message(struct ser_ret *ret, /* return values */
1518 struct CtdlMessage *msg) /* unserialized msg */
1522 static char *forder = FORDER;
1524 if (is_valid_message(msg) == 0) return; /* self check */
1527 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1528 ret->len = ret->len +
1529 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1531 lprintf(9, "calling malloc(%d)\n", ret->len);
1532 ret->ser = mallok(ret->len);
1533 if (ret->ser == NULL) {
1539 ret->ser[1] = msg->cm_anon_type;
1540 ret->ser[2] = msg->cm_format_type;
1543 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1544 ret->ser[wlen++] = (char)forder[i];
1545 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1546 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1548 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1557 * Back end for the ReplicationChecks() function
1559 void check_repl(long msgnum, void *userdata) {
1560 struct CtdlMessage *msg;
1561 time_t timestamp = (-1L);
1563 lprintf(9, "check_repl() found message %ld\n", msgnum);
1564 msg = CtdlFetchMessage(msgnum);
1565 if (msg == NULL) return;
1566 if (msg->cm_fields['T'] != NULL) {
1567 timestamp = atol(msg->cm_fields['T']);
1569 CtdlFreeMessage(msg);
1571 if (timestamp > msg_repl->highest) {
1572 msg_repl->highest = timestamp; /* newer! */
1573 lprintf(9, "newer!\n");
1576 lprintf(9, "older!\n");
1578 /* Existing isn't newer? Then delete the old one(s). */
1579 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, "");
1584 * Check to see if any messages already exist which carry the same Extended ID
1588 * -> With older timestamps: delete them and return 0. Message will be saved.
1589 * -> With newer timestamps: return 1. Message save will be aborted.
1591 int ReplicationChecks(struct CtdlMessage *msg) {
1592 struct CtdlMessage *template;
1595 lprintf(9, "ReplicationChecks() started\n");
1596 /* No extended id? Don't do anything. */
1597 if (msg->cm_fields['E'] == NULL) return 0;
1598 if (strlen(msg->cm_fields['E']) == 0) return 0;
1599 lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1601 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1602 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1603 msg_repl->highest = atol(msg->cm_fields['T']);
1605 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1606 memset(template, 0, sizeof(struct CtdlMessage));
1607 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1609 CtdlForEachMessage(MSGS_ALL, 0L, (-127), NULL, template,
1612 /* If a newer message exists with the same Extended ID, abort
1615 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1619 CtdlFreeMessage(template);
1620 lprintf(9, "ReplicationChecks() returning %d\n", abort_this);
1628 * Save a message to disk
1630 long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1631 char *rec, /* Recipient (mail) */
1632 char *force, /* force a particular room? */
1633 int supplied_mailtype) /* local or remote type */
1636 char hold_rm[ROOMNAMELEN];
1637 char actual_rm[ROOMNAMELEN];
1638 char force_room[ROOMNAMELEN];
1639 char content_type[SIZ]; /* We have to learn this */
1640 char recipient[SIZ];
1643 struct usersupp userbuf;
1645 struct SuppMsgInfo smi;
1646 FILE *network_fp = NULL;
1647 static int seqnum = 1;
1648 struct CtdlMessage *imsg;
1652 lprintf(9, "CtdlSaveMsg() called\n");
1653 if (is_valid_message(msg) == 0) return(-1); /* self check */
1654 mailtype = supplied_mailtype;
1656 /* If this message has no timestamp, we take the liberty of
1657 * giving it one, right now.
1659 if (msg->cm_fields['T'] == NULL) {
1660 lprintf(9, "Generating timestamp\n");
1661 sprintf(aaa, "%ld", time(NULL));
1662 msg->cm_fields['T'] = strdoop(aaa);
1665 /* If this message has no path, we generate one.
1667 if (msg->cm_fields['P'] == NULL) {
1668 lprintf(9, "Generating path\n");
1669 if (msg->cm_fields['A'] != NULL) {
1670 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1671 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1672 if (isspace(msg->cm_fields['P'][a])) {
1673 msg->cm_fields['P'][a] = ' ';
1678 msg->cm_fields['P'] = strdoop("unknown");
1682 strcpy(force_room, force);
1684 /* Strip non-printable characters out of the recipient name */
1685 lprintf(9, "Checking recipient (if present)\n");
1686 strcpy(recipient, rec);
1687 for (a = 0; a < strlen(recipient); ++a)
1688 if (!isprint(recipient[a]))
1689 strcpy(&recipient[a], &recipient[a + 1]);
1691 /* Change "user @ xxx" to "user" if xxx is an alias for this host */
1692 for (a=0; a<strlen(recipient); ++a) {
1693 if (recipient[a] == '@') {
1694 if (CtdlHostAlias(&recipient[a+1])
1695 == hostalias_localhost) {
1697 lprintf(7, "Changed to <%s>\n", recipient);
1698 mailtype = MES_LOCAL;
1703 lprintf(9, "Recipient is <%s>\n", recipient);
1705 /* Learn about what's inside, because it's what's inside that counts */
1706 lprintf(9, "Learning what's inside\n");
1707 if (msg->cm_fields['M'] == NULL) {
1708 lprintf(1, "ERROR: attempt to save message with NULL body\n");
1711 switch (msg->cm_format_type) {
1713 strcpy(content_type, "text/x-citadel-variformat");
1716 strcpy(content_type, "text/plain");
1719 strcpy(content_type, "text/plain");
1720 /* advance past header fields */
1721 mptr = msg->cm_fields['M'];
1724 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1725 safestrncpy(content_type, mptr,
1726 sizeof(content_type));
1727 strcpy(content_type, &content_type[14]);
1728 for (a = 0; a < strlen(content_type); ++a)
1729 if ((content_type[a] == ';')
1730 || (content_type[a] == ' ')
1731 || (content_type[a] == 13)
1732 || (content_type[a] == 10))
1733 content_type[a] = 0;
1740 /* Goto the correct room */
1741 lprintf(9, "Switching rooms\n");
1742 strcpy(hold_rm, CC->quickroom.QRname);
1743 strcpy(actual_rm, CC->quickroom.QRname);
1745 /* If the user is a twit, move to the twit room for posting */
1746 lprintf(9, "Handling twit stuff\n");
1748 if (CC->usersupp.axlevel == 2) {
1749 strcpy(hold_rm, actual_rm);
1750 strcpy(actual_rm, config.c_twitroom);
1754 /* ...or if this message is destined for Aide> then go there. */
1755 if (strlen(force_room) > 0) {
1756 strcpy(actual_rm, force_room);
1759 lprintf(9, "Possibly relocating\n");
1760 if (strcasecmp(actual_rm, CC->quickroom.QRname)) {
1761 getroom(&CC->quickroom, actual_rm);
1765 * If this message has no O (room) field, generate one.
1767 if (msg->cm_fields['O'] == NULL) {
1768 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1771 /* Perform "before save" hooks (aborting if any return nonzero) */
1772 lprintf(9, "Performing before-save hooks\n");
1773 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return(-1);
1775 /* If this message has an Extended ID, perform replication checks */
1776 lprintf(9, "Performing replication checks\n");
1777 if (ReplicationChecks(msg) > 0) return(-1);
1779 /* Network mail - send a copy to the network program. */
1780 if ((strlen(recipient) > 0) && (mailtype == MES_BINARY)) {
1781 lprintf(9, "Sending network spool\n");
1782 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1783 (long) getpid(), CC->cs_pid, ++seqnum);
1784 lprintf(9, "Saving a copy to %s\n", aaa);
1785 network_fp = fopen(aaa, "ab+");
1786 if (network_fp == NULL)
1787 lprintf(2, "ERROR: %s\n", strerror(errno));
1790 /* Save it to disk */
1791 lprintf(9, "Saving to disk\n");
1792 newmsgid = send_message(msg, network_fp);
1793 if (network_fp != NULL) {
1795 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1798 if (newmsgid <= 0L) return(-1);
1800 /* Write a supplemental message info record. This doesn't have to
1801 * be a critical section because nobody else knows about this message
1804 lprintf(9, "Creating SuppMsgInfo record\n");
1805 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1806 smi.smi_msgnum = newmsgid;
1807 smi.smi_refcount = 0;
1808 safestrncpy(smi.smi_content_type, content_type, 64);
1809 PutSuppMsgInfo(&smi);
1811 /* Now figure out where to store the pointers */
1812 lprintf(9, "Storing pointers\n");
1814 /* If this is being done by the networker delivering a private
1815 * message, we want to BYPASS saving the sender's copy (because there
1816 * is no local sender; it would otherwise go to the Trashcan).
1818 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1819 if (CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0) != 0) {
1820 lprintf(3, "ERROR saving message pointer!\n");
1821 CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
1825 /* For internet mail, drop a copy in the outbound queue room */
1826 if (mailtype == MES_INTERNET) {
1827 CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0);
1830 /* Bump this user's messages posted counter. */
1831 lprintf(9, "Updating user\n");
1832 lgetuser(&CC->usersupp, CC->curr_user);
1833 CC->usersupp.posted = CC->usersupp.posted + 1;
1834 lputuser(&CC->usersupp);
1836 /* If this is private, local mail, make a copy in the
1837 * recipient's mailbox and bump the reference count.
1839 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1840 if (getuser(&userbuf, recipient) == 0) {
1841 lprintf(9, "Delivering private mail\n");
1842 MailboxName(actual_rm, &userbuf, MAILROOM);
1843 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1846 lprintf(9, "No user <%s>, saving in %s> instead\n",
1847 recipient, AIDEROOM);
1848 CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
1852 /* Perform "after save" hooks */
1853 lprintf(9, "Performing after-save hooks\n");
1854 PerformMessageHooks(msg, EVT_AFTERSAVE);
1857 lprintf(9, "Returning to original room\n");
1858 if (strcasecmp(hold_rm, CC->quickroom.QRname))
1859 getroom(&CC->quickroom, hold_rm);
1861 /* For internet mail, generate delivery instructions
1862 * (Yes, this is recursive! Deal with it!)
1864 if (mailtype == MES_INTERNET) {
1865 lprintf(9, "Generating delivery instructions\n");
1866 instr = mallok(2048);
1868 "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
1871 SPOOLMIME, newmsgid, time(NULL),
1872 msg->cm_fields['A'], msg->cm_fields['N'],
1875 imsg = mallok(sizeof(struct CtdlMessage));
1876 memset(imsg, 0, sizeof(struct CtdlMessage));
1877 imsg->cm_magic = CTDLMESSAGE_MAGIC;
1878 imsg->cm_anon_type = MES_NORMAL;
1879 imsg->cm_format_type = FMT_RFC822;
1880 imsg->cm_fields['A'] = strdoop("Citadel");
1881 imsg->cm_fields['M'] = instr;
1882 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
1883 CtdlFreeMessage(imsg);
1892 * Convenience function for generating small administrative messages.
1894 void quickie_message(char *from, char *to, char *room, char *text)
1896 struct CtdlMessage *msg;
1898 msg = mallok(sizeof(struct CtdlMessage));
1899 memset(msg, 0, sizeof(struct CtdlMessage));
1900 msg->cm_magic = CTDLMESSAGE_MAGIC;
1901 msg->cm_anon_type = MES_NORMAL;
1902 msg->cm_format_type = 0;
1903 msg->cm_fields['A'] = strdoop(from);
1904 msg->cm_fields['O'] = strdoop(room);
1905 msg->cm_fields['N'] = strdoop(NODENAME);
1907 msg->cm_fields['R'] = strdoop(to);
1908 msg->cm_fields['M'] = strdoop(text);
1910 CtdlSaveMsg(msg, "", room, MES_LOCAL);
1911 CtdlFreeMessage(msg);
1912 syslog(LOG_NOTICE, text);
1918 * Back end function used by make_message() and similar functions
1920 char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */
1921 size_t maxlen, /* maximum message length */
1922 char *exist /* if non-null, append to it;
1923 exist is ALWAYS freed */
1927 size_t message_len = 0;
1928 size_t buffer_len = 0;
1932 if (exist == NULL) {
1936 m = reallok(exist, strlen(exist) + 4096);
1937 if (m == NULL) phree(exist);
1940 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) ;;
1947 /* read in the lines of message text one by one */
1948 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) {
1950 /* strip trailing newline type stuff */
1951 if (buf[strlen(buf)-1]==10) buf[strlen(buf)-1]=0;
1952 if (buf[strlen(buf)-1]==13) buf[strlen(buf)-1]=0;
1954 linelen = strlen(buf);
1956 /* augment the buffer if we have to */
1957 if ((message_len + linelen + 2) > buffer_len) {
1958 lprintf(9, "realloking\n");
1959 ptr = reallok(m, (buffer_len * 2) );
1960 if (ptr == NULL) { /* flush if can't allocate */
1961 while ( (client_gets(buf)>0) &&
1962 strcmp(buf, terminator)) ;;
1965 buffer_len = (buffer_len * 2);
1967 lprintf(9, "buffer_len is %d\n", buffer_len);
1971 /* Add the new line to the buffer. We avoid using strcat()
1972 * because that would involve traversing the entire message
1973 * after each line, and this function needs to run fast.
1975 strcpy(&m[message_len], buf);
1976 m[message_len + linelen] = '\n';
1977 m[message_len + linelen + 1] = 0;
1978 message_len = message_len + linelen + 1;
1980 /* if we've hit the max msg length, flush the rest */
1981 if (message_len >= maxlen) {
1982 while ( (client_gets(buf)>0)
1983 && strcmp(buf, terminator)) ;;
1994 * Build a binary message to be saved on disk.
1997 struct CtdlMessage *make_message(
1998 struct usersupp *author, /* author's usersupp structure */
1999 char *recipient, /* NULL if it's not mail */
2000 char *room, /* room where it's going */
2001 int type, /* see MES_ types in header file */
2002 int net_type, /* see MES_ types in header file */
2003 int format_type, /* local or remote (see citadel.h) */
2004 char *fake_name) /* who we're masquerading as */
2010 struct CtdlMessage *msg;
2012 msg = mallok(sizeof(struct CtdlMessage));
2013 memset(msg, 0, sizeof(struct CtdlMessage));
2014 msg->cm_magic = CTDLMESSAGE_MAGIC;
2015 msg->cm_anon_type = type;
2016 msg->cm_format_type = format_type;
2018 /* Don't confuse the poor folks if it's not routed mail. */
2019 strcpy(dest_node, "");
2021 /* If net_type is MES_BINARY, split out the destination node. */
2022 if (net_type == MES_BINARY) {
2023 strcpy(dest_node, NODENAME);
2024 for (a = 0; a < strlen(recipient); ++a) {
2025 if (recipient[a] == '@') {
2027 strcpy(dest_node, &recipient[a + 1]);
2032 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
2033 if (net_type == MES_INTERNET) {
2034 strcpy(dest_node, "internet");
2037 while (isspace(recipient[strlen(recipient) - 1]))
2038 recipient[strlen(recipient) - 1] = 0;
2040 sprintf(buf, "cit%ld", author->usernum); /* Path */
2041 msg->cm_fields['P'] = strdoop(buf);
2043 sprintf(buf, "%ld", time(NULL)); /* timestamp */
2044 msg->cm_fields['T'] = strdoop(buf);
2046 if (fake_name[0]) /* author */
2047 msg->cm_fields['A'] = strdoop(fake_name);
2049 msg->cm_fields['A'] = strdoop(author->fullname);
2051 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
2052 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
2054 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
2056 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
2057 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
2059 if (recipient[0] != 0)
2060 msg->cm_fields['R'] = strdoop(recipient);
2061 if (dest_node[0] != 0)
2062 msg->cm_fields['D'] = strdoop(dest_node);
2065 msg->cm_fields['M'] = CtdlReadMessageBody("000",
2066 config.c_maxmsglen, NULL);
2074 * Check to see whether we have permission to post a message in the current
2075 * room. Returns a *CITADEL ERROR CODE* and puts a message in errmsgbuf, or
2076 * returns 0 on success.
2078 int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf) {
2080 if (!(CC->logged_in)) {
2081 sprintf(errmsgbuf, "Not logged in.");
2082 return (ERROR + NOT_LOGGED_IN);
2085 if ((CC->usersupp.axlevel < 2)
2086 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
2087 sprintf(errmsgbuf, "Need to be validated to enter "
2088 "(except in %s> to sysop)", MAILROOM);
2089 return (ERROR + HIGHER_ACCESS_REQUIRED);
2092 if ((CC->usersupp.axlevel < 4)
2093 && (CC->quickroom.QRflags & QR_NETWORK)) {
2094 sprintf(errmsgbuf, "Need net privileges to enter here.");
2095 return (ERROR + HIGHER_ACCESS_REQUIRED);
2098 if ((CC->usersupp.axlevel < 6)
2099 && (CC->quickroom.QRflags & QR_READONLY)) {
2100 sprintf(errmsgbuf, "Sorry, this is a read-only room.");
2101 return (ERROR + HIGHER_ACCESS_REQUIRED);
2104 strcpy(errmsgbuf, "Ok");
2112 * message entry - mode 0 (normal)
2114 void cmd_ent0(char *entargs)
2117 char recipient[SIZ];
2119 int format_type = 0;
2120 char newusername[SIZ];
2121 struct CtdlMessage *msg;
2125 struct usersupp tempUS;
2129 post = extract_int(entargs, 0);
2130 extract(recipient, entargs, 1);
2131 anon_flag = extract_int(entargs, 2);
2132 format_type = extract_int(entargs, 3);
2134 /* first check to make sure the request is valid. */
2136 err = CtdlDoIHavePermissionToPostInThisRoom(buf);
2138 cprintf("%d %s\n", err, buf);
2142 /* Check some other permission type things. */
2145 if (CC->usersupp.axlevel < 6) {
2146 cprintf("%d You don't have permission to masquerade.\n",
2147 ERROR + HIGHER_ACCESS_REQUIRED);
2150 extract(newusername, entargs, 4);
2151 memset(CC->fake_postname, 0, 32);
2152 strcpy(CC->fake_postname, newusername);
2153 cprintf("%d Ok\n", OK);
2156 CC->cs_flags |= CS_POSTING;
2159 if (CC->quickroom.QRflags & QR_MAILBOX) {
2160 if (CC->usersupp.axlevel >= 2) {
2161 strcpy(buf, recipient);
2163 strcpy(buf, "sysop");
2164 e = alias(buf); /* alias and mail type */
2165 if ((buf[0] == 0) || (e == MES_ERROR)) {
2166 cprintf("%d Unknown address - cannot send message.\n",
2167 ERROR + NO_SUCH_USER);
2170 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
2171 cprintf("%d Net privileges required for network mail.\n",
2172 ERROR + HIGHER_ACCESS_REQUIRED);
2175 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
2176 && ((CC->usersupp.flags & US_INTERNET) == 0)
2177 && (!CC->internal_pgm)) {
2178 cprintf("%d You don't have access to Internet mail.\n",
2179 ERROR + HIGHER_ACCESS_REQUIRED);
2182 if (!strcasecmp(buf, "sysop")) {
2185 else if (e == MES_LOCAL) { /* don't search local file */
2186 if (!strcasecmp(buf, CC->usersupp.fullname)) {
2187 cprintf("%d Can't send mail to yourself!\n",
2188 ERROR + NO_SUCH_USER);
2191 /* Check to make sure the user exists; also get the correct
2192 * upper/lower casing of the name.
2194 a = getuser(&tempUS, buf);
2196 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
2199 strcpy(buf, tempUS.fullname);
2204 if (CC->quickroom.QRflags & QR_ANONONLY)
2206 if (CC->quickroom.QRflags & QR_ANONOPT) {
2210 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2213 /* If we're only checking the validity of the request, return
2214 * success without creating the message.
2217 cprintf("%d %s\n", OK, buf);
2221 cprintf("%d send message\n", SEND_LISTING);
2223 /* Read in the message from the client. */
2224 if (CC->fake_postname[0])
2225 msg = make_message(&CC->usersupp, buf,
2226 CC->quickroom.QRname, b, e, format_type,
2228 else if (CC->fake_username[0])
2229 msg = make_message(&CC->usersupp, buf,
2230 CC->quickroom.QRname, b, e, format_type,
2233 msg = make_message(&CC->usersupp, buf,
2234 CC->quickroom.QRname, b, e, format_type, "");
2237 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e);
2238 CtdlFreeMessage(msg);
2239 CC->fake_postname[0] = '\0';
2246 * message entry - mode 3 (raw)
2248 void cmd_ent3(char *entargs)
2254 unsigned char ch, which_field;
2255 struct usersupp tempUS;
2257 struct CtdlMessage *msg;
2260 if (CC->internal_pgm == 0) {
2261 cprintf("%d This command is for internal programs only.\n",
2266 /* See if there's a recipient, but make sure it's a real one */
2267 extract(recp, entargs, 1);
2268 for (a = 0; a < strlen(recp); ++a)
2269 if (!isprint(recp[a]))
2270 strcpy(&recp[a], &recp[a + 1]);
2271 while (isspace(recp[0]))
2272 strcpy(recp, &recp[1]);
2273 while (isspace(recp[strlen(recp) - 1]))
2274 recp[strlen(recp) - 1] = 0;
2276 /* If we're in Mail, check the recipient */
2277 if (strlen(recp) > 0) {
2278 e = alias(recp); /* alias and mail type */
2279 if ((recp[0] == 0) || (e == MES_ERROR)) {
2280 cprintf("%d Unknown address - cannot send message.\n",
2281 ERROR + NO_SUCH_USER);
2284 if (e == MES_LOCAL) {
2285 a = getuser(&tempUS, recp);
2287 cprintf("%d No such user.\n",
2288 ERROR + NO_SUCH_USER);
2294 /* At this point, message has been approved. */
2295 if (extract_int(entargs, 0) == 0) {
2296 cprintf("%d OK to send\n", OK);
2300 msglen = extract_long(entargs, 2);
2301 msg = mallok(sizeof(struct CtdlMessage));
2303 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
2307 memset(msg, 0, sizeof(struct CtdlMessage));
2308 tempbuf = mallok(msglen);
2309 if (tempbuf == NULL) {
2310 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
2315 cprintf("%d %ld\n", SEND_BINARY, msglen);
2317 client_read(&ch, 1); /* 0xFF magic number */
2318 msg->cm_magic = CTDLMESSAGE_MAGIC;
2319 client_read(&ch, 1); /* anon type */
2320 msg->cm_anon_type = ch;
2321 client_read(&ch, 1); /* format type */
2322 msg->cm_format_type = ch;
2323 msglen = msglen - 3;
2325 while (msglen > 0) {
2326 client_read(&which_field, 1);
2327 if (!isalpha(which_field)) valid_msg = 0;
2331 client_read(&ch, 1);
2333 a = strlen(tempbuf);
2336 } while ( (ch != 0) && (msglen > 0) );
2338 msg->cm_fields[which_field] = strdoop(tempbuf);
2341 msg->cm_flags = CM_SKIP_HOOKS;
2342 if (valid_msg) CtdlSaveMsg(msg, recp, "", e);
2343 CtdlFreeMessage(msg);
2349 * API function to delete messages which match a set of criteria
2350 * (returns the actual number of messages deleted)
2352 int CtdlDeleteMessages(char *room_name, /* which room */
2353 long dmsgnum, /* or "0" for any */
2354 char *content_type /* or "" for any */
2358 struct quickroom qrbuf;
2359 struct cdbdata *cdbfr;
2360 long *msglist = NULL;
2363 int num_deleted = 0;
2365 struct SuppMsgInfo smi;
2367 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
2368 room_name, dmsgnum, content_type);
2370 /* get room record, obtaining a lock... */
2371 if (lgetroom(&qrbuf, room_name) != 0) {
2372 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
2374 return (0); /* room not found */
2376 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
2378 if (cdbfr != NULL) {
2379 msglist = mallok(cdbfr->len);
2380 memcpy(msglist, cdbfr->ptr, cdbfr->len);
2381 num_msgs = cdbfr->len / sizeof(long);
2385 for (i = 0; i < num_msgs; ++i) {
2388 /* Set/clear a bit for each criterion */
2390 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
2391 delete_this |= 0x01;
2393 if (strlen(content_type) == 0) {
2394 delete_this |= 0x02;
2396 GetSuppMsgInfo(&smi, msglist[i]);
2397 if (!strcasecmp(smi.smi_content_type,
2399 delete_this |= 0x02;
2403 /* Delete message only if all bits are set */
2404 if (delete_this == 0x03) {
2405 AdjRefCount(msglist[i], -1);
2411 num_msgs = sort_msglist(msglist, num_msgs);
2412 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
2413 msglist, (num_msgs * sizeof(long)));
2415 qrbuf.QRhighest = msglist[num_msgs - 1];
2419 lprintf(9, "%d message(s) deleted.\n", num_deleted);
2420 return (num_deleted);
2426 * Check whether the current user has permission to delete messages from
2427 * the current room (returns 1 for yes, 0 for no)
2429 int CtdlDoIHavePermissionToDeleteMessagesFromThisRoom(void) {
2430 getuser(&CC->usersupp, CC->curr_user);
2431 if ((CC->usersupp.axlevel < 6)
2432 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2433 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2434 && (!(CC->internal_pgm))) {
2443 * Delete message from current room
2445 void cmd_dele(char *delstr)
2450 if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom() == 0) {
2451 cprintf("%d Higher access required.\n",
2452 ERROR + HIGHER_ACCESS_REQUIRED);
2455 delnum = extract_long(delstr, 0);
2457 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, "");
2460 cprintf("%d %d message%s deleted.\n", OK,
2461 num_deleted, ((num_deleted != 1) ? "s" : ""));
2463 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2469 * Back end API function for moves and deletes
2471 int CtdlCopyMsgToRoom(long msgnum, char *dest) {
2474 err = CtdlSaveMsgPointerInRoom(dest, msgnum,
2475 (SM_VERIFY_GOODNESS | SM_DO_REPL_CHECK) );
2476 if (err != 0) return(err);
2484 * move or copy a message to another room
2486 void cmd_move(char *args)
2490 struct quickroom qtemp;
2494 num = extract_long(args, 0);
2495 extract(targ, args, 1);
2496 targ[ROOMNAMELEN - 1] = 0;
2497 is_copy = extract_int(args, 2);
2499 getuser(&CC->usersupp, CC->curr_user);
2500 if ((CC->usersupp.axlevel < 6)
2501 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2502 cprintf("%d Higher access required.\n",
2503 ERROR + HIGHER_ACCESS_REQUIRED);
2507 if (getroom(&qtemp, targ) != 0) {
2508 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2512 err = CtdlCopyMsgToRoom(num, targ);
2514 cprintf("%d Cannot store message in %s: error %d\n",
2519 /* Now delete the message from the source room,
2520 * if this is a 'move' rather than a 'copy' operation.
2522 if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, "");
2524 cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") );
2530 * GetSuppMsgInfo() - Get the supplementary record for a message
2532 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2535 struct cdbdata *cdbsmi;
2538 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2539 smibuf->smi_msgnum = msgnum;
2540 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2542 /* Use the negative of the message number for its supp record index */
2543 TheIndex = (0L - msgnum);
2545 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2546 if (cdbsmi == NULL) {
2547 return; /* record not found; go with defaults */
2549 memcpy(smibuf, cdbsmi->ptr,
2550 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2551 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2558 * PutSuppMsgInfo() - (re)write supplementary record for a message
2560 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2564 /* Use the negative of the message number for its supp record index */
2565 TheIndex = (0L - smibuf->smi_msgnum);
2567 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2568 smibuf->smi_msgnum, smibuf->smi_refcount);
2570 cdb_store(CDB_MSGMAIN,
2571 &TheIndex, sizeof(long),
2572 smibuf, sizeof(struct SuppMsgInfo));
2577 * AdjRefCount - change the reference count for a message;
2578 * delete the message if it reaches zero
2580 void AdjRefCount(long msgnum, int incr)
2583 struct SuppMsgInfo smi;
2586 /* This is a *tight* critical section; please keep it that way, as
2587 * it may get called while nested in other critical sections.
2588 * Complicating this any further will surely cause deadlock!
2590 begin_critical_section(S_SUPPMSGMAIN);
2591 GetSuppMsgInfo(&smi, msgnum);
2592 lprintf(9, "Ref count for message <%ld> before write is <%d>\n",
2593 msgnum, smi.smi_refcount);
2594 smi.smi_refcount += incr;
2595 PutSuppMsgInfo(&smi);
2596 end_critical_section(S_SUPPMSGMAIN);
2597 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2598 msgnum, smi.smi_refcount);
2600 /* If the reference count is now zero, delete the message
2601 * (and its supplementary record as well).
2603 if (smi.smi_refcount == 0) {
2604 lprintf(9, "Deleting message <%ld>\n", msgnum);
2606 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2607 delnum = (0L - msgnum);
2608 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2613 * Write a generic object to this room
2615 * Note: this could be much more efficient. Right now we use two temporary
2616 * files, and still pull the message into memory as with all others.
2618 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2619 char *content_type, /* MIME type of this object */
2620 char *tempfilename, /* Where to fetch it from */
2621 struct usersupp *is_mailbox, /* Mailbox room? */
2622 int is_binary, /* Is encoding necessary? */
2623 int is_unique, /* Del others of this type? */
2624 unsigned int flags /* Internal save flags */
2629 char filename[PATH_MAX];
2632 struct quickroom qrbuf;
2633 char roomname[ROOMNAMELEN];
2634 struct CtdlMessage *msg;
2637 if (is_mailbox != NULL)
2638 MailboxName(roomname, is_mailbox, req_room);
2640 safestrncpy(roomname, req_room, sizeof(roomname));
2641 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2643 strcpy(filename, tmpnam(NULL));
2644 fp = fopen(filename, "w");
2648 tempfp = fopen(tempfilename, "r");
2649 if (tempfp == NULL) {
2655 fprintf(fp, "Content-type: %s\n", content_type);
2656 lprintf(9, "Content-type: %s\n", content_type);
2658 if (is_binary == 0) {
2659 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2660 while (ch = getc(tempfp), ch > 0)
2666 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2669 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2670 tempfilename, filename);
2674 lprintf(9, "Allocating\n");
2675 msg = mallok(sizeof(struct CtdlMessage));
2676 memset(msg, 0, sizeof(struct CtdlMessage));
2677 msg->cm_magic = CTDLMESSAGE_MAGIC;
2678 msg->cm_anon_type = MES_NORMAL;
2679 msg->cm_format_type = 4;
2680 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2681 msg->cm_fields['O'] = strdoop(req_room);
2682 msg->cm_fields['N'] = strdoop(config.c_nodename);
2683 msg->cm_fields['H'] = strdoop(config.c_humannode);
2684 msg->cm_flags = flags;
2686 lprintf(9, "Loading\n");
2687 fp = fopen(filename, "rb");
2688 fseek(fp, 0L, SEEK_END);
2691 msg->cm_fields['M'] = mallok(len);
2692 fread(msg->cm_fields['M'], len, 1, fp);
2696 /* Create the requested room if we have to. */
2697 if (getroom(&qrbuf, roomname) != 0) {
2698 create_room(roomname,
2699 ( (is_mailbox != NULL) ? 5 : 3 ),
2702 /* If the caller specified this object as unique, delete all
2703 * other objects of this type that are currently in the room.
2706 lprintf(9, "Deleted %d other msgs of this type\n",
2707 CtdlDeleteMessages(roomname, 0L, content_type));
2709 /* Now write the data */
2710 CtdlSaveMsg(msg, "", roomname, MES_LOCAL);
2711 CtdlFreeMessage(msg);
2719 void CtdlGetSysConfigBackend(long msgnum, void *userdata) {
2720 config_msgnum = msgnum;
2724 char *CtdlGetSysConfig(char *sysconfname) {
2725 char hold_rm[ROOMNAMELEN];
2728 struct CtdlMessage *msg;
2731 strcpy(hold_rm, CC->quickroom.QRname);
2732 if (getroom(&CC->quickroom, SYSCONFIGROOM) != 0) {
2733 getroom(&CC->quickroom, hold_rm);
2738 /* We want the last (and probably only) config in this room */
2739 begin_critical_section(S_CONFIG);
2740 config_msgnum = (-1L);
2741 CtdlForEachMessage(MSGS_LAST, 1, (-127), sysconfname, NULL,
2742 CtdlGetSysConfigBackend, NULL);
2743 msgnum = config_msgnum;
2744 end_critical_section(S_CONFIG);
2750 msg = CtdlFetchMessage(msgnum);
2752 conf = strdoop(msg->cm_fields['M']);
2753 CtdlFreeMessage(msg);
2760 getroom(&CC->quickroom, hold_rm);
2762 lprintf(9, "eggstracting...\n");
2763 if (conf != NULL) do {
2764 extract_token(buf, conf, 0, '\n');
2765 lprintf(9, "eggstracted <%s>\n", buf);
2766 strcpy(conf, &conf[strlen(buf)+1]);
2767 } while ( (strlen(conf)>0) && (strlen(buf)>0) );
2772 void CtdlPutSysConfig(char *sysconfname, char *sysconfdata) {
2773 char temp[PATH_MAX];
2776 strcpy(temp, tmpnam(NULL));
2778 fp = fopen(temp, "w");
2779 if (fp == NULL) return;
2780 fprintf(fp, "%s", sysconfdata);
2783 /* this handy API function does all the work for us */
2784 CtdlWriteObject(SYSCONFIGROOM, sysconfname, temp, NULL, 0, 1, 0);