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) ) {
844 if (ch==10) cprintf("\r\n");
845 else cprintf("%c", ch);
849 if (ch != '\n') cprintf("\n");
851 else if (!strcasecmp(cbtype, "text/html")) {
852 ptr = html_to_ascii(content, 80, 0);
857 if (ch==10) cprintf("\r\n");
858 else cprintf("%c", ch);
862 else if (strncasecmp(cbtype, "multipart/", 10)) {
863 cprintf("Part %s: %s (%s) (%d bytes)\r\n",
864 partnum, filename, cbtype, length);
870 * Get a message off disk. (returns om_* values found in msgbase.h)
873 int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */
874 int mode, /* how would you like that message? */
875 int headers_only, /* eschew the message body? */
876 int do_proto, /* do Citadel protocol responses? */
877 int crlf /* Use CRLF newlines instead of LF? */
879 struct CtdlMessage *TheMessage;
882 lprintf(7, "CtdlOutputMsg() msgnum=%ld, mode=%d\n",
887 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
888 if (do_proto) cprintf("%d Not logged in.\n",
889 ERROR + NOT_LOGGED_IN);
890 return(om_not_logged_in);
893 /* FIXME ... small security issue
894 * We need to check to make sure the requested message is actually
895 * in the current room, and set msg_ok to 1 only if it is. This
896 * functionality is currently missing because I'm in a hurry to replace
897 * broken production code with nonbroken pre-beta code. :( -- ajc
900 if (do_proto) cprintf("%d Message %ld is not in this room.\n",
902 return(om_no_such_msg);
907 * Fetch the message from disk
909 TheMessage = CtdlFetchMessage(msg_num);
910 if (TheMessage == NULL) {
911 if (do_proto) cprintf("%d Can't locate msg %ld on disk\n",
913 return(om_no_such_msg);
916 retcode = CtdlOutputPreLoadedMsg(
917 TheMessage, msg_num, mode,
918 headers_only, do_proto, crlf);
920 CtdlFreeMessage(TheMessage);
926 * Get a message off disk. (returns om_* values found in msgbase.h)
929 int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage,
931 int mode, /* how would you like that message? */
932 int headers_only, /* eschew the message body? */
933 int do_proto, /* do Citadel protocol responses? */
934 int crlf /* Use CRLF newlines instead of LF? */
940 char display_name[SIZ];
942 char *nl; /* newline string */
944 /* buffers needed for RFC822 translation */
954 sprintf(mid, "%ld", msg_num);
955 nl = (crlf ? "\r\n" : "\n");
957 if (!is_valid_message(TheMessage)) {
958 lprintf(1, "ERROR: invalid preloaded message for output\n");
959 return(om_no_such_msg);
962 /* Are we downloading a MIME component? */
963 if (mode == MT_DOWNLOAD) {
964 if (TheMessage->cm_format_type != FMT_RFC822) {
966 cprintf("%d This is not a MIME message.\n",
968 } else if (CC->download_fp != NULL) {
969 if (do_proto) cprintf(
970 "%d You already have a download open.\n",
973 /* Parse the message text component */
974 mptr = TheMessage->cm_fields['M'];
975 mime_parser(mptr, NULL,
976 *mime_download, NULL, NULL,
978 /* If there's no file open by this time, the requested
979 * section wasn't found, so print an error
981 if (CC->download_fp == NULL) {
982 if (do_proto) cprintf(
983 "%d Section %s not found.\n",
984 ERROR + FILE_NOT_FOUND,
988 return((CC->download_fp != NULL) ? om_ok : om_mime_error);
991 /* now for the user-mode message reading loops */
992 if (do_proto) cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
994 /* Tell the client which format type we're using. If this is a
995 * MIME message, *lie* about it and tell the user it's fixed-format.
997 if (mode == MT_CITADEL) {
998 if (TheMessage->cm_format_type == FMT_RFC822) {
999 if (do_proto) cprintf("type=1\n");
1002 if (do_proto) cprintf("type=%d\n",
1003 TheMessage->cm_format_type);
1007 /* nhdr=yes means that we're only displaying headers, no body */
1008 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
1009 if (do_proto) cprintf("nhdr=yes\n");
1012 /* begin header processing loop for Citadel message format */
1014 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
1016 strcpy(display_name, "<unknown>");
1017 if (TheMessage->cm_fields['A']) {
1018 strcpy(buf, TheMessage->cm_fields['A']);
1019 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
1020 if (TheMessage->cm_anon_type == MES_ANON)
1021 strcpy(display_name, "****");
1022 else if (TheMessage->cm_anon_type == MES_AN2)
1023 strcpy(display_name, "anonymous");
1025 strcpy(display_name, buf);
1026 if ((is_room_aide())
1027 && ((TheMessage->cm_anon_type == MES_ANON)
1028 || (TheMessage->cm_anon_type == MES_AN2))) {
1029 sprintf(&display_name[strlen(display_name)],
1034 strcpy(allkeys, FORDER);
1035 for (i=0; i<strlen(allkeys); ++i) {
1036 k = (int) allkeys[i];
1038 if (TheMessage->cm_fields[k] != NULL) {
1040 if (do_proto) cprintf("%s=%s\n",
1045 if (do_proto) cprintf("%s=%s\n",
1047 TheMessage->cm_fields[k]
1056 /* begin header processing loop for RFC822 transfer format */
1061 strcpy(snode, NODENAME);
1062 strcpy(lnode, HUMANNODE);
1063 if (mode == MT_RFC822) {
1064 cprintf("X-UIDL: %ld%s", msg_num, nl);
1065 for (i = 0; i < 256; ++i) {
1066 if (TheMessage->cm_fields[i]) {
1067 mptr = TheMessage->cm_fields[i];
1070 strcpy(luser, mptr);
1071 strcpy(suser, mptr);
1074 "Path:" removed for now because it confuses brain-dead Microsoft shitware
1075 into thinking that mail messages are newsgroup messages instead. When we
1076 add NNTP support back into Citadel we'll have to add code to only output
1077 this field when appropriate.
1078 else if (i == 'P') {
1079 cprintf("Path: %s%s", mptr, nl);
1083 cprintf("Subject: %s%s", mptr, nl);
1087 strcpy(lnode, mptr);
1089 cprintf("X-Citadel-Room: %s%s",
1092 strcpy(snode, mptr);
1094 cprintf("To: %s%s", mptr, nl);
1095 else if (i == 'T') {
1096 datestring(datestamp, atol(mptr),
1097 DATESTRING_RFC822 );
1098 cprintf("Date: %s%s", datestamp, nl);
1104 for (i=0; i<strlen(suser); ++i) {
1105 suser[i] = tolower(suser[i]);
1106 if (!isalnum(suser[i])) suser[i]='_';
1109 if (mode == MT_RFC822) {
1110 if (!strcasecmp(snode, NODENAME)) {
1111 strcpy(snode, FQDN);
1114 /* Construct a fun message id */
1115 cprintf("Message-ID: <%s", mid);
1116 if (strchr(mid, '@')==NULL) {
1117 cprintf("@%s", snode);
1121 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
1123 if (strlen(fuser) > 0) {
1124 cprintf("From: %s (%s)%s", fuser, luser, nl);
1127 cprintf("From: %s@%s (%s)%s", suser, snode, luser, nl);
1130 cprintf("Organization: %s%s", lnode, nl);
1133 /* end header processing loop ... at this point, we're in the text */
1135 mptr = TheMessage->cm_fields['M'];
1137 /* Tell the client about the MIME parts in this message */
1138 if (TheMessage->cm_format_type == FMT_RFC822) {
1139 if (mode == MT_CITADEL) {
1140 mime_parser(mptr, NULL,
1141 *list_this_part, NULL, NULL,
1144 else if (mode == MT_MIME) { /* list parts only */
1145 mime_parser(mptr, NULL,
1146 *list_this_part, NULL, NULL,
1148 if (do_proto) cprintf("000\n");
1151 else if (mode == MT_RFC822) { /* unparsed RFC822 dump */
1152 /* FIXME ... we have to put some code in here to avoid
1153 * printing duplicate header information when both
1154 * Citadel and RFC822 headers exist. Preference should
1155 * probably be given to the RFC822 headers.
1157 while (ch=*(mptr++), ch!=0) {
1159 else if (ch==10) cprintf("%s", nl);
1160 else cprintf("%c", ch);
1162 if (do_proto) cprintf("000\n");
1168 if (do_proto) cprintf("000\n");
1172 /* signify start of msg text */
1173 if (mode == MT_CITADEL)
1174 if (do_proto) cprintf("text\n");
1175 if (mode == MT_RFC822) {
1176 if (TheMessage->cm_fields['U'] == NULL) {
1177 cprintf("Subject: (no subject)%s", nl);
1182 /* If the format type on disk is 1 (fixed-format), then we want
1183 * everything to be output completely literally ... regardless of
1184 * what message transfer format is in use.
1186 if (TheMessage->cm_format_type == FMT_FIXED) {
1188 while (ch = *mptr++, ch > 0) {
1191 if ((ch == 10) || (strlen(buf) > 250)) {
1192 cprintf("%s%s", buf, nl);
1195 buf[strlen(buf) + 1] = 0;
1196 buf[strlen(buf)] = ch;
1199 if (strlen(buf) > 0)
1200 cprintf("%s%s", buf, nl);
1203 /* If the message on disk is format 0 (Citadel vari-format), we
1204 * output using the formatter at 80 columns. This is the final output
1205 * form if the transfer format is RFC822, but if the transfer format
1206 * is Citadel proprietary, it'll still work, because the indentation
1207 * for new paragraphs is correct and the client will reformat the
1208 * message to the reader's screen width.
1210 if (TheMessage->cm_format_type == FMT_CITADEL) {
1211 memfmout(80, mptr, 0, nl);
1214 /* If the message on disk is format 4 (MIME), we've gotta hand it
1215 * off to the MIME parser. The client has already been told that
1216 * this message is format 1 (fixed format), so the callback function
1217 * we use will display those parts as-is.
1219 if (TheMessage->cm_format_type == FMT_RFC822) {
1220 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
1221 memset(ma, 0, sizeof(struct ma_info));
1222 mime_parser(mptr, NULL,
1223 *fixed_output, NULL, NULL,
1227 /* now we're done */
1228 if (do_proto) cprintf("000\n");
1235 * display a message (mode 0 - Citadel proprietary)
1237 void cmd_msg0(char *cmdbuf)
1240 int headers_only = 0;
1242 msgid = extract_long(cmdbuf, 0);
1243 headers_only = extract_int(cmdbuf, 1);
1245 CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0);
1251 * display a message (mode 2 - RFC822)
1253 void cmd_msg2(char *cmdbuf)
1256 int headers_only = 0;
1258 msgid = extract_long(cmdbuf, 0);
1259 headers_only = extract_int(cmdbuf, 1);
1261 CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1);
1267 * display a message (mode 3 - IGnet raw format - internal programs only)
1269 void cmd_msg3(char *cmdbuf)
1272 struct CtdlMessage *msg;
1275 if (CC->internal_pgm == 0) {
1276 cprintf("%d This command is for internal programs only.\n",
1281 msgnum = extract_long(cmdbuf, 0);
1282 msg = CtdlFetchMessage(msgnum);
1284 cprintf("%d Message %ld not found.\n",
1289 serialize_message(&smr, msg);
1290 CtdlFreeMessage(msg);
1293 cprintf("%d Unable to serialize message\n",
1294 ERROR+INTERNAL_ERROR);
1298 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1299 client_write(smr.ser, smr.len);
1306 * display a message (mode 4 - MIME) (FIXME ... still evolving, not complete)
1308 void cmd_msg4(char *cmdbuf)
1312 msgid = extract_long(cmdbuf, 0);
1313 CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0);
1317 * Open a component of a MIME message as a download file
1319 void cmd_opna(char *cmdbuf)
1323 CtdlAllocUserData(SYM_DESIRED_SECTION, SIZ);
1325 msgid = extract_long(cmdbuf, 0);
1326 extract(desired_section, cmdbuf, 1);
1328 CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1);
1333 * Save a message pointer into a specified room
1334 * (Returns 0 for success, nonzero for failure)
1335 * roomname may be NULL to use the current room
1337 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
1339 char hold_rm[ROOMNAMELEN];
1340 struct cdbdata *cdbfr;
1343 long highest_msg = 0L;
1344 struct CtdlMessage *msg = NULL;
1346 lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n",
1347 roomname, msgid, flags);
1349 strcpy(hold_rm, CC->quickroom.QRname);
1351 /* We may need to check to see if this message is real */
1352 if ( (flags & SM_VERIFY_GOODNESS)
1353 || (flags & SM_DO_REPL_CHECK)
1355 msg = CtdlFetchMessage(msgid);
1356 if (msg == NULL) return(ERROR + ILLEGAL_VALUE);
1359 /* Perform replication checks if necessary */
1360 if ( (flags & SM_DO_REPL_CHECK) && (msg != NULL) ) {
1362 if (getroom(&CC->quickroom,
1363 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1365 lprintf(9, "No such room <%s>\n", roomname);
1366 if (msg != NULL) CtdlFreeMessage(msg);
1367 return(ERROR + ROOM_NOT_FOUND);
1370 if (ReplicationChecks(msg) != 0) {
1371 getroom(&CC->quickroom, hold_rm);
1372 if (msg != NULL) CtdlFreeMessage(msg);
1373 lprintf(9, "Did replication, and newer exists\n");
1378 /* Now the regular stuff */
1379 if (lgetroom(&CC->quickroom,
1380 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1382 lprintf(9, "No such room <%s>\n", roomname);
1383 if (msg != NULL) CtdlFreeMessage(msg);
1384 return(ERROR + ROOM_NOT_FOUND);
1387 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
1388 if (cdbfr == NULL) {
1392 msglist = mallok(cdbfr->len);
1393 if (msglist == NULL)
1394 lprintf(3, "ERROR malloc msglist!\n");
1395 num_msgs = cdbfr->len / sizeof(long);
1396 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1401 /* Make sure the message doesn't already exist in this room. It
1402 * is absolutely taboo to have more than one reference to the same
1403 * message in a room.
1405 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1406 if (msglist[i] == msgid) {
1407 lputroom(&CC->quickroom); /* unlock the room */
1408 getroom(&CC->quickroom, hold_rm);
1409 if (msg != NULL) CtdlFreeMessage(msg);
1410 return(ERROR + ALREADY_EXISTS);
1414 /* Now add the new message */
1416 msglist = reallok(msglist,
1417 (num_msgs * sizeof(long)));
1419 if (msglist == NULL) {
1420 lprintf(3, "ERROR: can't realloc message list!\n");
1422 msglist[num_msgs - 1] = msgid;
1424 /* Sort the message list, so all the msgid's are in order */
1425 num_msgs = sort_msglist(msglist, num_msgs);
1427 /* Determine the highest message number */
1428 highest_msg = msglist[num_msgs - 1];
1430 /* Write it back to disk. */
1431 cdb_store(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long),
1432 msglist, num_msgs * sizeof(long));
1434 /* Free up the memory we used. */
1437 /* Update the highest-message pointer and unlock the room. */
1438 CC->quickroom.QRhighest = highest_msg;
1439 lputroom(&CC->quickroom);
1440 getroom(&CC->quickroom, hold_rm);
1442 /* Bump the reference count for this message. */
1443 if ((flags & SM_DONT_BUMP_REF)==0) {
1444 AdjRefCount(msgid, +1);
1447 /* Return success. */
1448 if (msg != NULL) CtdlFreeMessage(msg);
1455 * Message base operation to send a message to the master file
1456 * (returns new message number)
1458 * This is the back end for CtdlSaveMsg() and should not be directly
1459 * called by server-side modules.
1462 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1463 FILE *save_a_copy) /* save a copy to disk? */
1470 /* Get a new message number */
1471 newmsgid = get_new_message_number();
1472 sprintf(msgidbuf, "%ld@%s", newmsgid, config.c_fqdn);
1474 /* Generate an ID if we don't have one already */
1475 if (msg->cm_fields['I']==NULL) {
1476 msg->cm_fields['I'] = strdoop(msgidbuf);
1479 serialize_message(&smr, msg);
1482 cprintf("%d Unable to serialize message\n",
1483 ERROR+INTERNAL_ERROR);
1487 /* Write our little bundle of joy into the message base */
1488 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1489 smr.ser, smr.len) < 0) {
1490 lprintf(2, "Can't store message\n");
1496 /* If the caller specified that a copy should be saved to a particular
1497 * file handle, do that now too.
1499 if (save_a_copy != NULL) {
1500 fwrite(smr.ser, smr.len, 1, save_a_copy);
1503 /* Free the memory we used for the serialized message */
1506 /* Return the *local* message ID to the caller
1507 * (even if we're storing an incoming network message)
1515 * Serialize a struct CtdlMessage into the format used on disk and network.
1517 * This function loads up a "struct ser_ret" (defined in server.h) which
1518 * contains the length of the serialized message and a pointer to the
1519 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1521 void serialize_message(struct ser_ret *ret, /* return values */
1522 struct CtdlMessage *msg) /* unserialized msg */
1526 static char *forder = FORDER;
1528 if (is_valid_message(msg) == 0) return; /* self check */
1531 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1532 ret->len = ret->len +
1533 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1535 lprintf(9, "calling malloc(%d)\n", ret->len);
1536 ret->ser = mallok(ret->len);
1537 if (ret->ser == NULL) {
1543 ret->ser[1] = msg->cm_anon_type;
1544 ret->ser[2] = msg->cm_format_type;
1547 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1548 ret->ser[wlen++] = (char)forder[i];
1549 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1550 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1552 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1561 * Back end for the ReplicationChecks() function
1563 void check_repl(long msgnum, void *userdata) {
1564 struct CtdlMessage *msg;
1565 time_t timestamp = (-1L);
1567 lprintf(9, "check_repl() found message %ld\n", msgnum);
1568 msg = CtdlFetchMessage(msgnum);
1569 if (msg == NULL) return;
1570 if (msg->cm_fields['T'] != NULL) {
1571 timestamp = atol(msg->cm_fields['T']);
1573 CtdlFreeMessage(msg);
1575 if (timestamp > msg_repl->highest) {
1576 msg_repl->highest = timestamp; /* newer! */
1577 lprintf(9, "newer!\n");
1580 lprintf(9, "older!\n");
1582 /* Existing isn't newer? Then delete the old one(s). */
1583 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, "");
1588 * Check to see if any messages already exist which carry the same Extended ID
1592 * -> With older timestamps: delete them and return 0. Message will be saved.
1593 * -> With newer timestamps: return 1. Message save will be aborted.
1595 int ReplicationChecks(struct CtdlMessage *msg) {
1596 struct CtdlMessage *template;
1599 lprintf(9, "ReplicationChecks() started\n");
1600 /* No extended id? Don't do anything. */
1601 if (msg->cm_fields['E'] == NULL) return 0;
1602 if (strlen(msg->cm_fields['E']) == 0) return 0;
1603 lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1605 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1606 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1607 msg_repl->highest = atol(msg->cm_fields['T']);
1609 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1610 memset(template, 0, sizeof(struct CtdlMessage));
1611 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1613 CtdlForEachMessage(MSGS_ALL, 0L, (-127), NULL, template,
1616 /* If a newer message exists with the same Extended ID, abort
1619 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1623 CtdlFreeMessage(template);
1624 lprintf(9, "ReplicationChecks() returning %d\n", abort_this);
1632 * Save a message to disk
1634 long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1635 char *rec, /* Recipient (mail) */
1636 char *force, /* force a particular room? */
1637 int supplied_mailtype) /* local or remote type */
1640 char hold_rm[ROOMNAMELEN];
1641 char actual_rm[ROOMNAMELEN];
1642 char force_room[ROOMNAMELEN];
1643 char content_type[SIZ]; /* We have to learn this */
1644 char recipient[SIZ];
1647 struct usersupp userbuf;
1649 struct SuppMsgInfo smi;
1650 FILE *network_fp = NULL;
1651 static int seqnum = 1;
1652 struct CtdlMessage *imsg;
1656 lprintf(9, "CtdlSaveMsg() called\n");
1657 if (is_valid_message(msg) == 0) return(-1); /* self check */
1658 mailtype = supplied_mailtype;
1660 /* If this message has no timestamp, we take the liberty of
1661 * giving it one, right now.
1663 if (msg->cm_fields['T'] == NULL) {
1664 lprintf(9, "Generating timestamp\n");
1665 sprintf(aaa, "%ld", time(NULL));
1666 msg->cm_fields['T'] = strdoop(aaa);
1669 /* If this message has no path, we generate one.
1671 if (msg->cm_fields['P'] == NULL) {
1672 lprintf(9, "Generating path\n");
1673 if (msg->cm_fields['A'] != NULL) {
1674 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1675 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1676 if (isspace(msg->cm_fields['P'][a])) {
1677 msg->cm_fields['P'][a] = ' ';
1682 msg->cm_fields['P'] = strdoop("unknown");
1686 strcpy(force_room, force);
1688 /* Strip non-printable characters out of the recipient name */
1689 lprintf(9, "Checking recipient (if present)\n");
1690 strcpy(recipient, rec);
1691 for (a = 0; a < strlen(recipient); ++a)
1692 if (!isprint(recipient[a]))
1693 strcpy(&recipient[a], &recipient[a + 1]);
1695 /* Change "user @ xxx" to "user" if xxx is an alias for this host */
1696 for (a=0; a<strlen(recipient); ++a) {
1697 if (recipient[a] == '@') {
1698 if (CtdlHostAlias(&recipient[a+1])
1699 == hostalias_localhost) {
1701 lprintf(7, "Changed to <%s>\n", recipient);
1702 mailtype = MES_LOCAL;
1707 lprintf(9, "Recipient is <%s>\n", recipient);
1709 /* Learn about what's inside, because it's what's inside that counts */
1710 lprintf(9, "Learning what's inside\n");
1711 if (msg->cm_fields['M'] == NULL) {
1712 lprintf(1, "ERROR: attempt to save message with NULL body\n");
1715 switch (msg->cm_format_type) {
1717 strcpy(content_type, "text/x-citadel-variformat");
1720 strcpy(content_type, "text/plain");
1723 strcpy(content_type, "text/plain");
1724 /* advance past header fields */
1725 mptr = msg->cm_fields['M'];
1728 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1729 safestrncpy(content_type, mptr,
1730 sizeof(content_type));
1731 strcpy(content_type, &content_type[14]);
1732 for (a = 0; a < strlen(content_type); ++a)
1733 if ((content_type[a] == ';')
1734 || (content_type[a] == ' ')
1735 || (content_type[a] == 13)
1736 || (content_type[a] == 10))
1737 content_type[a] = 0;
1744 /* Goto the correct room */
1745 lprintf(9, "Switching rooms\n");
1746 strcpy(hold_rm, CC->quickroom.QRname);
1747 strcpy(actual_rm, CC->quickroom.QRname);
1749 /* If the user is a twit, move to the twit room for posting */
1750 lprintf(9, "Handling twit stuff\n");
1752 if (CC->usersupp.axlevel == 2) {
1753 strcpy(hold_rm, actual_rm);
1754 strcpy(actual_rm, config.c_twitroom);
1758 /* ...or if this message is destined for Aide> then go there. */
1759 if (strlen(force_room) > 0) {
1760 strcpy(actual_rm, force_room);
1763 lprintf(9, "Possibly relocating\n");
1764 if (strcasecmp(actual_rm, CC->quickroom.QRname)) {
1765 getroom(&CC->quickroom, actual_rm);
1769 * If this message has no O (room) field, generate one.
1771 if (msg->cm_fields['O'] == NULL) {
1772 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1775 /* Perform "before save" hooks (aborting if any return nonzero) */
1776 lprintf(9, "Performing before-save hooks\n");
1777 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return(-1);
1779 /* If this message has an Extended ID, perform replication checks */
1780 lprintf(9, "Performing replication checks\n");
1781 if (ReplicationChecks(msg) > 0) return(-1);
1783 /* Network mail - send a copy to the network program. */
1784 if ((strlen(recipient) > 0) && (mailtype == MES_BINARY)) {
1785 lprintf(9, "Sending network spool\n");
1786 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1787 (long) getpid(), CC->cs_pid, ++seqnum);
1788 lprintf(9, "Saving a copy to %s\n", aaa);
1789 network_fp = fopen(aaa, "ab+");
1790 if (network_fp == NULL)
1791 lprintf(2, "ERROR: %s\n", strerror(errno));
1794 /* Save it to disk */
1795 lprintf(9, "Saving to disk\n");
1796 newmsgid = send_message(msg, network_fp);
1797 if (network_fp != NULL) {
1799 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1802 if (newmsgid <= 0L) return(-1);
1804 /* Write a supplemental message info record. This doesn't have to
1805 * be a critical section because nobody else knows about this message
1808 lprintf(9, "Creating SuppMsgInfo record\n");
1809 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1810 smi.smi_msgnum = newmsgid;
1811 smi.smi_refcount = 0;
1812 safestrncpy(smi.smi_content_type, content_type, 64);
1813 PutSuppMsgInfo(&smi);
1815 /* Now figure out where to store the pointers */
1816 lprintf(9, "Storing pointers\n");
1818 /* If this is being done by the networker delivering a private
1819 * message, we want to BYPASS saving the sender's copy (because there
1820 * is no local sender; it would otherwise go to the Trashcan).
1822 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1823 if (CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0) != 0) {
1824 lprintf(3, "ERROR saving message pointer!\n");
1825 CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
1829 /* For internet mail, drop a copy in the outbound queue room */
1830 if (mailtype == MES_INTERNET) {
1831 CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0);
1834 /* Bump this user's messages posted counter. */
1835 lprintf(9, "Updating user\n");
1836 lgetuser(&CC->usersupp, CC->curr_user);
1837 CC->usersupp.posted = CC->usersupp.posted + 1;
1838 lputuser(&CC->usersupp);
1840 /* If this is private, local mail, make a copy in the
1841 * recipient's mailbox and bump the reference count.
1843 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1844 if (getuser(&userbuf, recipient) == 0) {
1845 lprintf(9, "Delivering private mail\n");
1846 MailboxName(actual_rm, &userbuf, MAILROOM);
1847 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1850 lprintf(9, "No user <%s>, saving in %s> instead\n",
1851 recipient, AIDEROOM);
1852 CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
1856 /* Perform "after save" hooks */
1857 lprintf(9, "Performing after-save hooks\n");
1858 PerformMessageHooks(msg, EVT_AFTERSAVE);
1861 lprintf(9, "Returning to original room\n");
1862 if (strcasecmp(hold_rm, CC->quickroom.QRname))
1863 getroom(&CC->quickroom, hold_rm);
1865 /* For internet mail, generate delivery instructions
1866 * (Yes, this is recursive! Deal with it!)
1868 if (mailtype == MES_INTERNET) {
1869 lprintf(9, "Generating delivery instructions\n");
1870 instr = mallok(2048);
1872 "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
1875 SPOOLMIME, newmsgid, time(NULL),
1876 msg->cm_fields['A'], msg->cm_fields['N'],
1879 imsg = mallok(sizeof(struct CtdlMessage));
1880 memset(imsg, 0, sizeof(struct CtdlMessage));
1881 imsg->cm_magic = CTDLMESSAGE_MAGIC;
1882 imsg->cm_anon_type = MES_NORMAL;
1883 imsg->cm_format_type = FMT_RFC822;
1884 imsg->cm_fields['A'] = strdoop("Citadel");
1885 imsg->cm_fields['M'] = instr;
1886 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
1887 CtdlFreeMessage(imsg);
1896 * Convenience function for generating small administrative messages.
1898 void quickie_message(char *from, char *to, char *room, char *text)
1900 struct CtdlMessage *msg;
1902 msg = mallok(sizeof(struct CtdlMessage));
1903 memset(msg, 0, sizeof(struct CtdlMessage));
1904 msg->cm_magic = CTDLMESSAGE_MAGIC;
1905 msg->cm_anon_type = MES_NORMAL;
1906 msg->cm_format_type = 0;
1907 msg->cm_fields['A'] = strdoop(from);
1908 msg->cm_fields['O'] = strdoop(room);
1909 msg->cm_fields['N'] = strdoop(NODENAME);
1911 msg->cm_fields['R'] = strdoop(to);
1912 msg->cm_fields['M'] = strdoop(text);
1914 CtdlSaveMsg(msg, "", room, MES_LOCAL);
1915 CtdlFreeMessage(msg);
1916 syslog(LOG_NOTICE, text);
1922 * Back end function used by make_message() and similar functions
1924 char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */
1925 size_t maxlen, /* maximum message length */
1926 char *exist /* if non-null, append to it;
1927 exist is ALWAYS freed */
1931 size_t message_len = 0;
1932 size_t buffer_len = 0;
1936 if (exist == NULL) {
1940 m = reallok(exist, strlen(exist) + 4096);
1941 if (m == NULL) phree(exist);
1944 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) ;;
1951 /* read in the lines of message text one by one */
1952 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) {
1954 /* strip trailing newline type stuff */
1955 if (buf[strlen(buf)-1]==10) buf[strlen(buf)-1]=0;
1956 if (buf[strlen(buf)-1]==13) buf[strlen(buf)-1]=0;
1958 linelen = strlen(buf);
1960 /* augment the buffer if we have to */
1961 if ((message_len + linelen + 2) > buffer_len) {
1962 lprintf(9, "realloking\n");
1963 ptr = reallok(m, (buffer_len * 2) );
1964 if (ptr == NULL) { /* flush if can't allocate */
1965 while ( (client_gets(buf)>0) &&
1966 strcmp(buf, terminator)) ;;
1969 buffer_len = (buffer_len * 2);
1971 lprintf(9, "buffer_len is %d\n", buffer_len);
1975 /* Add the new line to the buffer. We avoid using strcat()
1976 * because that would involve traversing the entire message
1977 * after each line, and this function needs to run fast.
1979 strcpy(&m[message_len], buf);
1980 m[message_len + linelen] = '\n';
1981 m[message_len + linelen + 1] = 0;
1982 message_len = message_len + linelen + 1;
1984 /* if we've hit the max msg length, flush the rest */
1985 if (message_len >= maxlen) {
1986 while ( (client_gets(buf)>0)
1987 && strcmp(buf, terminator)) ;;
1998 * Build a binary message to be saved on disk.
2001 struct CtdlMessage *make_message(
2002 struct usersupp *author, /* author's usersupp structure */
2003 char *recipient, /* NULL if it's not mail */
2004 char *room, /* room where it's going */
2005 int type, /* see MES_ types in header file */
2006 int net_type, /* see MES_ types in header file */
2007 int format_type, /* local or remote (see citadel.h) */
2008 char *fake_name) /* who we're masquerading as */
2014 struct CtdlMessage *msg;
2016 msg = mallok(sizeof(struct CtdlMessage));
2017 memset(msg, 0, sizeof(struct CtdlMessage));
2018 msg->cm_magic = CTDLMESSAGE_MAGIC;
2019 msg->cm_anon_type = type;
2020 msg->cm_format_type = format_type;
2022 /* Don't confuse the poor folks if it's not routed mail. */
2023 strcpy(dest_node, "");
2025 /* If net_type is MES_BINARY, split out the destination node. */
2026 if (net_type == MES_BINARY) {
2027 strcpy(dest_node, NODENAME);
2028 for (a = 0; a < strlen(recipient); ++a) {
2029 if (recipient[a] == '@') {
2031 strcpy(dest_node, &recipient[a + 1]);
2036 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
2037 if (net_type == MES_INTERNET) {
2038 strcpy(dest_node, "internet");
2041 while (isspace(recipient[strlen(recipient) - 1]))
2042 recipient[strlen(recipient) - 1] = 0;
2044 sprintf(buf, "cit%ld", author->usernum); /* Path */
2045 msg->cm_fields['P'] = strdoop(buf);
2047 sprintf(buf, "%ld", time(NULL)); /* timestamp */
2048 msg->cm_fields['T'] = strdoop(buf);
2050 if (fake_name[0]) /* author */
2051 msg->cm_fields['A'] = strdoop(fake_name);
2053 msg->cm_fields['A'] = strdoop(author->fullname);
2055 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
2056 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
2058 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
2060 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
2061 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
2063 if (recipient[0] != 0)
2064 msg->cm_fields['R'] = strdoop(recipient);
2065 if (dest_node[0] != 0)
2066 msg->cm_fields['D'] = strdoop(dest_node);
2069 msg->cm_fields['M'] = CtdlReadMessageBody("000",
2070 config.c_maxmsglen, NULL);
2078 * Check to see whether we have permission to post a message in the current
2079 * room. Returns a *CITADEL ERROR CODE* and puts a message in errmsgbuf, or
2080 * returns 0 on success.
2082 int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf) {
2084 if (!(CC->logged_in)) {
2085 sprintf(errmsgbuf, "Not logged in.");
2086 return (ERROR + NOT_LOGGED_IN);
2089 if ((CC->usersupp.axlevel < 2)
2090 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
2091 sprintf(errmsgbuf, "Need to be validated to enter "
2092 "(except in %s> to sysop)", MAILROOM);
2093 return (ERROR + HIGHER_ACCESS_REQUIRED);
2096 if ((CC->usersupp.axlevel < 4)
2097 && (CC->quickroom.QRflags & QR_NETWORK)) {
2098 sprintf(errmsgbuf, "Need net privileges to enter here.");
2099 return (ERROR + HIGHER_ACCESS_REQUIRED);
2102 if ((CC->usersupp.axlevel < 6)
2103 && (CC->quickroom.QRflags & QR_READONLY)) {
2104 sprintf(errmsgbuf, "Sorry, this is a read-only room.");
2105 return (ERROR + HIGHER_ACCESS_REQUIRED);
2108 strcpy(errmsgbuf, "Ok");
2116 * message entry - mode 0 (normal)
2118 void cmd_ent0(char *entargs)
2121 char recipient[SIZ];
2123 int format_type = 0;
2124 char newusername[SIZ];
2125 struct CtdlMessage *msg;
2129 struct usersupp tempUS;
2133 post = extract_int(entargs, 0);
2134 extract(recipient, entargs, 1);
2135 anon_flag = extract_int(entargs, 2);
2136 format_type = extract_int(entargs, 3);
2138 /* first check to make sure the request is valid. */
2140 err = CtdlDoIHavePermissionToPostInThisRoom(buf);
2142 cprintf("%d %s\n", err, buf);
2146 /* Check some other permission type things. */
2149 if (CC->usersupp.axlevel < 6) {
2150 cprintf("%d You don't have permission to masquerade.\n",
2151 ERROR + HIGHER_ACCESS_REQUIRED);
2154 extract(newusername, entargs, 4);
2155 memset(CC->fake_postname, 0, 32);
2156 strcpy(CC->fake_postname, newusername);
2157 cprintf("%d Ok\n", OK);
2160 CC->cs_flags |= CS_POSTING;
2163 if (CC->quickroom.QRflags & QR_MAILBOX) {
2164 if (CC->usersupp.axlevel >= 2) {
2165 strcpy(buf, recipient);
2167 strcpy(buf, "sysop");
2168 e = alias(buf); /* alias and mail type */
2169 if ((buf[0] == 0) || (e == MES_ERROR)) {
2170 cprintf("%d Unknown address - cannot send message.\n",
2171 ERROR + NO_SUCH_USER);
2174 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
2175 cprintf("%d Net privileges required for network mail.\n",
2176 ERROR + HIGHER_ACCESS_REQUIRED);
2179 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
2180 && ((CC->usersupp.flags & US_INTERNET) == 0)
2181 && (!CC->internal_pgm)) {
2182 cprintf("%d You don't have access to Internet mail.\n",
2183 ERROR + HIGHER_ACCESS_REQUIRED);
2186 if (!strcasecmp(buf, "sysop")) {
2189 else if (e == MES_LOCAL) { /* don't search local file */
2190 if (!strcasecmp(buf, CC->usersupp.fullname)) {
2191 cprintf("%d Can't send mail to yourself!\n",
2192 ERROR + NO_SUCH_USER);
2195 /* Check to make sure the user exists; also get the correct
2196 * upper/lower casing of the name.
2198 a = getuser(&tempUS, buf);
2200 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
2203 strcpy(buf, tempUS.fullname);
2208 if (CC->quickroom.QRflags & QR_ANONONLY)
2210 if (CC->quickroom.QRflags & QR_ANONOPT) {
2214 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2217 /* If we're only checking the validity of the request, return
2218 * success without creating the message.
2221 cprintf("%d %s\n", OK, buf);
2225 cprintf("%d send message\n", SEND_LISTING);
2227 /* Read in the message from the client. */
2228 if (CC->fake_postname[0])
2229 msg = make_message(&CC->usersupp, buf,
2230 CC->quickroom.QRname, b, e, format_type,
2232 else if (CC->fake_username[0])
2233 msg = make_message(&CC->usersupp, buf,
2234 CC->quickroom.QRname, b, e, format_type,
2237 msg = make_message(&CC->usersupp, buf,
2238 CC->quickroom.QRname, b, e, format_type, "");
2241 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e);
2242 CtdlFreeMessage(msg);
2243 CC->fake_postname[0] = '\0';
2250 * message entry - mode 3 (raw)
2252 void cmd_ent3(char *entargs)
2258 unsigned char ch, which_field;
2259 struct usersupp tempUS;
2261 struct CtdlMessage *msg;
2264 if (CC->internal_pgm == 0) {
2265 cprintf("%d This command is for internal programs only.\n",
2270 /* See if there's a recipient, but make sure it's a real one */
2271 extract(recp, entargs, 1);
2272 for (a = 0; a < strlen(recp); ++a)
2273 if (!isprint(recp[a]))
2274 strcpy(&recp[a], &recp[a + 1]);
2275 while (isspace(recp[0]))
2276 strcpy(recp, &recp[1]);
2277 while (isspace(recp[strlen(recp) - 1]))
2278 recp[strlen(recp) - 1] = 0;
2280 /* If we're in Mail, check the recipient */
2281 if (strlen(recp) > 0) {
2282 e = alias(recp); /* alias and mail type */
2283 if ((recp[0] == 0) || (e == MES_ERROR)) {
2284 cprintf("%d Unknown address - cannot send message.\n",
2285 ERROR + NO_SUCH_USER);
2288 if (e == MES_LOCAL) {
2289 a = getuser(&tempUS, recp);
2291 cprintf("%d No such user.\n",
2292 ERROR + NO_SUCH_USER);
2298 /* At this point, message has been approved. */
2299 if (extract_int(entargs, 0) == 0) {
2300 cprintf("%d OK to send\n", OK);
2304 msglen = extract_long(entargs, 2);
2305 msg = mallok(sizeof(struct CtdlMessage));
2307 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
2311 memset(msg, 0, sizeof(struct CtdlMessage));
2312 tempbuf = mallok(msglen);
2313 if (tempbuf == NULL) {
2314 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
2319 cprintf("%d %ld\n", SEND_BINARY, msglen);
2321 client_read(&ch, 1); /* 0xFF magic number */
2322 msg->cm_magic = CTDLMESSAGE_MAGIC;
2323 client_read(&ch, 1); /* anon type */
2324 msg->cm_anon_type = ch;
2325 client_read(&ch, 1); /* format type */
2326 msg->cm_format_type = ch;
2327 msglen = msglen - 3;
2329 while (msglen > 0) {
2330 client_read(&which_field, 1);
2331 if (!isalpha(which_field)) valid_msg = 0;
2335 client_read(&ch, 1);
2337 a = strlen(tempbuf);
2340 } while ( (ch != 0) && (msglen > 0) );
2342 msg->cm_fields[which_field] = strdoop(tempbuf);
2345 msg->cm_flags = CM_SKIP_HOOKS;
2346 if (valid_msg) CtdlSaveMsg(msg, recp, "", e);
2347 CtdlFreeMessage(msg);
2353 * API function to delete messages which match a set of criteria
2354 * (returns the actual number of messages deleted)
2356 int CtdlDeleteMessages(char *room_name, /* which room */
2357 long dmsgnum, /* or "0" for any */
2358 char *content_type /* or "" for any */
2362 struct quickroom qrbuf;
2363 struct cdbdata *cdbfr;
2364 long *msglist = NULL;
2367 int num_deleted = 0;
2369 struct SuppMsgInfo smi;
2371 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
2372 room_name, dmsgnum, content_type);
2374 /* get room record, obtaining a lock... */
2375 if (lgetroom(&qrbuf, room_name) != 0) {
2376 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
2378 return (0); /* room not found */
2380 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
2382 if (cdbfr != NULL) {
2383 msglist = mallok(cdbfr->len);
2384 memcpy(msglist, cdbfr->ptr, cdbfr->len);
2385 num_msgs = cdbfr->len / sizeof(long);
2389 for (i = 0; i < num_msgs; ++i) {
2392 /* Set/clear a bit for each criterion */
2394 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
2395 delete_this |= 0x01;
2397 if (strlen(content_type) == 0) {
2398 delete_this |= 0x02;
2400 GetSuppMsgInfo(&smi, msglist[i]);
2401 if (!strcasecmp(smi.smi_content_type,
2403 delete_this |= 0x02;
2407 /* Delete message only if all bits are set */
2408 if (delete_this == 0x03) {
2409 AdjRefCount(msglist[i], -1);
2415 num_msgs = sort_msglist(msglist, num_msgs);
2416 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
2417 msglist, (num_msgs * sizeof(long)));
2419 qrbuf.QRhighest = msglist[num_msgs - 1];
2423 lprintf(9, "%d message(s) deleted.\n", num_deleted);
2424 return (num_deleted);
2430 * Check whether the current user has permission to delete messages from
2431 * the current room (returns 1 for yes, 0 for no)
2433 int CtdlDoIHavePermissionToDeleteMessagesFromThisRoom(void) {
2434 getuser(&CC->usersupp, CC->curr_user);
2435 if ((CC->usersupp.axlevel < 6)
2436 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2437 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2438 && (!(CC->internal_pgm))) {
2447 * Delete message from current room
2449 void cmd_dele(char *delstr)
2454 if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom() == 0) {
2455 cprintf("%d Higher access required.\n",
2456 ERROR + HIGHER_ACCESS_REQUIRED);
2459 delnum = extract_long(delstr, 0);
2461 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, "");
2464 cprintf("%d %d message%s deleted.\n", OK,
2465 num_deleted, ((num_deleted != 1) ? "s" : ""));
2467 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2473 * Back end API function for moves and deletes
2475 int CtdlCopyMsgToRoom(long msgnum, char *dest) {
2478 err = CtdlSaveMsgPointerInRoom(dest, msgnum,
2479 (SM_VERIFY_GOODNESS | SM_DO_REPL_CHECK) );
2480 if (err != 0) return(err);
2488 * move or copy a message to another room
2490 void cmd_move(char *args)
2494 struct quickroom qtemp;
2498 num = extract_long(args, 0);
2499 extract(targ, args, 1);
2500 targ[ROOMNAMELEN - 1] = 0;
2501 is_copy = extract_int(args, 2);
2503 getuser(&CC->usersupp, CC->curr_user);
2504 if ((CC->usersupp.axlevel < 6)
2505 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2506 cprintf("%d Higher access required.\n",
2507 ERROR + HIGHER_ACCESS_REQUIRED);
2511 if (getroom(&qtemp, targ) != 0) {
2512 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2516 err = CtdlCopyMsgToRoom(num, targ);
2518 cprintf("%d Cannot store message in %s: error %d\n",
2523 /* Now delete the message from the source room,
2524 * if this is a 'move' rather than a 'copy' operation.
2526 if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, "");
2528 cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") );
2534 * GetSuppMsgInfo() - Get the supplementary record for a message
2536 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2539 struct cdbdata *cdbsmi;
2542 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2543 smibuf->smi_msgnum = msgnum;
2544 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2546 /* Use the negative of the message number for its supp record index */
2547 TheIndex = (0L - msgnum);
2549 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2550 if (cdbsmi == NULL) {
2551 return; /* record not found; go with defaults */
2553 memcpy(smibuf, cdbsmi->ptr,
2554 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2555 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2562 * PutSuppMsgInfo() - (re)write supplementary record for a message
2564 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2568 /* Use the negative of the message number for its supp record index */
2569 TheIndex = (0L - smibuf->smi_msgnum);
2571 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2572 smibuf->smi_msgnum, smibuf->smi_refcount);
2574 cdb_store(CDB_MSGMAIN,
2575 &TheIndex, sizeof(long),
2576 smibuf, sizeof(struct SuppMsgInfo));
2581 * AdjRefCount - change the reference count for a message;
2582 * delete the message if it reaches zero
2584 void AdjRefCount(long msgnum, int incr)
2587 struct SuppMsgInfo smi;
2590 /* This is a *tight* critical section; please keep it that way, as
2591 * it may get called while nested in other critical sections.
2592 * Complicating this any further will surely cause deadlock!
2594 begin_critical_section(S_SUPPMSGMAIN);
2595 GetSuppMsgInfo(&smi, msgnum);
2596 lprintf(9, "Ref count for message <%ld> before write is <%d>\n",
2597 msgnum, smi.smi_refcount);
2598 smi.smi_refcount += incr;
2599 PutSuppMsgInfo(&smi);
2600 end_critical_section(S_SUPPMSGMAIN);
2601 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2602 msgnum, smi.smi_refcount);
2604 /* If the reference count is now zero, delete the message
2605 * (and its supplementary record as well).
2607 if (smi.smi_refcount == 0) {
2608 lprintf(9, "Deleting message <%ld>\n", msgnum);
2610 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2611 delnum = (0L - msgnum);
2612 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2617 * Write a generic object to this room
2619 * Note: this could be much more efficient. Right now we use two temporary
2620 * files, and still pull the message into memory as with all others.
2622 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2623 char *content_type, /* MIME type of this object */
2624 char *tempfilename, /* Where to fetch it from */
2625 struct usersupp *is_mailbox, /* Mailbox room? */
2626 int is_binary, /* Is encoding necessary? */
2627 int is_unique, /* Del others of this type? */
2628 unsigned int flags /* Internal save flags */
2633 char filename[PATH_MAX];
2636 struct quickroom qrbuf;
2637 char roomname[ROOMNAMELEN];
2638 struct CtdlMessage *msg;
2641 if (is_mailbox != NULL)
2642 MailboxName(roomname, is_mailbox, req_room);
2644 safestrncpy(roomname, req_room, sizeof(roomname));
2645 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2647 strcpy(filename, tmpnam(NULL));
2648 fp = fopen(filename, "w");
2652 tempfp = fopen(tempfilename, "r");
2653 if (tempfp == NULL) {
2659 fprintf(fp, "Content-type: %s\n", content_type);
2660 lprintf(9, "Content-type: %s\n", content_type);
2662 if (is_binary == 0) {
2663 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2664 while (ch = getc(tempfp), ch > 0)
2670 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2673 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2674 tempfilename, filename);
2678 lprintf(9, "Allocating\n");
2679 msg = mallok(sizeof(struct CtdlMessage));
2680 memset(msg, 0, sizeof(struct CtdlMessage));
2681 msg->cm_magic = CTDLMESSAGE_MAGIC;
2682 msg->cm_anon_type = MES_NORMAL;
2683 msg->cm_format_type = 4;
2684 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2685 msg->cm_fields['O'] = strdoop(req_room);
2686 msg->cm_fields['N'] = strdoop(config.c_nodename);
2687 msg->cm_fields['H'] = strdoop(config.c_humannode);
2688 msg->cm_flags = flags;
2690 lprintf(9, "Loading\n");
2691 fp = fopen(filename, "rb");
2692 fseek(fp, 0L, SEEK_END);
2695 msg->cm_fields['M'] = mallok(len);
2696 fread(msg->cm_fields['M'], len, 1, fp);
2700 /* Create the requested room if we have to. */
2701 if (getroom(&qrbuf, roomname) != 0) {
2702 create_room(roomname,
2703 ( (is_mailbox != NULL) ? 5 : 3 ),
2706 /* If the caller specified this object as unique, delete all
2707 * other objects of this type that are currently in the room.
2710 lprintf(9, "Deleted %d other msgs of this type\n",
2711 CtdlDeleteMessages(roomname, 0L, content_type));
2713 /* Now write the data */
2714 CtdlSaveMsg(msg, "", roomname, MES_LOCAL);
2715 CtdlFreeMessage(msg);
2723 void CtdlGetSysConfigBackend(long msgnum, void *userdata) {
2724 config_msgnum = msgnum;
2728 char *CtdlGetSysConfig(char *sysconfname) {
2729 char hold_rm[ROOMNAMELEN];
2732 struct CtdlMessage *msg;
2735 strcpy(hold_rm, CC->quickroom.QRname);
2736 if (getroom(&CC->quickroom, SYSCONFIGROOM) != 0) {
2737 getroom(&CC->quickroom, hold_rm);
2742 /* We want the last (and probably only) config in this room */
2743 begin_critical_section(S_CONFIG);
2744 config_msgnum = (-1L);
2745 CtdlForEachMessage(MSGS_LAST, 1, (-127), sysconfname, NULL,
2746 CtdlGetSysConfigBackend, NULL);
2747 msgnum = config_msgnum;
2748 end_critical_section(S_CONFIG);
2754 msg = CtdlFetchMessage(msgnum);
2756 conf = strdoop(msg->cm_fields['M']);
2757 CtdlFreeMessage(msg);
2764 getroom(&CC->quickroom, hold_rm);
2766 lprintf(9, "eggstracting...\n");
2767 if (conf != NULL) do {
2768 extract_token(buf, conf, 0, '\n');
2769 lprintf(9, "eggstracted <%s>\n", buf);
2770 strcpy(conf, &conf[strlen(buf)+1]);
2771 } while ( (strlen(conf)>0) && (strlen(buf)>0) );
2776 void CtdlPutSysConfig(char *sysconfname, char *sysconfdata) {
2777 char temp[PATH_MAX];
2780 strcpy(temp, tmpnam(NULL));
2782 fp = fopen(temp, "w");
2783 if (fp == NULL) return;
2784 fprintf(fp, "%s", sysconfdata);
2787 /* this handy API function does all the work for us */
2788 CtdlWriteObject(SYSCONFIGROOM, sysconfname, temp, NULL, 0, 1, 0);