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 "", "", "", "", "", "", "", "",
40 "", "", "", "", "", "", "", "",
41 "", "", "", "", "", "", "", "",
42 "", "", "", "", "", "", "", "",
43 "", "", "", "", "", "", "", "",
44 "", "", "", "", "", "", "", "",
45 "", "", "", "", "", "", "", "",
46 "", "", "", "", "", "", "", "",
49 "", "", "", "", "", "",
70 * This function is self explanatory.
71 * (What can I say, I'm in a weird mood today...)
73 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
77 for (i = 0; i < strlen(name); ++i)
80 if (isspace(name[i - 1])) {
81 strcpy(&name[i - 1], &name[i]);
84 while (isspace(name[i + 1])) {
85 strcpy(&name[i + 1], &name[i + 2]);
92 * Aliasing for network mail.
93 * (Error messages have been commented out, because this is a server.)
96 { /* process alias and routing info for mail */
99 char aaa[300], bbb[300];
101 lprintf(9, "alias() called for <%s>\n", name);
103 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
105 fp = fopen("network/mail.aliases", "r");
107 fp = fopen("/dev/null", "r");
112 while (fgets(aaa, sizeof aaa, fp) != NULL) {
113 while (isspace(name[0]))
114 strcpy(name, &name[1]);
115 aaa[strlen(aaa) - 1] = 0;
117 for (a = 0; a < strlen(aaa); ++a) {
119 strcpy(bbb, &aaa[a + 1]);
123 if (!strcasecmp(name, aaa))
127 lprintf(7, "Mail is being forwarded to %s\n", name);
129 /* determine local or remote type, see citadel.h */
130 for (a = 0; a < strlen(name); ++a)
132 return (MES_INTERNET);
133 for (a = 0; a < strlen(name); ++a)
135 for (b = a; b < strlen(name); ++b)
137 return (MES_INTERNET);
139 for (a = 0; a < strlen(name); ++a)
143 lprintf(7, "Too many @'s in address\n");
147 for (a = 0; a < strlen(name); ++a)
149 strcpy(bbb, &name[a + 1]);
151 strcpy(bbb, &bbb[1]);
152 fp = fopen("network/mail.sysinfo", "r");
156 a = getstring(fp, aaa);
157 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
158 a = getstring(fp, aaa);
159 if (!strncmp(aaa, "use ", 4)) {
160 strcpy(bbb, &aaa[4]);
165 if (!strncmp(aaa, "uum", 3)) {
167 for (a = 0; a < strlen(bbb); ++a) {
173 while (bbb[strlen(bbb) - 1] == '_')
174 bbb[strlen(bbb) - 1] = 0;
175 sprintf(name, &aaa[4], bbb);
176 return (MES_INTERNET);
178 if (!strncmp(aaa, "bin", 3)) {
181 while (aaa[strlen(aaa) - 1] != '@')
182 aaa[strlen(aaa) - 1] = 0;
183 aaa[strlen(aaa) - 1] = 0;
184 while (aaa[strlen(aaa) - 1] == ' ')
185 aaa[strlen(aaa) - 1] = 0;
186 while (bbb[0] != '@')
187 strcpy(bbb, &bbb[1]);
188 strcpy(bbb, &bbb[1]);
189 while (bbb[0] == ' ')
190 strcpy(bbb, &bbb[1]);
191 sprintf(name, "%s @%s", aaa, bbb);
204 fp = fopen("citadel.control", "r");
205 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
211 void simple_listing(long msgnum)
213 cprintf("%ld\n", msgnum);
218 /* Determine if a given message matches the fields in a message template.
219 * Return 0 for a successful match.
221 int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
224 /* If there aren't any fields in the template, all messages will
227 if (template == NULL) return(0);
229 /* Null messages are bogus. */
230 if (msg == NULL) return(1);
232 for (i='A'; i<='Z'; ++i) {
233 if (template->cm_fields[i] != NULL) {
234 if (msg->cm_fields[i] == NULL) {
237 if (strcasecmp(msg->cm_fields[i],
238 template->cm_fields[i])) return 1;
242 /* All compares succeeded: we have a match! */
250 * API function to perform an operation for each qualifying message in the
253 void CtdlForEachMessage(int mode, long ref,
255 struct CtdlMessage *compare,
256 void (*CallBack) (long msgnum))
261 struct cdbdata *cdbfr;
262 long *msglist = NULL;
265 struct SuppMsgInfo smi;
266 struct CtdlMessage *msg;
268 /* Learn about the user and room in question */
270 getuser(&CC->usersupp, CC->curr_user);
271 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
273 /* Load the message list */
274 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
276 msglist = mallok(cdbfr->len);
277 memcpy(msglist, cdbfr->ptr, cdbfr->len);
278 num_msgs = cdbfr->len / sizeof(long);
281 return; /* No messages at all? No further action. */
285 /* If the caller is looking for a specific MIME type, then filter
286 * out all messages which are not of the type requested.
289 if (content_type != NULL)
290 if (strlen(content_type) > 0)
291 for (a = 0; a < num_msgs; ++a) {
292 GetSuppMsgInfo(&smi, msglist[a]);
293 if (strcasecmp(smi.smi_content_type, content_type)) {
298 num_msgs = sort_msglist(msglist, num_msgs);
300 /* If a template was supplied, filter out the messages which
301 * don't match. (This could induce some delays!)
304 if (compare != NULL) {
305 for (a = 0; a < num_msgs; ++a) {
306 msg = CtdlFetchMessage(msglist[a]);
308 if (CtdlMsgCmp(msg, compare)) {
311 CtdlFreeMessage(msg);
319 * Now iterate through the message list, according to the
320 * criteria supplied by the caller.
323 for (a = 0; a < num_msgs; ++a) {
324 thismsg = msglist[a];
329 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
330 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
331 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
332 && (CC->usersupp.flags & US_LASTOLD))
333 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
334 || ((mode == MSGS_FIRST) && (a < ref))
335 || ((mode == MSGS_GT) && (thismsg > ref))
341 phree(msglist); /* Clean up */
347 * cmd_msgs() - get list of message #'s in this room
348 * implements the MSGS server command using CtdlForEachMessage()
350 void cmd_msgs(char *cmdbuf)
359 int with_template = 0;
360 struct CtdlMessage *template = NULL;
362 extract(which, cmdbuf, 0);
363 cm_ref = extract_int(cmdbuf, 1);
364 with_template = extract_int(cmdbuf, 2);
368 if (!strncasecmp(which, "OLD", 3))
370 else if (!strncasecmp(which, "NEW", 3))
372 else if (!strncasecmp(which, "FIRST", 5))
374 else if (!strncasecmp(which, "LAST", 4))
376 else if (!strncasecmp(which, "GT", 2))
379 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
380 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
385 cprintf("%d Send template then receive message list\n",
387 template = (struct CtdlMessage *)
388 mallok(sizeof(struct CtdlMessage));
389 memset(template, 0, sizeof(struct CtdlMessage));
390 while(client_gets(buf), strcmp(buf,"000")) {
391 extract(tfield, buf, 0);
392 extract(tvalue, buf, 1);
393 for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
394 if (!strcasecmp(tfield, msgkeys[i])) {
395 template->cm_fields[i] =
402 cprintf("%d Message list...\n", LISTING_FOLLOWS);
405 CtdlForEachMessage(mode, cm_ref, NULL, template, simple_listing);
406 if (template != NULL) CtdlFreeMessage(template);
414 * help_subst() - support routine for help file viewer
416 void help_subst(char *strbuf, char *source, char *dest)
421 while (p = pattern2(strbuf, source), (p >= 0)) {
422 strcpy(workbuf, &strbuf[p + strlen(source)]);
423 strcpy(&strbuf[p], dest);
424 strcat(strbuf, workbuf);
429 void do_help_subst(char *buffer)
433 help_subst(buffer, "^nodename", config.c_nodename);
434 help_subst(buffer, "^humannode", config.c_humannode);
435 help_subst(buffer, "^fqdn", config.c_fqdn);
436 help_subst(buffer, "^username", CC->usersupp.fullname);
437 sprintf(buf2, "%ld", CC->usersupp.usernum);
438 help_subst(buffer, "^usernum", buf2);
439 help_subst(buffer, "^sysadm", config.c_sysadm);
440 help_subst(buffer, "^variantname", CITADEL);
441 sprintf(buf2, "%d", config.c_maxsessions);
442 help_subst(buffer, "^maxsessions", buf2);
448 * memfmout() - Citadel text formatter and paginator.
449 * Although the original purpose of this routine was to format
450 * text to the reader's screen width, all we're really using it
451 * for here is to format text out to 80 columns before sending it
452 * to the client. The client software may reformat it again.
454 void memfmout(int width, char *mptr, char subst)
455 /* screen width to use */
456 /* where are we going to get our text from? */
457 /* nonzero if we should use hypertext mode */
469 c = 1; /* c is the current pos */
472 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
474 buffer[strlen(buffer) + 1] = 0;
475 buffer[strlen(buffer)] = ch;
478 if (buffer[0] == '^')
479 do_help_subst(buffer);
481 buffer[strlen(buffer) + 1] = 0;
483 strcpy(buffer, &buffer[1]);
493 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
495 if (((old == 13) || (old == 10)) && (isspace(real))) {
503 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
504 cprintf("\n%s", aaa);
513 if ((strlen(aaa) + c) > (width - 5)) {
523 if ((ch == 13) || (ch == 10)) {
524 cprintf("%s\n", aaa);
531 FMTEND: cprintf("%s\n", aaa);
537 * Callback function for mime parser that simply lists the part
539 void list_this_part(char *name, char *filename, char *partnum, char *disp,
540 void *content, char *cbtype, size_t length)
543 cprintf("part=%s|%s|%s|%s|%s|%d\n",
544 name, filename, partnum, disp, cbtype, length);
549 * Callback function for mime parser that wants to display text
551 void fixed_output(char *name, char *filename, char *partnum, char *disp,
552 void *content, char *cbtype, size_t length)
556 if (!strcasecmp(cbtype, "multipart/alternative")) {
557 strcpy(ma->prefix, partnum);
558 strcat(ma->prefix, ".");
564 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
566 && (ma->did_print == 1) ) {
567 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
573 if (!strcasecmp(cbtype, "text/plain")) {
574 client_write(content, length);
576 else if (!strcasecmp(cbtype, "text/html")) {
577 ptr = html_to_ascii(content, 80, 0);
578 client_write(ptr, strlen(ptr));
581 else if (strncasecmp(cbtype, "multipart/", 10)) {
582 cprintf("Part %s: %s (%s) (%d bytes)\n",
583 partnum, filename, cbtype, length);
589 * Callback function for mime parser that opens a section for downloading
591 void mime_download(char *name, char *filename, char *partnum, char *disp,
592 void *content, char *cbtype, size_t length)
595 /* Silently go away if there's already a download open... */
596 if (CC->download_fp != NULL)
599 /* ...or if this is not the desired section */
600 if (strcasecmp(desired_section, partnum))
603 CC->download_fp = tmpfile();
604 if (CC->download_fp == NULL)
607 fwrite(content, length, 1, CC->download_fp);
608 fflush(CC->download_fp);
609 rewind(CC->download_fp);
611 OpenCmdResult(filename, cbtype);
617 * Load a message from disk into memory.
618 * This is used by output_message() and other fetch functions.
620 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
621 * using the CtdlMessageFree() function.
623 struct CtdlMessage *CtdlFetchMessage(long msgnum)
625 struct cdbdata *dmsgtext;
626 struct CtdlMessage *ret = NULL;
629 CIT_UBYTE field_header;
633 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
634 if (dmsgtext == NULL) {
635 lprintf(9, "CtdlFetchMessage(%ld) failed.\n");
638 mptr = dmsgtext->ptr;
640 /* Parse the three bytes that begin EVERY message on disk.
641 * The first is always 0xFF, the on-disk magic number.
642 * The second is the anonymous/public type byte.
643 * The third is the format type byte (vari, fixed, or MIME).
647 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
651 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
652 memset(ret, 0, sizeof(struct CtdlMessage));
654 ret->cm_magic = CTDLMESSAGE_MAGIC;
655 ret->cm_anon_type = *mptr++; /* Anon type byte */
656 ret->cm_format_type = *mptr++; /* Format type byte */
659 * The rest is zero or more arbitrary fields. Load them in.
660 * We're done when we encounter either a zero-length field or
661 * have just processed the 'M' (message text) field.
664 field_length = strlen(mptr);
665 if (field_length == 0)
667 field_header = *mptr++;
668 ret->cm_fields[field_header] = mallok(field_length);
669 strcpy(ret->cm_fields[field_header], mptr);
671 while (*mptr++ != 0); /* advance to next field */
673 } while ((field_length > 0) && (field_header != 'M'));
677 /* Perform "before read" hooks (aborting if any return nonzero) */
678 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
679 CtdlFreeMessage(ret);
688 * Returns 1 if the supplied pointer points to a valid Citadel message.
689 * If the pointer is NULL or the magic number check fails, returns 0.
691 int is_valid_message(struct CtdlMessage *msg) {
694 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
695 lprintf(3, "is_valid_message() -- self-check failed\n");
703 * 'Destructor' for struct CtdlMessage
705 void CtdlFreeMessage(struct CtdlMessage *msg)
709 if (is_valid_message(msg) == 0) return;
711 for (i = 0; i < 256; ++i)
712 if (msg->cm_fields[i] != NULL)
713 phree(msg->cm_fields[i]);
715 msg->cm_magic = 0; /* just in case */
722 * Get a message off disk. (return value is the message's timestamp)
725 void output_message(char *msgid, int mode, int headers_only)
733 char display_name[256];
735 struct CtdlMessage *TheMessage = NULL;
739 /* buffers needed for RFC822 translation */
747 msg_num = atol(msgid);
749 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
750 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
754 /* FIX ... small security issue
755 * We need to check to make sure the requested message is actually
756 * in the current room, and set msg_ok to 1 only if it is. This
757 * functionality is currently missing because I'm in a hurry to replace
758 * broken production code with nonbroken pre-beta code. :( -- ajc
761 cprintf("%d Message %ld is not in this room.\n",
768 * Fetch the message from disk
770 TheMessage = CtdlFetchMessage(msg_num);
771 if (TheMessage == NULL) {
772 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
776 /* Are we downloading a MIME component? */
777 if (mode == MT_DOWNLOAD) {
778 if (TheMessage->cm_format_type != 4) {
779 cprintf("%d This is not a MIME message.\n",
781 } else if (CC->download_fp != NULL) {
782 cprintf("%d You already have a download open.\n",
785 /* Parse the message text component */
786 mptr = TheMessage->cm_fields['M'];
787 mime_parser(mptr, NULL, *mime_download);
788 /* If there's no file open by this time, the requested
789 * section wasn't found, so print an error
791 if (CC->download_fp == NULL) {
792 cprintf("%d Section %s not found.\n",
793 ERROR + FILE_NOT_FOUND,
797 CtdlFreeMessage(TheMessage);
801 /* now for the user-mode message reading loops */
802 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
804 /* Tell the client which format type we're using. If this is a
805 * MIME message, *lie* about it and tell the user it's fixed-format.
807 if (mode == MT_CITADEL) {
808 if (TheMessage->cm_format_type == 4)
811 cprintf("type=%d\n", TheMessage->cm_format_type);
814 /* nhdr=yes means that we're only displaying headers, no body */
815 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
816 cprintf("nhdr=yes\n");
819 /* begin header processing loop for Citadel message format */
821 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
823 strcpy(display_name, "<unknown>");
824 if (TheMessage->cm_fields['A']) {
825 strcpy(buf, TheMessage->cm_fields['A']);
826 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
827 if (TheMessage->cm_anon_type == MES_ANON)
828 strcpy(display_name, "****");
829 else if (TheMessage->cm_anon_type == MES_AN2)
830 strcpy(display_name, "anonymous");
832 strcpy(display_name, buf);
834 && ((TheMessage->cm_anon_type == MES_ANON)
835 || (TheMessage->cm_anon_type == MES_AN2))) {
836 sprintf(&display_name[strlen(display_name)],
841 strcpy(allkeys, FORDER);
842 for (i=0; i<strlen(allkeys); ++i) {
843 k = (int) allkeys[i];
845 if (TheMessage->cm_fields[k] != NULL) {
854 TheMessage->cm_fields[k]
863 /* begin header processing loop for RFC822 transfer format */
867 strcpy(snode, NODENAME);
868 strcpy(lnode, HUMANNODE);
869 if (mode == MT_RFC822) {
870 for (i = 0; i < 256; ++i) {
871 if (TheMessage->cm_fields[i]) {
872 mptr = TheMessage->cm_fields[i];
876 } else if (i == 'P') {
877 cprintf("Path: %s\n", mptr);
878 for (a = 0; a < strlen(mptr); ++a) {
879 if (mptr[a] == '!') {
880 strcpy(mptr, &mptr[a + 1]);
886 cprintf("Subject: %s\n", mptr);
892 cprintf("X-Citadel-Room: %s\n", mptr);
896 cprintf("To: %s\n", mptr);
899 cprintf("Date: %s", asctime(localtime(&xtime)));
905 if (mode == MT_RFC822) {
906 if (!strcasecmp(snode, NODENAME)) {
909 cprintf("Message-ID: <%s@%s>\n", mid, snode);
910 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
911 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
912 cprintf("Organization: %s\n", lnode);
915 /* end header processing loop ... at this point, we're in the text */
917 mptr = TheMessage->cm_fields['M'];
919 /* Tell the client about the MIME parts in this message */
920 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
921 if (mode == MT_CITADEL) {
922 mime_parser(mptr, NULL, *list_this_part);
924 else if (mode == MT_MIME) { /* list parts only */
925 mime_parser(mptr, NULL, *list_this_part);
927 CtdlFreeMessage(TheMessage);
934 CtdlFreeMessage(TheMessage);
938 /* signify start of msg text */
939 if (mode == MT_CITADEL)
941 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
944 /* If the format type on disk is 1 (fixed-format), then we want
945 * everything to be output completely literally ... regardless of
946 * what message transfer format is in use.
948 if (TheMessage->cm_format_type == 1) {
950 while (ch = *mptr++, ch > 0) {
953 if ((ch == 10) || (strlen(buf) > 250)) {
954 cprintf("%s\n", buf);
957 buf[strlen(buf) + 1] = 0;
958 buf[strlen(buf)] = ch;
962 cprintf("%s\n", buf);
965 /* If the message on disk is format 0 (Citadel vari-format), we
966 * output using the formatter at 80 columns. This is the final output
967 * form if the transfer format is RFC822, but if the transfer format
968 * is Citadel proprietary, it'll still work, because the indentation
969 * for new paragraphs is correct and the client will reformat the
970 * message to the reader's screen width.
972 if (TheMessage->cm_format_type == 0) {
973 memfmout(80, mptr, 0);
976 /* If the message on disk is format 4 (MIME), we've gotta hand it
977 * off to the MIME parser. The client has already been told that
978 * this message is format 1 (fixed format), so the callback function
979 * we use will display those parts as-is.
981 if (TheMessage->cm_format_type == 4) {
982 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
983 memset(ma, 0, sizeof(struct ma_info));
984 mime_parser(mptr, NULL, *fixed_output);
989 CtdlFreeMessage(TheMessage);
996 * display a message (mode 0 - Citadel proprietary)
998 void cmd_msg0(char *cmdbuf)
1001 int headers_only = 0;
1003 extract(msgid, cmdbuf, 0);
1004 headers_only = extract_int(cmdbuf, 1);
1006 output_message(msgid, MT_CITADEL, headers_only);
1012 * display a message (mode 2 - RFC822)
1014 void cmd_msg2(char *cmdbuf)
1017 int headers_only = 0;
1019 extract(msgid, cmdbuf, 0);
1020 headers_only = extract_int(cmdbuf, 1);
1022 output_message(msgid, MT_RFC822, headers_only);
1028 * display a message (mode 3 - IGnet raw format - internal programs only)
1030 void cmd_msg3(char *cmdbuf)
1033 struct CtdlMessage *msg;
1036 if (CC->internal_pgm == 0) {
1037 cprintf("%d This command is for internal programs only.\n",
1042 msgnum = extract_long(cmdbuf, 0);
1043 msg = CtdlFetchMessage(msgnum);
1045 cprintf("%d Message %ld not found.\n",
1050 serialize_message(&smr, msg);
1051 CtdlFreeMessage(msg);
1054 cprintf("%d Unable to serialize message\n",
1055 ERROR+INTERNAL_ERROR);
1059 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1060 client_write(smr.ser, smr.len);
1067 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
1069 void cmd_msg4(char *cmdbuf)
1073 extract(msgid, cmdbuf, 0);
1075 output_message(msgid, MT_MIME, 0);
1079 * Open a component of a MIME message as a download file
1081 void cmd_opna(char *cmdbuf)
1085 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
1087 extract(msgid, cmdbuf, 0);
1088 extract(desired_section, cmdbuf, 1);
1090 output_message(msgid, MT_DOWNLOAD, 0);
1094 * Message base operation to send a message to the master file
1095 * (returns new message number)
1097 * This is the back end for CtdlSaveMsg() and should not be directly
1098 * called by server-side modules.
1101 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1102 int generate_id, /* generate 'I' field? */
1103 FILE *save_a_copy) /* save a copy to disk? */
1110 /* Get a new message number */
1111 newmsgid = get_new_message_number();
1112 sprintf(msgidbuf, "%ld", newmsgid);
1115 msg->cm_fields['I'] = strdoop(msgidbuf);
1118 serialize_message(&smr, msg);
1121 cprintf("%d Unable to serialize message\n",
1122 ERROR+INTERNAL_ERROR);
1126 /* Write our little bundle of joy into the message base */
1127 begin_critical_section(S_MSGMAIN);
1128 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1129 smr.ser, smr.len) < 0) {
1130 lprintf(2, "Can't store message\n");
1135 end_critical_section(S_MSGMAIN);
1137 /* If the caller specified that a copy should be saved to a particular
1138 * file handle, do that now too.
1140 if (save_a_copy != NULL) {
1141 fwrite(smr.ser, smr.len, 1, save_a_copy);
1144 /* Free the memory we used for the serialized message */
1147 /* Return the *local* message ID to the caller
1148 * (even if we're storing an incoming network message)
1156 * Serialize a struct CtdlMessage into the format used on disk and network.
1158 * This function loads up a "struct ser_ret" (defined in server.h) which
1159 * contains the length of the serialized message and a pointer to the
1160 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1162 void serialize_message(struct ser_ret *ret, /* return values */
1163 struct CtdlMessage *msg) /* unserialized msg */
1167 static char *forder = FORDER;
1169 lprintf(9, "serialize_message() called\n");
1171 if (is_valid_message(msg) == 0) return; /* self check */
1173 lprintf(9, "magic number check OK.\n");
1176 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1177 ret->len = ret->len +
1178 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1180 lprintf(9, "message is %d bytes\n", ret->len);
1182 lprintf(9, "calling malloc\n");
1183 ret->ser = mallok(ret->len);
1184 if (ret->ser == NULL) {
1190 ret->ser[1] = msg->cm_anon_type;
1191 ret->ser[2] = msg->cm_format_type;
1194 lprintf(9, "stuff\n");
1195 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1196 ret->ser[wlen++] = (char)forder[i];
1197 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1198 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1200 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1202 lprintf(9, "done serializing\n");
1210 * Save a message to disk
1212 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1213 char *rec, /* Recipient (mail) */
1214 char *force, /* force a particular room? */
1215 int mailtype, /* local or remote type */
1216 int generate_id) /* 1 = generate 'I' field */
1219 char hold_rm[ROOMNAMELEN];
1220 char actual_rm[ROOMNAMELEN];
1221 char force_room[ROOMNAMELEN];
1222 char content_type[256]; /* We have to learn this */
1223 char recipient[256];
1226 struct usersupp userbuf;
1228 int successful_local_recipients = 0;
1229 struct quickroom qtemp;
1230 struct SuppMsgInfo smi;
1231 FILE *network_fp = NULL;
1232 static int seqnum = 1;
1234 lprintf(9, "CtdlSaveMsg() called\n");
1235 if (is_valid_message(msg) == 0) return; /* self check */
1237 /* If this message has no timestamp, we take the liberty of
1238 * giving it one, right now.
1240 if (msg->cm_fields['T'] == NULL) {
1241 sprintf(aaa, "%ld", time(NULL));
1242 msg->cm_fields['T'] = strdoop(aaa);
1245 /* If this message has no path, we generate one.
1247 if (msg->cm_fields['P'] == NULL) {
1248 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1249 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1250 if (isspace(msg->cm_fields['P'][a])) {
1251 msg->cm_fields['P'][a] = ' ';
1256 strcpy(force_room, force);
1258 /* Strip non-printable characters out of the recipient name */
1259 strcpy(recipient, rec);
1260 for (a = 0; a < strlen(recipient); ++a)
1261 if (!isprint(recipient[a]))
1262 strcpy(&recipient[a], &recipient[a + 1]);
1264 /* Learn about what's inside, because it's what's inside that counts */
1266 switch (msg->cm_format_type) {
1268 strcpy(content_type, "text/x-citadel-variformat");
1271 strcpy(content_type, "text/plain");
1274 strcpy(content_type, "text/plain");
1275 /* advance past header fields */
1276 mptr = msg->cm_fields['M'];
1279 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1280 safestrncpy(content_type, mptr,
1281 sizeof(content_type));
1282 strcpy(content_type, &content_type[14]);
1283 for (a = 0; a < strlen(content_type); ++a)
1284 if ((content_type[a] == ';')
1285 || (content_type[a] == ' ')
1286 || (content_type[a] == 13)
1287 || (content_type[a] == 10))
1288 content_type[a] = 0;
1295 /* Perform "before save" hooks (aborting if any return nonzero) */
1296 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1298 /* Network mail - send a copy to the network program. */
1299 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1300 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1301 (long) getpid(), CC->cs_pid, ++seqnum);
1302 lprintf(9, "Saving a copy to %s\n", aaa);
1303 network_fp = fopen(aaa, "ab+");
1304 if (network_fp == NULL)
1305 lprintf(2, "ERROR: %s\n", strerror(errno));
1308 /* Save it to disk */
1309 newmsgid = send_message(msg, generate_id, network_fp);
1310 if (network_fp != NULL) {
1312 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1317 strcpy(actual_rm, CC->quickroom.QRname);
1318 strcpy(hold_rm, "");
1320 /* If this is being done by the networker delivering a private
1321 * message, we want to BYPASS saving the sender's copy (because there
1322 * is no local sender; it would otherwise go to the Trashcan).
1324 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1325 /* If the user is a twit, move to the twit room for posting */
1327 if (CC->usersupp.axlevel == 2) {
1328 strcpy(hold_rm, actual_rm);
1329 strcpy(actual_rm, config.c_twitroom);
1331 /* ...or if this message is destined for Aide> then go there. */
1332 if (strlen(force_room) > 0) {
1333 strcpy(hold_rm, actual_rm);
1334 strcpy(actual_rm, force_room);
1336 /* This call to usergoto() changes rooms if necessary. It also
1337 * causes the latest message list to be read into memory.
1339 usergoto(actual_rm, 0);
1341 /* read in the quickroom record, obtaining a lock... */
1342 lgetroom(&CC->quickroom, actual_rm);
1344 /* Fix an obscure bug */
1345 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1346 CC->quickroom.QRflags =
1347 CC->quickroom.QRflags & ~QR_MAILBOX;
1349 /* Add the message pointer to the room */
1350 CC->quickroom.QRhighest =
1351 AddMessageToRoom(&CC->quickroom, newmsgid);
1353 /* update quickroom */
1354 lputroom(&CC->quickroom);
1355 ++successful_local_recipients;
1358 /* Bump this user's messages posted counter. */
1359 lgetuser(&CC->usersupp, CC->curr_user);
1360 CC->usersupp.posted = CC->usersupp.posted + 1;
1361 lputuser(&CC->usersupp);
1363 /* If this is private, local mail, make a copy in the
1364 * recipient's mailbox and bump the reference count.
1366 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1367 if (getuser(&userbuf, recipient) == 0) {
1368 MailboxName(actual_rm, &userbuf, MAILROOM);
1369 if (lgetroom(&qtemp, actual_rm) == 0) {
1371 AddMessageToRoom(&qtemp, newmsgid);
1373 ++successful_local_recipients;
1377 /* If we've posted in a room other than the current room, then we
1378 * have to now go back to the current room...
1380 if (strlen(hold_rm) > 0) {
1381 usergoto(hold_rm, 0);
1384 /* Write a supplemental message info record. This doesn't have to
1385 * be a critical section because nobody else knows about this message
1388 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1389 smi.smi_msgnum = newmsgid;
1390 smi.smi_refcount = successful_local_recipients;
1391 safestrncpy(smi.smi_content_type, content_type, 64);
1392 PutSuppMsgInfo(&smi);
1394 /* Perform "after save" hooks */
1395 PerformMessageHooks(msg, EVT_AFTERSAVE);
1401 * Convenience function for generating small administrative messages.
1403 void quickie_message(char *from, char *to, char *room, char *text)
1405 struct CtdlMessage *msg;
1407 msg = mallok(sizeof(struct CtdlMessage));
1408 memset(msg, 0, sizeof(struct CtdlMessage));
1409 msg->cm_magic = CTDLMESSAGE_MAGIC;
1410 msg->cm_anon_type = MES_NORMAL;
1411 msg->cm_format_type = 0;
1412 msg->cm_fields['A'] = strdoop(from);
1413 msg->cm_fields['O'] = strdoop(room);
1414 msg->cm_fields['N'] = strdoop(NODENAME);
1416 msg->cm_fields['R'] = strdoop(to);
1417 msg->cm_fields['M'] = strdoop(text);
1419 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1420 CtdlFreeMessage(msg);
1421 syslog(LOG_NOTICE, text);
1426 * Build a binary message to be saved on disk.
1429 struct CtdlMessage *make_message(
1430 struct usersupp *author, /* author's usersupp structure */
1431 char *recipient, /* NULL if it's not mail */
1432 char *room, /* room where it's going */
1433 int type, /* see MES_ types in header file */
1434 int net_type, /* see MES_ types in header file */
1435 int format_type, /* local or remote (see citadel.h) */
1436 char *fake_name) /* who we're masquerading as */
1442 size_t message_len = 0;
1443 size_t buffer_len = 0;
1445 struct CtdlMessage *msg;
1447 msg = mallok(sizeof(struct CtdlMessage));
1448 memset(msg, 0, sizeof(struct CtdlMessage));
1449 msg->cm_magic = CTDLMESSAGE_MAGIC;
1450 msg->cm_anon_type = type;
1451 msg->cm_format_type = format_type;
1453 /* Don't confuse the poor folks if it's not routed mail. */
1454 strcpy(dest_node, "");
1456 /* If net_type is MES_BINARY, split out the destination node. */
1457 if (net_type == MES_BINARY) {
1458 strcpy(dest_node, NODENAME);
1459 for (a = 0; a < strlen(recipient); ++a) {
1460 if (recipient[a] == '@') {
1462 strcpy(dest_node, &recipient[a + 1]);
1467 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1468 if (net_type == MES_INTERNET) {
1469 strcpy(dest_node, "internet");
1472 while (isspace(recipient[strlen(recipient) - 1]))
1473 recipient[strlen(recipient) - 1] = 0;
1475 sprintf(buf, "cit%ld", author->usernum); /* Path */
1476 msg->cm_fields['P'] = strdoop(buf);
1478 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1479 msg->cm_fields['T'] = strdoop(buf);
1481 if (fake_name[0]) /* author */
1482 msg->cm_fields['A'] = strdoop(fake_name);
1484 msg->cm_fields['A'] = strdoop(author->fullname);
1486 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1487 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1489 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1491 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1492 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1494 if (recipient[0] != 0)
1495 msg->cm_fields['R'] = strdoop(recipient);
1496 if (dest_node[0] != 0)
1497 msg->cm_fields['D'] = strdoop(dest_node);
1499 msg->cm_fields['M'] = mallok(4096);
1500 if (msg->cm_fields['M'] == NULL) {
1501 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1505 msg->cm_fields['M'][0] = 0;
1509 /* read in the lines of message text one by one */
1510 while (client_gets(buf), strcmp(buf, "000")) {
1512 /* augment the buffer if we have to */
1513 if ((message_len + strlen(buf) + 2) > buffer_len) {
1514 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1515 if (ptr == NULL) { /* flush if can't allocate */
1516 while (client_gets(buf), strcmp(buf, "000")) ;;
1519 buffer_len = (buffer_len * 2);
1520 msg->cm_fields['M'] = ptr;
1524 strcat(msg->cm_fields['M'], buf);
1525 strcat(msg->cm_fields['M'], "\n");
1527 /* if we've hit the max msg length, flush the rest */
1528 if (message_len >= config.c_maxmsglen) {
1529 while (client_gets(buf), strcmp(buf, "000")) ;;
1542 * message entry - mode 0 (normal)
1544 void cmd_ent0(char *entargs)
1547 char recipient[256];
1549 int format_type = 0;
1550 char newusername[256];
1551 struct CtdlMessage *msg;
1555 struct usersupp tempUS;
1558 post = extract_int(entargs, 0);
1559 extract(recipient, entargs, 1);
1560 anon_flag = extract_int(entargs, 2);
1561 format_type = extract_int(entargs, 3);
1563 /* first check to make sure the request is valid. */
1565 if (!(CC->logged_in)) {
1566 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1569 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1570 cprintf("%d Need to be validated to enter ",
1571 ERROR + HIGHER_ACCESS_REQUIRED);
1572 cprintf("(except in %s> to sysop)\n", MAILROOM);
1575 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1576 cprintf("%d Need net privileges to enter here.\n",
1577 ERROR + HIGHER_ACCESS_REQUIRED);
1580 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1581 cprintf("%d Sorry, this is a read-only room.\n",
1582 ERROR + HIGHER_ACCESS_REQUIRED);
1589 if (CC->usersupp.axlevel < 6) {
1590 cprintf("%d You don't have permission to masquerade.\n",
1591 ERROR + HIGHER_ACCESS_REQUIRED);
1594 extract(newusername, entargs, 4);
1595 memset(CC->fake_postname, 0, 32);
1596 strcpy(CC->fake_postname, newusername);
1597 cprintf("%d Ok\n", OK);
1600 CC->cs_flags |= CS_POSTING;
1603 if (CC->quickroom.QRflags & QR_MAILBOX) {
1604 if (CC->usersupp.axlevel >= 2) {
1605 strcpy(buf, recipient);
1607 strcpy(buf, "sysop");
1608 e = alias(buf); /* alias and mail type */
1609 if ((buf[0] == 0) || (e == MES_ERROR)) {
1610 cprintf("%d Unknown address - cannot send message.\n",
1611 ERROR + NO_SUCH_USER);
1614 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1615 cprintf("%d Net privileges required for network mail.\n",
1616 ERROR + HIGHER_ACCESS_REQUIRED);
1619 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1620 && ((CC->usersupp.flags & US_INTERNET) == 0)
1621 && (!CC->internal_pgm)) {
1622 cprintf("%d You don't have access to Internet mail.\n",
1623 ERROR + HIGHER_ACCESS_REQUIRED);
1626 if (!strcasecmp(buf, "sysop")) {
1631 goto SKFALL; /* don't search local file */
1632 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1633 cprintf("%d Can't send mail to yourself!\n",
1634 ERROR + NO_SUCH_USER);
1637 /* Check to make sure the user exists; also get the correct
1638 * upper/lower casing of the name.
1640 a = getuser(&tempUS, buf);
1642 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1645 strcpy(buf, tempUS.fullname);
1648 SKFALL: b = MES_NORMAL;
1649 if (CC->quickroom.QRflags & QR_ANONONLY)
1651 if (CC->quickroom.QRflags & QR_ANONOPT) {
1655 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1658 /* If we're only checking the validity of the request, return
1659 * success without creating the message.
1662 cprintf("%d %s\n", OK, buf);
1666 cprintf("%d send message\n", SEND_LISTING);
1668 /* Read in the message from the client. */
1669 if (CC->fake_postname[0])
1670 msg = make_message(&CC->usersupp, buf,
1671 CC->quickroom.QRname, b, e, format_type,
1673 else if (CC->fake_username[0])
1674 msg = make_message(&CC->usersupp, buf,
1675 CC->quickroom.QRname, b, e, format_type,
1678 msg = make_message(&CC->usersupp, buf,
1679 CC->quickroom.QRname, b, e, format_type, "");
1682 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1683 CtdlFreeMessage(msg);
1684 CC->fake_postname[0] = '\0';
1691 * message entry - mode 3 (raw)
1693 void cmd_ent3(char *entargs)
1698 unsigned char ch, which_field;
1699 struct usersupp tempUS;
1701 struct CtdlMessage *msg;
1704 if (CC->internal_pgm == 0) {
1705 cprintf("%d This command is for internal programs only.\n",
1710 /* See if there's a recipient, but make sure it's a real one */
1711 extract(recp, entargs, 1);
1712 for (a = 0; a < strlen(recp); ++a)
1713 if (!isprint(recp[a]))
1714 strcpy(&recp[a], &recp[a + 1]);
1715 while (isspace(recp[0]))
1716 strcpy(recp, &recp[1]);
1717 while (isspace(recp[strlen(recp) - 1]))
1718 recp[strlen(recp) - 1] = 0;
1720 /* If we're in Mail, check the recipient */
1721 if (strlen(recp) > 0) {
1722 e = alias(recp); /* alias and mail type */
1723 if ((recp[0] == 0) || (e == MES_ERROR)) {
1724 cprintf("%d Unknown address - cannot send message.\n",
1725 ERROR + NO_SUCH_USER);
1728 if (e == MES_LOCAL) {
1729 a = getuser(&tempUS, recp);
1731 cprintf("%d No such user.\n",
1732 ERROR + NO_SUCH_USER);
1738 /* At this point, message has been approved. */
1739 if (extract_int(entargs, 0) == 0) {
1740 cprintf("%d OK to send\n", OK);
1744 msglen = extract_long(entargs, 2);
1745 msg = mallok(sizeof(struct CtdlMessage));
1747 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1751 memset(msg, 0, sizeof(struct CtdlMessage));
1752 tempbuf = mallok(msglen);
1753 if (tempbuf == NULL) {
1754 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1759 cprintf("%d %ld\n", SEND_BINARY, msglen);
1761 client_read(&ch, 1); /* 0xFF magic number */
1762 msg->cm_magic = CTDLMESSAGE_MAGIC;
1763 client_read(&ch, 1); /* anon type */
1764 msg->cm_anon_type = ch;
1765 client_read(&ch, 1); /* format type */
1766 msg->cm_format_type = ch;
1767 msglen = msglen - 3;
1769 while (msglen > 0) {
1770 client_read(&which_field, 1);
1774 client_read(&ch, 1);
1776 a = strlen(tempbuf);
1779 } while ( (ch != 0) && (msglen > 0) );
1780 msg->cm_fields[which_field] = strdoop(tempbuf);
1783 msg->cm_flags = CM_SKIP_HOOKS;
1784 CtdlSaveMsg(msg, recp, "", e, 0);
1785 CtdlFreeMessage(msg);
1791 * API function to delete messages which match a set of criteria
1792 * (returns the actual number of messages deleted)
1794 int CtdlDeleteMessages(char *room_name, /* which room */
1795 long dmsgnum, /* or "0" for any */
1796 char *content_type /* or NULL for any */
1800 struct quickroom qrbuf;
1801 struct cdbdata *cdbfr;
1802 long *msglist = NULL;
1805 int num_deleted = 0;
1807 struct SuppMsgInfo smi;
1809 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1810 room_name, dmsgnum, content_type);
1812 /* get room record, obtaining a lock... */
1813 if (lgetroom(&qrbuf, room_name) != 0) {
1814 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1816 return (0); /* room not found */
1818 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1820 if (cdbfr != NULL) {
1821 msglist = mallok(cdbfr->len);
1822 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1823 num_msgs = cdbfr->len / sizeof(long);
1827 for (i = 0; i < num_msgs; ++i) {
1830 /* Set/clear a bit for each criterion */
1832 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1833 delete_this |= 0x01;
1835 if (content_type == NULL) {
1836 delete_this |= 0x02;
1838 GetSuppMsgInfo(&smi, msglist[i]);
1839 if (!strcasecmp(smi.smi_content_type,
1841 delete_this |= 0x02;
1845 /* Delete message only if all bits are set */
1846 if (delete_this == 0x03) {
1847 AdjRefCount(msglist[i], -1);
1853 num_msgs = sort_msglist(msglist, num_msgs);
1854 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1855 msglist, (num_msgs * sizeof(long)));
1857 qrbuf.QRhighest = msglist[num_msgs - 1];
1861 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1862 return (num_deleted);
1868 * Delete message from current room
1870 void cmd_dele(char *delstr)
1875 getuser(&CC->usersupp, CC->curr_user);
1876 if ((CC->usersupp.axlevel < 6)
1877 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1878 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1879 && (!(CC->internal_pgm))) {
1880 cprintf("%d Higher access required.\n",
1881 ERROR + HIGHER_ACCESS_REQUIRED);
1884 delnum = extract_long(delstr, 0);
1886 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1889 cprintf("%d %d message%s deleted.\n", OK,
1890 num_deleted, ((num_deleted != 1) ? "s" : ""));
1892 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1898 * move a message to another room
1900 void cmd_move(char *args)
1904 struct quickroom qtemp;
1907 num = extract_long(args, 0);
1908 extract(targ, args, 1);
1910 getuser(&CC->usersupp, CC->curr_user);
1911 if ((CC->usersupp.axlevel < 6)
1912 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1913 cprintf("%d Higher access required.\n",
1914 ERROR + HIGHER_ACCESS_REQUIRED);
1917 if (getroom(&qtemp, targ) != 0) {
1918 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1921 /* Bump the reference count, otherwise the message will be deleted
1922 * from disk when we remove it from the source room.
1924 AdjRefCount(num, 1);
1926 /* yank the message out of the current room... */
1927 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
1930 /* put the message into the target room */
1931 lgetroom(&qtemp, targ);
1932 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1934 cprintf("%d Message moved.\n", OK);
1936 AdjRefCount(num, (-1)); /* oops */
1937 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1944 * GetSuppMsgInfo() - Get the supplementary record for a message
1946 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1949 struct cdbdata *cdbsmi;
1952 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1953 smibuf->smi_msgnum = msgnum;
1954 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1956 /* Use the negative of the message number for its supp record index */
1957 TheIndex = (0L - msgnum);
1959 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1960 if (cdbsmi == NULL) {
1961 return; /* record not found; go with defaults */
1963 memcpy(smibuf, cdbsmi->ptr,
1964 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1965 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1972 * PutSuppMsgInfo() - (re)write supplementary record for a message
1974 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1978 /* Use the negative of the message number for its supp record index */
1979 TheIndex = (0L - smibuf->smi_msgnum);
1981 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1982 smibuf->smi_msgnum, smibuf->smi_refcount);
1984 cdb_store(CDB_MSGMAIN,
1985 &TheIndex, sizeof(long),
1986 smibuf, sizeof(struct SuppMsgInfo));
1991 * AdjRefCount - change the reference count for a message;
1992 * delete the message if it reaches zero
1994 void AdjRefCount(long msgnum, int incr)
1997 struct SuppMsgInfo smi;
2000 /* This is a *tight* critical section; please keep it that way, as
2001 * it may get called while nested in other critical sections.
2002 * Complicating this any further will surely cause deadlock!
2004 begin_critical_section(S_SUPPMSGMAIN);
2005 GetSuppMsgInfo(&smi, msgnum);
2006 smi.smi_refcount += incr;
2007 PutSuppMsgInfo(&smi);
2008 end_critical_section(S_SUPPMSGMAIN);
2010 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2011 msgnum, smi.smi_refcount);
2013 /* If the reference count is now zero, delete the message
2014 * (and its supplementary record as well).
2016 if (smi.smi_refcount == 0) {
2017 lprintf(9, "Deleting message <%ld>\n", msgnum);
2019 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2020 delnum = (0L - msgnum);
2021 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2026 * Write a generic object to this room
2028 * Note: this could be much more efficient. Right now we use two temporary
2029 * files, and still pull the message into memory as with all others.
2031 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2032 char *content_type, /* MIME type of this object */
2033 char *tempfilename, /* Where to fetch it from */
2034 struct usersupp *is_mailbox, /* Mailbox room? */
2035 int is_binary, /* Is encoding necessary? */
2036 int is_unique, /* Del others of this type? */
2037 unsigned int flags /* Internal save flags */
2042 char filename[PATH_MAX];
2045 struct quickroom qrbuf;
2046 char roomname[ROOMNAMELEN];
2047 struct CtdlMessage *msg;
2050 if (is_mailbox != NULL)
2051 MailboxName(roomname, is_mailbox, req_room);
2053 safestrncpy(roomname, req_room, sizeof(roomname));
2054 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2056 strcpy(filename, tmpnam(NULL));
2057 fp = fopen(filename, "w");
2061 tempfp = fopen(tempfilename, "r");
2062 if (tempfp == NULL) {
2068 fprintf(fp, "Content-type: %s\n", content_type);
2069 lprintf(9, "Content-type: %s\n", content_type);
2071 if (is_binary == 0) {
2072 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2073 while (ch = getc(tempfp), ch > 0)
2079 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2082 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2083 tempfilename, filename);
2087 lprintf(9, "Allocating\n");
2088 msg = mallok(sizeof(struct CtdlMessage));
2089 memset(msg, 0, sizeof(struct CtdlMessage));
2090 msg->cm_magic = CTDLMESSAGE_MAGIC;
2091 msg->cm_anon_type = MES_NORMAL;
2092 msg->cm_format_type = 4;
2093 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2094 msg->cm_fields['O'] = strdoop(req_room);
2095 msg->cm_fields['N'] = strdoop(config.c_nodename);
2096 msg->cm_fields['H'] = strdoop(config.c_humannode);
2097 msg->cm_flags = flags;
2099 lprintf(9, "Loading\n");
2100 fp = fopen(filename, "rb");
2101 fseek(fp, 0L, SEEK_END);
2104 msg->cm_fields['M'] = mallok(len);
2105 fread(msg->cm_fields['M'], len, 1, fp);
2109 /* Create the requested room if we have to. */
2110 if (getroom(&qrbuf, roomname) != 0) {
2111 create_room(roomname, 4, "", 0);
2113 /* If the caller specified this object as unique, delete all
2114 * other objects of this type that are currently in the room.
2117 lprintf(9, "Deleted %d other msgs of this type\n",
2118 CtdlDeleteMessages(roomname, 0L, content_type));
2120 /* Now write the data */
2121 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2122 CtdlFreeMessage(msg);