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;
39 * This function is self explanatory.
40 * (What can I say, I'm in a weird mood today...)
42 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
46 for (i = 0; i < strlen(name); ++i)
49 if (isspace(name[i - 1])) {
50 strcpy(&name[i - 1], &name[i]);
53 while (isspace(name[i + 1])) {
54 strcpy(&name[i + 1], &name[i + 2]);
61 * Aliasing for network mail.
62 * (Error messages have been commented out, because this is a server.)
65 { /* process alias and routing info for mail */
68 char aaa[300], bbb[300];
70 lprintf(9, "alias() called for <%s>\n", name);
72 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
74 fp = fopen("network/mail.aliases", "r");
76 fp = fopen("/dev/null", "r");
81 while (fgets(aaa, sizeof aaa, fp) != NULL) {
82 while (isspace(name[0]))
83 strcpy(name, &name[1]);
84 aaa[strlen(aaa) - 1] = 0;
86 for (a = 0; a < strlen(aaa); ++a) {
88 strcpy(bbb, &aaa[a + 1]);
92 if (!strcasecmp(name, aaa))
96 lprintf(7, "Mail is being forwarded to %s\n", name);
98 /* determine local or remote type, see citadel.h */
99 for (a = 0; a < strlen(name); ++a)
101 return (MES_INTERNET);
102 for (a = 0; a < strlen(name); ++a)
104 for (b = a; b < strlen(name); ++b)
106 return (MES_INTERNET);
108 for (a = 0; a < strlen(name); ++a)
112 lprintf(7, "Too many @'s in address\n");
116 for (a = 0; a < strlen(name); ++a)
118 strcpy(bbb, &name[a + 1]);
120 strcpy(bbb, &bbb[1]);
121 fp = fopen("network/mail.sysinfo", "r");
125 a = getstring(fp, aaa);
126 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
127 a = getstring(fp, aaa);
128 if (!strncmp(aaa, "use ", 4)) {
129 strcpy(bbb, &aaa[4]);
134 if (!strncmp(aaa, "uum", 3)) {
136 for (a = 0; a < strlen(bbb); ++a) {
142 while (bbb[strlen(bbb) - 1] == '_')
143 bbb[strlen(bbb) - 1] = 0;
144 sprintf(name, &aaa[4], bbb);
145 return (MES_INTERNET);
147 if (!strncmp(aaa, "bin", 3)) {
150 while (aaa[strlen(aaa) - 1] != '@')
151 aaa[strlen(aaa) - 1] = 0;
152 aaa[strlen(aaa) - 1] = 0;
153 while (aaa[strlen(aaa) - 1] == ' ')
154 aaa[strlen(aaa) - 1] = 0;
155 while (bbb[0] != '@')
156 strcpy(bbb, &bbb[1]);
157 strcpy(bbb, &bbb[1]);
158 while (bbb[0] == ' ')
159 strcpy(bbb, &bbb[1]);
160 sprintf(name, "%s @%s", aaa, bbb);
173 fp = fopen("citadel.control", "r");
174 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
180 void simple_listing(long msgnum)
182 cprintf("%ld\n", msgnum);
187 * API function to perform an operation for each qualifying message in the
190 void CtdlForEachMessage(int mode, long ref,
192 void (*CallBack) (long msgnum))
197 struct cdbdata *cdbfr;
198 long *msglist = NULL;
201 struct SuppMsgInfo smi;
203 /* Learn about the user and room in question */
205 getuser(&CC->usersupp, CC->curr_user);
206 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
208 /* Load the message list */
209 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
211 msglist = mallok(cdbfr->len);
212 memcpy(msglist, cdbfr->ptr, cdbfr->len);
213 num_msgs = cdbfr->len / sizeof(long);
216 return; /* No messages at all? No further action. */
220 /* If the caller is looking for a specific MIME type, then filter
221 * out all messages which are not of the type requested.
224 if (content_type != NULL)
225 if (strlen(content_type) > 0)
226 for (a = 0; a < num_msgs; ++a) {
227 GetSuppMsgInfo(&smi, msglist[a]);
228 if (strcasecmp(smi.smi_content_type, content_type)) {
233 num_msgs = sort_msglist(msglist, num_msgs);
236 * Now iterate through the message list, according to the
237 * criteria supplied by the caller.
240 for (a = 0; a < num_msgs; ++a) {
241 thismsg = msglist[a];
246 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
247 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
248 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
249 && (CC->usersupp.flags & US_LASTOLD))
250 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
251 || ((mode == MSGS_FIRST) && (a < ref))
252 || ((mode == MSGS_GT) && (thismsg > ref))
258 phree(msglist); /* Clean up */
264 * cmd_msgs() - get list of message #'s in this room
265 * implements the MSGS server command using CtdlForEachMessage()
267 void cmd_msgs(char *cmdbuf)
273 extract(which, cmdbuf, 0);
274 cm_ref = extract_int(cmdbuf, 1);
278 if (!strncasecmp(which, "OLD", 3))
280 else if (!strncasecmp(which, "NEW", 3))
282 else if (!strncasecmp(which, "FIRST", 5))
284 else if (!strncasecmp(which, "LAST", 4))
286 else if (!strncasecmp(which, "GT", 2))
289 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
290 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
293 cprintf("%d Message list...\n", LISTING_FOLLOWS);
294 CtdlForEachMessage(mode, cm_ref, NULL, simple_listing);
302 * help_subst() - support routine for help file viewer
304 void help_subst(char *strbuf, char *source, char *dest)
309 while (p = pattern2(strbuf, source), (p >= 0)) {
310 strcpy(workbuf, &strbuf[p + strlen(source)]);
311 strcpy(&strbuf[p], dest);
312 strcat(strbuf, workbuf);
317 void do_help_subst(char *buffer)
321 help_subst(buffer, "^nodename", config.c_nodename);
322 help_subst(buffer, "^humannode", config.c_humannode);
323 help_subst(buffer, "^fqdn", config.c_fqdn);
324 help_subst(buffer, "^username", CC->usersupp.fullname);
325 sprintf(buf2, "%ld", CC->usersupp.usernum);
326 help_subst(buffer, "^usernum", buf2);
327 help_subst(buffer, "^sysadm", config.c_sysadm);
328 help_subst(buffer, "^variantname", CITADEL);
329 sprintf(buf2, "%d", config.c_maxsessions);
330 help_subst(buffer, "^maxsessions", buf2);
336 * memfmout() - Citadel text formatter and paginator.
337 * Although the original purpose of this routine was to format
338 * text to the reader's screen width, all we're really using it
339 * for here is to format text out to 80 columns before sending it
340 * to the client. The client software may reformat it again.
342 void memfmout(int width, char *mptr, char subst)
343 /* screen width to use */
344 /* where are we going to get our text from? */
345 /* nonzero if we should use hypertext mode */
357 c = 1; /* c is the current pos */
360 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
362 buffer[strlen(buffer) + 1] = 0;
363 buffer[strlen(buffer)] = ch;
366 if (buffer[0] == '^')
367 do_help_subst(buffer);
369 buffer[strlen(buffer) + 1] = 0;
371 strcpy(buffer, &buffer[1]);
381 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
383 if (((old == 13) || (old == 10)) && (isspace(real))) {
391 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
392 cprintf("\n%s", aaa);
401 if ((strlen(aaa) + c) > (width - 5)) {
411 if ((ch == 13) || (ch == 10)) {
412 cprintf("%s\n", aaa);
419 FMTEND: cprintf("%s\n", aaa);
425 * Callback function for mime parser that simply lists the part
427 void list_this_part(char *name, char *filename, char *partnum, char *disp,
428 void *content, char *cbtype, size_t length)
431 cprintf("part=%s|%s|%s|%s|%s|%d\n",
432 name, filename, partnum, disp, cbtype, length);
437 * Callback function for mime parser that wants to display text
439 void fixed_output(char *name, char *filename, char *partnum, char *disp,
440 void *content, char *cbtype, size_t length)
444 if (!strcasecmp(cbtype, "multipart/alternative")) {
445 strcpy(ma->prefix, partnum);
446 strcat(ma->prefix, ".");
452 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
454 && (ma->did_print == 1) ) {
455 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
461 if (!strcasecmp(cbtype, "text/plain")) {
462 client_write(content, length);
464 else if (!strcasecmp(cbtype, "text/html")) {
465 ptr = html_to_ascii(content, 80, 0);
466 client_write(ptr, strlen(ptr));
469 else if (strncasecmp(cbtype, "multipart/", 10)) {
470 cprintf("Part %s: %s (%s) (%d bytes)\n",
471 partnum, filename, cbtype, length);
477 * Callback function for mime parser that opens a section for downloading
479 void mime_download(char *name, char *filename, char *partnum, char *disp,
480 void *content, char *cbtype, size_t length)
483 /* Silently go away if there's already a download open... */
484 if (CC->download_fp != NULL)
487 /* ...or if this is not the desired section */
488 if (strcasecmp(desired_section, partnum))
491 CC->download_fp = tmpfile();
492 if (CC->download_fp == NULL)
495 fwrite(content, length, 1, CC->download_fp);
496 fflush(CC->download_fp);
497 rewind(CC->download_fp);
499 OpenCmdResult(filename, cbtype);
505 * Load a message from disk into memory.
506 * This is used by output_message() and other fetch functions.
508 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
509 * using the CtdlMessageFree() function.
511 struct CtdlMessage *CtdlFetchMessage(long msgnum)
513 struct cdbdata *dmsgtext;
514 struct CtdlMessage *ret = NULL;
517 CIT_UBYTE field_header;
521 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
522 if (dmsgtext == NULL) {
523 lprintf(9, "CtdlFetchMessage(%ld) failed.\n");
526 mptr = dmsgtext->ptr;
528 /* Parse the three bytes that begin EVERY message on disk.
529 * The first is always 0xFF, the on-disk magic number.
530 * The second is the anonymous/public type byte.
531 * The third is the format type byte (vari, fixed, or MIME).
535 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
539 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
540 memset(ret, 0, sizeof(struct CtdlMessage));
542 ret->cm_magic = CTDLMESSAGE_MAGIC;
543 ret->cm_anon_type = *mptr++; /* Anon type byte */
544 ret->cm_format_type = *mptr++; /* Format type byte */
547 * The rest is zero or more arbitrary fields. Load them in.
548 * We're done when we encounter either a zero-length field or
549 * have just processed the 'M' (message text) field.
552 field_length = strlen(mptr);
553 if (field_length == 0)
555 field_header = *mptr++;
556 ret->cm_fields[field_header] = mallok(field_length);
557 strcpy(ret->cm_fields[field_header], mptr);
559 while (*mptr++ != 0); /* advance to next field */
561 } while ((field_length > 0) && (field_header != 'M'));
569 * Returns 1 if the supplied pointer points to a valid Citadel message.
570 * If the pointer is NULL or the magic number check fails, returns 0.
572 int is_valid_message(struct CtdlMessage *msg) {
575 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
576 lprintf(3, "is_valid_message() -- self-check failed\n");
584 * 'Destructor' for struct CtdlMessage
586 void CtdlFreeMessage(struct CtdlMessage *msg)
590 if (is_valid_message(msg) == 0) return;
592 for (i = 0; i < 256; ++i)
593 if (msg->cm_fields[i] != NULL)
594 phree(msg->cm_fields[i]);
596 msg->cm_magic = 0; /* just in case */
603 * Get a message off disk. (return value is the message's timestamp)
606 void output_message(char *msgid, int mode, int headers_only)
614 struct CtdlMessage *TheMessage = NULL;
618 /* buffers needed for RFC822 translation */
626 msg_num = atol(msgid);
628 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
629 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
633 /* FIX ... small security issue
634 * We need to check to make sure the requested message is actually
635 * in the current room, and set msg_ok to 1 only if it is. This
636 * functionality is currently missing because I'm in a hurry to replace
637 * broken production code with nonbroken pre-beta code. :( -- ajc
640 cprintf("%d Message %ld is not in this room.\n",
647 * Fetch the message from disk
649 TheMessage = CtdlFetchMessage(msg_num);
650 if (TheMessage == NULL) {
651 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
655 /* Are we downloading a MIME component? */
656 if (mode == MT_DOWNLOAD) {
657 if (TheMessage->cm_format_type != 4) {
658 cprintf("%d This is not a MIME message.\n",
660 } else if (CC->download_fp != NULL) {
661 cprintf("%d You already have a download open.\n",
664 /* Parse the message text component */
665 mptr = TheMessage->cm_fields['M'];
666 mime_parser(mptr, NULL, *mime_download);
667 /* If there's no file open by this time, the requested
668 * section wasn't found, so print an error
670 if (CC->download_fp == NULL) {
671 cprintf("%d Section %s not found.\n",
672 ERROR + FILE_NOT_FOUND,
676 CtdlFreeMessage(TheMessage);
680 /* now for the user-mode message reading loops */
681 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
683 /* Tell the client which format type we're using. If this is a
684 * MIME message, *lie* about it and tell the user it's fixed-format.
686 if (mode == MT_CITADEL) {
687 if (TheMessage->cm_format_type == 4)
690 cprintf("type=%d\n", TheMessage->cm_format_type);
693 /* nhdr=yes means that we're only displaying headers, no body */
694 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
695 cprintf("nhdr=yes\n");
698 /* begin header processing loop for Citadel message format */
700 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
702 if (TheMessage->cm_fields['P']) {
703 cprintf("path=%s\n", TheMessage->cm_fields['P']);
705 if (TheMessage->cm_fields['I']) {
706 cprintf("msgn=%s\n", TheMessage->cm_fields['I']);
708 if (TheMessage->cm_fields['T']) {
709 cprintf("time=%s\n", TheMessage->cm_fields['T']);
711 if (TheMessage->cm_fields['A']) {
712 strcpy(buf, TheMessage->cm_fields['A']);
713 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
714 if (TheMessage->cm_anon_type == MES_ANON)
715 cprintf("from=****");
716 else if (TheMessage->cm_anon_type == MES_AN2)
717 cprintf("from=anonymous");
719 cprintf("from=%s", buf);
721 && ((TheMessage->cm_anon_type == MES_ANON)
722 || (TheMessage->cm_anon_type == MES_AN2))) {
723 cprintf(" [%s]", buf);
727 if (TheMessage->cm_fields['O']) {
728 cprintf("room=%s\n", TheMessage->cm_fields['O']);
730 if (TheMessage->cm_fields['N']) {
731 cprintf("node=%s\n", TheMessage->cm_fields['N']);
733 if (TheMessage->cm_fields['H']) {
734 cprintf("hnod=%s\n", TheMessage->cm_fields['H']);
736 if (TheMessage->cm_fields['R']) {
737 cprintf("rcpt=%s\n", TheMessage->cm_fields['R']);
739 if (TheMessage->cm_fields['U']) {
740 cprintf("subj=%s\n", TheMessage->cm_fields['U']);
744 /* begin header processing loop for RFC822 transfer format */
748 strcpy(snode, NODENAME);
749 strcpy(lnode, HUMANNODE);
750 if (mode == MT_RFC822) {
751 for (i = 0; i < 256; ++i) {
752 if (TheMessage->cm_fields[i]) {
753 mptr = TheMessage->cm_fields[i];
757 } else if (i == 'P') {
758 cprintf("Path: %s\n", mptr);
759 for (a = 0; a < strlen(mptr); ++a) {
760 if (mptr[a] == '!') {
761 strcpy(mptr, &mptr[a + 1]);
767 cprintf("Subject: %s\n", mptr);
773 cprintf("X-Citadel-Room: %s\n", mptr);
777 cprintf("To: %s\n", mptr);
780 cprintf("Date: %s", asctime(localtime(&xtime)));
786 if (mode == MT_RFC822) {
787 if (!strcasecmp(snode, NODENAME)) {
790 cprintf("Message-ID: <%s@%s>\n", mid, snode);
791 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
792 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
793 cprintf("Organization: %s\n", lnode);
796 /* end header processing loop ... at this point, we're in the text */
798 mptr = TheMessage->cm_fields['M'];
800 /* Tell the client about the MIME parts in this message */
801 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
802 if (mode == MT_CITADEL) {
803 mime_parser(mptr, NULL, *list_this_part);
805 else if (mode == MT_MIME) { /* list parts only */
806 mime_parser(mptr, NULL, *list_this_part);
808 CtdlFreeMessage(TheMessage);
815 CtdlFreeMessage(TheMessage);
819 /* signify start of msg text */
820 if (mode == MT_CITADEL)
822 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
825 /* If the format type on disk is 1 (fixed-format), then we want
826 * everything to be output completely literally ... regardless of
827 * what message transfer format is in use.
829 if (TheMessage->cm_format_type == 1) {
831 while (ch = *mptr++, ch > 0) {
834 if ((ch == 10) || (strlen(buf) > 250)) {
835 cprintf("%s\n", buf);
838 buf[strlen(buf) + 1] = 0;
839 buf[strlen(buf)] = ch;
843 cprintf("%s\n", buf);
846 /* If the message on disk is format 0 (Citadel vari-format), we
847 * output using the formatter at 80 columns. This is the final output
848 * form if the transfer format is RFC822, but if the transfer format
849 * is Citadel proprietary, it'll still work, because the indentation
850 * for new paragraphs is correct and the client will reformat the
851 * message to the reader's screen width.
853 if (TheMessage->cm_format_type == 0) {
854 memfmout(80, mptr, 0);
857 /* If the message on disk is format 4 (MIME), we've gotta hand it
858 * off to the MIME parser. The client has already been told that
859 * this message is format 1 (fixed format), so the callback function
860 * we use will display those parts as-is.
862 if (TheMessage->cm_format_type == 4) {
863 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
864 memset(ma, 0, sizeof(struct ma_info));
865 mime_parser(mptr, NULL, *fixed_output);
870 CtdlFreeMessage(TheMessage);
877 * display a message (mode 0 - Citadel proprietary)
879 void cmd_msg0(char *cmdbuf)
882 int headers_only = 0;
884 extract(msgid, cmdbuf, 0);
885 headers_only = extract_int(cmdbuf, 1);
887 output_message(msgid, MT_CITADEL, headers_only);
893 * display a message (mode 2 - RFC822)
895 void cmd_msg2(char *cmdbuf)
898 int headers_only = 0;
900 extract(msgid, cmdbuf, 0);
901 headers_only = extract_int(cmdbuf, 1);
903 output_message(msgid, MT_RFC822, headers_only);
909 * display a message (mode 3 - IGnet raw format - internal programs only)
911 void cmd_msg3(char *cmdbuf)
914 struct CtdlMessage *msg;
915 struct sermsgret smr;
917 if (CC->internal_pgm == 0) {
918 cprintf("%d This command is for internal programs only.\n",
923 msgnum = extract_long(cmdbuf, 0);
924 msg = CtdlFetchMessage(msgnum);
926 cprintf("%d Message %ld not found.\n",
931 serialize_message(&smr, msg);
932 CtdlFreeMessage(msg);
935 cprintf("%d Unable to serialize message\n",
936 ERROR+INTERNAL_ERROR);
940 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
941 client_write(smr.ser, smr.len);
948 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
950 void cmd_msg4(char *cmdbuf)
954 extract(msgid, cmdbuf, 0);
956 output_message(msgid, MT_MIME, 0);
960 * Open a component of a MIME message as a download file
962 void cmd_opna(char *cmdbuf)
966 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
968 extract(msgid, cmdbuf, 0);
969 extract(desired_section, cmdbuf, 1);
971 output_message(msgid, MT_DOWNLOAD, 0);
975 * Message base operation to send a message to the master file
976 * (returns new message number)
978 * This is the back end for CtdlSaveMsg() and should not be directly
979 * called by server-side modules.
982 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
983 int generate_id, /* generate 'I' field? */
984 FILE *save_a_copy) /* save a copy to disk? */
989 struct sermsgret smr;
991 /* Get a new message number */
992 newmsgid = get_new_message_number();
993 sprintf(msgidbuf, "%ld", newmsgid);
996 msg->cm_fields['I'] = strdoop(msgidbuf);
999 serialize_message(&smr, msg);
1002 cprintf("%d Unable to serialize message\n",
1003 ERROR+INTERNAL_ERROR);
1007 /* Write our little bundle of joy into the message base */
1008 begin_critical_section(S_MSGMAIN);
1009 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1010 smr.ser, smr.len) < 0) {
1011 lprintf(2, "Can't store message\n");
1016 end_critical_section(S_MSGMAIN);
1018 /* If the caller specified that a copy should be saved to a particular
1019 * file handle, do that now too.
1021 if (save_a_copy != NULL) {
1022 fwrite(smr.ser, smr.len, 1, save_a_copy);
1025 /* Free the memory we used for the serialized message */
1028 /* Return the *local* message ID to the caller
1029 * (even if we're storing an incoming network message)
1037 * Serialize a struct CtdlMessage into the format used on disk and network.
1039 * This function returns a "struct sermsgret" (defined in msgbase.h) which
1040 * contains the length of the serialized message and a pointer to the
1041 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1043 void serialize_message(struct sermsgret *ret, /* return values */
1044 struct CtdlMessage *msg) /* unserialized msg */
1048 static char *forder = FORDER;
1050 lprintf(9, "serialize_message() called\n");
1052 if (is_valid_message(msg) == 0) return; /* self check */
1054 lprintf(9, "magic number check OK.\n");
1057 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1058 ret->len = ret->len +
1059 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1061 lprintf(9, "message is %d bytes\n", ret->len);
1063 lprintf(9, "calling malloc\n");
1064 ret->ser = mallok(ret->len);
1065 if (ret->ser == NULL) {
1071 ret->ser[1] = msg->cm_anon_type;
1072 ret->ser[2] = msg->cm_format_type;
1074 lprintf(9, "stuff\n");
1076 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1077 ret->ser[wlen++] = (char)forder[i];
1078 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1079 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1081 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1090 * Save a message to disk
1092 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1093 char *rec, /* Recipient (mail) */
1094 char *force, /* force a particular room? */
1095 int mailtype, /* local or remote type */
1096 int generate_id) /* 1 = generate 'I' field */
1099 char hold_rm[ROOMNAMELEN];
1100 char actual_rm[ROOMNAMELEN];
1101 char force_room[ROOMNAMELEN];
1102 char content_type[256]; /* We have to learn this */
1103 char recipient[256];
1106 struct usersupp userbuf;
1108 int successful_local_recipients = 0;
1109 struct quickroom qtemp;
1110 struct SuppMsgInfo smi;
1111 FILE *network_fp = NULL;
1112 static int seqnum = 1;
1114 lprintf(9, "CtdlSaveMsg() called\n");
1115 if (is_valid_message(msg) == 0) return; /* self check */
1117 /* If this message has no timestamp, we take the liberty of
1118 * giving it one, right now.
1120 if (msg->cm_fields['T'] == NULL) {
1121 sprintf(aaa, "%ld", time(NULL));
1122 msg->cm_fields['T'] = strdoop(aaa);
1125 /* If this message has no path, we generate one.
1127 if (msg->cm_fields['P'] == NULL) {
1128 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1129 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1130 if (isspace(msg->cm_fields['P'][a])) {
1131 msg->cm_fields['P'][a] = ' ';
1136 strcpy(force_room, force);
1138 /* Strip non-printable characters out of the recipient name */
1139 strcpy(recipient, rec);
1140 for (a = 0; a < strlen(recipient); ++a)
1141 if (!isprint(recipient[a]))
1142 strcpy(&recipient[a], &recipient[a + 1]);
1144 /* Learn about what's inside, because it's what's inside that counts */
1146 switch (msg->cm_format_type) {
1148 strcpy(content_type, "text/x-citadel-variformat");
1151 strcpy(content_type, "text/plain");
1154 strcpy(content_type, "text/plain");
1155 /* advance past header fields */
1156 mptr = msg->cm_fields['M'];
1159 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1160 safestrncpy(content_type, mptr,
1161 sizeof(content_type));
1162 strcpy(content_type, &content_type[14]);
1163 for (a = 0; a < strlen(content_type); ++a)
1164 if ((content_type[a] == ';')
1165 || (content_type[a] == ' ')
1166 || (content_type[a] == 13)
1167 || (content_type[a] == 10))
1168 content_type[a] = 0;
1175 /* Network mail - send a copy to the network program. */
1176 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1177 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1178 (long) getpid(), CC->cs_pid, ++seqnum);
1179 lprintf(9, "Saving a copy to %s\n", aaa);
1180 network_fp = fopen(aaa, "ab+");
1181 if (network_fp == NULL)
1182 lprintf(2, "ERROR: %s\n", strerror(errno));
1185 /* Save it to disk */
1186 newmsgid = send_message(msg, generate_id, network_fp);
1187 if (network_fp != NULL) {
1189 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1194 strcpy(actual_rm, CC->quickroom.QRname);
1195 strcpy(hold_rm, "");
1197 /* If this is being done by the networker delivering a private
1198 * message, we want to BYPASS saving the sender's copy (because there
1199 * is no local sender; it would otherwise go to the Trashcan).
1201 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1202 /* If the user is a twit, move to the twit room for posting */
1204 if (CC->usersupp.axlevel == 2) {
1205 strcpy(hold_rm, actual_rm);
1206 strcpy(actual_rm, config.c_twitroom);
1208 /* ...or if this message is destined for Aide> then go there. */
1209 if (strlen(force_room) > 0) {
1210 strcpy(hold_rm, actual_rm);
1211 strcpy(actual_rm, force_room);
1213 /* This call to usergoto() changes rooms if necessary. It also
1214 * causes the latest message list to be read into memory.
1216 usergoto(actual_rm, 0);
1218 /* read in the quickroom record, obtaining a lock... */
1219 lgetroom(&CC->quickroom, actual_rm);
1221 /* Fix an obscure bug */
1222 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1223 CC->quickroom.QRflags =
1224 CC->quickroom.QRflags & ~QR_MAILBOX;
1226 /* Add the message pointer to the room */
1227 CC->quickroom.QRhighest =
1228 AddMessageToRoom(&CC->quickroom, newmsgid);
1230 /* update quickroom */
1231 lputroom(&CC->quickroom);
1232 ++successful_local_recipients;
1235 /* Bump this user's messages posted counter. */
1236 lgetuser(&CC->usersupp, CC->curr_user);
1237 CC->usersupp.posted = CC->usersupp.posted + 1;
1238 lputuser(&CC->usersupp);
1240 /* If this is private, local mail, make a copy in the
1241 * recipient's mailbox and bump the reference count.
1243 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1244 if (getuser(&userbuf, recipient) == 0) {
1245 MailboxName(actual_rm, &userbuf, MAILROOM);
1246 if (lgetroom(&qtemp, actual_rm) == 0) {
1248 AddMessageToRoom(&qtemp, newmsgid);
1250 ++successful_local_recipients;
1254 /* If we've posted in a room other than the current room, then we
1255 * have to now go back to the current room...
1257 if (strlen(hold_rm) > 0) {
1258 usergoto(hold_rm, 0);
1261 /* Write a supplemental message info record. This doesn't have to
1262 * be a critical section because nobody else knows about this message
1265 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1266 smi.smi_msgnum = newmsgid;
1267 smi.smi_refcount = successful_local_recipients;
1268 safestrncpy(smi.smi_content_type, content_type, 64);
1269 PutSuppMsgInfo(&smi);
1275 * Convenience function for generating small administrative messages.
1277 void quickie_message(char *from, char *to, char *room, char *text)
1279 struct CtdlMessage *msg;
1281 msg = mallok(sizeof(struct CtdlMessage));
1282 memset(msg, 0, sizeof(struct CtdlMessage));
1283 msg->cm_magic = CTDLMESSAGE_MAGIC;
1284 msg->cm_anon_type = MES_NORMAL;
1285 msg->cm_format_type = 0;
1286 msg->cm_fields['A'] = strdoop(from);
1287 msg->cm_fields['O'] = strdoop(room);
1288 msg->cm_fields['N'] = strdoop(NODENAME);
1290 msg->cm_fields['R'] = strdoop(to);
1291 msg->cm_fields['M'] = strdoop(text);
1293 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1294 CtdlFreeMessage(msg);
1295 syslog(LOG_NOTICE, text);
1300 * Build a binary message to be saved on disk.
1303 struct CtdlMessage *make_message(
1304 struct usersupp *author, /* author's usersupp structure */
1305 char *recipient, /* NULL if it's not mail */
1306 char *room, /* room where it's going */
1307 int type, /* see MES_ types in header file */
1308 int net_type, /* see MES_ types in header file */
1309 int format_type, /* local or remote (see citadel.h) */
1310 char *fake_name) /* who we're masquerading as */
1316 size_t message_len = 0;
1317 size_t buffer_len = 0;
1319 struct CtdlMessage *msg;
1321 msg = mallok(sizeof(struct CtdlMessage));
1322 memset(msg, 0, sizeof(struct CtdlMessage));
1323 msg->cm_magic = CTDLMESSAGE_MAGIC;
1324 msg->cm_anon_type = type;
1325 msg->cm_format_type = format_type;
1327 /* Don't confuse the poor folks if it's not routed mail. */
1328 strcpy(dest_node, "");
1330 /* If net_type is MES_BINARY, split out the destination node. */
1331 if (net_type == MES_BINARY) {
1332 strcpy(dest_node, NODENAME);
1333 for (a = 0; a < strlen(recipient); ++a) {
1334 if (recipient[a] == '@') {
1336 strcpy(dest_node, &recipient[a + 1]);
1341 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1342 if (net_type == MES_INTERNET) {
1343 strcpy(dest_node, "internet");
1346 while (isspace(recipient[strlen(recipient) - 1]))
1347 recipient[strlen(recipient) - 1] = 0;
1349 sprintf(buf, "cit%ld", author->usernum); /* Path */
1350 msg->cm_fields['P'] = strdoop(buf);
1352 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1353 msg->cm_fields['T'] = strdoop(buf);
1355 if (fake_name[0]) /* author */
1356 msg->cm_fields['A'] = strdoop(fake_name);
1358 msg->cm_fields['A'] = strdoop(author->fullname);
1360 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1361 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1363 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1365 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1366 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1368 if (recipient[0] != 0)
1369 msg->cm_fields['R'] = strdoop(recipient);
1370 if (dest_node[0] != 0)
1371 msg->cm_fields['D'] = strdoop(dest_node);
1373 msg->cm_fields['M'] = mallok(4096);
1374 if (msg->cm_fields['M'] == NULL) {
1375 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1379 msg->cm_fields['M'][0] = 0;
1383 /* read in the lines of message text one by one */
1384 while (client_gets(buf), strcmp(buf, "000")) {
1386 /* augment the buffer if we have to */
1387 if ((message_len + strlen(buf) + 2) > buffer_len) {
1388 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1389 if (ptr == NULL) { /* flush if can't allocate */
1390 while (client_gets(buf), strcmp(buf, "000")) ;;
1393 buffer_len = (buffer_len * 2);
1394 msg->cm_fields['M'] = ptr;
1398 strcat(msg->cm_fields['M'], buf);
1399 strcat(msg->cm_fields['M'], "\n");
1401 /* if we've hit the max msg length, flush the rest */
1402 if (message_len >= config.c_maxmsglen) {
1403 while (client_gets(buf), strcmp(buf, "000")) ;;
1416 * message entry - mode 0 (normal)
1418 void cmd_ent0(char *entargs)
1421 char recipient[256];
1423 int format_type = 0;
1424 char newusername[256];
1425 struct CtdlMessage *msg;
1429 struct usersupp tempUS;
1432 post = extract_int(entargs, 0);
1433 extract(recipient, entargs, 1);
1434 anon_flag = extract_int(entargs, 2);
1435 format_type = extract_int(entargs, 3);
1437 /* first check to make sure the request is valid. */
1439 if (!(CC->logged_in)) {
1440 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1443 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1444 cprintf("%d Need to be validated to enter ",
1445 ERROR + HIGHER_ACCESS_REQUIRED);
1446 cprintf("(except in %s> to sysop)\n", MAILROOM);
1449 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1450 cprintf("%d Need net privileges to enter here.\n",
1451 ERROR + HIGHER_ACCESS_REQUIRED);
1454 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1455 cprintf("%d Sorry, this is a read-only room.\n",
1456 ERROR + HIGHER_ACCESS_REQUIRED);
1463 if (CC->usersupp.axlevel < 6) {
1464 cprintf("%d You don't have permission to masquerade.\n",
1465 ERROR + HIGHER_ACCESS_REQUIRED);
1468 extract(newusername, entargs, 4);
1469 memset(CC->fake_postname, 0, 32);
1470 strcpy(CC->fake_postname, newusername);
1471 cprintf("%d Ok\n", OK);
1474 CC->cs_flags |= CS_POSTING;
1477 if (CC->quickroom.QRflags & QR_MAILBOX) {
1478 if (CC->usersupp.axlevel >= 2) {
1479 strcpy(buf, recipient);
1481 strcpy(buf, "sysop");
1482 e = alias(buf); /* alias and mail type */
1483 if ((buf[0] == 0) || (e == MES_ERROR)) {
1484 cprintf("%d Unknown address - cannot send message.\n",
1485 ERROR + NO_SUCH_USER);
1488 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1489 cprintf("%d Net privileges required for network mail.\n",
1490 ERROR + HIGHER_ACCESS_REQUIRED);
1493 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1494 && ((CC->usersupp.flags & US_INTERNET) == 0)
1495 && (!CC->internal_pgm)) {
1496 cprintf("%d You don't have access to Internet mail.\n",
1497 ERROR + HIGHER_ACCESS_REQUIRED);
1500 if (!strcasecmp(buf, "sysop")) {
1505 goto SKFALL; /* don't search local file */
1506 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1507 cprintf("%d Can't send mail to yourself!\n",
1508 ERROR + NO_SUCH_USER);
1511 /* Check to make sure the user exists; also get the correct
1512 * upper/lower casing of the name.
1514 a = getuser(&tempUS, buf);
1516 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1519 strcpy(buf, tempUS.fullname);
1522 SKFALL: b = MES_NORMAL;
1523 if (CC->quickroom.QRflags & QR_ANONONLY)
1525 if (CC->quickroom.QRflags & QR_ANONOPT) {
1529 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1532 /* If we're only checking the validity of the request, return
1533 * success without creating the message.
1536 cprintf("%d %s\n", OK, buf);
1540 cprintf("%d send message\n", SEND_LISTING);
1542 /* Read in the message from the client. */
1543 if (CC->fake_postname[0])
1544 msg = make_message(&CC->usersupp, buf,
1545 CC->quickroom.QRname, b, e, format_type,
1547 else if (CC->fake_username[0])
1548 msg = make_message(&CC->usersupp, buf,
1549 CC->quickroom.QRname, b, e, format_type,
1552 msg = make_message(&CC->usersupp, buf,
1553 CC->quickroom.QRname, b, e, format_type, "");
1556 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1557 CtdlFreeMessage(msg);
1558 CC->fake_postname[0] = '\0';
1565 * message entry - mode 3 (raw)
1567 void cmd_ent3(char *entargs)
1572 unsigned char ch, which_field;
1573 struct usersupp tempUS;
1575 struct CtdlMessage *msg;
1578 if (CC->internal_pgm == 0) {
1579 cprintf("%d This command is for internal programs only.\n",
1584 /* See if there's a recipient, but make sure it's a real one */
1585 extract(recp, entargs, 1);
1586 for (a = 0; a < strlen(recp); ++a)
1587 if (!isprint(recp[a]))
1588 strcpy(&recp[a], &recp[a + 1]);
1589 while (isspace(recp[0]))
1590 strcpy(recp, &recp[1]);
1591 while (isspace(recp[strlen(recp) - 1]))
1592 recp[strlen(recp) - 1] = 0;
1594 /* If we're in Mail, check the recipient */
1595 if (strlen(recp) > 0) {
1596 e = alias(recp); /* alias and mail type */
1597 if ((recp[0] == 0) || (e == MES_ERROR)) {
1598 cprintf("%d Unknown address - cannot send message.\n",
1599 ERROR + NO_SUCH_USER);
1602 if (e == MES_LOCAL) {
1603 a = getuser(&tempUS, recp);
1605 cprintf("%d No such user.\n",
1606 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);
1618 msglen = extract_long(entargs, 2);
1619 msg = mallok(sizeof(struct CtdlMessage));
1621 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1625 memset(msg, 0, sizeof(struct CtdlMessage));
1626 tempbuf = mallok(msglen);
1627 if (tempbuf == NULL) {
1628 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1633 cprintf("%d %ld\n", SEND_BINARY, msglen);
1635 client_read(&ch, 1); /* 0xFF magic number */
1636 msg->cm_magic = CTDLMESSAGE_MAGIC;
1637 client_read(&ch, 1); /* anon type */
1638 msg->cm_anon_type = ch;
1639 client_read(&ch, 1); /* format type */
1640 msg->cm_format_type = ch;
1641 msglen = msglen - 3;
1643 while (msglen > 0) {
1644 client_read(&which_field, 1);
1648 client_read(&ch, 1);
1649 a = strlen(tempbuf);
1652 } while ( (a != 0) && (msglen > 0) );
1653 msg->cm_fields[which_field] = strdoop(tempbuf);
1656 CtdlSaveMsg(msg, recp, "", e, 0);
1657 CtdlFreeMessage(msg);
1663 * API function to delete messages which match a set of criteria
1664 * (returns the actual number of messages deleted)
1666 int CtdlDeleteMessages(char *room_name, /* which room */
1667 long dmsgnum, /* or "0" for any */
1668 char *content_type /* or NULL for any */
1672 struct quickroom qrbuf;
1673 struct cdbdata *cdbfr;
1674 long *msglist = NULL;
1677 int num_deleted = 0;
1679 struct SuppMsgInfo smi;
1681 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1682 room_name, dmsgnum, content_type);
1684 /* get room record, obtaining a lock... */
1685 if (lgetroom(&qrbuf, room_name) != 0) {
1686 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1688 return (0); /* room not found */
1690 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1692 if (cdbfr != NULL) {
1693 msglist = mallok(cdbfr->len);
1694 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1695 num_msgs = cdbfr->len / sizeof(long);
1699 for (i = 0; i < num_msgs; ++i) {
1702 /* Set/clear a bit for each criterion */
1704 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1705 delete_this |= 0x01;
1707 if (content_type == NULL) {
1708 delete_this |= 0x02;
1710 GetSuppMsgInfo(&smi, msglist[i]);
1711 if (!strcasecmp(smi.smi_content_type,
1713 delete_this |= 0x02;
1717 /* Delete message only if all bits are set */
1718 if (delete_this == 0x03) {
1719 AdjRefCount(msglist[i], -1);
1725 num_msgs = sort_msglist(msglist, num_msgs);
1726 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1727 msglist, (num_msgs * sizeof(long)));
1729 qrbuf.QRhighest = msglist[num_msgs - 1];
1733 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1734 return (num_deleted);
1740 * Delete message from current room
1742 void cmd_dele(char *delstr)
1747 getuser(&CC->usersupp, CC->curr_user);
1748 if ((CC->usersupp.axlevel < 6)
1749 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1750 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1751 cprintf("%d Higher access required.\n",
1752 ERROR + HIGHER_ACCESS_REQUIRED);
1755 delnum = extract_long(delstr, 0);
1757 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1760 cprintf("%d %d message%s deleted.\n", OK,
1761 num_deleted, ((num_deleted != 1) ? "s" : ""));
1763 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1769 * move a message to another room
1771 void cmd_move(char *args)
1775 struct quickroom qtemp;
1778 num = extract_long(args, 0);
1779 extract(targ, args, 1);
1781 getuser(&CC->usersupp, CC->curr_user);
1782 if ((CC->usersupp.axlevel < 6)
1783 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1784 cprintf("%d Higher access required.\n",
1785 ERROR + HIGHER_ACCESS_REQUIRED);
1788 if (getroom(&qtemp, targ) != 0) {
1789 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1792 /* Bump the reference count, otherwise the message will be deleted
1793 * from disk when we remove it from the source room.
1795 AdjRefCount(num, 1);
1797 /* yank the message out of the current room... */
1798 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
1801 /* put the message into the target room */
1802 lgetroom(&qtemp, targ);
1803 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1805 cprintf("%d Message moved.\n", OK);
1807 AdjRefCount(num, (-1)); /* oops */
1808 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1815 * GetSuppMsgInfo() - Get the supplementary record for a message
1817 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1820 struct cdbdata *cdbsmi;
1823 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1824 smibuf->smi_msgnum = msgnum;
1825 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1827 /* Use the negative of the message number for its supp record index */
1828 TheIndex = (0L - msgnum);
1830 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1831 if (cdbsmi == NULL) {
1832 return; /* record not found; go with defaults */
1834 memcpy(smibuf, cdbsmi->ptr,
1835 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1836 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1843 * PutSuppMsgInfo() - (re)write supplementary record for a message
1845 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1849 /* Use the negative of the message number for its supp record index */
1850 TheIndex = (0L - smibuf->smi_msgnum);
1852 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1853 smibuf->smi_msgnum, smibuf->smi_refcount);
1855 cdb_store(CDB_MSGMAIN,
1856 &TheIndex, sizeof(long),
1857 smibuf, sizeof(struct SuppMsgInfo));
1862 * AdjRefCount - change the reference count for a message;
1863 * delete the message if it reaches zero
1865 void AdjRefCount(long msgnum, int incr)
1868 struct SuppMsgInfo smi;
1871 /* This is a *tight* critical section; please keep it that way, as
1872 * it may get called while nested in other critical sections.
1873 * Complicating this any further will surely cause deadlock!
1875 begin_critical_section(S_SUPPMSGMAIN);
1876 GetSuppMsgInfo(&smi, msgnum);
1877 smi.smi_refcount += incr;
1878 PutSuppMsgInfo(&smi);
1879 end_critical_section(S_SUPPMSGMAIN);
1881 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1882 msgnum, smi.smi_refcount);
1884 /* If the reference count is now zero, delete the message
1885 * (and its supplementary record as well).
1887 if (smi.smi_refcount == 0) {
1888 lprintf(9, "Deleting message <%ld>\n", msgnum);
1890 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1891 delnum = (0L - msgnum);
1892 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1897 * Write a generic object to this room
1899 * Note: this could be much more efficient. Right now we use two temporary
1900 * files, and still pull the message into memory as with all others.
1902 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
1903 char *content_type, /* MIME type of this object */
1904 char *tempfilename, /* Where to fetch it from */
1905 int is_mailbox, /* Private mailbox room? */
1906 int is_binary, /* Is encoding necessary? */
1907 int is_unique /* Del others of this type? */
1912 char filename[PATH_MAX];
1915 struct quickroom qrbuf;
1916 char roomname[ROOMNAMELEN];
1917 struct CtdlMessage *msg;
1920 lprintf(9, "CtdlWriteObject() called\n");
1923 MailboxName(roomname, &CC->usersupp, req_room);
1925 safestrncpy(roomname, req_room, sizeof(roomname));
1927 strcpy(filename, tmpnam(NULL));
1928 fp = fopen(filename, "w");
1932 tempfp = fopen(tempfilename, "r");
1933 if (tempfp == NULL) {
1939 fprintf(fp, "Content-type: %s\n", content_type);
1940 lprintf(9, "Content-type: %s\n", content_type);
1942 if (is_binary == 0) {
1943 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
1944 while (ch = getc(tempfp), ch > 0)
1950 fprintf(fp, "Content-transfer-encoding: base64\n\n");
1953 sprintf(cmdbuf, "./base64 -e <%s >>%s",
1954 tempfilename, filename);
1958 lprintf(9, "Allocating\n");
1959 msg = mallok(sizeof(struct CtdlMessage));
1960 memset(msg, 0, sizeof(struct CtdlMessage));
1961 msg->cm_magic = CTDLMESSAGE_MAGIC;
1962 msg->cm_anon_type = MES_NORMAL;
1963 msg->cm_format_type = 4;
1964 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
1965 msg->cm_fields['O'] = strdoop(roomname);
1966 msg->cm_fields['N'] = strdoop(config.c_nodename);
1967 msg->cm_fields['H'] = strdoop(config.c_humannode);
1969 lprintf(9, "Loading\n");
1970 fp = fopen(filename, "rb");
1971 fseek(fp, 0L, SEEK_END);
1974 msg->cm_fields['M'] = mallok(len);
1975 fread(msg->cm_fields['M'], len, 1, fp);
1979 /* Create the requested room if we have to. */
1980 if (getroom(&qrbuf, roomname) != 0) {
1981 create_room(roomname, 4, "", 0);
1983 /* If the caller specified this object as unique, delete all
1984 * other objects of this type that are currently in the room.
1987 lprintf(9, "Deleted %d other msgs of this type\n",
1988 CtdlDeleteMessages(roomname, 0L, content_type));
1990 /* Now write the data */
1991 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
1992 CtdlFreeMessage(msg);