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'));
565 /* Perform "before read" hooks (aborting if any return nonzero) */
566 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
567 CtdlFreeMessage(ret);
576 * Returns 1 if the supplied pointer points to a valid Citadel message.
577 * If the pointer is NULL or the magic number check fails, returns 0.
579 int is_valid_message(struct CtdlMessage *msg) {
582 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
583 lprintf(3, "is_valid_message() -- self-check failed\n");
591 * 'Destructor' for struct CtdlMessage
593 void CtdlFreeMessage(struct CtdlMessage *msg)
597 if (is_valid_message(msg) == 0) return;
599 for (i = 0; i < 256; ++i)
600 if (msg->cm_fields[i] != NULL)
601 phree(msg->cm_fields[i]);
603 msg->cm_magic = 0; /* just in case */
610 * Get a message off disk. (return value is the message's timestamp)
613 void output_message(char *msgid, int mode, int headers_only)
621 struct CtdlMessage *TheMessage = NULL;
625 /* buffers needed for RFC822 translation */
633 msg_num = atol(msgid);
635 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
636 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
640 /* FIX ... small security issue
641 * We need to check to make sure the requested message is actually
642 * in the current room, and set msg_ok to 1 only if it is. This
643 * functionality is currently missing because I'm in a hurry to replace
644 * broken production code with nonbroken pre-beta code. :( -- ajc
647 cprintf("%d Message %ld is not in this room.\n",
654 * Fetch the message from disk
656 TheMessage = CtdlFetchMessage(msg_num);
657 if (TheMessage == NULL) {
658 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
662 /* Are we downloading a MIME component? */
663 if (mode == MT_DOWNLOAD) {
664 if (TheMessage->cm_format_type != 4) {
665 cprintf("%d This is not a MIME message.\n",
667 } else if (CC->download_fp != NULL) {
668 cprintf("%d You already have a download open.\n",
671 /* Parse the message text component */
672 mptr = TheMessage->cm_fields['M'];
673 mime_parser(mptr, NULL, *mime_download);
674 /* If there's no file open by this time, the requested
675 * section wasn't found, so print an error
677 if (CC->download_fp == NULL) {
678 cprintf("%d Section %s not found.\n",
679 ERROR + FILE_NOT_FOUND,
683 CtdlFreeMessage(TheMessage);
687 /* now for the user-mode message reading loops */
688 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
690 /* Tell the client which format type we're using. If this is a
691 * MIME message, *lie* about it and tell the user it's fixed-format.
693 if (mode == MT_CITADEL) {
694 if (TheMessage->cm_format_type == 4)
697 cprintf("type=%d\n", TheMessage->cm_format_type);
700 /* nhdr=yes means that we're only displaying headers, no body */
701 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
702 cprintf("nhdr=yes\n");
705 /* begin header processing loop for Citadel message format */
707 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
709 if (TheMessage->cm_fields['P']) {
710 cprintf("path=%s\n", TheMessage->cm_fields['P']);
712 if (TheMessage->cm_fields['I']) {
713 cprintf("msgn=%s\n", TheMessage->cm_fields['I']);
715 if (TheMessage->cm_fields['T']) {
716 cprintf("time=%s\n", TheMessage->cm_fields['T']);
718 if (TheMessage->cm_fields['A']) {
719 strcpy(buf, TheMessage->cm_fields['A']);
720 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
721 if (TheMessage->cm_anon_type == MES_ANON)
722 cprintf("from=****");
723 else if (TheMessage->cm_anon_type == MES_AN2)
724 cprintf("from=anonymous");
726 cprintf("from=%s", buf);
728 && ((TheMessage->cm_anon_type == MES_ANON)
729 || (TheMessage->cm_anon_type == MES_AN2))) {
730 cprintf(" [%s]", buf);
734 if (TheMessage->cm_fields['O']) {
735 cprintf("room=%s\n", TheMessage->cm_fields['O']);
737 if (TheMessage->cm_fields['N']) {
738 cprintf("node=%s\n", TheMessage->cm_fields['N']);
740 if (TheMessage->cm_fields['H']) {
741 cprintf("hnod=%s\n", TheMessage->cm_fields['H']);
743 if (TheMessage->cm_fields['R']) {
744 cprintf("rcpt=%s\n", TheMessage->cm_fields['R']);
746 if (TheMessage->cm_fields['U']) {
747 cprintf("subj=%s\n", TheMessage->cm_fields['U']);
751 /* begin header processing loop for RFC822 transfer format */
755 strcpy(snode, NODENAME);
756 strcpy(lnode, HUMANNODE);
757 if (mode == MT_RFC822) {
758 for (i = 0; i < 256; ++i) {
759 if (TheMessage->cm_fields[i]) {
760 mptr = TheMessage->cm_fields[i];
764 } else if (i == 'P') {
765 cprintf("Path: %s\n", mptr);
766 for (a = 0; a < strlen(mptr); ++a) {
767 if (mptr[a] == '!') {
768 strcpy(mptr, &mptr[a + 1]);
774 cprintf("Subject: %s\n", mptr);
780 cprintf("X-Citadel-Room: %s\n", mptr);
784 cprintf("To: %s\n", mptr);
787 cprintf("Date: %s", asctime(localtime(&xtime)));
793 if (mode == MT_RFC822) {
794 if (!strcasecmp(snode, NODENAME)) {
797 cprintf("Message-ID: <%s@%s>\n", mid, snode);
798 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
799 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
800 cprintf("Organization: %s\n", lnode);
803 /* end header processing loop ... at this point, we're in the text */
805 mptr = TheMessage->cm_fields['M'];
807 /* Tell the client about the MIME parts in this message */
808 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
809 if (mode == MT_CITADEL) {
810 mime_parser(mptr, NULL, *list_this_part);
812 else if (mode == MT_MIME) { /* list parts only */
813 mime_parser(mptr, NULL, *list_this_part);
815 CtdlFreeMessage(TheMessage);
822 CtdlFreeMessage(TheMessage);
826 /* signify start of msg text */
827 if (mode == MT_CITADEL)
829 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
832 /* If the format type on disk is 1 (fixed-format), then we want
833 * everything to be output completely literally ... regardless of
834 * what message transfer format is in use.
836 if (TheMessage->cm_format_type == 1) {
838 while (ch = *mptr++, ch > 0) {
841 if ((ch == 10) || (strlen(buf) > 250)) {
842 cprintf("%s\n", buf);
845 buf[strlen(buf) + 1] = 0;
846 buf[strlen(buf)] = ch;
850 cprintf("%s\n", buf);
853 /* If the message on disk is format 0 (Citadel vari-format), we
854 * output using the formatter at 80 columns. This is the final output
855 * form if the transfer format is RFC822, but if the transfer format
856 * is Citadel proprietary, it'll still work, because the indentation
857 * for new paragraphs is correct and the client will reformat the
858 * message to the reader's screen width.
860 if (TheMessage->cm_format_type == 0) {
861 memfmout(80, mptr, 0);
864 /* If the message on disk is format 4 (MIME), we've gotta hand it
865 * off to the MIME parser. The client has already been told that
866 * this message is format 1 (fixed format), so the callback function
867 * we use will display those parts as-is.
869 if (TheMessage->cm_format_type == 4) {
870 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
871 memset(ma, 0, sizeof(struct ma_info));
872 mime_parser(mptr, NULL, *fixed_output);
877 CtdlFreeMessage(TheMessage);
884 * display a message (mode 0 - Citadel proprietary)
886 void cmd_msg0(char *cmdbuf)
889 int headers_only = 0;
891 extract(msgid, cmdbuf, 0);
892 headers_only = extract_int(cmdbuf, 1);
894 output_message(msgid, MT_CITADEL, headers_only);
900 * display a message (mode 2 - RFC822)
902 void cmd_msg2(char *cmdbuf)
905 int headers_only = 0;
907 extract(msgid, cmdbuf, 0);
908 headers_only = extract_int(cmdbuf, 1);
910 output_message(msgid, MT_RFC822, headers_only);
916 * display a message (mode 3 - IGnet raw format - internal programs only)
918 void cmd_msg3(char *cmdbuf)
921 struct CtdlMessage *msg;
924 if (CC->internal_pgm == 0) {
925 cprintf("%d This command is for internal programs only.\n",
930 msgnum = extract_long(cmdbuf, 0);
931 msg = CtdlFetchMessage(msgnum);
933 cprintf("%d Message %ld not found.\n",
938 serialize_message(&smr, msg);
939 CtdlFreeMessage(msg);
942 cprintf("%d Unable to serialize message\n",
943 ERROR+INTERNAL_ERROR);
947 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
948 client_write(smr.ser, smr.len);
955 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
957 void cmd_msg4(char *cmdbuf)
961 extract(msgid, cmdbuf, 0);
963 output_message(msgid, MT_MIME, 0);
967 * Open a component of a MIME message as a download file
969 void cmd_opna(char *cmdbuf)
973 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
975 extract(msgid, cmdbuf, 0);
976 extract(desired_section, cmdbuf, 1);
978 output_message(msgid, MT_DOWNLOAD, 0);
982 * Message base operation to send a message to the master file
983 * (returns new message number)
985 * This is the back end for CtdlSaveMsg() and should not be directly
986 * called by server-side modules.
989 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
990 int generate_id, /* generate 'I' field? */
991 FILE *save_a_copy) /* save a copy to disk? */
998 /* Get a new message number */
999 newmsgid = get_new_message_number();
1000 sprintf(msgidbuf, "%ld", newmsgid);
1003 msg->cm_fields['I'] = strdoop(msgidbuf);
1006 serialize_message(&smr, msg);
1009 cprintf("%d Unable to serialize message\n",
1010 ERROR+INTERNAL_ERROR);
1014 /* Write our little bundle of joy into the message base */
1015 begin_critical_section(S_MSGMAIN);
1016 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1017 smr.ser, smr.len) < 0) {
1018 lprintf(2, "Can't store message\n");
1023 end_critical_section(S_MSGMAIN);
1025 /* If the caller specified that a copy should be saved to a particular
1026 * file handle, do that now too.
1028 if (save_a_copy != NULL) {
1029 fwrite(smr.ser, smr.len, 1, save_a_copy);
1032 /* Free the memory we used for the serialized message */
1035 /* Return the *local* message ID to the caller
1036 * (even if we're storing an incoming network message)
1044 * Serialize a struct CtdlMessage into the format used on disk and network.
1046 * This function loads up a "struct ser_ret" (defined in server.h) which
1047 * contains the length of the serialized message and a pointer to the
1048 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1050 void serialize_message(struct ser_ret *ret, /* return values */
1051 struct CtdlMessage *msg) /* unserialized msg */
1055 static char *forder = FORDER;
1057 lprintf(9, "serialize_message() called\n");
1059 if (is_valid_message(msg) == 0) return; /* self check */
1061 lprintf(9, "magic number check OK.\n");
1064 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1065 ret->len = ret->len +
1066 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1068 lprintf(9, "message is %d bytes\n", ret->len);
1070 lprintf(9, "calling malloc\n");
1071 ret->ser = mallok(ret->len);
1072 if (ret->ser == NULL) {
1078 ret->ser[1] = msg->cm_anon_type;
1079 ret->ser[2] = msg->cm_format_type;
1081 lprintf(9, "stuff\n");
1083 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1084 ret->ser[wlen++] = (char)forder[i];
1085 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1086 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1088 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1097 * Save a message to disk
1099 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1100 char *rec, /* Recipient (mail) */
1101 char *force, /* force a particular room? */
1102 int mailtype, /* local or remote type */
1103 int generate_id) /* 1 = generate 'I' field */
1106 char hold_rm[ROOMNAMELEN];
1107 char actual_rm[ROOMNAMELEN];
1108 char force_room[ROOMNAMELEN];
1109 char content_type[256]; /* We have to learn this */
1110 char recipient[256];
1113 struct usersupp userbuf;
1115 int successful_local_recipients = 0;
1116 struct quickroom qtemp;
1117 struct SuppMsgInfo smi;
1118 FILE *network_fp = NULL;
1119 static int seqnum = 1;
1121 lprintf(9, "CtdlSaveMsg() called\n");
1122 if (is_valid_message(msg) == 0) return; /* self check */
1124 /* If this message has no timestamp, we take the liberty of
1125 * giving it one, right now.
1127 if (msg->cm_fields['T'] == NULL) {
1128 sprintf(aaa, "%ld", time(NULL));
1129 msg->cm_fields['T'] = strdoop(aaa);
1132 /* If this message has no path, we generate one.
1134 if (msg->cm_fields['P'] == NULL) {
1135 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1136 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1137 if (isspace(msg->cm_fields['P'][a])) {
1138 msg->cm_fields['P'][a] = ' ';
1143 strcpy(force_room, force);
1145 /* Strip non-printable characters out of the recipient name */
1146 strcpy(recipient, rec);
1147 for (a = 0; a < strlen(recipient); ++a)
1148 if (!isprint(recipient[a]))
1149 strcpy(&recipient[a], &recipient[a + 1]);
1151 /* Learn about what's inside, because it's what's inside that counts */
1153 switch (msg->cm_format_type) {
1155 strcpy(content_type, "text/x-citadel-variformat");
1158 strcpy(content_type, "text/plain");
1161 strcpy(content_type, "text/plain");
1162 /* advance past header fields */
1163 mptr = msg->cm_fields['M'];
1166 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1167 safestrncpy(content_type, mptr,
1168 sizeof(content_type));
1169 strcpy(content_type, &content_type[14]);
1170 for (a = 0; a < strlen(content_type); ++a)
1171 if ((content_type[a] == ';')
1172 || (content_type[a] == ' ')
1173 || (content_type[a] == 13)
1174 || (content_type[a] == 10))
1175 content_type[a] = 0;
1182 /* Perform "before save" hooks (aborting if any return nonzero) */
1183 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1185 /* Network mail - send a copy to the network program. */
1186 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1187 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1188 (long) getpid(), CC->cs_pid, ++seqnum);
1189 lprintf(9, "Saving a copy to %s\n", aaa);
1190 network_fp = fopen(aaa, "ab+");
1191 if (network_fp == NULL)
1192 lprintf(2, "ERROR: %s\n", strerror(errno));
1195 /* Save it to disk */
1196 newmsgid = send_message(msg, generate_id, network_fp);
1197 if (network_fp != NULL) {
1199 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1204 strcpy(actual_rm, CC->quickroom.QRname);
1205 strcpy(hold_rm, "");
1207 /* If this is being done by the networker delivering a private
1208 * message, we want to BYPASS saving the sender's copy (because there
1209 * is no local sender; it would otherwise go to the Trashcan).
1211 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1212 /* If the user is a twit, move to the twit room for posting */
1214 if (CC->usersupp.axlevel == 2) {
1215 strcpy(hold_rm, actual_rm);
1216 strcpy(actual_rm, config.c_twitroom);
1218 /* ...or if this message is destined for Aide> then go there. */
1219 if (strlen(force_room) > 0) {
1220 strcpy(hold_rm, actual_rm);
1221 strcpy(actual_rm, force_room);
1223 /* This call to usergoto() changes rooms if necessary. It also
1224 * causes the latest message list to be read into memory.
1226 usergoto(actual_rm, 0);
1228 /* read in the quickroom record, obtaining a lock... */
1229 lgetroom(&CC->quickroom, actual_rm);
1231 /* Fix an obscure bug */
1232 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1233 CC->quickroom.QRflags =
1234 CC->quickroom.QRflags & ~QR_MAILBOX;
1236 /* Add the message pointer to the room */
1237 CC->quickroom.QRhighest =
1238 AddMessageToRoom(&CC->quickroom, newmsgid);
1240 /* update quickroom */
1241 lputroom(&CC->quickroom);
1242 ++successful_local_recipients;
1245 /* Bump this user's messages posted counter. */
1246 lgetuser(&CC->usersupp, CC->curr_user);
1247 CC->usersupp.posted = CC->usersupp.posted + 1;
1248 lputuser(&CC->usersupp);
1250 /* If this is private, local mail, make a copy in the
1251 * recipient's mailbox and bump the reference count.
1253 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1254 if (getuser(&userbuf, recipient) == 0) {
1255 MailboxName(actual_rm, &userbuf, MAILROOM);
1256 if (lgetroom(&qtemp, actual_rm) == 0) {
1258 AddMessageToRoom(&qtemp, newmsgid);
1260 ++successful_local_recipients;
1264 /* If we've posted in a room other than the current room, then we
1265 * have to now go back to the current room...
1267 if (strlen(hold_rm) > 0) {
1268 usergoto(hold_rm, 0);
1271 /* Write a supplemental message info record. This doesn't have to
1272 * be a critical section because nobody else knows about this message
1275 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1276 smi.smi_msgnum = newmsgid;
1277 smi.smi_refcount = successful_local_recipients;
1278 safestrncpy(smi.smi_content_type, content_type, 64);
1279 PutSuppMsgInfo(&smi);
1281 /* Perform "after save" hooks */
1282 PerformMessageHooks(msg, EVT_AFTERSAVE);
1288 * Convenience function for generating small administrative messages.
1290 void quickie_message(char *from, char *to, char *room, char *text)
1292 struct CtdlMessage *msg;
1294 msg = mallok(sizeof(struct CtdlMessage));
1295 memset(msg, 0, sizeof(struct CtdlMessage));
1296 msg->cm_magic = CTDLMESSAGE_MAGIC;
1297 msg->cm_anon_type = MES_NORMAL;
1298 msg->cm_format_type = 0;
1299 msg->cm_fields['A'] = strdoop(from);
1300 msg->cm_fields['O'] = strdoop(room);
1301 msg->cm_fields['N'] = strdoop(NODENAME);
1303 msg->cm_fields['R'] = strdoop(to);
1304 msg->cm_fields['M'] = strdoop(text);
1306 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1307 CtdlFreeMessage(msg);
1308 syslog(LOG_NOTICE, text);
1313 * Build a binary message to be saved on disk.
1316 struct CtdlMessage *make_message(
1317 struct usersupp *author, /* author's usersupp structure */
1318 char *recipient, /* NULL if it's not mail */
1319 char *room, /* room where it's going */
1320 int type, /* see MES_ types in header file */
1321 int net_type, /* see MES_ types in header file */
1322 int format_type, /* local or remote (see citadel.h) */
1323 char *fake_name) /* who we're masquerading as */
1329 size_t message_len = 0;
1330 size_t buffer_len = 0;
1332 struct CtdlMessage *msg;
1334 msg = mallok(sizeof(struct CtdlMessage));
1335 memset(msg, 0, sizeof(struct CtdlMessage));
1336 msg->cm_magic = CTDLMESSAGE_MAGIC;
1337 msg->cm_anon_type = type;
1338 msg->cm_format_type = format_type;
1340 /* Don't confuse the poor folks if it's not routed mail. */
1341 strcpy(dest_node, "");
1343 /* If net_type is MES_BINARY, split out the destination node. */
1344 if (net_type == MES_BINARY) {
1345 strcpy(dest_node, NODENAME);
1346 for (a = 0; a < strlen(recipient); ++a) {
1347 if (recipient[a] == '@') {
1349 strcpy(dest_node, &recipient[a + 1]);
1354 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1355 if (net_type == MES_INTERNET) {
1356 strcpy(dest_node, "internet");
1359 while (isspace(recipient[strlen(recipient) - 1]))
1360 recipient[strlen(recipient) - 1] = 0;
1362 sprintf(buf, "cit%ld", author->usernum); /* Path */
1363 msg->cm_fields['P'] = strdoop(buf);
1365 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1366 msg->cm_fields['T'] = strdoop(buf);
1368 if (fake_name[0]) /* author */
1369 msg->cm_fields['A'] = strdoop(fake_name);
1371 msg->cm_fields['A'] = strdoop(author->fullname);
1373 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1374 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1376 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1378 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1379 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1381 if (recipient[0] != 0)
1382 msg->cm_fields['R'] = strdoop(recipient);
1383 if (dest_node[0] != 0)
1384 msg->cm_fields['D'] = strdoop(dest_node);
1386 msg->cm_fields['M'] = mallok(4096);
1387 if (msg->cm_fields['M'] == NULL) {
1388 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1392 msg->cm_fields['M'][0] = 0;
1396 /* read in the lines of message text one by one */
1397 while (client_gets(buf), strcmp(buf, "000")) {
1399 /* augment the buffer if we have to */
1400 if ((message_len + strlen(buf) + 2) > buffer_len) {
1401 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1402 if (ptr == NULL) { /* flush if can't allocate */
1403 while (client_gets(buf), strcmp(buf, "000")) ;;
1406 buffer_len = (buffer_len * 2);
1407 msg->cm_fields['M'] = ptr;
1411 strcat(msg->cm_fields['M'], buf);
1412 strcat(msg->cm_fields['M'], "\n");
1414 /* if we've hit the max msg length, flush the rest */
1415 if (message_len >= config.c_maxmsglen) {
1416 while (client_gets(buf), strcmp(buf, "000")) ;;
1429 * message entry - mode 0 (normal)
1431 void cmd_ent0(char *entargs)
1434 char recipient[256];
1436 int format_type = 0;
1437 char newusername[256];
1438 struct CtdlMessage *msg;
1442 struct usersupp tempUS;
1445 post = extract_int(entargs, 0);
1446 extract(recipient, entargs, 1);
1447 anon_flag = extract_int(entargs, 2);
1448 format_type = extract_int(entargs, 3);
1450 /* first check to make sure the request is valid. */
1452 if (!(CC->logged_in)) {
1453 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1456 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1457 cprintf("%d Need to be validated to enter ",
1458 ERROR + HIGHER_ACCESS_REQUIRED);
1459 cprintf("(except in %s> to sysop)\n", MAILROOM);
1462 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1463 cprintf("%d Need net privileges to enter here.\n",
1464 ERROR + HIGHER_ACCESS_REQUIRED);
1467 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1468 cprintf("%d Sorry, this is a read-only room.\n",
1469 ERROR + HIGHER_ACCESS_REQUIRED);
1476 if (CC->usersupp.axlevel < 6) {
1477 cprintf("%d You don't have permission to masquerade.\n",
1478 ERROR + HIGHER_ACCESS_REQUIRED);
1481 extract(newusername, entargs, 4);
1482 memset(CC->fake_postname, 0, 32);
1483 strcpy(CC->fake_postname, newusername);
1484 cprintf("%d Ok\n", OK);
1487 CC->cs_flags |= CS_POSTING;
1490 if (CC->quickroom.QRflags & QR_MAILBOX) {
1491 if (CC->usersupp.axlevel >= 2) {
1492 strcpy(buf, recipient);
1494 strcpy(buf, "sysop");
1495 e = alias(buf); /* alias and mail type */
1496 if ((buf[0] == 0) || (e == MES_ERROR)) {
1497 cprintf("%d Unknown address - cannot send message.\n",
1498 ERROR + NO_SUCH_USER);
1501 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1502 cprintf("%d Net privileges required for network mail.\n",
1503 ERROR + HIGHER_ACCESS_REQUIRED);
1506 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1507 && ((CC->usersupp.flags & US_INTERNET) == 0)
1508 && (!CC->internal_pgm)) {
1509 cprintf("%d You don't have access to Internet mail.\n",
1510 ERROR + HIGHER_ACCESS_REQUIRED);
1513 if (!strcasecmp(buf, "sysop")) {
1518 goto SKFALL; /* don't search local file */
1519 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1520 cprintf("%d Can't send mail to yourself!\n",
1521 ERROR + NO_SUCH_USER);
1524 /* Check to make sure the user exists; also get the correct
1525 * upper/lower casing of the name.
1527 a = getuser(&tempUS, buf);
1529 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1532 strcpy(buf, tempUS.fullname);
1535 SKFALL: b = MES_NORMAL;
1536 if (CC->quickroom.QRflags & QR_ANONONLY)
1538 if (CC->quickroom.QRflags & QR_ANONOPT) {
1542 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1545 /* If we're only checking the validity of the request, return
1546 * success without creating the message.
1549 cprintf("%d %s\n", OK, buf);
1553 cprintf("%d send message\n", SEND_LISTING);
1555 /* Read in the message from the client. */
1556 if (CC->fake_postname[0])
1557 msg = make_message(&CC->usersupp, buf,
1558 CC->quickroom.QRname, b, e, format_type,
1560 else if (CC->fake_username[0])
1561 msg = make_message(&CC->usersupp, buf,
1562 CC->quickroom.QRname, b, e, format_type,
1565 msg = make_message(&CC->usersupp, buf,
1566 CC->quickroom.QRname, b, e, format_type, "");
1569 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1570 CtdlFreeMessage(msg);
1571 CC->fake_postname[0] = '\0';
1578 * message entry - mode 3 (raw)
1580 void cmd_ent3(char *entargs)
1585 unsigned char ch, which_field;
1586 struct usersupp tempUS;
1588 struct CtdlMessage *msg;
1591 if (CC->internal_pgm == 0) {
1592 cprintf("%d This command is for internal programs only.\n",
1597 /* See if there's a recipient, but make sure it's a real one */
1598 extract(recp, entargs, 1);
1599 for (a = 0; a < strlen(recp); ++a)
1600 if (!isprint(recp[a]))
1601 strcpy(&recp[a], &recp[a + 1]);
1602 while (isspace(recp[0]))
1603 strcpy(recp, &recp[1]);
1604 while (isspace(recp[strlen(recp) - 1]))
1605 recp[strlen(recp) - 1] = 0;
1607 /* If we're in Mail, check the recipient */
1608 if (strlen(recp) > 0) {
1609 e = alias(recp); /* alias and mail type */
1610 if ((recp[0] == 0) || (e == MES_ERROR)) {
1611 cprintf("%d Unknown address - cannot send message.\n",
1612 ERROR + NO_SUCH_USER);
1615 if (e == MES_LOCAL) {
1616 a = getuser(&tempUS, recp);
1618 cprintf("%d No such user.\n",
1619 ERROR + NO_SUCH_USER);
1625 /* At this point, message has been approved. */
1626 if (extract_int(entargs, 0) == 0) {
1627 cprintf("%d OK to send\n", OK);
1631 msglen = extract_long(entargs, 2);
1632 msg = mallok(sizeof(struct CtdlMessage));
1634 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1638 memset(msg, 0, sizeof(struct CtdlMessage));
1639 tempbuf = mallok(msglen);
1640 if (tempbuf == NULL) {
1641 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1646 cprintf("%d %ld\n", SEND_BINARY, msglen);
1648 client_read(&ch, 1); /* 0xFF magic number */
1649 msg->cm_magic = CTDLMESSAGE_MAGIC;
1650 client_read(&ch, 1); /* anon type */
1651 msg->cm_anon_type = ch;
1652 client_read(&ch, 1); /* format type */
1653 msg->cm_format_type = ch;
1654 msglen = msglen - 3;
1656 while (msglen > 0) {
1657 client_read(&which_field, 1);
1661 client_read(&ch, 1);
1663 a = strlen(tempbuf);
1666 } while ( (ch != 0) && (msglen > 0) );
1667 msg->cm_fields[which_field] = strdoop(tempbuf);
1670 CtdlSaveMsg(msg, recp, "", e, 0);
1671 CtdlFreeMessage(msg);
1677 * API function to delete messages which match a set of criteria
1678 * (returns the actual number of messages deleted)
1680 int CtdlDeleteMessages(char *room_name, /* which room */
1681 long dmsgnum, /* or "0" for any */
1682 char *content_type /* or NULL for any */
1686 struct quickroom qrbuf;
1687 struct cdbdata *cdbfr;
1688 long *msglist = NULL;
1691 int num_deleted = 0;
1693 struct SuppMsgInfo smi;
1695 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1696 room_name, dmsgnum, content_type);
1698 /* get room record, obtaining a lock... */
1699 if (lgetroom(&qrbuf, room_name) != 0) {
1700 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1702 return (0); /* room not found */
1704 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1706 if (cdbfr != NULL) {
1707 msglist = mallok(cdbfr->len);
1708 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1709 num_msgs = cdbfr->len / sizeof(long);
1713 for (i = 0; i < num_msgs; ++i) {
1716 /* Set/clear a bit for each criterion */
1718 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1719 delete_this |= 0x01;
1721 if (content_type == NULL) {
1722 delete_this |= 0x02;
1724 GetSuppMsgInfo(&smi, msglist[i]);
1725 if (!strcasecmp(smi.smi_content_type,
1727 delete_this |= 0x02;
1731 /* Delete message only if all bits are set */
1732 if (delete_this == 0x03) {
1733 AdjRefCount(msglist[i], -1);
1739 num_msgs = sort_msglist(msglist, num_msgs);
1740 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1741 msglist, (num_msgs * sizeof(long)));
1743 qrbuf.QRhighest = msglist[num_msgs - 1];
1747 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1748 return (num_deleted);
1754 * Delete message from current room
1756 void cmd_dele(char *delstr)
1761 getuser(&CC->usersupp, CC->curr_user);
1762 if ((CC->usersupp.axlevel < 6)
1763 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1764 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1765 cprintf("%d Higher access required.\n",
1766 ERROR + HIGHER_ACCESS_REQUIRED);
1769 delnum = extract_long(delstr, 0);
1771 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1774 cprintf("%d %d message%s deleted.\n", OK,
1775 num_deleted, ((num_deleted != 1) ? "s" : ""));
1777 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1783 * move a message to another room
1785 void cmd_move(char *args)
1789 struct quickroom qtemp;
1792 num = extract_long(args, 0);
1793 extract(targ, args, 1);
1795 getuser(&CC->usersupp, CC->curr_user);
1796 if ((CC->usersupp.axlevel < 6)
1797 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1798 cprintf("%d Higher access required.\n",
1799 ERROR + HIGHER_ACCESS_REQUIRED);
1802 if (getroom(&qtemp, targ) != 0) {
1803 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1806 /* Bump the reference count, otherwise the message will be deleted
1807 * from disk when we remove it from the source room.
1809 AdjRefCount(num, 1);
1811 /* yank the message out of the current room... */
1812 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
1815 /* put the message into the target room */
1816 lgetroom(&qtemp, targ);
1817 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1819 cprintf("%d Message moved.\n", OK);
1821 AdjRefCount(num, (-1)); /* oops */
1822 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1829 * GetSuppMsgInfo() - Get the supplementary record for a message
1831 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1834 struct cdbdata *cdbsmi;
1837 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1838 smibuf->smi_msgnum = msgnum;
1839 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1841 /* Use the negative of the message number for its supp record index */
1842 TheIndex = (0L - msgnum);
1844 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1845 if (cdbsmi == NULL) {
1846 return; /* record not found; go with defaults */
1848 memcpy(smibuf, cdbsmi->ptr,
1849 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1850 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1857 * PutSuppMsgInfo() - (re)write supplementary record for a message
1859 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1863 /* Use the negative of the message number for its supp record index */
1864 TheIndex = (0L - smibuf->smi_msgnum);
1866 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1867 smibuf->smi_msgnum, smibuf->smi_refcount);
1869 cdb_store(CDB_MSGMAIN,
1870 &TheIndex, sizeof(long),
1871 smibuf, sizeof(struct SuppMsgInfo));
1876 * AdjRefCount - change the reference count for a message;
1877 * delete the message if it reaches zero
1879 void AdjRefCount(long msgnum, int incr)
1882 struct SuppMsgInfo smi;
1885 /* This is a *tight* critical section; please keep it that way, as
1886 * it may get called while nested in other critical sections.
1887 * Complicating this any further will surely cause deadlock!
1889 begin_critical_section(S_SUPPMSGMAIN);
1890 GetSuppMsgInfo(&smi, msgnum);
1891 smi.smi_refcount += incr;
1892 PutSuppMsgInfo(&smi);
1893 end_critical_section(S_SUPPMSGMAIN);
1895 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1896 msgnum, smi.smi_refcount);
1898 /* If the reference count is now zero, delete the message
1899 * (and its supplementary record as well).
1901 if (smi.smi_refcount == 0) {
1902 lprintf(9, "Deleting message <%ld>\n", msgnum);
1904 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1905 delnum = (0L - msgnum);
1906 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1911 * Write a generic object to this room
1913 * Note: this could be much more efficient. Right now we use two temporary
1914 * files, and still pull the message into memory as with all others.
1916 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
1917 char *content_type, /* MIME type of this object */
1918 char *tempfilename, /* Where to fetch it from */
1919 struct usersupp *is_mailbox, /* Mailbox room? */
1920 int is_binary, /* Is encoding necessary? */
1921 int is_unique /* Del others of this type? */
1926 char filename[PATH_MAX];
1929 struct quickroom qrbuf;
1930 char roomname[ROOMNAMELEN];
1931 struct CtdlMessage *msg;
1934 if (is_mailbox != NULL)
1935 MailboxName(roomname, is_mailbox, req_room);
1937 safestrncpy(roomname, req_room, sizeof(roomname));
1938 lprintf(9, "CtdlWriteObject() to <%s>\n", roomname);
1940 strcpy(filename, tmpnam(NULL));
1941 fp = fopen(filename, "w");
1945 tempfp = fopen(tempfilename, "r");
1946 if (tempfp == NULL) {
1952 fprintf(fp, "Content-type: %s\n", content_type);
1953 lprintf(9, "Content-type: %s\n", content_type);
1955 if (is_binary == 0) {
1956 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
1957 while (ch = getc(tempfp), ch > 0)
1963 fprintf(fp, "Content-transfer-encoding: base64\n\n");
1966 sprintf(cmdbuf, "./base64 -e <%s >>%s",
1967 tempfilename, filename);
1971 lprintf(9, "Allocating\n");
1972 msg = mallok(sizeof(struct CtdlMessage));
1973 memset(msg, 0, sizeof(struct CtdlMessage));
1974 msg->cm_magic = CTDLMESSAGE_MAGIC;
1975 msg->cm_anon_type = MES_NORMAL;
1976 msg->cm_format_type = 4;
1977 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
1978 msg->cm_fields['O'] = strdoop(req_room);
1979 msg->cm_fields['N'] = strdoop(config.c_nodename);
1980 msg->cm_fields['H'] = strdoop(config.c_humannode);
1982 lprintf(9, "Loading\n");
1983 fp = fopen(filename, "rb");
1984 fseek(fp, 0L, SEEK_END);
1987 msg->cm_fields['M'] = mallok(len);
1988 fread(msg->cm_fields['M'], len, 1, fp);
1992 /* Create the requested room if we have to. */
1993 if (getroom(&qrbuf, roomname) != 0) {
1994 create_room(roomname, 4, "", 0);
1996 /* If the caller specified this object as unique, delete all
1997 * other objects of this type that are currently in the room.
2000 lprintf(9, "Deleted %d other msgs of this type\n",
2001 CtdlDeleteMessages(roomname, 0L, content_type));
2003 /* Now write the data */
2004 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2005 CtdlFreeMessage(msg);