22 #include "sysdep_decls.h"
23 #include "citserver.h"
28 #include "dynloader.h"
30 #include "mime_parser.h"
33 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
34 #define ma ((struct ma_info *)CtdlGetUserData(SYM_MA_INFO))
36 extern struct config config;
38 /* FIX dumb stub function */
39 void save_message(char *junk, char *more_junk, char *foo, int wahw, int ee) {
43 * This function is self explanatory.
44 * (What can I say, I'm in a weird mood today...)
46 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
50 for (i = 0; i < strlen(name); ++i)
53 if (isspace(name[i - 1])) {
54 strcpy(&name[i - 1], &name[i]);
57 while (isspace(name[i + 1])) {
58 strcpy(&name[i + 1], &name[i + 2]);
65 * Aliasing for network mail.
66 * (Error messages have been commented out, because this is a server.)
69 { /* process alias and routing info for mail */
72 char aaa[300], bbb[300];
74 lprintf(9, "alias() called for <%s>\n", name);
76 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
78 fp = fopen("network/mail.aliases", "r");
80 fp = fopen("/dev/null", "r");
85 while (fgets(aaa, sizeof aaa, fp) != NULL) {
86 while (isspace(name[0]))
87 strcpy(name, &name[1]);
88 aaa[strlen(aaa) - 1] = 0;
90 for (a = 0; a < strlen(aaa); ++a) {
92 strcpy(bbb, &aaa[a + 1]);
96 if (!strcasecmp(name, aaa))
100 lprintf(7, "Mail is being forwarded to %s\n", name);
102 /* determine local or remote type, see citadel.h */
103 for (a = 0; a < strlen(name); ++a)
105 return (MES_INTERNET);
106 for (a = 0; a < strlen(name); ++a)
108 for (b = a; b < strlen(name); ++b)
110 return (MES_INTERNET);
112 for (a = 0; a < strlen(name); ++a)
116 lprintf(7, "Too many @'s in address\n");
120 for (a = 0; a < strlen(name); ++a)
122 strcpy(bbb, &name[a + 1]);
124 strcpy(bbb, &bbb[1]);
125 fp = fopen("network/mail.sysinfo", "r");
129 a = getstring(fp, aaa);
130 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
131 a = getstring(fp, aaa);
132 if (!strncmp(aaa, "use ", 4)) {
133 strcpy(bbb, &aaa[4]);
138 if (!strncmp(aaa, "uum", 3)) {
140 for (a = 0; a < strlen(bbb); ++a) {
146 while (bbb[strlen(bbb) - 1] == '_')
147 bbb[strlen(bbb) - 1] = 0;
148 sprintf(name, &aaa[4], bbb);
149 return (MES_INTERNET);
151 if (!strncmp(aaa, "bin", 3)) {
154 while (aaa[strlen(aaa) - 1] != '@')
155 aaa[strlen(aaa) - 1] = 0;
156 aaa[strlen(aaa) - 1] = 0;
157 while (aaa[strlen(aaa) - 1] == ' ')
158 aaa[strlen(aaa) - 1] = 0;
159 while (bbb[0] != '@')
160 strcpy(bbb, &bbb[1]);
161 strcpy(bbb, &bbb[1]);
162 while (bbb[0] == ' ')
163 strcpy(bbb, &bbb[1]);
164 sprintf(name, "%s @%s", aaa, bbb);
177 fp = fopen("citadel.control", "r");
178 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
184 void simple_listing(long msgnum)
186 cprintf("%ld\n", msgnum);
191 * API function to perform an operation for each qualifying message in the
194 void CtdlForEachMessage(int mode, long ref,
196 void (*CallBack) (long msgnum))
201 struct cdbdata *cdbfr;
202 long *msglist = NULL;
205 struct SuppMsgInfo smi;
207 /* Learn about the user and room in question */
209 getuser(&CC->usersupp, CC->curr_user);
210 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
212 /* Load the message list */
213 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
215 msglist = mallok(cdbfr->len);
216 memcpy(msglist, cdbfr->ptr, cdbfr->len);
217 num_msgs = cdbfr->len / sizeof(long);
220 return; /* No messages at all? No further action. */
224 /* If the caller is looking for a specific MIME type, then filter
225 * out all messages which are not of the type requested.
228 if (content_type != NULL)
229 if (strlen(content_type) > 0)
230 for (a = 0; a < num_msgs; ++a) {
231 GetSuppMsgInfo(&smi, msglist[a]);
232 if (strcasecmp(smi.smi_content_type, content_type)) {
237 num_msgs = sort_msglist(msglist, num_msgs);
240 * Now iterate through the message list, according to the
241 * criteria supplied by the caller.
244 for (a = 0; a < num_msgs; ++a) {
245 thismsg = msglist[a];
250 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
251 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
252 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
253 && (CC->usersupp.flags & US_LASTOLD))
254 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
255 || ((mode == MSGS_FIRST) && (a < ref))
256 || ((mode == MSGS_GT) && (thismsg > ref))
262 phree(msglist); /* Clean up */
268 * cmd_msgs() - get list of message #'s in this room
269 * implements the MSGS server command using CtdlForEachMessage()
271 void cmd_msgs(char *cmdbuf)
277 extract(which, cmdbuf, 0);
278 cm_ref = extract_int(cmdbuf, 1);
282 if (!strncasecmp(which, "OLD", 3))
284 else if (!strncasecmp(which, "NEW", 3))
286 else if (!strncasecmp(which, "FIRST", 5))
288 else if (!strncasecmp(which, "LAST", 4))
290 else if (!strncasecmp(which, "GT", 2))
293 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
294 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
297 cprintf("%d Message list...\n", LISTING_FOLLOWS);
298 CtdlForEachMessage(mode, cm_ref, NULL, simple_listing);
306 * help_subst() - support routine for help file viewer
308 void help_subst(char *strbuf, char *source, char *dest)
313 while (p = pattern2(strbuf, source), (p >= 0)) {
314 strcpy(workbuf, &strbuf[p + strlen(source)]);
315 strcpy(&strbuf[p], dest);
316 strcat(strbuf, workbuf);
321 void do_help_subst(char *buffer)
325 help_subst(buffer, "^nodename", config.c_nodename);
326 help_subst(buffer, "^humannode", config.c_humannode);
327 help_subst(buffer, "^fqdn", config.c_fqdn);
328 help_subst(buffer, "^username", CC->usersupp.fullname);
329 sprintf(buf2, "%ld", CC->usersupp.usernum);
330 help_subst(buffer, "^usernum", buf2);
331 help_subst(buffer, "^sysadm", config.c_sysadm);
332 help_subst(buffer, "^variantname", CITADEL);
333 sprintf(buf2, "%d", config.c_maxsessions);
334 help_subst(buffer, "^maxsessions", buf2);
340 * memfmout() - Citadel text formatter and paginator.
341 * Although the original purpose of this routine was to format
342 * text to the reader's screen width, all we're really using it
343 * for here is to format text out to 80 columns before sending it
344 * to the client. The client software may reformat it again.
346 void memfmout(int width, char *mptr, char subst)
347 /* screen width to use */
348 /* where are we going to get our text from? */
349 /* nonzero if we should use hypertext mode */
361 c = 1; /* c is the current pos */
364 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
366 buffer[strlen(buffer) + 1] = 0;
367 buffer[strlen(buffer)] = ch;
370 if (buffer[0] == '^')
371 do_help_subst(buffer);
373 buffer[strlen(buffer) + 1] = 0;
375 strcpy(buffer, &buffer[1]);
385 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
387 if (((old == 13) || (old == 10)) && (isspace(real))) {
395 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
396 cprintf("\n%s", aaa);
405 if ((strlen(aaa) + c) > (width - 5)) {
415 if ((ch == 13) || (ch == 10)) {
416 cprintf("%s\n", aaa);
423 FMTEND: cprintf("%s\n", aaa);
429 * Callback function for mime parser that simply lists the part
431 void list_this_part(char *name, char *filename, char *partnum, char *disp,
432 void *content, char *cbtype, size_t length)
435 cprintf("part=%s|%s|%s|%s|%s|%d\n",
436 name, filename, partnum, disp, cbtype, length);
441 * Callback function for mime parser that wants to display text
443 void fixed_output(char *name, char *filename, char *partnum, char *disp,
444 void *content, char *cbtype, size_t length)
448 if (!strcasecmp(cbtype, "multipart/alternative")) {
449 strcpy(ma->prefix, partnum);
450 strcat(ma->prefix, ".");
456 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
458 && (ma->did_print == 1) ) {
459 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
465 if (!strcasecmp(cbtype, "text/plain")) {
466 client_write(content, length);
468 else if (!strcasecmp(cbtype, "text/html")) {
469 ptr = html_to_ascii(content, 80, 0);
470 client_write(ptr, strlen(ptr));
473 else if (strncasecmp(cbtype, "multipart/", 10)) {
474 cprintf("Part %s: %s (%s) (%d bytes)\n",
475 partnum, filename, cbtype, length);
481 * Callback function for mime parser that opens a section for downloading
483 void mime_download(char *name, char *filename, char *partnum, char *disp,
484 void *content, char *cbtype, size_t length)
487 /* Silently go away if there's already a download open... */
488 if (CC->download_fp != NULL)
491 /* ...or if this is not the desired section */
492 if (strcasecmp(desired_section, partnum))
495 CC->download_fp = tmpfile();
496 if (CC->download_fp == NULL)
499 fwrite(content, length, 1, CC->download_fp);
500 fflush(CC->download_fp);
501 rewind(CC->download_fp);
503 OpenCmdResult(filename, cbtype);
509 * Load a message from disk into memory.
510 * This is used by output_message() and other fetch functions.
512 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
513 * using the CtdlMessageFree() function.
515 struct CtdlMessage *CtdlFetchMessage(long msgnum)
517 struct cdbdata *dmsgtext;
518 struct CtdlMessage *ret = NULL;
521 CIT_UBYTE field_header;
525 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
526 if (dmsgtext == NULL) {
527 lprintf(9, "CtdlFetchMessage(%ld) failed.\n");
530 mptr = dmsgtext->ptr;
532 /* Parse the three bytes that begin EVERY message on disk.
533 * The first is always 0xFF, the on-disk magic number.
534 * The second is the anonymous/public type byte.
535 * The third is the format type byte (vari, fixed, or MIME).
539 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
543 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
544 memset(ret, 0, sizeof(struct CtdlMessage));
546 ret->cm_magic = CTDLMESSAGE_MAGIC;
547 ret->cm_anon_type = *mptr++; /* Anon type byte */
548 ret->cm_format_type = *mptr++; /* Format type byte */
551 * The rest is zero or more arbitrary fields. Load them in.
552 * We're done when we encounter either a zero-length field or
553 * have just processed the 'M' (message text) field.
556 field_length = strlen(mptr);
557 if (field_length == 0)
559 field_header = *mptr++;
560 ret->cm_fields[field_header] = mallok(field_length);
561 strcpy(ret->cm_fields[field_header], mptr);
563 while (*mptr++ != 0); /* advance to next field */
565 } while ((field_length > 0) && (field_header != 'M'));
573 * Returns 1 if the supplied pointer points to a valid Citadel message.
574 * If the pointer is NULL or the magic number check fails, returns 0.
576 int is_valid_message(struct CtdlMessage *msg) {
579 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
580 lprintf(3, "is_valid_message() -- self-check failed\n");
588 * 'Destructor' for struct CtdlMessage
590 void CtdlFreeMessage(struct CtdlMessage *msg)
594 if (is_valid_message(msg) == 0) return;
596 for (i = 0; i < 256; ++i)
597 if (msg->cm_fields[i] != NULL)
598 phree(msg->cm_fields[i]);
600 msg->cm_magic = 0; /* just in case */
607 * Get a message off disk. (return value is the message's timestamp)
610 void output_message(char *msgid, int mode, int headers_only)
618 struct CtdlMessage *TheMessage = NULL;
622 /* buffers needed for RFC822 translation */
630 msg_num = atol(msgid);
632 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
633 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
637 /* FIX ... small security issue
638 * We need to check to make sure the requested message is actually
639 * in the current room, and set msg_ok to 1 only if it is. This
640 * functionality is currently missing because I'm in a hurry to replace
641 * broken production code with nonbroken pre-beta code. :( -- ajc
644 cprintf("%d Message %ld is not in this room.\n",
651 * Fetch the message from disk
653 TheMessage = CtdlFetchMessage(msg_num);
654 if (TheMessage == NULL) {
655 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
659 /* Are we downloading a MIME component? */
660 if (mode == MT_DOWNLOAD) {
661 if (TheMessage->cm_format_type != 4) {
662 cprintf("%d This is not a MIME message.\n",
664 } else if (CC->download_fp != NULL) {
665 cprintf("%d You already have a download open.\n",
668 /* Parse the message text component */
669 mptr = TheMessage->cm_fields['M'];
670 mime_parser(mptr, NULL, *mime_download);
671 /* If there's no file open by this time, the requested
672 * section wasn't found, so print an error
674 if (CC->download_fp == NULL) {
675 cprintf("%d Section %s not found.\n",
676 ERROR + FILE_NOT_FOUND,
680 CtdlFreeMessage(TheMessage);
684 /* now for the user-mode message reading loops */
685 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
687 /* Tell the client which format type we're using. If this is a
688 * MIME message, *lie* about it and tell the user it's fixed-format.
690 if (mode == MT_CITADEL) {
691 if (TheMessage->cm_format_type == 4)
694 cprintf("type=%d\n", TheMessage->cm_format_type);
697 /* nhdr=yes means that we're only displaying headers, no body */
698 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
699 cprintf("nhdr=yes\n");
702 /* begin header processing loop for Citadel message format */
704 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
706 if (TheMessage->cm_fields['P']) {
707 cprintf("path=%s\n", TheMessage->cm_fields['P']);
709 if (TheMessage->cm_fields['I']) {
710 cprintf("msgn=%s\n", TheMessage->cm_fields['I']);
712 if (TheMessage->cm_fields['T']) {
713 cprintf("time=%s\n", TheMessage->cm_fields['T']);
715 if (TheMessage->cm_fields['A']) {
716 strcpy(buf, TheMessage->cm_fields['A']);
717 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
718 if (TheMessage->cm_anon_type == MES_ANON)
719 cprintf("from=****");
720 else if (TheMessage->cm_anon_type == MES_AN2)
721 cprintf("from=anonymous");
723 cprintf("from=%s", buf);
725 && ((TheMessage->cm_anon_type == MES_ANON)
726 || (TheMessage->cm_anon_type == MES_AN2))) {
727 cprintf(" [%s]", buf);
731 if (TheMessage->cm_fields['O']) {
732 cprintf("room=%s\n", TheMessage->cm_fields['O']);
734 if (TheMessage->cm_fields['N']) {
735 cprintf("node=%s\n", TheMessage->cm_fields['N']);
737 if (TheMessage->cm_fields['H']) {
738 cprintf("hnod=%s\n", TheMessage->cm_fields['H']);
740 if (TheMessage->cm_fields['R']) {
741 cprintf("rcpt=%s\n", TheMessage->cm_fields['R']);
743 if (TheMessage->cm_fields['U']) {
744 cprintf("subj=%s\n", TheMessage->cm_fields['U']);
748 /* begin header processing loop for RFC822 transfer format */
752 strcpy(snode, NODENAME);
753 strcpy(lnode, HUMANNODE);
754 if (mode == MT_RFC822) {
755 for (i = 0; i < 256; ++i) {
756 if (TheMessage->cm_fields[i]) {
757 mptr = TheMessage->cm_fields[i];
761 } else if (i == 'P') {
762 cprintf("Path: %s\n", mptr);
763 for (a = 0; a < strlen(mptr); ++a) {
764 if (mptr[a] == '!') {
765 strcpy(mptr, &mptr[a + 1]);
771 cprintf("Subject: %s\n", mptr);
777 cprintf("X-Citadel-Room: %s\n", mptr);
781 cprintf("To: %s\n", mptr);
784 cprintf("Date: %s", asctime(localtime(&xtime)));
790 if (mode == MT_RFC822) {
791 if (!strcasecmp(snode, NODENAME)) {
794 cprintf("Message-ID: <%s@%s>\n", mid, snode);
795 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
796 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
797 cprintf("Organization: %s\n", lnode);
800 /* end header processing loop ... at this point, we're in the text */
802 mptr = TheMessage->cm_fields['M'];
804 /* Tell the client about the MIME parts in this message */
805 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
806 if (mode == MT_CITADEL) {
807 mime_parser(mptr, NULL, *list_this_part);
809 else if (mode == MT_MIME) { /* list parts only */
810 mime_parser(mptr, NULL, *list_this_part);
812 CtdlFreeMessage(TheMessage);
819 CtdlFreeMessage(TheMessage);
823 /* signify start of msg text */
824 if (mode == MT_CITADEL)
826 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
829 /* If the format type on disk is 1 (fixed-format), then we want
830 * everything to be output completely literally ... regardless of
831 * what message transfer format is in use.
833 if (TheMessage->cm_format_type == 1) {
835 while (ch = *mptr++, ch > 0) {
838 if ((ch == 10) || (strlen(buf) > 250)) {
839 cprintf("%s\n", buf);
842 buf[strlen(buf) + 1] = 0;
843 buf[strlen(buf)] = ch;
847 cprintf("%s\n", buf);
850 /* If the message on disk is format 0 (Citadel vari-format), we
851 * output using the formatter at 80 columns. This is the final output
852 * form if the transfer format is RFC822, but if the transfer format
853 * is Citadel proprietary, it'll still work, because the indentation
854 * for new paragraphs is correct and the client will reformat the
855 * message to the reader's screen width.
857 if (TheMessage->cm_format_type == 0) {
858 memfmout(80, mptr, 0);
861 /* If the message on disk is format 4 (MIME), we've gotta hand it
862 * off to the MIME parser. The client has already been told that
863 * this message is format 1 (fixed format), so the callback function
864 * we use will display those parts as-is.
866 if (TheMessage->cm_format_type == 4) {
867 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
868 memset(ma, 0, sizeof(struct ma_info));
869 mime_parser(mptr, NULL, *fixed_output);
874 CtdlFreeMessage(TheMessage);
881 * display a message (mode 0 - Citadel proprietary)
883 void cmd_msg0(char *cmdbuf)
886 int headers_only = 0;
888 extract(msgid, cmdbuf, 0);
889 headers_only = extract_int(cmdbuf, 1);
891 output_message(msgid, MT_CITADEL, headers_only);
897 * display a message (mode 2 - RFC822)
899 void cmd_msg2(char *cmdbuf)
902 int headers_only = 0;
904 extract(msgid, cmdbuf, 0);
905 headers_only = extract_int(cmdbuf, 1);
907 output_message(msgid, MT_RFC822, headers_only);
913 * display a message (mode 3 - IGnet raw format - internal programs only)
915 void cmd_msg3(char *cmdbuf)
918 struct CtdlMessage *msg;
919 struct sermsgret smr;
921 if (CC->internal_pgm == 0) {
922 cprintf("%d This command is for internal programs only.\n",
927 msgnum = extract_long(cmdbuf, 0);
928 msg = CtdlFetchMessage(msgnum);
930 cprintf("%d Message %ld not found.\n",
935 serialize_message(&smr, msg);
936 CtdlFreeMessage(msg);
939 cprintf("%d Unable to serialize message\n",
940 ERROR+INTERNAL_ERROR);
944 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
945 client_write(smr.ser, smr.len);
952 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
954 void cmd_msg4(char *cmdbuf)
958 extract(msgid, cmdbuf, 0);
960 output_message(msgid, MT_MIME, 0);
964 * Open a component of a MIME message as a download file
966 void cmd_opna(char *cmdbuf)
970 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
972 extract(msgid, cmdbuf, 0);
973 extract(desired_section, cmdbuf, 1);
975 output_message(msgid, MT_DOWNLOAD, 0);
979 * Message base operation to send a message to the master file
980 * (returns new message number)
982 * This is the back end for CtdlSaveMsg() and should not be directly
983 * called by server-side modules.
986 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
987 int generate_id, /* generate 'I' field? */
988 FILE *save_a_copy) /* save a copy to disk? */
993 struct sermsgret smr;
995 /* Get a new message number */
996 newmsgid = get_new_message_number();
997 sprintf(msgidbuf, "%ld", newmsgid);
1000 msg->cm_fields['I'] = strdoop(msgidbuf);
1003 serialize_message(&smr, msg);
1006 cprintf("%d Unable to serialize message\n",
1007 ERROR+INTERNAL_ERROR);
1011 /* Write our little bundle of joy into the message base */
1012 begin_critical_section(S_MSGMAIN);
1013 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1014 smr.ser, smr.len) < 0) {
1015 lprintf(2, "Can't store message\n");
1020 end_critical_section(S_MSGMAIN);
1022 /* If the caller specified that a copy should be saved to a particular
1023 * file handle, do that now too.
1025 if (save_a_copy != NULL) {
1026 fwrite(smr.ser, smr.len, 1, save_a_copy);
1029 /* Free the memory we used for the serialized message */
1032 /* Return the *local* message ID to the caller
1033 * (even if we're storing an incoming network message)
1041 * Serialize a struct CtdlMessage into the format used on disk and network.
1043 * This function returns a "struct sermsgret" (defined in msgbase.h) which
1044 * contains the length of the serialized message and a pointer to the
1045 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1047 void serialize_message(struct sermsgret *ret, /* return values */
1048 struct CtdlMessage *msg) /* unserialized msg */
1052 static char *forder = FORDER;
1054 lprintf(9, "serialize_message() called\n");
1056 if (is_valid_message(msg) == 0) return; /* self check */
1058 lprintf(9, "magic number check OK.\n");
1061 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1062 ret->len = ret->len +
1063 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1065 lprintf(9, "message is %d bytes\n", ret->len);
1067 lprintf(9, "calling malloc\n");
1068 ret->ser = mallok(ret->len);
1069 if (ret->ser == NULL) {
1075 ret->ser[1] = msg->cm_anon_type;
1076 ret->ser[2] = msg->cm_format_type;
1078 lprintf(9, "stuff\n");
1080 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1081 ret->ser[wlen++] = (char)forder[i];
1082 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1083 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1085 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1094 * Save a message to disk
1096 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1097 char *rec, /* Recipient (mail) */
1098 char *force, /* force a particular room? */
1099 int mailtype, /* local or remote type */
1100 int generate_id) /* 1 = generate 'I' field */
1103 char hold_rm[ROOMNAMELEN];
1104 char actual_rm[ROOMNAMELEN];
1105 char force_room[ROOMNAMELEN];
1106 char content_type[256]; /* We have to learn this */
1107 char recipient[256];
1110 struct usersupp userbuf;
1112 int successful_local_recipients = 0;
1113 struct quickroom qtemp;
1114 struct SuppMsgInfo smi;
1115 FILE *network_fp = NULL;
1116 static int seqnum = 1;
1118 lprintf(9, "CtdlSaveMsg() called\n");
1119 if (is_valid_message(msg) == 0) return; /* self check */
1121 /* If this message has no timestamp, we take the liberty of
1122 * giving it one, right now.
1124 if (msg->cm_fields['T'] == NULL) {
1125 sprintf(aaa, "%ld", time(NULL));
1126 msg->cm_fields['T'] = strdoop(aaa);
1129 /* If this message has no path, we generate one.
1131 if (msg->cm_fields['P'] == NULL) {
1132 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1133 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1134 if (isspace(msg->cm_fields['P'][a])) {
1135 msg->cm_fields['P'][a] = ' ';
1140 strcpy(force_room, force);
1142 /* Strip non-printable characters out of the recipient name */
1143 strcpy(recipient, rec);
1144 for (a = 0; a < strlen(recipient); ++a)
1145 if (!isprint(recipient[a]))
1146 strcpy(&recipient[a], &recipient[a + 1]);
1148 /* Learn about what's inside, because it's what's inside that counts */
1150 switch (msg->cm_format_type) {
1152 strcpy(content_type, "text/x-citadel-variformat");
1155 strcpy(content_type, "text/plain");
1158 strcpy(content_type, "text/plain");
1159 /* advance past header fields */
1160 mptr = msg->cm_fields['M'];
1163 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1164 safestrncpy(content_type, mptr,
1165 sizeof(content_type));
1166 strcpy(content_type, &content_type[14]);
1167 for (a = 0; a < strlen(content_type); ++a)
1168 if ((content_type[a] == ';')
1169 || (content_type[a] == ' ')
1170 || (content_type[a] == 13)
1171 || (content_type[a] == 10))
1172 content_type[a] = 0;
1179 /* Network mail - send a copy to the network program. */
1180 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1181 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1182 (long) getpid(), CC->cs_pid, ++seqnum);
1183 lprintf(9, "Saving a copy to %s\n", aaa);
1184 network_fp = fopen(aaa, "ab+");
1185 if (network_fp == NULL)
1186 lprintf(2, "ERROR: %s\n", strerror(errno));
1189 /* Save it to disk */
1190 newmsgid = send_message(msg, generate_id, network_fp);
1191 if (network_fp != NULL) {
1193 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1198 strcpy(actual_rm, CC->quickroom.QRname);
1199 strcpy(hold_rm, "");
1201 /* If this is being done by the networker delivering a private
1202 * message, we want to BYPASS saving the sender's copy (because there
1203 * is no local sender; it would otherwise go to the Trashcan).
1205 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1206 /* If the user is a twit, move to the twit room for posting */
1208 if (CC->usersupp.axlevel == 2) {
1209 strcpy(hold_rm, actual_rm);
1210 strcpy(actual_rm, config.c_twitroom);
1212 /* ...or if this message is destined for Aide> then go there. */
1213 if (strlen(force_room) > 0) {
1214 strcpy(hold_rm, actual_rm);
1215 strcpy(actual_rm, force_room);
1217 /* This call to usergoto() changes rooms if necessary. It also
1218 * causes the latest message list to be read into memory.
1220 usergoto(actual_rm, 0);
1222 /* read in the quickroom record, obtaining a lock... */
1223 lgetroom(&CC->quickroom, actual_rm);
1225 /* Fix an obscure bug */
1226 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1227 CC->quickroom.QRflags =
1228 CC->quickroom.QRflags & ~QR_MAILBOX;
1230 /* Add the message pointer to the room */
1231 CC->quickroom.QRhighest =
1232 AddMessageToRoom(&CC->quickroom, newmsgid);
1234 /* update quickroom */
1235 lputroom(&CC->quickroom);
1236 ++successful_local_recipients;
1239 /* Bump this user's messages posted counter. */
1240 lgetuser(&CC->usersupp, CC->curr_user);
1241 CC->usersupp.posted = CC->usersupp.posted + 1;
1242 lputuser(&CC->usersupp);
1244 /* If this is private, local mail, make a copy in the
1245 * recipient's mailbox and bump the reference count.
1247 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1248 if (getuser(&userbuf, recipient) == 0) {
1249 MailboxName(actual_rm, &userbuf, MAILROOM);
1250 if (lgetroom(&qtemp, actual_rm) == 0) {
1252 AddMessageToRoom(&qtemp, newmsgid);
1254 ++successful_local_recipients;
1258 /* If we've posted in a room other than the current room, then we
1259 * have to now go back to the current room...
1261 if (strlen(hold_rm) > 0) {
1262 usergoto(hold_rm, 0);
1265 /* Write a supplemental message info record. This doesn't have to
1266 * be a critical section because nobody else knows about this message
1269 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1270 smi.smi_msgnum = newmsgid;
1271 smi.smi_refcount = successful_local_recipients;
1272 safestrncpy(smi.smi_content_type, content_type, 64);
1273 PutSuppMsgInfo(&smi);
1279 * Convenience function for generating small administrative messages.
1281 void quickie_message(char *from, char *room, char *text)
1283 struct CtdlMessage *msg;
1285 msg = mallok(sizeof(struct CtdlMessage));
1286 memset(msg, 0, sizeof(struct CtdlMessage));
1287 msg->cm_magic = CTDLMESSAGE_MAGIC;
1288 msg->cm_anon_type = MES_NORMAL;
1289 msg->cm_format_type = 0;
1290 msg->cm_fields['A'] = strdoop(from);
1291 msg->cm_fields['O'] = strdoop(room);
1292 msg->cm_fields['N'] = strdoop(NODENAME);
1293 msg->cm_fields['M'] = strdoop(text);
1295 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1296 CtdlFreeMessage(msg);
1297 syslog(LOG_NOTICE, text);
1302 * Build a binary message to be saved on disk.
1305 struct CtdlMessage *make_message(
1306 struct usersupp *author, /* author's usersupp structure */
1307 char *recipient, /* NULL if it's not mail */
1308 char *room, /* room where it's going */
1309 int type, /* see MES_ types in header file */
1310 int net_type, /* see MES_ types in header file */
1311 int format_type, /* local or remote (see citadel.h) */
1312 char *fake_name) /* who we're masquerading as */
1318 size_t message_len = 0;
1319 size_t buffer_len = 0;
1321 struct CtdlMessage *msg;
1323 msg = mallok(sizeof(struct CtdlMessage));
1324 memset(msg, 0, sizeof(struct CtdlMessage));
1325 msg->cm_magic = CTDLMESSAGE_MAGIC;
1326 msg->cm_anon_type = type;
1327 msg->cm_format_type = format_type;
1329 /* Don't confuse the poor folks if it's not routed mail. */
1330 strcpy(dest_node, "");
1332 /* If net_type is MES_BINARY, split out the destination node. */
1333 if (net_type == MES_BINARY) {
1334 strcpy(dest_node, NODENAME);
1335 for (a = 0; a < strlen(recipient); ++a) {
1336 if (recipient[a] == '@') {
1338 strcpy(dest_node, &recipient[a + 1]);
1343 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1344 if (net_type == MES_INTERNET) {
1345 strcpy(dest_node, "internet");
1348 while (isspace(recipient[strlen(recipient) - 1]))
1349 recipient[strlen(recipient) - 1] = 0;
1351 sprintf(buf, "cit%ld", author->usernum); /* Path */
1352 msg->cm_fields['P'] = strdoop(buf);
1354 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1355 msg->cm_fields['T'] = strdoop(buf);
1357 if (fake_name[0]) /* author */
1358 msg->cm_fields['A'] = strdoop(fake_name);
1360 msg->cm_fields['A'] = strdoop(author->fullname);
1362 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1363 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1365 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1367 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1368 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1370 if (recipient[0] != 0)
1371 msg->cm_fields['R'] = strdoop(recipient);
1372 if (dest_node[0] != 0)
1373 msg->cm_fields['D'] = strdoop(dest_node);
1375 msg->cm_fields['M'] = mallok(4096);
1376 if (msg->cm_fields['M'] == NULL) {
1377 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1381 msg->cm_fields['M'][0] = 0;
1385 /* read in the lines of message text one by one */
1386 while (client_gets(buf), strcmp(buf, "000")) {
1388 /* augment the buffer if we have to */
1389 if ((message_len + strlen(buf) + 2) > buffer_len) {
1390 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1391 if (ptr == NULL) { /* flush if can't allocate */
1392 while (client_gets(buf), strcmp(buf, "000")) ;;
1395 buffer_len = (buffer_len * 2);
1396 msg->cm_fields['M'] = ptr;
1400 strcat(msg->cm_fields['M'], buf);
1401 strcat(msg->cm_fields['M'], "\n");
1403 /* if we've hit the max msg length, flush the rest */
1404 if (message_len >= config.c_maxmsglen) {
1405 while (client_gets(buf), strcmp(buf, "000")) ;;
1418 * message entry - mode 0 (normal)
1420 void cmd_ent0(char *entargs)
1423 char recipient[256];
1425 int format_type = 0;
1426 char newusername[256];
1427 struct CtdlMessage *msg;
1431 struct usersupp tempUS;
1434 post = extract_int(entargs, 0);
1435 extract(recipient, entargs, 1);
1436 anon_flag = extract_int(entargs, 2);
1437 format_type = extract_int(entargs, 3);
1439 /* first check to make sure the request is valid. */
1441 if (!(CC->logged_in)) {
1442 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1445 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1446 cprintf("%d Need to be validated to enter ",
1447 ERROR + HIGHER_ACCESS_REQUIRED);
1448 cprintf("(except in %s> to sysop)\n", MAILROOM);
1451 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1452 cprintf("%d Need net privileges to enter here.\n",
1453 ERROR + HIGHER_ACCESS_REQUIRED);
1456 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1457 cprintf("%d Sorry, this is a read-only room.\n",
1458 ERROR + HIGHER_ACCESS_REQUIRED);
1465 if (CC->usersupp.axlevel < 6) {
1466 cprintf("%d You don't have permission to masquerade.\n",
1467 ERROR + HIGHER_ACCESS_REQUIRED);
1470 extract(newusername, entargs, 4);
1471 memset(CC->fake_postname, 0, 32);
1472 strcpy(CC->fake_postname, newusername);
1473 cprintf("%d Ok\n", OK);
1476 CC->cs_flags |= CS_POSTING;
1479 if (CC->quickroom.QRflags & QR_MAILBOX) {
1480 if (CC->usersupp.axlevel >= 2) {
1481 strcpy(buf, recipient);
1483 strcpy(buf, "sysop");
1484 e = alias(buf); /* alias and mail type */
1485 if ((buf[0] == 0) || (e == MES_ERROR)) {
1486 cprintf("%d Unknown address - cannot send message.\n",
1487 ERROR + NO_SUCH_USER);
1490 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1491 cprintf("%d Net privileges required for network mail.\n",
1492 ERROR + HIGHER_ACCESS_REQUIRED);
1495 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1496 && ((CC->usersupp.flags & US_INTERNET) == 0)
1497 && (!CC->internal_pgm)) {
1498 cprintf("%d You don't have access to Internet mail.\n",
1499 ERROR + HIGHER_ACCESS_REQUIRED);
1502 if (!strcasecmp(buf, "sysop")) {
1507 goto SKFALL; /* don't search local file */
1508 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1509 cprintf("%d Can't send mail to yourself!\n",
1510 ERROR + NO_SUCH_USER);
1513 /* Check to make sure the user exists; also get the correct
1514 * upper/lower casing of the name.
1516 a = getuser(&tempUS, buf);
1518 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1521 strcpy(buf, tempUS.fullname);
1524 SKFALL: b = MES_NORMAL;
1525 if (CC->quickroom.QRflags & QR_ANONONLY)
1527 if (CC->quickroom.QRflags & QR_ANONOPT) {
1531 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1534 /* If we're only checking the validity of the request, return
1535 * success without creating the message.
1538 cprintf("%d %s\n", OK, buf);
1542 cprintf("%d send message\n", SEND_LISTING);
1544 /* Read in the message from the client. */
1545 if (CC->fake_postname[0])
1546 msg = make_message(&CC->usersupp, buf,
1547 CC->quickroom.QRname, b, e, format_type,
1549 else if (CC->fake_username[0])
1550 msg = make_message(&CC->usersupp, buf,
1551 CC->quickroom.QRname, b, e, format_type,
1554 msg = make_message(&CC->usersupp, buf,
1555 CC->quickroom.QRname, b, e, format_type, "");
1558 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1559 CtdlFreeMessage(msg);
1560 CC->fake_postname[0] = '\0';
1567 * message entry - mode 3 (raw)
1569 void cmd_ent3(char *entargs)
1575 struct usersupp tempUS;
1580 if (CC->internal_pgm == 0) {
1581 cprintf("%d This command is for internal programs only.\n",
1586 /* See if there's a recipient, but make sure it's a real one */
1587 extract(recp, entargs, 1);
1588 for (a = 0; a < strlen(recp); ++a)
1589 if (!isprint(recp[a]))
1590 strcpy(&recp[a], &recp[a + 1]);
1591 while (isspace(recp[0]))
1592 strcpy(recp, &recp[1]);
1593 while (isspace(recp[strlen(recp) - 1]))
1594 recp[strlen(recp) - 1] = 0;
1596 /* If we're in Mail, check the recipient */
1597 if (strlen(recp) > 0) {
1598 e = alias(recp); /* alias and mail type */
1599 if ((recp[0] == 0) || (e == MES_ERROR)) {
1600 cprintf("%d Unknown address - cannot send message.\n",
1601 ERROR + NO_SUCH_USER);
1604 if (e == MES_LOCAL) {
1605 a = getuser(&tempUS, recp);
1607 cprintf("%d No such user.\n",
1608 ERROR + NO_SUCH_USER);
1613 /* At this point, message has been approved. */
1614 if (extract_int(entargs, 0) == 0) {
1615 cprintf("%d OK to send\n", OK);
1618 /* open a temp file to hold the message */
1619 fp = fopen(CC->temp, "wb");
1621 cprintf("%d Cannot open %s: %s\n",
1622 ERROR + INTERNAL_ERROR,
1623 CC->temp, strerror(errno));
1626 msglen = extract_long(entargs, 2);
1627 cprintf("%d %ld\n", SEND_BINARY, msglen);
1628 while (msglen > 0L) {
1629 bloklen = ((msglen >= 255L) ? 255 : msglen);
1630 client_read(buf, (int) bloklen);
1631 fwrite(buf, (int) bloklen, 1, fp);
1632 msglen = msglen - bloklen;
1636 save_message(CC->temp, recp, "", e, 0);
1641 * API function to delete messages which match a set of criteria
1642 * (returns the actual number of messages deleted)
1643 * FIX ... still need to implement delete by content type
1645 int CtdlDeleteMessages(char *room_name, /* which room */
1646 long dmsgnum, /* or "0" for any */
1647 char *content_type /* or NULL for any */
1651 struct quickroom qrbuf;
1652 struct cdbdata *cdbfr;
1653 long *msglist = NULL;
1656 int num_deleted = 0;
1658 struct SuppMsgInfo smi;
1660 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1661 room_name, dmsgnum, content_type);
1663 /* get room record, obtaining a lock... */
1664 if (lgetroom(&qrbuf, room_name) != 0) {
1665 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1667 return (0); /* room not found */
1669 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1671 if (cdbfr != NULL) {
1672 msglist = mallok(cdbfr->len);
1673 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1674 num_msgs = cdbfr->len / sizeof(long);
1678 for (i = 0; i < num_msgs; ++i) {
1681 /* Set/clear a bit for each criterion */
1683 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1684 delete_this |= 0x01;
1686 if (content_type == NULL) {
1687 delete_this |= 0x02;
1689 GetSuppMsgInfo(&smi, msglist[i]);
1690 if (!strcasecmp(smi.smi_content_type,
1692 delete_this |= 0x02;
1696 /* Delete message only if all bits are set */
1697 if (delete_this == 0x03) {
1698 AdjRefCount(msglist[i], -1);
1704 num_msgs = sort_msglist(msglist, num_msgs);
1705 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1706 msglist, (num_msgs * sizeof(long)));
1708 qrbuf.QRhighest = msglist[num_msgs - 1];
1712 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1713 return (num_deleted);
1719 * Delete message from current room
1721 void cmd_dele(char *delstr)
1726 getuser(&CC->usersupp, CC->curr_user);
1727 if ((CC->usersupp.axlevel < 6)
1728 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1729 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1730 cprintf("%d Higher access required.\n",
1731 ERROR + HIGHER_ACCESS_REQUIRED);
1734 delnum = extract_long(delstr, 0);
1736 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1739 cprintf("%d %d message%s deleted.\n", OK,
1740 num_deleted, ((num_deleted != 1) ? "s" : ""));
1742 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1748 * move a message to another room
1750 void cmd_move(char *args)
1754 struct quickroom qtemp;
1757 num = extract_long(args, 0);
1758 extract(targ, args, 1);
1760 getuser(&CC->usersupp, CC->curr_user);
1761 if ((CC->usersupp.axlevel < 6)
1762 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1763 cprintf("%d Higher access required.\n",
1764 ERROR + HIGHER_ACCESS_REQUIRED);
1767 if (getroom(&qtemp, targ) != 0) {
1768 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1771 /* Bump the reference count, otherwise the message will be deleted
1772 * from disk when we remove it from the source room.
1774 AdjRefCount(num, 1);
1776 /* yank the message out of the current room... */
1777 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
1780 /* put the message into the target room */
1781 lgetroom(&qtemp, targ);
1782 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1784 cprintf("%d Message moved.\n", OK);
1786 AdjRefCount(num, (-1)); /* oops */
1787 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1794 * GetSuppMsgInfo() - Get the supplementary record for a message
1796 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1799 struct cdbdata *cdbsmi;
1802 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1803 smibuf->smi_msgnum = msgnum;
1804 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1806 /* Use the negative of the message number for its supp record index */
1807 TheIndex = (0L - msgnum);
1809 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1810 if (cdbsmi == NULL) {
1811 return; /* record not found; go with defaults */
1813 memcpy(smibuf, cdbsmi->ptr,
1814 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1815 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1822 * PutSuppMsgInfo() - (re)write supplementary record for a message
1824 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1828 /* Use the negative of the message number for its supp record index */
1829 TheIndex = (0L - smibuf->smi_msgnum);
1831 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1832 smibuf->smi_msgnum, smibuf->smi_refcount);
1834 cdb_store(CDB_MSGMAIN,
1835 &TheIndex, sizeof(long),
1836 smibuf, sizeof(struct SuppMsgInfo));
1841 * AdjRefCount - change the reference count for a message;
1842 * delete the message if it reaches zero
1844 void AdjRefCount(long msgnum, int incr)
1847 struct SuppMsgInfo smi;
1850 /* This is a *tight* critical section; please keep it that way, as
1851 * it may get called while nested in other critical sections.
1852 * Complicating this any further will surely cause deadlock!
1854 begin_critical_section(S_SUPPMSGMAIN);
1855 GetSuppMsgInfo(&smi, msgnum);
1856 smi.smi_refcount += incr;
1857 PutSuppMsgInfo(&smi);
1858 end_critical_section(S_SUPPMSGMAIN);
1860 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1861 msgnum, smi.smi_refcount);
1863 /* If the reference count is now zero, delete the message
1864 * (and its supplementary record as well).
1866 if (smi.smi_refcount == 0) {
1867 lprintf(9, "Deleting message <%ld>\n", msgnum);
1869 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1870 delnum = (0L - msgnum);
1871 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1876 * Write a generic object to this room
1878 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
1879 char *content_type, /* MIME type of this object */
1880 char *tempfilename, /* Where to fetch it from */
1881 int is_mailbox, /* Private mailbox room? */
1882 int is_binary, /* Is encoding necessary? */
1883 int is_unique /* Del others of this type? */
1888 char filename[PATH_MAX];
1891 struct quickroom qrbuf;
1892 char roomname[ROOMNAMELEN];
1893 struct CtdlMessage *msg;
1896 lprintf(9, "CtdlWriteObject() called\n");
1899 MailboxName(roomname, &CC->usersupp, req_room);
1901 safestrncpy(roomname, req_room, sizeof(roomname));
1903 strcpy(filename, tmpnam(NULL));
1904 fp = fopen(filename, "w");
1908 tempfp = fopen(tempfilename, "r");
1909 if (tempfp == NULL) {
1915 fprintf(fp, "Content-type: %s\n", content_type);
1916 lprintf(9, "Content-type: %s\n", content_type);
1918 if (is_binary == 0) {
1919 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
1920 while (ch = getc(tempfp), ch > 0)
1926 fprintf(fp, "Content-transfer-encoding: base64\n\n");
1929 sprintf(cmdbuf, "./base64 -e <%s >>%s",
1930 tempfilename, filename);
1934 lprintf(9, "Allocating\n");
1935 msg = mallok(sizeof(struct CtdlMessage));
1936 memset(msg, 0, sizeof(struct CtdlMessage));
1937 msg->cm_magic = CTDLMESSAGE_MAGIC;
1938 msg->cm_anon_type = MES_NORMAL;
1939 msg->cm_format_type = 4;
1940 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
1941 msg->cm_fields['O'] = strdoop(roomname);
1942 msg->cm_fields['N'] = strdoop(config.c_nodename);
1943 msg->cm_fields['H'] = strdoop(config.c_humannode);
1945 lprintf(9, "Loading\n");
1946 fp = fopen(filename, "rb");
1947 fseek(fp, 0L, SEEK_END);
1950 msg->cm_fields['M'] = mallok(len);
1951 fread(msg->cm_fields['M'], len, 1, fp);
1955 /* Create the requested room if we have to. */
1956 lprintf(9, "Checking room\n");
1957 if (getroom(&qrbuf, roomname) != 0) {
1958 create_room(roomname, 4, "", 0);
1960 /* If the caller specified this object as unique, delete all
1961 * other objects of this type that are currently in the room.
1964 lprintf(9, "Deleted %d other msgs of this type\n",
1965 CtdlDeleteMessages(roomname, 0L, content_type));
1967 /* Now write the data */
1968 lprintf(9, "Calling CtdlSaveMsg()\n");
1969 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
1970 lprintf(9, "is valid? %d\n",
1971 is_valid_message(msg) );
1972 lprintf(9, "Calling CtdlFreeMsg()\n");
1973 CtdlFreeMessage(msg);
1974 lprintf(9, "Done.\n");