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)
734 struct CtdlMessage *TheMessage = NULL;
738 /* buffers needed for RFC822 translation */
746 msg_num = atol(msgid);
748 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
749 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
753 /* FIX ... small security issue
754 * We need to check to make sure the requested message is actually
755 * in the current room, and set msg_ok to 1 only if it is. This
756 * functionality is currently missing because I'm in a hurry to replace
757 * broken production code with nonbroken pre-beta code. :( -- ajc
760 cprintf("%d Message %ld is not in this room.\n",
767 * Fetch the message from disk
769 TheMessage = CtdlFetchMessage(msg_num);
770 if (TheMessage == NULL) {
771 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
775 /* Are we downloading a MIME component? */
776 if (mode == MT_DOWNLOAD) {
777 if (TheMessage->cm_format_type != 4) {
778 cprintf("%d This is not a MIME message.\n",
780 } else if (CC->download_fp != NULL) {
781 cprintf("%d You already have a download open.\n",
784 /* Parse the message text component */
785 mptr = TheMessage->cm_fields['M'];
786 mime_parser(mptr, NULL, *mime_download);
787 /* If there's no file open by this time, the requested
788 * section wasn't found, so print an error
790 if (CC->download_fp == NULL) {
791 cprintf("%d Section %s not found.\n",
792 ERROR + FILE_NOT_FOUND,
796 CtdlFreeMessage(TheMessage);
800 /* now for the user-mode message reading loops */
801 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
803 /* Tell the client which format type we're using. If this is a
804 * MIME message, *lie* about it and tell the user it's fixed-format.
806 if (mode == MT_CITADEL) {
807 if (TheMessage->cm_format_type == 4)
810 cprintf("type=%d\n", TheMessage->cm_format_type);
813 /* nhdr=yes means that we're only displaying headers, no body */
814 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
815 cprintf("nhdr=yes\n");
818 /* begin header processing loop for Citadel message format */
820 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
822 if (TheMessage->cm_fields['A']) {
823 strcpy(buf, TheMessage->cm_fields['A']);
824 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
825 if (TheMessage->cm_anon_type == MES_ANON)
826 cprintf("from=****");
827 else if (TheMessage->cm_anon_type == MES_AN2)
828 cprintf("from=anonymous");
830 cprintf("from=%s", buf);
832 && ((TheMessage->cm_anon_type == MES_ANON)
833 || (TheMessage->cm_anon_type == MES_AN2))) {
834 cprintf(" [%s]", buf);
839 strcpy(allkeys, FORDER);
840 for (i=0; i<strlen(allkeys); ++i) {
841 k = (int) allkeys[i];
842 if ((k != 'A') && (k != 'M')) {
843 if (TheMessage->cm_fields[k] != NULL)
846 TheMessage->cm_fields[k]
853 /* begin header processing loop for RFC822 transfer format */
857 strcpy(snode, NODENAME);
858 strcpy(lnode, HUMANNODE);
859 if (mode == MT_RFC822) {
860 for (i = 0; i < 256; ++i) {
861 if (TheMessage->cm_fields[i]) {
862 mptr = TheMessage->cm_fields[i];
866 } else if (i == 'P') {
867 cprintf("Path: %s\n", mptr);
868 for (a = 0; a < strlen(mptr); ++a) {
869 if (mptr[a] == '!') {
870 strcpy(mptr, &mptr[a + 1]);
876 cprintf("Subject: %s\n", mptr);
882 cprintf("X-Citadel-Room: %s\n", mptr);
886 cprintf("To: %s\n", mptr);
889 cprintf("Date: %s", asctime(localtime(&xtime)));
895 if (mode == MT_RFC822) {
896 if (!strcasecmp(snode, NODENAME)) {
899 cprintf("Message-ID: <%s@%s>\n", mid, snode);
900 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
901 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
902 cprintf("Organization: %s\n", lnode);
905 /* end header processing loop ... at this point, we're in the text */
907 mptr = TheMessage->cm_fields['M'];
909 /* Tell the client about the MIME parts in this message */
910 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
911 if (mode == MT_CITADEL) {
912 mime_parser(mptr, NULL, *list_this_part);
914 else if (mode == MT_MIME) { /* list parts only */
915 mime_parser(mptr, NULL, *list_this_part);
917 CtdlFreeMessage(TheMessage);
924 CtdlFreeMessage(TheMessage);
928 /* signify start of msg text */
929 if (mode == MT_CITADEL)
931 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
934 /* If the format type on disk is 1 (fixed-format), then we want
935 * everything to be output completely literally ... regardless of
936 * what message transfer format is in use.
938 if (TheMessage->cm_format_type == 1) {
940 while (ch = *mptr++, ch > 0) {
943 if ((ch == 10) || (strlen(buf) > 250)) {
944 cprintf("%s\n", buf);
947 buf[strlen(buf) + 1] = 0;
948 buf[strlen(buf)] = ch;
952 cprintf("%s\n", buf);
955 /* If the message on disk is format 0 (Citadel vari-format), we
956 * output using the formatter at 80 columns. This is the final output
957 * form if the transfer format is RFC822, but if the transfer format
958 * is Citadel proprietary, it'll still work, because the indentation
959 * for new paragraphs is correct and the client will reformat the
960 * message to the reader's screen width.
962 if (TheMessage->cm_format_type == 0) {
963 memfmout(80, mptr, 0);
966 /* If the message on disk is format 4 (MIME), we've gotta hand it
967 * off to the MIME parser. The client has already been told that
968 * this message is format 1 (fixed format), so the callback function
969 * we use will display those parts as-is.
971 if (TheMessage->cm_format_type == 4) {
972 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
973 memset(ma, 0, sizeof(struct ma_info));
974 mime_parser(mptr, NULL, *fixed_output);
979 CtdlFreeMessage(TheMessage);
986 * display a message (mode 0 - Citadel proprietary)
988 void cmd_msg0(char *cmdbuf)
991 int headers_only = 0;
993 extract(msgid, cmdbuf, 0);
994 headers_only = extract_int(cmdbuf, 1);
996 output_message(msgid, MT_CITADEL, headers_only);
1002 * display a message (mode 2 - RFC822)
1004 void cmd_msg2(char *cmdbuf)
1007 int headers_only = 0;
1009 extract(msgid, cmdbuf, 0);
1010 headers_only = extract_int(cmdbuf, 1);
1012 output_message(msgid, MT_RFC822, headers_only);
1018 * display a message (mode 3 - IGnet raw format - internal programs only)
1020 void cmd_msg3(char *cmdbuf)
1023 struct CtdlMessage *msg;
1026 if (CC->internal_pgm == 0) {
1027 cprintf("%d This command is for internal programs only.\n",
1032 msgnum = extract_long(cmdbuf, 0);
1033 msg = CtdlFetchMessage(msgnum);
1035 cprintf("%d Message %ld not found.\n",
1040 serialize_message(&smr, msg);
1041 CtdlFreeMessage(msg);
1044 cprintf("%d Unable to serialize message\n",
1045 ERROR+INTERNAL_ERROR);
1049 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1050 client_write(smr.ser, smr.len);
1057 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
1059 void cmd_msg4(char *cmdbuf)
1063 extract(msgid, cmdbuf, 0);
1065 output_message(msgid, MT_MIME, 0);
1069 * Open a component of a MIME message as a download file
1071 void cmd_opna(char *cmdbuf)
1075 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
1077 extract(msgid, cmdbuf, 0);
1078 extract(desired_section, cmdbuf, 1);
1080 output_message(msgid, MT_DOWNLOAD, 0);
1084 * Message base operation to send a message to the master file
1085 * (returns new message number)
1087 * This is the back end for CtdlSaveMsg() and should not be directly
1088 * called by server-side modules.
1091 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1092 int generate_id, /* generate 'I' field? */
1093 FILE *save_a_copy) /* save a copy to disk? */
1100 /* Get a new message number */
1101 newmsgid = get_new_message_number();
1102 sprintf(msgidbuf, "%ld", newmsgid);
1105 msg->cm_fields['I'] = strdoop(msgidbuf);
1108 serialize_message(&smr, msg);
1111 cprintf("%d Unable to serialize message\n",
1112 ERROR+INTERNAL_ERROR);
1116 /* Write our little bundle of joy into the message base */
1117 begin_critical_section(S_MSGMAIN);
1118 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1119 smr.ser, smr.len) < 0) {
1120 lprintf(2, "Can't store message\n");
1125 end_critical_section(S_MSGMAIN);
1127 /* If the caller specified that a copy should be saved to a particular
1128 * file handle, do that now too.
1130 if (save_a_copy != NULL) {
1131 fwrite(smr.ser, smr.len, 1, save_a_copy);
1134 /* Free the memory we used for the serialized message */
1137 /* Return the *local* message ID to the caller
1138 * (even if we're storing an incoming network message)
1146 * Serialize a struct CtdlMessage into the format used on disk and network.
1148 * This function loads up a "struct ser_ret" (defined in server.h) which
1149 * contains the length of the serialized message and a pointer to the
1150 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1152 void serialize_message(struct ser_ret *ret, /* return values */
1153 struct CtdlMessage *msg) /* unserialized msg */
1157 static char *forder = FORDER;
1159 lprintf(9, "serialize_message() called\n");
1161 if (is_valid_message(msg) == 0) return; /* self check */
1163 lprintf(9, "magic number check OK.\n");
1166 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1167 ret->len = ret->len +
1168 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1170 lprintf(9, "message is %d bytes\n", ret->len);
1172 lprintf(9, "calling malloc\n");
1173 ret->ser = mallok(ret->len);
1174 if (ret->ser == NULL) {
1180 ret->ser[1] = msg->cm_anon_type;
1181 ret->ser[2] = msg->cm_format_type;
1184 lprintf(9, "stuff\n");
1185 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1186 ret->ser[wlen++] = (char)forder[i];
1187 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1188 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1190 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1192 lprintf(9, "done serializing\n");
1200 * Save a message to disk
1202 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1203 char *rec, /* Recipient (mail) */
1204 char *force, /* force a particular room? */
1205 int mailtype, /* local or remote type */
1206 int generate_id) /* 1 = generate 'I' field */
1209 char hold_rm[ROOMNAMELEN];
1210 char actual_rm[ROOMNAMELEN];
1211 char force_room[ROOMNAMELEN];
1212 char content_type[256]; /* We have to learn this */
1213 char recipient[256];
1216 struct usersupp userbuf;
1218 int successful_local_recipients = 0;
1219 struct quickroom qtemp;
1220 struct SuppMsgInfo smi;
1221 FILE *network_fp = NULL;
1222 static int seqnum = 1;
1224 lprintf(9, "CtdlSaveMsg() called\n");
1225 if (is_valid_message(msg) == 0) return; /* self check */
1227 /* If this message has no timestamp, we take the liberty of
1228 * giving it one, right now.
1230 if (msg->cm_fields['T'] == NULL) {
1231 sprintf(aaa, "%ld", time(NULL));
1232 msg->cm_fields['T'] = strdoop(aaa);
1235 /* If this message has no path, we generate one.
1237 if (msg->cm_fields['P'] == NULL) {
1238 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1239 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1240 if (isspace(msg->cm_fields['P'][a])) {
1241 msg->cm_fields['P'][a] = ' ';
1246 strcpy(force_room, force);
1248 /* Strip non-printable characters out of the recipient name */
1249 strcpy(recipient, rec);
1250 for (a = 0; a < strlen(recipient); ++a)
1251 if (!isprint(recipient[a]))
1252 strcpy(&recipient[a], &recipient[a + 1]);
1254 /* Learn about what's inside, because it's what's inside that counts */
1256 switch (msg->cm_format_type) {
1258 strcpy(content_type, "text/x-citadel-variformat");
1261 strcpy(content_type, "text/plain");
1264 strcpy(content_type, "text/plain");
1265 /* advance past header fields */
1266 mptr = msg->cm_fields['M'];
1269 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1270 safestrncpy(content_type, mptr,
1271 sizeof(content_type));
1272 strcpy(content_type, &content_type[14]);
1273 for (a = 0; a < strlen(content_type); ++a)
1274 if ((content_type[a] == ';')
1275 || (content_type[a] == ' ')
1276 || (content_type[a] == 13)
1277 || (content_type[a] == 10))
1278 content_type[a] = 0;
1285 /* Perform "before save" hooks (aborting if any return nonzero) */
1286 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1288 /* Network mail - send a copy to the network program. */
1289 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1290 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1291 (long) getpid(), CC->cs_pid, ++seqnum);
1292 lprintf(9, "Saving a copy to %s\n", aaa);
1293 network_fp = fopen(aaa, "ab+");
1294 if (network_fp == NULL)
1295 lprintf(2, "ERROR: %s\n", strerror(errno));
1298 /* Save it to disk */
1299 newmsgid = send_message(msg, generate_id, network_fp);
1300 if (network_fp != NULL) {
1302 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1307 strcpy(actual_rm, CC->quickroom.QRname);
1308 strcpy(hold_rm, "");
1310 /* If this is being done by the networker delivering a private
1311 * message, we want to BYPASS saving the sender's copy (because there
1312 * is no local sender; it would otherwise go to the Trashcan).
1314 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1315 /* If the user is a twit, move to the twit room for posting */
1317 if (CC->usersupp.axlevel == 2) {
1318 strcpy(hold_rm, actual_rm);
1319 strcpy(actual_rm, config.c_twitroom);
1321 /* ...or if this message is destined for Aide> then go there. */
1322 if (strlen(force_room) > 0) {
1323 strcpy(hold_rm, actual_rm);
1324 strcpy(actual_rm, force_room);
1326 /* This call to usergoto() changes rooms if necessary. It also
1327 * causes the latest message list to be read into memory.
1329 usergoto(actual_rm, 0);
1331 /* read in the quickroom record, obtaining a lock... */
1332 lgetroom(&CC->quickroom, actual_rm);
1334 /* Fix an obscure bug */
1335 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1336 CC->quickroom.QRflags =
1337 CC->quickroom.QRflags & ~QR_MAILBOX;
1339 /* Add the message pointer to the room */
1340 CC->quickroom.QRhighest =
1341 AddMessageToRoom(&CC->quickroom, newmsgid);
1343 /* update quickroom */
1344 lputroom(&CC->quickroom);
1345 ++successful_local_recipients;
1348 /* Bump this user's messages posted counter. */
1349 lgetuser(&CC->usersupp, CC->curr_user);
1350 CC->usersupp.posted = CC->usersupp.posted + 1;
1351 lputuser(&CC->usersupp);
1353 /* If this is private, local mail, make a copy in the
1354 * recipient's mailbox and bump the reference count.
1356 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1357 if (getuser(&userbuf, recipient) == 0) {
1358 MailboxName(actual_rm, &userbuf, MAILROOM);
1359 if (lgetroom(&qtemp, actual_rm) == 0) {
1361 AddMessageToRoom(&qtemp, newmsgid);
1363 ++successful_local_recipients;
1367 /* If we've posted in a room other than the current room, then we
1368 * have to now go back to the current room...
1370 if (strlen(hold_rm) > 0) {
1371 usergoto(hold_rm, 0);
1374 /* Write a supplemental message info record. This doesn't have to
1375 * be a critical section because nobody else knows about this message
1378 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1379 smi.smi_msgnum = newmsgid;
1380 smi.smi_refcount = successful_local_recipients;
1381 safestrncpy(smi.smi_content_type, content_type, 64);
1382 PutSuppMsgInfo(&smi);
1384 /* Perform "after save" hooks */
1385 PerformMessageHooks(msg, EVT_AFTERSAVE);
1391 * Convenience function for generating small administrative messages.
1393 void quickie_message(char *from, char *to, char *room, char *text)
1395 struct CtdlMessage *msg;
1397 msg = mallok(sizeof(struct CtdlMessage));
1398 memset(msg, 0, sizeof(struct CtdlMessage));
1399 msg->cm_magic = CTDLMESSAGE_MAGIC;
1400 msg->cm_anon_type = MES_NORMAL;
1401 msg->cm_format_type = 0;
1402 msg->cm_fields['A'] = strdoop(from);
1403 msg->cm_fields['O'] = strdoop(room);
1404 msg->cm_fields['N'] = strdoop(NODENAME);
1406 msg->cm_fields['R'] = strdoop(to);
1407 msg->cm_fields['M'] = strdoop(text);
1409 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1410 CtdlFreeMessage(msg);
1411 syslog(LOG_NOTICE, text);
1416 * Build a binary message to be saved on disk.
1419 struct CtdlMessage *make_message(
1420 struct usersupp *author, /* author's usersupp structure */
1421 char *recipient, /* NULL if it's not mail */
1422 char *room, /* room where it's going */
1423 int type, /* see MES_ types in header file */
1424 int net_type, /* see MES_ types in header file */
1425 int format_type, /* local or remote (see citadel.h) */
1426 char *fake_name) /* who we're masquerading as */
1432 size_t message_len = 0;
1433 size_t buffer_len = 0;
1435 struct CtdlMessage *msg;
1437 msg = mallok(sizeof(struct CtdlMessage));
1438 memset(msg, 0, sizeof(struct CtdlMessage));
1439 msg->cm_magic = CTDLMESSAGE_MAGIC;
1440 msg->cm_anon_type = type;
1441 msg->cm_format_type = format_type;
1443 /* Don't confuse the poor folks if it's not routed mail. */
1444 strcpy(dest_node, "");
1446 /* If net_type is MES_BINARY, split out the destination node. */
1447 if (net_type == MES_BINARY) {
1448 strcpy(dest_node, NODENAME);
1449 for (a = 0; a < strlen(recipient); ++a) {
1450 if (recipient[a] == '@') {
1452 strcpy(dest_node, &recipient[a + 1]);
1457 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1458 if (net_type == MES_INTERNET) {
1459 strcpy(dest_node, "internet");
1462 while (isspace(recipient[strlen(recipient) - 1]))
1463 recipient[strlen(recipient) - 1] = 0;
1465 sprintf(buf, "cit%ld", author->usernum); /* Path */
1466 msg->cm_fields['P'] = strdoop(buf);
1468 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1469 msg->cm_fields['T'] = strdoop(buf);
1471 if (fake_name[0]) /* author */
1472 msg->cm_fields['A'] = strdoop(fake_name);
1474 msg->cm_fields['A'] = strdoop(author->fullname);
1476 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1477 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1479 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1481 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1482 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1484 if (recipient[0] != 0)
1485 msg->cm_fields['R'] = strdoop(recipient);
1486 if (dest_node[0] != 0)
1487 msg->cm_fields['D'] = strdoop(dest_node);
1489 msg->cm_fields['M'] = mallok(4096);
1490 if (msg->cm_fields['M'] == NULL) {
1491 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1495 msg->cm_fields['M'][0] = 0;
1499 /* read in the lines of message text one by one */
1500 while (client_gets(buf), strcmp(buf, "000")) {
1502 /* augment the buffer if we have to */
1503 if ((message_len + strlen(buf) + 2) > buffer_len) {
1504 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1505 if (ptr == NULL) { /* flush if can't allocate */
1506 while (client_gets(buf), strcmp(buf, "000")) ;;
1509 buffer_len = (buffer_len * 2);
1510 msg->cm_fields['M'] = ptr;
1514 strcat(msg->cm_fields['M'], buf);
1515 strcat(msg->cm_fields['M'], "\n");
1517 /* if we've hit the max msg length, flush the rest */
1518 if (message_len >= config.c_maxmsglen) {
1519 while (client_gets(buf), strcmp(buf, "000")) ;;
1532 * message entry - mode 0 (normal)
1534 void cmd_ent0(char *entargs)
1537 char recipient[256];
1539 int format_type = 0;
1540 char newusername[256];
1541 struct CtdlMessage *msg;
1545 struct usersupp tempUS;
1548 post = extract_int(entargs, 0);
1549 extract(recipient, entargs, 1);
1550 anon_flag = extract_int(entargs, 2);
1551 format_type = extract_int(entargs, 3);
1553 /* first check to make sure the request is valid. */
1555 if (!(CC->logged_in)) {
1556 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1559 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1560 cprintf("%d Need to be validated to enter ",
1561 ERROR + HIGHER_ACCESS_REQUIRED);
1562 cprintf("(except in %s> to sysop)\n", MAILROOM);
1565 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1566 cprintf("%d Need net privileges to enter here.\n",
1567 ERROR + HIGHER_ACCESS_REQUIRED);
1570 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1571 cprintf("%d Sorry, this is a read-only room.\n",
1572 ERROR + HIGHER_ACCESS_REQUIRED);
1579 if (CC->usersupp.axlevel < 6) {
1580 cprintf("%d You don't have permission to masquerade.\n",
1581 ERROR + HIGHER_ACCESS_REQUIRED);
1584 extract(newusername, entargs, 4);
1585 memset(CC->fake_postname, 0, 32);
1586 strcpy(CC->fake_postname, newusername);
1587 cprintf("%d Ok\n", OK);
1590 CC->cs_flags |= CS_POSTING;
1593 if (CC->quickroom.QRflags & QR_MAILBOX) {
1594 if (CC->usersupp.axlevel >= 2) {
1595 strcpy(buf, recipient);
1597 strcpy(buf, "sysop");
1598 e = alias(buf); /* alias and mail type */
1599 if ((buf[0] == 0) || (e == MES_ERROR)) {
1600 cprintf("%d Unknown address - cannot send message.\n",
1601 ERROR + NO_SUCH_USER);
1604 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1605 cprintf("%d Net privileges required for network mail.\n",
1606 ERROR + HIGHER_ACCESS_REQUIRED);
1609 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1610 && ((CC->usersupp.flags & US_INTERNET) == 0)
1611 && (!CC->internal_pgm)) {
1612 cprintf("%d You don't have access to Internet mail.\n",
1613 ERROR + HIGHER_ACCESS_REQUIRED);
1616 if (!strcasecmp(buf, "sysop")) {
1621 goto SKFALL; /* don't search local file */
1622 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1623 cprintf("%d Can't send mail to yourself!\n",
1624 ERROR + NO_SUCH_USER);
1627 /* Check to make sure the user exists; also get the correct
1628 * upper/lower casing of the name.
1630 a = getuser(&tempUS, buf);
1632 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1635 strcpy(buf, tempUS.fullname);
1638 SKFALL: b = MES_NORMAL;
1639 if (CC->quickroom.QRflags & QR_ANONONLY)
1641 if (CC->quickroom.QRflags & QR_ANONOPT) {
1645 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1648 /* If we're only checking the validity of the request, return
1649 * success without creating the message.
1652 cprintf("%d %s\n", OK, buf);
1656 cprintf("%d send message\n", SEND_LISTING);
1658 /* Read in the message from the client. */
1659 if (CC->fake_postname[0])
1660 msg = make_message(&CC->usersupp, buf,
1661 CC->quickroom.QRname, b, e, format_type,
1663 else if (CC->fake_username[0])
1664 msg = make_message(&CC->usersupp, buf,
1665 CC->quickroom.QRname, b, e, format_type,
1668 msg = make_message(&CC->usersupp, buf,
1669 CC->quickroom.QRname, b, e, format_type, "");
1672 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1673 CtdlFreeMessage(msg);
1674 CC->fake_postname[0] = '\0';
1681 * message entry - mode 3 (raw)
1683 void cmd_ent3(char *entargs)
1688 unsigned char ch, which_field;
1689 struct usersupp tempUS;
1691 struct CtdlMessage *msg;
1694 if (CC->internal_pgm == 0) {
1695 cprintf("%d This command is for internal programs only.\n",
1700 /* See if there's a recipient, but make sure it's a real one */
1701 extract(recp, entargs, 1);
1702 for (a = 0; a < strlen(recp); ++a)
1703 if (!isprint(recp[a]))
1704 strcpy(&recp[a], &recp[a + 1]);
1705 while (isspace(recp[0]))
1706 strcpy(recp, &recp[1]);
1707 while (isspace(recp[strlen(recp) - 1]))
1708 recp[strlen(recp) - 1] = 0;
1710 /* If we're in Mail, check the recipient */
1711 if (strlen(recp) > 0) {
1712 e = alias(recp); /* alias and mail type */
1713 if ((recp[0] == 0) || (e == MES_ERROR)) {
1714 cprintf("%d Unknown address - cannot send message.\n",
1715 ERROR + NO_SUCH_USER);
1718 if (e == MES_LOCAL) {
1719 a = getuser(&tempUS, recp);
1721 cprintf("%d No such user.\n",
1722 ERROR + NO_SUCH_USER);
1728 /* At this point, message has been approved. */
1729 if (extract_int(entargs, 0) == 0) {
1730 cprintf("%d OK to send\n", OK);
1734 msglen = extract_long(entargs, 2);
1735 msg = mallok(sizeof(struct CtdlMessage));
1737 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1741 memset(msg, 0, sizeof(struct CtdlMessage));
1742 tempbuf = mallok(msglen);
1743 if (tempbuf == NULL) {
1744 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1749 cprintf("%d %ld\n", SEND_BINARY, msglen);
1751 client_read(&ch, 1); /* 0xFF magic number */
1752 msg->cm_magic = CTDLMESSAGE_MAGIC;
1753 client_read(&ch, 1); /* anon type */
1754 msg->cm_anon_type = ch;
1755 client_read(&ch, 1); /* format type */
1756 msg->cm_format_type = ch;
1757 msglen = msglen - 3;
1759 while (msglen > 0) {
1760 client_read(&which_field, 1);
1764 client_read(&ch, 1);
1766 a = strlen(tempbuf);
1769 } while ( (ch != 0) && (msglen > 0) );
1770 msg->cm_fields[which_field] = strdoop(tempbuf);
1773 CtdlSaveMsg(msg, recp, "", e, 0);
1774 CtdlFreeMessage(msg);
1780 * API function to delete messages which match a set of criteria
1781 * (returns the actual number of messages deleted)
1783 int CtdlDeleteMessages(char *room_name, /* which room */
1784 long dmsgnum, /* or "0" for any */
1785 char *content_type /* or NULL for any */
1789 struct quickroom qrbuf;
1790 struct cdbdata *cdbfr;
1791 long *msglist = NULL;
1794 int num_deleted = 0;
1796 struct SuppMsgInfo smi;
1798 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1799 room_name, dmsgnum, content_type);
1801 /* get room record, obtaining a lock... */
1802 if (lgetroom(&qrbuf, room_name) != 0) {
1803 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1805 return (0); /* room not found */
1807 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1809 if (cdbfr != NULL) {
1810 msglist = mallok(cdbfr->len);
1811 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1812 num_msgs = cdbfr->len / sizeof(long);
1816 for (i = 0; i < num_msgs; ++i) {
1819 /* Set/clear a bit for each criterion */
1821 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1822 delete_this |= 0x01;
1824 if (content_type == NULL) {
1825 delete_this |= 0x02;
1827 GetSuppMsgInfo(&smi, msglist[i]);
1828 if (!strcasecmp(smi.smi_content_type,
1830 delete_this |= 0x02;
1834 /* Delete message only if all bits are set */
1835 if (delete_this == 0x03) {
1836 AdjRefCount(msglist[i], -1);
1842 num_msgs = sort_msglist(msglist, num_msgs);
1843 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1844 msglist, (num_msgs * sizeof(long)));
1846 qrbuf.QRhighest = msglist[num_msgs - 1];
1850 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1851 return (num_deleted);
1857 * Delete message from current room
1859 void cmd_dele(char *delstr)
1864 getuser(&CC->usersupp, CC->curr_user);
1865 if ((CC->usersupp.axlevel < 6)
1866 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1867 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1868 cprintf("%d Higher access required.\n",
1869 ERROR + HIGHER_ACCESS_REQUIRED);
1872 delnum = extract_long(delstr, 0);
1874 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1877 cprintf("%d %d message%s deleted.\n", OK,
1878 num_deleted, ((num_deleted != 1) ? "s" : ""));
1880 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1886 * move a message to another room
1888 void cmd_move(char *args)
1892 struct quickroom qtemp;
1895 num = extract_long(args, 0);
1896 extract(targ, args, 1);
1898 getuser(&CC->usersupp, CC->curr_user);
1899 if ((CC->usersupp.axlevel < 6)
1900 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1901 cprintf("%d Higher access required.\n",
1902 ERROR + HIGHER_ACCESS_REQUIRED);
1905 if (getroom(&qtemp, targ) != 0) {
1906 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1909 /* Bump the reference count, otherwise the message will be deleted
1910 * from disk when we remove it from the source room.
1912 AdjRefCount(num, 1);
1914 /* yank the message out of the current room... */
1915 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
1918 /* put the message into the target room */
1919 lgetroom(&qtemp, targ);
1920 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1922 cprintf("%d Message moved.\n", OK);
1924 AdjRefCount(num, (-1)); /* oops */
1925 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1932 * GetSuppMsgInfo() - Get the supplementary record for a message
1934 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1937 struct cdbdata *cdbsmi;
1940 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1941 smibuf->smi_msgnum = msgnum;
1942 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1944 /* Use the negative of the message number for its supp record index */
1945 TheIndex = (0L - msgnum);
1947 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1948 if (cdbsmi == NULL) {
1949 return; /* record not found; go with defaults */
1951 memcpy(smibuf, cdbsmi->ptr,
1952 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1953 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1960 * PutSuppMsgInfo() - (re)write supplementary record for a message
1962 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1966 /* Use the negative of the message number for its supp record index */
1967 TheIndex = (0L - smibuf->smi_msgnum);
1969 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1970 smibuf->smi_msgnum, smibuf->smi_refcount);
1972 cdb_store(CDB_MSGMAIN,
1973 &TheIndex, sizeof(long),
1974 smibuf, sizeof(struct SuppMsgInfo));
1979 * AdjRefCount - change the reference count for a message;
1980 * delete the message if it reaches zero
1982 void AdjRefCount(long msgnum, int incr)
1985 struct SuppMsgInfo smi;
1988 /* This is a *tight* critical section; please keep it that way, as
1989 * it may get called while nested in other critical sections.
1990 * Complicating this any further will surely cause deadlock!
1992 begin_critical_section(S_SUPPMSGMAIN);
1993 GetSuppMsgInfo(&smi, msgnum);
1994 smi.smi_refcount += incr;
1995 PutSuppMsgInfo(&smi);
1996 end_critical_section(S_SUPPMSGMAIN);
1998 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1999 msgnum, smi.smi_refcount);
2001 /* If the reference count is now zero, delete the message
2002 * (and its supplementary record as well).
2004 if (smi.smi_refcount == 0) {
2005 lprintf(9, "Deleting message <%ld>\n", msgnum);
2007 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2008 delnum = (0L - msgnum);
2009 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2014 * Write a generic object to this room
2016 * Note: this could be much more efficient. Right now we use two temporary
2017 * files, and still pull the message into memory as with all others.
2019 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2020 char *content_type, /* MIME type of this object */
2021 char *tempfilename, /* Where to fetch it from */
2022 struct usersupp *is_mailbox, /* Mailbox room? */
2023 int is_binary, /* Is encoding necessary? */
2024 int is_unique, /* Del others of this type? */
2025 unsigned int flags /* Internal save flags */
2030 char filename[PATH_MAX];
2033 struct quickroom qrbuf;
2034 char roomname[ROOMNAMELEN];
2035 struct CtdlMessage *msg;
2038 if (is_mailbox != NULL)
2039 MailboxName(roomname, is_mailbox, req_room);
2041 safestrncpy(roomname, req_room, sizeof(roomname));
2042 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2044 strcpy(filename, tmpnam(NULL));
2045 fp = fopen(filename, "w");
2049 tempfp = fopen(tempfilename, "r");
2050 if (tempfp == NULL) {
2056 fprintf(fp, "Content-type: %s\n", content_type);
2057 lprintf(9, "Content-type: %s\n", content_type);
2059 if (is_binary == 0) {
2060 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2061 while (ch = getc(tempfp), ch > 0)
2067 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2070 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2071 tempfilename, filename);
2075 lprintf(9, "Allocating\n");
2076 msg = mallok(sizeof(struct CtdlMessage));
2077 memset(msg, 0, sizeof(struct CtdlMessage));
2078 msg->cm_magic = CTDLMESSAGE_MAGIC;
2079 msg->cm_anon_type = MES_NORMAL;
2080 msg->cm_format_type = 4;
2081 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2082 msg->cm_fields['O'] = strdoop(req_room);
2083 msg->cm_fields['N'] = strdoop(config.c_nodename);
2084 msg->cm_fields['H'] = strdoop(config.c_humannode);
2085 msg->cm_flags = flags;
2087 lprintf(9, "Loading\n");
2088 fp = fopen(filename, "rb");
2089 fseek(fp, 0L, SEEK_END);
2092 msg->cm_fields['M'] = mallok(len);
2093 fread(msg->cm_fields['M'], len, 1, fp);
2097 /* Create the requested room if we have to. */
2098 if (getroom(&qrbuf, roomname) != 0) {
2099 create_room(roomname, 4, "", 0);
2101 /* If the caller specified this object as unique, delete all
2102 * other objects of this type that are currently in the room.
2105 lprintf(9, "Deleted %d other msgs of this type\n",
2106 CtdlDeleteMessages(roomname, 0L, content_type));
2108 /* Now write the data */
2109 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2110 CtdlFreeMessage(msg);