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]);
606 * Get a message off disk. (return value is the message's timestamp)
609 void output_message(char *msgid, int mode, int headers_only)
617 struct CtdlMessage *TheMessage = NULL;
621 /* buffers needed for RFC822 translation */
629 msg_num = atol(msgid);
631 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
632 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
636 /* FIX ... small security issue
637 * We need to check to make sure the requested message is actually
638 * in the current room, and set msg_ok to 1 only if it is. This
639 * functionality is currently missing because I'm in a hurry to replace
640 * broken production code with nonbroken pre-beta code. :( -- ajc
643 cprintf("%d Message %ld is not in this room.\n",
650 * Fetch the message from disk
652 TheMessage = CtdlFetchMessage(msg_num);
653 if (TheMessage == NULL) {
654 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
658 /* Are we downloading a MIME component? */
659 if (mode == MT_DOWNLOAD) {
660 if (TheMessage->cm_format_type != 4) {
661 cprintf("%d This is not a MIME message.\n",
663 } else if (CC->download_fp != NULL) {
664 cprintf("%d You already have a download open.\n",
667 /* Parse the message text component */
668 mptr = TheMessage->cm_fields['M'];
669 mime_parser(mptr, NULL, *mime_download);
670 /* If there's no file open by this time, the requested
671 * section wasn't found, so print an error
673 if (CC->download_fp == NULL) {
674 cprintf("%d Section %s not found.\n",
675 ERROR + FILE_NOT_FOUND,
679 CtdlFreeMessage(TheMessage);
683 /* now for the user-mode message reading loops */
684 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
686 /* Tell the client which format type we're using. If this is a
687 * MIME message, *lie* about it and tell the user it's fixed-format.
689 if (mode == MT_CITADEL) {
690 if (TheMessage->cm_format_type == 4)
693 cprintf("type=%d\n", TheMessage->cm_format_type);
696 /* nhdr=yes means that we're only displaying headers, no body */
697 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
698 cprintf("nhdr=yes\n");
701 /* begin header processing loop for Citadel message format */
703 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
705 if (TheMessage->cm_fields['P']) {
706 cprintf("path=%s\n", TheMessage->cm_fields['P']);
708 if (TheMessage->cm_fields['I']) {
709 cprintf("msgn=%s\n", TheMessage->cm_fields['I']);
711 if (TheMessage->cm_fields['T']) {
712 cprintf("time=%s\n", TheMessage->cm_fields['T']);
714 if (TheMessage->cm_fields['A']) {
715 strcpy(buf, TheMessage->cm_fields['A']);
716 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
717 if (TheMessage->cm_anon_type == MES_ANON)
718 cprintf("from=****");
719 else if (TheMessage->cm_anon_type == MES_AN2)
720 cprintf("from=anonymous");
722 cprintf("from=%s", buf);
724 && ((TheMessage->cm_anon_type == MES_ANON)
725 || (TheMessage->cm_anon_type == MES_AN2))) {
726 cprintf(" [%s]", buf);
730 if (TheMessage->cm_fields['O']) {
731 cprintf("room=%s\n", TheMessage->cm_fields['O']);
733 if (TheMessage->cm_fields['N']) {
734 cprintf("node=%s\n", TheMessage->cm_fields['N']);
736 if (TheMessage->cm_fields['H']) {
737 cprintf("hnod=%s\n", TheMessage->cm_fields['H']);
739 if (TheMessage->cm_fields['R']) {
740 cprintf("rcpt=%s\n", TheMessage->cm_fields['R']);
742 if (TheMessage->cm_fields['U']) {
743 cprintf("subj=%s\n", TheMessage->cm_fields['U']);
747 /* begin header processing loop for RFC822 transfer format */
751 strcpy(snode, NODENAME);
752 strcpy(lnode, HUMANNODE);
753 if (mode == MT_RFC822) {
754 for (i = 0; i < 256; ++i) {
755 if (TheMessage->cm_fields[i]) {
756 mptr = TheMessage->cm_fields[i];
760 } else if (i == 'P') {
761 cprintf("Path: %s\n", mptr);
762 for (a = 0; a < strlen(mptr); ++a) {
763 if (mptr[a] == '!') {
764 strcpy(mptr, &mptr[a + 1]);
770 cprintf("Subject: %s\n", mptr);
776 cprintf("X-Citadel-Room: %s\n", mptr);
780 cprintf("To: %s\n", mptr);
783 cprintf("Date: %s", asctime(localtime(&xtime)));
789 if (mode == MT_RFC822) {
790 if (!strcasecmp(snode, NODENAME)) {
793 cprintf("Message-ID: <%s@%s>\n", mid, snode);
794 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
795 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
796 cprintf("Organization: %s\n", lnode);
799 /* end header processing loop ... at this point, we're in the text */
801 mptr = TheMessage->cm_fields['M'];
803 /* Tell the client about the MIME parts in this message */
804 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
805 if (mode == MT_CITADEL) {
806 mime_parser(mptr, NULL, *list_this_part);
808 else if (mode == MT_MIME) { /* list parts only */
809 mime_parser(mptr, NULL, *list_this_part);
811 CtdlFreeMessage(TheMessage);
818 CtdlFreeMessage(TheMessage);
822 /* signify start of msg text */
823 if (mode == MT_CITADEL)
825 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
828 /* If the format type on disk is 1 (fixed-format), then we want
829 * everything to be output completely literally ... regardless of
830 * what message transfer format is in use.
832 if (TheMessage->cm_format_type == 1) {
834 while (ch = *mptr++, ch > 0) {
837 if ((ch == 10) || (strlen(buf) > 250)) {
838 cprintf("%s\n", buf);
841 buf[strlen(buf) + 1] = 0;
842 buf[strlen(buf)] = ch;
846 cprintf("%s\n", buf);
849 /* If the message on disk is format 0 (Citadel vari-format), we
850 * output using the formatter at 80 columns. This is the final output
851 * form if the transfer format is RFC822, but if the transfer format
852 * is Citadel proprietary, it'll still work, because the indentation
853 * for new paragraphs is correct and the client will reformat the
854 * message to the reader's screen width.
856 if (TheMessage->cm_format_type == 0) {
857 memfmout(80, mptr, 0);
860 /* If the message on disk is format 4 (MIME), we've gotta hand it
861 * off to the MIME parser. The client has already been told that
862 * this message is format 1 (fixed format), so the callback function
863 * we use will display those parts as-is.
865 if (TheMessage->cm_format_type == 4) {
866 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
867 memset(ma, 0, sizeof(struct ma_info));
868 mime_parser(mptr, NULL, *fixed_output);
873 CtdlFreeMessage(TheMessage);
880 * display a message (mode 0 - Citadel proprietary)
882 void cmd_msg0(char *cmdbuf)
885 int headers_only = 0;
887 extract(msgid, cmdbuf, 0);
888 headers_only = extract_int(cmdbuf, 1);
890 output_message(msgid, MT_CITADEL, headers_only);
896 * display a message (mode 2 - RFC822)
898 void cmd_msg2(char *cmdbuf)
901 int headers_only = 0;
903 extract(msgid, cmdbuf, 0);
904 headers_only = extract_int(cmdbuf, 1);
906 output_message(msgid, MT_RFC822, headers_only);
912 * display a message (mode 3 - IGnet raw format - internal programs only)
914 void cmd_msg3(char *cmdbuf)
917 struct CtdlMessage *msg;
918 struct sermsgret smr;
920 if (CC->internal_pgm == 0) {
921 cprintf("%d This command is for internal programs only.\n",
926 msgnum = extract_long(cmdbuf, 0);
927 msg = CtdlFetchMessage(msgnum);
929 cprintf("%d Message %ld not found.\n",
934 serialize_message(&smr, msg);
935 CtdlFreeMessage(msg);
938 cprintf("%d Unable to serialize message\n",
939 ERROR+INTERNAL_ERROR);
943 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
944 client_write(smr.ser, smr.len);
951 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
953 void cmd_msg4(char *cmdbuf)
957 extract(msgid, cmdbuf, 0);
959 output_message(msgid, MT_MIME, 0);
963 * Open a component of a MIME message as a download file
965 void cmd_opna(char *cmdbuf)
969 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
971 extract(msgid, cmdbuf, 0);
972 extract(desired_section, cmdbuf, 1);
974 output_message(msgid, MT_DOWNLOAD, 0);
978 * Message base operation to send a message to the master file
979 * (returns new message number)
981 * This is the back end for CtdlSaveMsg() and should not be directly
982 * called by server-side modules.
985 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
986 int generate_id, /* generate 'I' field? */
987 FILE *save_a_copy) /* save a copy to disk? */
992 struct sermsgret smr;
994 /* Get a new message number */
995 newmsgid = get_new_message_number();
996 sprintf(msgidbuf, "%ld", newmsgid);
999 msg->cm_fields['I'] = strdoop(msgidbuf);
1002 serialize_message(&smr, msg);
1005 cprintf("%d Unable to serialize message\n",
1006 ERROR+INTERNAL_ERROR);
1010 /* Write our little bundle of joy into the message base */
1011 begin_critical_section(S_MSGMAIN);
1012 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1013 smr.ser, smr.len) < 0) {
1014 lprintf(2, "Can't store message\n");
1019 end_critical_section(S_MSGMAIN);
1021 /* If the caller specified that a copy should be saved to a particular
1022 * file handle, do that now too.
1024 if (save_a_copy != NULL) {
1025 fwrite(smr.ser, smr.len, 1, save_a_copy);
1028 /* Free the memory we used for the serialized message */
1031 /* Return the *local* message ID to the caller
1032 * (even if we're storing an incoming network message)
1040 * Serialize a struct CtdlMessage into the format used on disk and network.
1042 * This function returns a "struct sermsgret" (defined in msgbase.h) which
1043 * contains the length of the serialized message and a pointer to the
1044 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1046 void serialize_message(struct sermsgret *ret, /* return values */
1047 struct CtdlMessage *msg) /* unserialized msg */
1051 static char *forder = FORDER;
1053 lprintf(9, "serialize_message() called\n");
1055 if (is_valid_message(msg) == 0) return; /* self check */
1057 lprintf(9, "magic number check OK.\n");
1060 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1061 ret->len = ret->len +
1062 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1064 lprintf(9, "message is %d bytes\n", ret->len);
1066 lprintf(9, "calling malloc\n");
1067 ret->ser = mallok(ret->len);
1068 if (ret->ser == NULL) {
1074 ret->ser[1] = msg->cm_anon_type;
1075 ret->ser[2] = msg->cm_format_type;
1077 lprintf(9, "stuff\n");
1079 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1080 ret->ser[wlen++] = (char)forder[i];
1081 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1082 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1084 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1093 * Save a message to disk
1095 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1096 char *rec, /* Recipient (mail) */
1097 char *force, /* force a particular room? */
1098 int mailtype, /* local or remote type */
1099 int generate_id) /* 1 = generate 'I' field */
1102 char hold_rm[ROOMNAMELEN];
1103 char actual_rm[ROOMNAMELEN];
1104 char force_room[ROOMNAMELEN];
1105 char content_type[256]; /* We have to learn this */
1106 char recipient[256];
1109 struct usersupp userbuf;
1111 int successful_local_recipients = 0;
1112 struct quickroom qtemp;
1113 struct SuppMsgInfo smi;
1114 FILE *network_fp = NULL;
1115 static int seqnum = 1;
1117 lprintf(9, "CtdlSaveMsg() called\n");
1118 if (is_valid_message(msg) == 0) return; /* self check */
1120 /* If this message has no timestamp, we take the liberty of
1121 * giving it one, right now.
1123 if (msg->cm_fields['T'] == NULL) {
1124 sprintf(aaa, "%ld", time(NULL));
1125 msg->cm_fields['T'] = strdoop(aaa);
1128 /* If this message has no path, we generate one.
1130 if (msg->cm_fields['P'] == NULL) {
1131 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1132 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1133 if (isspace(msg->cm_fields['P'][a])) {
1134 msg->cm_fields['P'][a] = ' ';
1139 strcpy(force_room, force);
1141 /* Strip non-printable characters out of the recipient name */
1142 strcpy(recipient, rec);
1143 for (a = 0; a < strlen(recipient); ++a)
1144 if (!isprint(recipient[a]))
1145 strcpy(&recipient[a], &recipient[a + 1]);
1147 /* Learn about what's inside, because it's what's inside that counts */
1149 switch (msg->cm_format_type) {
1151 strcpy(content_type, "text/x-citadel-variformat");
1154 strcpy(content_type, "text/plain");
1157 strcpy(content_type, "text/plain");
1158 /* advance past header fields */
1159 mptr = msg->cm_fields['M'];
1162 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1163 safestrncpy(content_type, mptr,
1164 sizeof(content_type));
1165 strcpy(content_type, &content_type[14]);
1166 for (a = 0; a < strlen(content_type); ++a)
1167 if ((content_type[a] == ';')
1168 || (content_type[a] == ' ')
1169 || (content_type[a] == 13)
1170 || (content_type[a] == 10))
1171 content_type[a] = 0;
1178 /* Network mail - send a copy to the network program. */
1179 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1180 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1181 (long) getpid(), CC->cs_pid, ++seqnum);
1182 lprintf(9, "Saving a copy to %s\n", aaa);
1183 network_fp = fopen(aaa, "ab+");
1184 if (network_fp == NULL)
1185 lprintf(2, "ERROR: %s\n", strerror(errno));
1188 /* Save it to disk */
1189 newmsgid = send_message(msg, generate_id, network_fp);
1190 if (network_fp != NULL) {
1192 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1197 strcpy(actual_rm, CC->quickroom.QRname);
1198 strcpy(hold_rm, "");
1200 /* If this is being done by the networker delivering a private
1201 * message, we want to BYPASS saving the sender's copy (because there
1202 * is no local sender; it would otherwise go to the Trashcan).
1204 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1205 /* If the user is a twit, move to the twit room for posting */
1207 if (CC->usersupp.axlevel == 2) {
1208 strcpy(hold_rm, actual_rm);
1209 strcpy(actual_rm, config.c_twitroom);
1211 /* ...or if this message is destined for Aide> then go there. */
1212 if (strlen(force_room) > 0) {
1213 strcpy(hold_rm, actual_rm);
1214 strcpy(actual_rm, force_room);
1216 /* This call to usergoto() changes rooms if necessary. It also
1217 * causes the latest message list to be read into memory.
1219 usergoto(actual_rm, 0);
1221 /* read in the quickroom record, obtaining a lock... */
1222 lgetroom(&CC->quickroom, actual_rm);
1224 /* Fix an obscure bug */
1225 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1226 CC->quickroom.QRflags =
1227 CC->quickroom.QRflags & ~QR_MAILBOX;
1229 /* Add the message pointer to the room */
1230 CC->quickroom.QRhighest =
1231 AddMessageToRoom(&CC->quickroom, newmsgid);
1233 /* update quickroom */
1234 lputroom(&CC->quickroom);
1235 ++successful_local_recipients;
1238 /* Bump this user's messages posted counter. */
1239 lgetuser(&CC->usersupp, CC->curr_user);
1240 CC->usersupp.posted = CC->usersupp.posted + 1;
1241 lputuser(&CC->usersupp);
1243 /* If this is private, local mail, make a copy in the
1244 * recipient's mailbox and bump the reference count.
1246 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1247 if (getuser(&userbuf, recipient) == 0) {
1248 MailboxName(actual_rm, &userbuf, MAILROOM);
1249 if (lgetroom(&qtemp, actual_rm) == 0) {
1251 AddMessageToRoom(&qtemp, newmsgid);
1253 ++successful_local_recipients;
1257 /* If we've posted in a room other than the current room, then we
1258 * have to now go back to the current room...
1260 if (strlen(hold_rm) > 0) {
1261 usergoto(hold_rm, 0);
1264 /* Write a supplemental message info record. This doesn't have to
1265 * be a critical section because nobody else knows about this message
1268 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1269 smi.smi_msgnum = newmsgid;
1270 smi.smi_refcount = successful_local_recipients;
1271 safestrncpy(smi.smi_content_type, content_type, 64);
1272 PutSuppMsgInfo(&smi);
1278 * Convenience function for generating small administrative messages.
1280 void quickie_message(char *from, char *room, char *text)
1282 struct CtdlMessage *msg;
1284 msg = mallok(sizeof(struct CtdlMessage));
1285 memset(msg, 0, sizeof(struct CtdlMessage));
1286 msg->cm_magic = CTDLMESSAGE_MAGIC;
1287 msg->cm_anon_type = MES_NORMAL;
1288 msg->cm_format_type = 0;
1289 msg->cm_fields['A'] = strdoop(from);
1290 msg->cm_fields['O'] = strdoop(room);
1291 msg->cm_fields['N'] = strdoop(NODENAME);
1292 msg->cm_fields['M'] = strdoop(text);
1294 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1295 CtdlFreeMessage(msg);
1296 syslog(LOG_NOTICE, text);
1301 * Build a binary message to be saved on disk.
1304 struct CtdlMessage *make_message(
1305 struct usersupp *author, /* author's usersupp structure */
1306 char *recipient, /* NULL if it's not mail */
1307 char *room, /* room where it's going */
1308 int type, /* see MES_ types in header file */
1309 int net_type, /* see MES_ types in header file */
1310 int format_type, /* local or remote (see citadel.h) */
1311 char *fake_name) /* who we're masquerading as */
1317 size_t message_len = 0;
1318 size_t buffer_len = 0;
1320 struct CtdlMessage *msg;
1322 msg = mallok(sizeof(struct CtdlMessage));
1323 memset(msg, 0, sizeof(struct CtdlMessage));
1324 msg->cm_magic = CTDLMESSAGE_MAGIC;
1325 msg->cm_anon_type = type;
1326 msg->cm_format_type = format_type;
1328 /* Don't confuse the poor folks if it's not routed mail. */
1329 strcpy(dest_node, "");
1331 /* If net_type is MES_BINARY, split out the destination node. */
1332 if (net_type == MES_BINARY) {
1333 strcpy(dest_node, NODENAME);
1334 for (a = 0; a < strlen(recipient); ++a) {
1335 if (recipient[a] == '@') {
1337 strcpy(dest_node, &recipient[a + 1]);
1342 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1343 if (net_type == MES_INTERNET) {
1344 strcpy(dest_node, "internet");
1347 while (isspace(recipient[strlen(recipient) - 1]))
1348 recipient[strlen(recipient) - 1] = 0;
1350 sprintf(buf, "cit%ld", author->usernum); /* Path */
1351 msg->cm_fields['P'] = strdoop(buf);
1353 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1354 msg->cm_fields['T'] = strdoop(buf);
1356 if (fake_name[0]) /* author */
1357 msg->cm_fields['A'] = strdoop(fake_name);
1359 msg->cm_fields['A'] = strdoop(author->fullname);
1361 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1362 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1364 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1366 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1367 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1369 if (recipient[0] != 0)
1370 msg->cm_fields['R'] = strdoop(recipient);
1371 if (dest_node[0] != 0)
1372 msg->cm_fields['D'] = strdoop(dest_node);
1374 msg->cm_fields['M'] = mallok(4096);
1375 if (msg->cm_fields['M'] == NULL) {
1376 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1380 msg->cm_fields['M'][0] = 0;
1384 /* read in the lines of message text one by one */
1385 while (client_gets(buf), strcmp(buf, "000")) {
1387 /* augment the buffer if we have to */
1388 if ((message_len + strlen(buf) + 2) > buffer_len) {
1389 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1390 if (ptr == NULL) { /* flush if can't allocate */
1391 while (client_gets(buf), strcmp(buf, "000")) ;;
1394 buffer_len = (buffer_len * 2);
1395 msg->cm_fields['M'] = ptr;
1399 strcat(msg->cm_fields['M'], buf);
1400 strcat(msg->cm_fields['M'], "\n");
1402 /* if we've hit the max msg length, flush the rest */
1403 if (message_len >= config.c_maxmsglen) {
1404 while (client_gets(buf), strcmp(buf, "000")) ;;
1417 * message entry - mode 0 (normal)
1419 void cmd_ent0(char *entargs)
1422 char recipient[256];
1424 int format_type = 0;
1425 char newusername[256];
1426 struct CtdlMessage *msg;
1430 struct usersupp tempUS;
1433 post = extract_int(entargs, 0);
1434 extract(recipient, entargs, 1);
1435 anon_flag = extract_int(entargs, 2);
1436 format_type = extract_int(entargs, 3);
1438 /* first check to make sure the request is valid. */
1440 if (!(CC->logged_in)) {
1441 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1444 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1445 cprintf("%d Need to be validated to enter ",
1446 ERROR + HIGHER_ACCESS_REQUIRED);
1447 cprintf("(except in %s> to sysop)\n", MAILROOM);
1450 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1451 cprintf("%d Need net privileges to enter here.\n",
1452 ERROR + HIGHER_ACCESS_REQUIRED);
1455 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1456 cprintf("%d Sorry, this is a read-only room.\n",
1457 ERROR + HIGHER_ACCESS_REQUIRED);
1464 if (CC->usersupp.axlevel < 6) {
1465 cprintf("%d You don't have permission to masquerade.\n",
1466 ERROR + HIGHER_ACCESS_REQUIRED);
1469 extract(newusername, entargs, 4);
1470 memset(CC->fake_postname, 0, 32);
1471 strcpy(CC->fake_postname, newusername);
1472 cprintf("%d Ok\n", OK);
1475 CC->cs_flags |= CS_POSTING;
1478 if (CC->quickroom.QRflags & QR_MAILBOX) {
1479 if (CC->usersupp.axlevel >= 2) {
1480 strcpy(buf, recipient);
1482 strcpy(buf, "sysop");
1483 e = alias(buf); /* alias and mail type */
1484 if ((buf[0] == 0) || (e == MES_ERROR)) {
1485 cprintf("%d Unknown address - cannot send message.\n",
1486 ERROR + NO_SUCH_USER);
1489 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1490 cprintf("%d Net privileges required for network mail.\n",
1491 ERROR + HIGHER_ACCESS_REQUIRED);
1494 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1495 && ((CC->usersupp.flags & US_INTERNET) == 0)
1496 && (!CC->internal_pgm)) {
1497 cprintf("%d You don't have access to Internet mail.\n",
1498 ERROR + HIGHER_ACCESS_REQUIRED);
1501 if (!strcasecmp(buf, "sysop")) {
1506 goto SKFALL; /* don't search local file */
1507 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1508 cprintf("%d Can't send mail to yourself!\n",
1509 ERROR + NO_SUCH_USER);
1512 /* Check to make sure the user exists; also get the correct
1513 * upper/lower casing of the name.
1515 a = getuser(&tempUS, buf);
1517 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1520 strcpy(buf, tempUS.fullname);
1523 SKFALL: b = MES_NORMAL;
1524 if (CC->quickroom.QRflags & QR_ANONONLY)
1526 if (CC->quickroom.QRflags & QR_ANONOPT) {
1530 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1533 /* If we're only checking the validity of the request, return
1534 * success without creating the message.
1537 cprintf("%d %s\n", OK, buf);
1541 cprintf("%d send message\n", SEND_LISTING);
1543 /* Read in the message from the client. */
1544 if (CC->fake_postname[0])
1545 msg = make_message(&CC->usersupp, buf,
1546 CC->quickroom.QRname, b, e, format_type,
1548 else if (CC->fake_username[0])
1549 msg = make_message(&CC->usersupp, buf,
1550 CC->quickroom.QRname, b, e, format_type,
1553 msg = make_message(&CC->usersupp, buf,
1554 CC->quickroom.QRname, b, e, format_type, "");
1557 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1558 CtdlFreeMessage(msg);
1559 CC->fake_postname[0] = '\0';
1566 * message entry - mode 3 (raw)
1568 void cmd_ent3(char *entargs)
1574 struct usersupp tempUS;
1579 if (CC->internal_pgm == 0) {
1580 cprintf("%d This command is for internal programs only.\n",
1585 /* See if there's a recipient, but make sure it's a real one */
1586 extract(recp, entargs, 1);
1587 for (a = 0; a < strlen(recp); ++a)
1588 if (!isprint(recp[a]))
1589 strcpy(&recp[a], &recp[a + 1]);
1590 while (isspace(recp[0]))
1591 strcpy(recp, &recp[1]);
1592 while (isspace(recp[strlen(recp) - 1]))
1593 recp[strlen(recp) - 1] = 0;
1595 /* If we're in Mail, check the recipient */
1596 if (strlen(recp) > 0) {
1597 e = alias(recp); /* alias and mail type */
1598 if ((recp[0] == 0) || (e == MES_ERROR)) {
1599 cprintf("%d Unknown address - cannot send message.\n",
1600 ERROR + NO_SUCH_USER);
1603 if (e == MES_LOCAL) {
1604 a = getuser(&tempUS, recp);
1606 cprintf("%d No such user.\n",
1607 ERROR + NO_SUCH_USER);
1612 /* At this point, message has been approved. */
1613 if (extract_int(entargs, 0) == 0) {
1614 cprintf("%d OK to send\n", OK);
1617 /* open a temp file to hold the message */
1618 fp = fopen(CC->temp, "wb");
1620 cprintf("%d Cannot open %s: %s\n",
1621 ERROR + INTERNAL_ERROR,
1622 CC->temp, strerror(errno));
1625 msglen = extract_long(entargs, 2);
1626 cprintf("%d %ld\n", SEND_BINARY, msglen);
1627 while (msglen > 0L) {
1628 bloklen = ((msglen >= 255L) ? 255 : msglen);
1629 client_read(buf, (int) bloklen);
1630 fwrite(buf, (int) bloklen, 1, fp);
1631 msglen = msglen - bloklen;
1635 save_message(CC->temp, recp, "", e, 0);
1640 * API function to delete messages which match a set of criteria
1641 * (returns the actual number of messages deleted)
1642 * FIX ... still need to implement delete by content type
1644 int CtdlDeleteMessages(char *room_name, /* which room */
1645 long dmsgnum, /* or "0" for any */
1646 char *content_type /* or NULL for any */
1650 struct quickroom qrbuf;
1651 struct cdbdata *cdbfr;
1652 long *msglist = NULL;
1655 int num_deleted = 0;
1657 struct SuppMsgInfo smi;
1659 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1660 room_name, dmsgnum, content_type);
1662 /* get room record, obtaining a lock... */
1663 if (lgetroom(&qrbuf, room_name) != 0) {
1664 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1666 return (0); /* room not found */
1668 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1670 if (cdbfr != NULL) {
1671 msglist = mallok(cdbfr->len);
1672 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1673 num_msgs = cdbfr->len / sizeof(long);
1677 for (i = 0; i < num_msgs; ++i) {
1680 /* Set/clear a bit for each criterion */
1682 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1683 delete_this |= 0x01;
1685 if (content_type == NULL) {
1686 delete_this |= 0x02;
1688 GetSuppMsgInfo(&smi, msglist[i]);
1689 if (!strcasecmp(smi.smi_content_type,
1691 delete_this |= 0x02;
1695 /* Delete message only if all bits are set */
1696 if (delete_this == 0x03) {
1697 AdjRefCount(msglist[i], -1);
1703 num_msgs = sort_msglist(msglist, num_msgs);
1704 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1705 msglist, (num_msgs * sizeof(long)));
1707 qrbuf.QRhighest = msglist[num_msgs - 1];
1711 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1712 return (num_deleted);
1718 * Delete message from current room
1720 void cmd_dele(char *delstr)
1725 getuser(&CC->usersupp, CC->curr_user);
1726 if ((CC->usersupp.axlevel < 6)
1727 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1728 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1729 cprintf("%d Higher access required.\n",
1730 ERROR + HIGHER_ACCESS_REQUIRED);
1733 delnum = extract_long(delstr, 0);
1735 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1738 cprintf("%d %d message%s deleted.\n", OK,
1739 num_deleted, ((num_deleted != 1) ? "s" : ""));
1741 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1747 * move a message to another room
1749 void cmd_move(char *args)
1753 struct quickroom qtemp;
1756 num = extract_long(args, 0);
1757 extract(targ, args, 1);
1759 getuser(&CC->usersupp, CC->curr_user);
1760 if ((CC->usersupp.axlevel < 6)
1761 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1762 cprintf("%d Higher access required.\n",
1763 ERROR + HIGHER_ACCESS_REQUIRED);
1766 if (getroom(&qtemp, targ) != 0) {
1767 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1770 /* Bump the reference count, otherwise the message will be deleted
1771 * from disk when we remove it from the source room.
1773 AdjRefCount(num, 1);
1775 /* yank the message out of the current room... */
1776 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
1779 /* put the message into the target room */
1780 lgetroom(&qtemp, targ);
1781 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1783 cprintf("%d Message moved.\n", OK);
1785 AdjRefCount(num, (-1)); /* oops */
1786 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1793 * GetSuppMsgInfo() - Get the supplementary record for a message
1795 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1798 struct cdbdata *cdbsmi;
1801 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1802 smibuf->smi_msgnum = msgnum;
1803 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1805 /* Use the negative of the message number for its supp record index */
1806 TheIndex = (0L - msgnum);
1808 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1809 if (cdbsmi == NULL) {
1810 return; /* record not found; go with defaults */
1812 memcpy(smibuf, cdbsmi->ptr,
1813 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1814 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1821 * PutSuppMsgInfo() - (re)write supplementary record for a message
1823 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1827 /* Use the negative of the message number for its supp record index */
1828 TheIndex = (0L - smibuf->smi_msgnum);
1830 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1831 smibuf->smi_msgnum, smibuf->smi_refcount);
1833 cdb_store(CDB_MSGMAIN,
1834 &TheIndex, sizeof(long),
1835 smibuf, sizeof(struct SuppMsgInfo));
1839 * AdjRefCount - change the reference count for a message;
1840 * delete the message if it reaches zero
1841 */ void AdjRefCount(long msgnum, int incr)
1844 struct SuppMsgInfo smi;
1847 /* This is a *tight* critical section; please keep it that way, as
1848 * it may get called while nested in other critical sections.
1849 * Complicating this any further will surely cause deadlock!
1851 begin_critical_section(S_SUPPMSGMAIN);
1852 GetSuppMsgInfo(&smi, msgnum);
1853 smi.smi_refcount += incr;
1854 PutSuppMsgInfo(&smi);
1855 end_critical_section(S_SUPPMSGMAIN);
1857 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1858 msgnum, smi.smi_refcount);
1860 /* If the reference count is now zero, delete the message
1861 * (and its supplementary record as well).
1863 if (smi.smi_refcount == 0) {
1864 lprintf(9, "Deleting message <%ld>\n", msgnum);
1866 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1867 delnum = (0L - msgnum);
1868 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1873 * Write a generic object to this room
1875 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
1876 char *content_type, /* MIME type of this object */
1877 char *tempfilename, /* Where to fetch it from */
1878 int is_mailbox, /* Private mailbox room? */
1879 int is_binary, /* Is encoding necessary? */
1880 int is_unique /* Del others of this type? */
1885 char filename[PATH_MAX];
1888 struct quickroom qrbuf;
1889 char roomname[ROOMNAMELEN];
1892 MailboxName(roomname, &CC->usersupp, req_room);
1894 safestrncpy(roomname, req_room, sizeof(roomname));
1896 strcpy(filename, tmpnam(NULL));
1897 fp = fopen(filename, "w");
1901 fprintf(fp, "%c%c%c", 0xFF, MES_NORMAL, 4);
1902 fprintf(fp, "T%ld%c", time(NULL), 0);
1903 fprintf(fp, "A%s%c", CC->usersupp.fullname, 0);
1904 fprintf(fp, "O%s%c", roomname, 0);
1905 fprintf(fp, "N%s%c", config.c_nodename, 0);
1906 fprintf(fp, "MContent-type: %s\n", content_type);
1908 tempfp = fopen(tempfilename, "r");
1909 if (tempfp == NULL) {
1914 if (is_binary == 0) {
1915 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
1916 while (ch = getc(tempfp), ch > 0)
1922 fprintf(fp, "Content-transfer-encoding: base64\n\n");
1925 sprintf(cmdbuf, "./base64 -e <%s >>%s",
1926 tempfilename, filename);
1930 /* Create the requested room if we have to. */
1931 if (getroom(&qrbuf, roomname) != 0) {
1932 create_room(roomname, 4, "", 0);
1934 /* If the caller specified this object as unique, delete all
1935 * other objects of this type that are currently in the room.
1938 lprintf(9, "Deleted %d other msgs of this type\n",
1939 CtdlDeleteMessages(roomname, 0L, content_type));
1941 /* Now write the data */
1942 save_message(filename, "", roomname, MES_LOCAL, 1);