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))
35 #define msg_repl ((struct repl *)CtdlGetUserData(SYM_REPL))
37 extern struct config config;
40 "", "", "", "", "", "", "", "",
41 "", "", "", "", "", "", "", "",
42 "", "", "", "", "", "", "", "",
43 "", "", "", "", "", "", "", "",
44 "", "", "", "", "", "", "", "",
45 "", "", "", "", "", "", "", "",
46 "", "", "", "", "", "", "", "",
47 "", "", "", "", "", "", "", "",
73 * This function is self explanatory.
74 * (What can I say, I'm in a weird mood today...)
76 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
80 for (i = 0; i < strlen(name); ++i)
83 if (isspace(name[i - 1])) {
84 strcpy(&name[i - 1], &name[i]);
87 while (isspace(name[i + 1])) {
88 strcpy(&name[i + 1], &name[i + 2]);
95 * Aliasing for network mail.
96 * (Error messages have been commented out, because this is a server.)
99 { /* process alias and routing info for mail */
102 char aaa[300], bbb[300];
104 lprintf(9, "alias() called for <%s>\n", name);
106 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
108 fp = fopen("network/mail.aliases", "r");
110 fp = fopen("/dev/null", "r");
115 while (fgets(aaa, sizeof aaa, fp) != NULL) {
116 while (isspace(name[0]))
117 strcpy(name, &name[1]);
118 aaa[strlen(aaa) - 1] = 0;
120 for (a = 0; a < strlen(aaa); ++a) {
122 strcpy(bbb, &aaa[a + 1]);
126 if (!strcasecmp(name, aaa))
130 lprintf(7, "Mail is being forwarded to %s\n", name);
132 /* determine local or remote type, see citadel.h */
133 for (a = 0; a < strlen(name); ++a)
135 return (MES_INTERNET);
136 for (a = 0; a < strlen(name); ++a)
138 for (b = a; b < strlen(name); ++b)
140 return (MES_INTERNET);
142 for (a = 0; a < strlen(name); ++a)
146 lprintf(7, "Too many @'s in address\n");
150 for (a = 0; a < strlen(name); ++a)
152 strcpy(bbb, &name[a + 1]);
154 strcpy(bbb, &bbb[1]);
155 fp = fopen("network/mail.sysinfo", "r");
159 a = getstring(fp, aaa);
160 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
161 a = getstring(fp, aaa);
162 if (!strncmp(aaa, "use ", 4)) {
163 strcpy(bbb, &aaa[4]);
168 if (!strncmp(aaa, "uum", 3)) {
170 for (a = 0; a < strlen(bbb); ++a) {
176 while (bbb[strlen(bbb) - 1] == '_')
177 bbb[strlen(bbb) - 1] = 0;
178 sprintf(name, &aaa[4], bbb);
179 return (MES_INTERNET);
181 if (!strncmp(aaa, "bin", 3)) {
184 while (aaa[strlen(aaa) - 1] != '@')
185 aaa[strlen(aaa) - 1] = 0;
186 aaa[strlen(aaa) - 1] = 0;
187 while (aaa[strlen(aaa) - 1] == ' ')
188 aaa[strlen(aaa) - 1] = 0;
189 while (bbb[0] != '@')
190 strcpy(bbb, &bbb[1]);
191 strcpy(bbb, &bbb[1]);
192 while (bbb[0] == ' ')
193 strcpy(bbb, &bbb[1]);
194 sprintf(name, "%s @%s", aaa, bbb);
207 fp = fopen("citadel.control", "r");
208 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
214 void simple_listing(long msgnum)
216 cprintf("%ld\n", msgnum);
221 /* Determine if a given message matches the fields in a message template.
222 * Return 0 for a successful match.
224 int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
227 /* If there aren't any fields in the template, all messages will
230 if (template == NULL) return(0);
232 /* Null messages are bogus. */
233 if (msg == NULL) return(1);
235 for (i='A'; i<='Z'; ++i) {
236 if (template->cm_fields[i] != NULL) {
237 if (msg->cm_fields[i] == NULL) {
240 if (strcasecmp(msg->cm_fields[i],
241 template->cm_fields[i])) return 1;
245 /* All compares succeeded: we have a match! */
253 * API function to perform an operation for each qualifying message in the
256 void CtdlForEachMessage(int mode, long ref,
258 struct CtdlMessage *compare,
259 void (*CallBack) (long msgnum))
264 struct cdbdata *cdbfr;
265 long *msglist = NULL;
268 struct SuppMsgInfo smi;
269 struct CtdlMessage *msg;
271 /* Learn about the user and room in question */
273 getuser(&CC->usersupp, CC->curr_user);
274 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
276 /* Load the message list */
277 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
279 msglist = mallok(cdbfr->len);
280 memcpy(msglist, cdbfr->ptr, cdbfr->len);
281 num_msgs = cdbfr->len / sizeof(long);
284 return; /* No messages at all? No further action. */
288 /* If the caller is looking for a specific MIME type, then filter
289 * out all messages which are not of the type requested.
292 if (content_type != NULL)
293 if (strlen(content_type) > 0)
294 for (a = 0; a < num_msgs; ++a) {
295 GetSuppMsgInfo(&smi, msglist[a]);
296 if (strcasecmp(smi.smi_content_type, content_type)) {
301 num_msgs = sort_msglist(msglist, num_msgs);
303 /* If a template was supplied, filter out the messages which
304 * don't match. (This could induce some delays!)
307 if (compare != NULL) {
308 for (a = 0; a < num_msgs; ++a) {
309 msg = CtdlFetchMessage(msglist[a]);
311 if (CtdlMsgCmp(msg, compare)) {
314 CtdlFreeMessage(msg);
322 * Now iterate through the message list, according to the
323 * criteria supplied by the caller.
326 for (a = 0; a < num_msgs; ++a) {
327 thismsg = msglist[a];
332 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
333 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
334 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
335 && (CC->usersupp.flags & US_LASTOLD))
336 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
337 || ((mode == MSGS_FIRST) && (a < ref))
338 || ((mode == MSGS_GT) && (thismsg > ref))
344 phree(msglist); /* Clean up */
350 * cmd_msgs() - get list of message #'s in this room
351 * implements the MSGS server command using CtdlForEachMessage()
353 void cmd_msgs(char *cmdbuf)
362 int with_template = 0;
363 struct CtdlMessage *template = NULL;
365 extract(which, cmdbuf, 0);
366 cm_ref = extract_int(cmdbuf, 1);
367 with_template = extract_int(cmdbuf, 2);
371 if (!strncasecmp(which, "OLD", 3))
373 else if (!strncasecmp(which, "NEW", 3))
375 else if (!strncasecmp(which, "FIRST", 5))
377 else if (!strncasecmp(which, "LAST", 4))
379 else if (!strncasecmp(which, "GT", 2))
382 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
383 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
388 cprintf("%d Send template then receive message list\n",
390 template = (struct CtdlMessage *)
391 mallok(sizeof(struct CtdlMessage));
392 memset(template, 0, sizeof(struct CtdlMessage));
393 while(client_gets(buf), strcmp(buf,"000")) {
394 extract(tfield, buf, 0);
395 extract(tvalue, buf, 1);
396 for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
397 if (!strcasecmp(tfield, msgkeys[i])) {
398 template->cm_fields[i] =
405 cprintf("%d Message list...\n", LISTING_FOLLOWS);
408 CtdlForEachMessage(mode, cm_ref, NULL, template, simple_listing);
409 if (template != NULL) CtdlFreeMessage(template);
417 * help_subst() - support routine for help file viewer
419 void help_subst(char *strbuf, char *source, char *dest)
424 while (p = pattern2(strbuf, source), (p >= 0)) {
425 strcpy(workbuf, &strbuf[p + strlen(source)]);
426 strcpy(&strbuf[p], dest);
427 strcat(strbuf, workbuf);
432 void do_help_subst(char *buffer)
436 help_subst(buffer, "^nodename", config.c_nodename);
437 help_subst(buffer, "^humannode", config.c_humannode);
438 help_subst(buffer, "^fqdn", config.c_fqdn);
439 help_subst(buffer, "^username", CC->usersupp.fullname);
440 sprintf(buf2, "%ld", CC->usersupp.usernum);
441 help_subst(buffer, "^usernum", buf2);
442 help_subst(buffer, "^sysadm", config.c_sysadm);
443 help_subst(buffer, "^variantname", CITADEL);
444 sprintf(buf2, "%d", config.c_maxsessions);
445 help_subst(buffer, "^maxsessions", buf2);
451 * memfmout() - Citadel text formatter and paginator.
452 * Although the original purpose of this routine was to format
453 * text to the reader's screen width, all we're really using it
454 * for here is to format text out to 80 columns before sending it
455 * to the client. The client software may reformat it again.
457 void memfmout(int width, char *mptr, char subst)
458 /* screen width to use */
459 /* where are we going to get our text from? */
460 /* nonzero if we should use hypertext mode */
472 c = 1; /* c is the current pos */
475 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
477 buffer[strlen(buffer) + 1] = 0;
478 buffer[strlen(buffer)] = ch;
481 if (buffer[0] == '^')
482 do_help_subst(buffer);
484 buffer[strlen(buffer) + 1] = 0;
486 strcpy(buffer, &buffer[1]);
496 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
498 if (((old == 13) || (old == 10)) && (isspace(real))) {
506 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
507 cprintf("\n%s", aaa);
516 if ((strlen(aaa) + c) > (width - 5)) {
526 if ((ch == 13) || (ch == 10)) {
527 cprintf("%s\n", aaa);
534 FMTEND: cprintf("%s\n", aaa);
540 * Callback function for mime parser that simply lists the part
542 void list_this_part(char *name, char *filename, char *partnum, char *disp,
543 void *content, char *cbtype, size_t length)
546 cprintf("part=%s|%s|%s|%s|%s|%d\n",
547 name, filename, partnum, disp, cbtype, length);
552 * Callback function for mime parser that wants to display text
554 void fixed_output(char *name, char *filename, char *partnum, char *disp,
555 void *content, char *cbtype, size_t length)
559 if (!strcasecmp(cbtype, "multipart/alternative")) {
560 strcpy(ma->prefix, partnum);
561 strcat(ma->prefix, ".");
567 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
569 && (ma->did_print == 1) ) {
570 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
576 if (!strcasecmp(cbtype, "text/plain")) {
577 client_write(content, length);
579 else if (!strcasecmp(cbtype, "text/html")) {
580 ptr = html_to_ascii(content, 80, 0);
581 client_write(ptr, strlen(ptr));
584 else if (strncasecmp(cbtype, "multipart/", 10)) {
585 cprintf("Part %s: %s (%s) (%d bytes)\n",
586 partnum, filename, cbtype, length);
592 * Callback function for mime parser that opens a section for downloading
594 void mime_download(char *name, char *filename, char *partnum, char *disp,
595 void *content, char *cbtype, size_t length)
598 /* Silently go away if there's already a download open... */
599 if (CC->download_fp != NULL)
602 /* ...or if this is not the desired section */
603 if (strcasecmp(desired_section, partnum))
606 CC->download_fp = tmpfile();
607 if (CC->download_fp == NULL)
610 fwrite(content, length, 1, CC->download_fp);
611 fflush(CC->download_fp);
612 rewind(CC->download_fp);
614 OpenCmdResult(filename, cbtype);
620 * Load a message from disk into memory.
621 * This is used by output_message() and other fetch functions.
623 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
624 * using the CtdlMessageFree() function.
626 struct CtdlMessage *CtdlFetchMessage(long msgnum)
628 struct cdbdata *dmsgtext;
629 struct CtdlMessage *ret = NULL;
632 CIT_UBYTE field_header;
636 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
637 if (dmsgtext == NULL) {
638 lprintf(9, "CtdlFetchMessage(%ld) failed.\n");
641 mptr = dmsgtext->ptr;
643 /* Parse the three bytes that begin EVERY message on disk.
644 * The first is always 0xFF, the on-disk magic number.
645 * The second is the anonymous/public type byte.
646 * The third is the format type byte (vari, fixed, or MIME).
650 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
654 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
655 memset(ret, 0, sizeof(struct CtdlMessage));
657 ret->cm_magic = CTDLMESSAGE_MAGIC;
658 ret->cm_anon_type = *mptr++; /* Anon type byte */
659 ret->cm_format_type = *mptr++; /* Format type byte */
662 * The rest is zero or more arbitrary fields. Load them in.
663 * We're done when we encounter either a zero-length field or
664 * have just processed the 'M' (message text) field.
667 field_length = strlen(mptr);
668 if (field_length == 0)
670 field_header = *mptr++;
671 ret->cm_fields[field_header] = mallok(field_length);
672 strcpy(ret->cm_fields[field_header], mptr);
674 while (*mptr++ != 0); /* advance to next field */
676 } while ((field_length > 0) && (field_header != 'M'));
680 /* Perform "before read" hooks (aborting if any return nonzero) */
681 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
682 CtdlFreeMessage(ret);
691 * Returns 1 if the supplied pointer points to a valid Citadel message.
692 * If the pointer is NULL or the magic number check fails, returns 0.
694 int is_valid_message(struct CtdlMessage *msg) {
697 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
698 lprintf(3, "is_valid_message() -- self-check failed\n");
706 * 'Destructor' for struct CtdlMessage
708 void CtdlFreeMessage(struct CtdlMessage *msg)
712 if (is_valid_message(msg) == 0) return;
714 for (i = 0; i < 256; ++i)
715 if (msg->cm_fields[i] != NULL)
716 phree(msg->cm_fields[i]);
718 msg->cm_magic = 0; /* just in case */
725 * Get a message off disk. (return value is the message's timestamp)
728 void output_message(char *msgid, int mode, int headers_only)
736 char display_name[256];
738 struct CtdlMessage *TheMessage = NULL;
742 /* buffers needed for RFC822 translation */
750 msg_num = atol(msgid);
752 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
753 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
757 /* FIX ... small security issue
758 * We need to check to make sure the requested message is actually
759 * in the current room, and set msg_ok to 1 only if it is. This
760 * functionality is currently missing because I'm in a hurry to replace
761 * broken production code with nonbroken pre-beta code. :( -- ajc
764 cprintf("%d Message %ld is not in this room.\n",
771 * Fetch the message from disk
773 TheMessage = CtdlFetchMessage(msg_num);
774 if (TheMessage == NULL) {
775 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
779 /* Are we downloading a MIME component? */
780 if (mode == MT_DOWNLOAD) {
781 if (TheMessage->cm_format_type != 4) {
782 cprintf("%d This is not a MIME message.\n",
784 } else if (CC->download_fp != NULL) {
785 cprintf("%d You already have a download open.\n",
788 /* Parse the message text component */
789 mptr = TheMessage->cm_fields['M'];
790 mime_parser(mptr, NULL, *mime_download);
791 /* If there's no file open by this time, the requested
792 * section wasn't found, so print an error
794 if (CC->download_fp == NULL) {
795 cprintf("%d Section %s not found.\n",
796 ERROR + FILE_NOT_FOUND,
800 CtdlFreeMessage(TheMessage);
804 /* now for the user-mode message reading loops */
805 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
807 /* Tell the client which format type we're using. If this is a
808 * MIME message, *lie* about it and tell the user it's fixed-format.
810 if (mode == MT_CITADEL) {
811 if (TheMessage->cm_format_type == 4)
814 cprintf("type=%d\n", TheMessage->cm_format_type);
817 /* nhdr=yes means that we're only displaying headers, no body */
818 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
819 cprintf("nhdr=yes\n");
822 /* begin header processing loop for Citadel message format */
824 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
826 strcpy(display_name, "<unknown>");
827 if (TheMessage->cm_fields['A']) {
828 strcpy(buf, TheMessage->cm_fields['A']);
829 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
830 if (TheMessage->cm_anon_type == MES_ANON)
831 strcpy(display_name, "****");
832 else if (TheMessage->cm_anon_type == MES_AN2)
833 strcpy(display_name, "anonymous");
835 strcpy(display_name, buf);
837 && ((TheMessage->cm_anon_type == MES_ANON)
838 || (TheMessage->cm_anon_type == MES_AN2))) {
839 sprintf(&display_name[strlen(display_name)],
844 strcpy(allkeys, FORDER);
845 for (i=0; i<strlen(allkeys); ++i) {
846 k = (int) allkeys[i];
848 if (TheMessage->cm_fields[k] != NULL) {
857 TheMessage->cm_fields[k]
866 /* begin header processing loop for RFC822 transfer format */
870 strcpy(snode, NODENAME);
871 strcpy(lnode, HUMANNODE);
872 if (mode == MT_RFC822) {
873 for (i = 0; i < 256; ++i) {
874 if (TheMessage->cm_fields[i]) {
875 mptr = TheMessage->cm_fields[i];
879 } else if (i == 'P') {
880 cprintf("Path: %s\n", mptr);
881 for (a = 0; a < strlen(mptr); ++a) {
882 if (mptr[a] == '!') {
883 strcpy(mptr, &mptr[a + 1]);
889 cprintf("Subject: %s\n", mptr);
895 cprintf("X-Citadel-Room: %s\n", mptr);
899 cprintf("To: %s\n", mptr);
902 cprintf("Date: %s", asctime(localtime(&xtime)));
908 if (mode == MT_RFC822) {
909 if (!strcasecmp(snode, NODENAME)) {
912 cprintf("Message-ID: <%s@%s>\n", mid, snode);
913 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
914 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
915 cprintf("Organization: %s\n", lnode);
918 /* end header processing loop ... at this point, we're in the text */
920 mptr = TheMessage->cm_fields['M'];
922 /* Tell the client about the MIME parts in this message */
923 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
924 if (mode == MT_CITADEL) {
925 mime_parser(mptr, NULL, *list_this_part);
927 else if (mode == MT_MIME) { /* list parts only */
928 mime_parser(mptr, NULL, *list_this_part);
930 CtdlFreeMessage(TheMessage);
937 CtdlFreeMessage(TheMessage);
941 /* signify start of msg text */
942 if (mode == MT_CITADEL)
944 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
947 /* If the format type on disk is 1 (fixed-format), then we want
948 * everything to be output completely literally ... regardless of
949 * what message transfer format is in use.
951 if (TheMessage->cm_format_type == 1) {
953 while (ch = *mptr++, ch > 0) {
956 if ((ch == 10) || (strlen(buf) > 250)) {
957 cprintf("%s\n", buf);
960 buf[strlen(buf) + 1] = 0;
961 buf[strlen(buf)] = ch;
965 cprintf("%s\n", buf);
968 /* If the message on disk is format 0 (Citadel vari-format), we
969 * output using the formatter at 80 columns. This is the final output
970 * form if the transfer format is RFC822, but if the transfer format
971 * is Citadel proprietary, it'll still work, because the indentation
972 * for new paragraphs is correct and the client will reformat the
973 * message to the reader's screen width.
975 if (TheMessage->cm_format_type == 0) {
976 memfmout(80, mptr, 0);
979 /* If the message on disk is format 4 (MIME), we've gotta hand it
980 * off to the MIME parser. The client has already been told that
981 * this message is format 1 (fixed format), so the callback function
982 * we use will display those parts as-is.
984 if (TheMessage->cm_format_type == 4) {
985 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
986 memset(ma, 0, sizeof(struct ma_info));
987 mime_parser(mptr, NULL, *fixed_output);
992 CtdlFreeMessage(TheMessage);
999 * display a message (mode 0 - Citadel proprietary)
1001 void cmd_msg0(char *cmdbuf)
1004 int headers_only = 0;
1006 extract(msgid, cmdbuf, 0);
1007 headers_only = extract_int(cmdbuf, 1);
1009 output_message(msgid, MT_CITADEL, headers_only);
1015 * display a message (mode 2 - RFC822)
1017 void cmd_msg2(char *cmdbuf)
1020 int headers_only = 0;
1022 extract(msgid, cmdbuf, 0);
1023 headers_only = extract_int(cmdbuf, 1);
1025 output_message(msgid, MT_RFC822, headers_only);
1031 * display a message (mode 3 - IGnet raw format - internal programs only)
1033 void cmd_msg3(char *cmdbuf)
1036 struct CtdlMessage *msg;
1039 if (CC->internal_pgm == 0) {
1040 cprintf("%d This command is for internal programs only.\n",
1045 msgnum = extract_long(cmdbuf, 0);
1046 msg = CtdlFetchMessage(msgnum);
1048 cprintf("%d Message %ld not found.\n",
1053 serialize_message(&smr, msg);
1054 CtdlFreeMessage(msg);
1057 cprintf("%d Unable to serialize message\n",
1058 ERROR+INTERNAL_ERROR);
1062 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1063 client_write(smr.ser, smr.len);
1070 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
1072 void cmd_msg4(char *cmdbuf)
1076 extract(msgid, cmdbuf, 0);
1078 output_message(msgid, MT_MIME, 0);
1082 * Open a component of a MIME message as a download file
1084 void cmd_opna(char *cmdbuf)
1088 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
1090 extract(msgid, cmdbuf, 0);
1091 extract(desired_section, cmdbuf, 1);
1093 output_message(msgid, MT_DOWNLOAD, 0);
1097 * Message base operation to send a message to the master file
1098 * (returns new message number)
1100 * This is the back end for CtdlSaveMsg() and should not be directly
1101 * called by server-side modules.
1104 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1105 int generate_id, /* generate 'I' field? */
1106 FILE *save_a_copy) /* save a copy to disk? */
1113 /* Get a new message number */
1114 newmsgid = get_new_message_number();
1115 sprintf(msgidbuf, "%ld", newmsgid);
1118 msg->cm_fields['I'] = strdoop(msgidbuf);
1121 serialize_message(&smr, msg);
1124 cprintf("%d Unable to serialize message\n",
1125 ERROR+INTERNAL_ERROR);
1129 /* Write our little bundle of joy into the message base */
1130 begin_critical_section(S_MSGMAIN);
1131 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1132 smr.ser, smr.len) < 0) {
1133 lprintf(2, "Can't store message\n");
1138 end_critical_section(S_MSGMAIN);
1140 /* If the caller specified that a copy should be saved to a particular
1141 * file handle, do that now too.
1143 if (save_a_copy != NULL) {
1144 fwrite(smr.ser, smr.len, 1, save_a_copy);
1147 /* Free the memory we used for the serialized message */
1150 /* Return the *local* message ID to the caller
1151 * (even if we're storing an incoming network message)
1159 * Serialize a struct CtdlMessage into the format used on disk and network.
1161 * This function loads up a "struct ser_ret" (defined in server.h) which
1162 * contains the length of the serialized message and a pointer to the
1163 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1165 void serialize_message(struct ser_ret *ret, /* return values */
1166 struct CtdlMessage *msg) /* unserialized msg */
1170 static char *forder = FORDER;
1172 lprintf(9, "serialize_message() called\n");
1174 if (is_valid_message(msg) == 0) return; /* self check */
1176 lprintf(9, "magic number check OK.\n");
1179 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1180 ret->len = ret->len +
1181 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1183 lprintf(9, "message is %d bytes\n", ret->len);
1185 lprintf(9, "calling malloc\n");
1186 ret->ser = mallok(ret->len);
1187 if (ret->ser == NULL) {
1193 ret->ser[1] = msg->cm_anon_type;
1194 ret->ser[2] = msg->cm_format_type;
1197 lprintf(9, "stuff\n");
1198 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1199 ret->ser[wlen++] = (char)forder[i];
1200 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1201 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1203 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1205 lprintf(9, "done serializing\n");
1213 * Back end for the ReplicationChecks() function
1215 void check_repl(long msgnum) {
1216 struct CtdlMessage *msg;
1217 time_t timestamp = (-1L);
1219 lprintf(9, "check_repl() found message %ld\n", msgnum);
1220 msg = CtdlFetchMessage(msgnum);
1221 if (msg == NULL) return;
1222 if (msg->cm_fields['T'] != NULL) {
1223 timestamp = atol(msg->cm_fields['T']);
1225 CtdlFreeMessage(msg);
1227 if (timestamp > msg_repl->highest) {
1228 msg_repl->highest = timestamp; /* newer! */
1229 lprintf(9, "newer!\n");
1232 lprintf(9, "older!\n");
1234 /* Existing isn't newer? Then delete the old one(s). */
1235 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, NULL);
1240 * Check to see if any messages already exist which carry the same Extended ID
1244 * -> With older timestamps: delete them and return 0. Message will be saved.
1245 * -> With newer timestamps: return 1. Message save will be aborted.
1247 int ReplicationChecks(struct CtdlMessage *msg) {
1248 struct CtdlMessage *template;
1251 lprintf(9, "ReplicationChecks() started\n");
1252 /* No extended id? Don't do anything. */
1253 if (msg->cm_fields['E'] == NULL) return 0;
1254 if (strlen(msg->cm_fields['E']) == 0) return 0;
1255 lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1257 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1258 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1259 msg_repl->highest = atol(msg->cm_fields['T']);
1261 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1262 memset(template, 0, sizeof(struct CtdlMessage));
1263 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1265 CtdlForEachMessage(MSGS_ALL, 0L, NULL, template, check_repl);
1267 /* If a newer message exists with the same Extended ID, abort
1270 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1274 CtdlFreeMessage(template);
1275 lprintf(9, "Returning %d\n", abort_this);
1287 * Save a message to disk
1289 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1290 char *rec, /* Recipient (mail) */
1291 char *force, /* force a particular room? */
1292 int mailtype, /* local or remote type */
1293 int generate_id) /* 1 = generate 'I' field */
1296 char hold_rm[ROOMNAMELEN];
1297 char actual_rm[ROOMNAMELEN];
1298 char force_room[ROOMNAMELEN];
1299 char content_type[256]; /* We have to learn this */
1300 char recipient[256];
1303 struct usersupp userbuf;
1305 int successful_local_recipients = 0;
1306 struct quickroom qtemp;
1307 struct SuppMsgInfo smi;
1308 FILE *network_fp = NULL;
1309 static int seqnum = 1;
1311 lprintf(9, "CtdlSaveMsg() called\n");
1312 if (is_valid_message(msg) == 0) return; /* self check */
1314 /* If this message has no timestamp, we take the liberty of
1315 * giving it one, right now.
1317 if (msg->cm_fields['T'] == NULL) {
1318 sprintf(aaa, "%ld", time(NULL));
1319 msg->cm_fields['T'] = strdoop(aaa);
1322 /* If this message has no path, we generate one.
1324 if (msg->cm_fields['P'] == NULL) {
1325 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1326 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1327 if (isspace(msg->cm_fields['P'][a])) {
1328 msg->cm_fields['P'][a] = ' ';
1333 strcpy(force_room, force);
1335 /* Strip non-printable characters out of the recipient name */
1336 strcpy(recipient, rec);
1337 for (a = 0; a < strlen(recipient); ++a)
1338 if (!isprint(recipient[a]))
1339 strcpy(&recipient[a], &recipient[a + 1]);
1341 /* Learn about what's inside, because it's what's inside that counts */
1343 switch (msg->cm_format_type) {
1345 strcpy(content_type, "text/x-citadel-variformat");
1348 strcpy(content_type, "text/plain");
1351 strcpy(content_type, "text/plain");
1352 /* advance past header fields */
1353 mptr = msg->cm_fields['M'];
1356 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1357 safestrncpy(content_type, mptr,
1358 sizeof(content_type));
1359 strcpy(content_type, &content_type[14]);
1360 for (a = 0; a < strlen(content_type); ++a)
1361 if ((content_type[a] == ';')
1362 || (content_type[a] == ' ')
1363 || (content_type[a] == 13)
1364 || (content_type[a] == 10))
1365 content_type[a] = 0;
1372 /* Perform "before save" hooks (aborting if any return nonzero) */
1373 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1375 /* If this message has an Extended ID, perform replication checks */
1376 if (ReplicationChecks(msg) > 0) return;
1378 /* Network mail - send a copy to the network program. */
1379 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1380 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1381 (long) getpid(), CC->cs_pid, ++seqnum);
1382 lprintf(9, "Saving a copy to %s\n", aaa);
1383 network_fp = fopen(aaa, "ab+");
1384 if (network_fp == NULL)
1385 lprintf(2, "ERROR: %s\n", strerror(errno));
1388 /* Save it to disk */
1389 newmsgid = send_message(msg, generate_id, network_fp);
1390 if (network_fp != NULL) {
1392 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1397 strcpy(actual_rm, CC->quickroom.QRname);
1398 strcpy(hold_rm, "");
1400 /* If this is being done by the networker delivering a private
1401 * message, we want to BYPASS saving the sender's copy (because there
1402 * is no local sender; it would otherwise go to the Trashcan).
1404 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1405 /* If the user is a twit, move to the twit room for posting */
1407 if (CC->usersupp.axlevel == 2) {
1408 strcpy(hold_rm, actual_rm);
1409 strcpy(actual_rm, config.c_twitroom);
1411 /* ...or if this message is destined for Aide> then go there. */
1412 if (strlen(force_room) > 0) {
1413 strcpy(hold_rm, actual_rm);
1414 strcpy(actual_rm, force_room);
1416 /* This call to usergoto() changes rooms if necessary. It also
1417 * causes the latest message list to be read into memory.
1419 usergoto(actual_rm, 0);
1421 /* read in the quickroom record, obtaining a lock... */
1422 lgetroom(&CC->quickroom, actual_rm);
1424 /* Fix an obscure bug */
1425 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1426 CC->quickroom.QRflags =
1427 CC->quickroom.QRflags & ~QR_MAILBOX;
1429 /* Add the message pointer to the room */
1430 CC->quickroom.QRhighest =
1431 AddMessageToRoom(&CC->quickroom, newmsgid);
1433 /* update quickroom */
1434 lputroom(&CC->quickroom);
1435 ++successful_local_recipients;
1438 /* Bump this user's messages posted counter. */
1439 lgetuser(&CC->usersupp, CC->curr_user);
1440 CC->usersupp.posted = CC->usersupp.posted + 1;
1441 lputuser(&CC->usersupp);
1443 /* If this is private, local mail, make a copy in the
1444 * recipient's mailbox and bump the reference count.
1446 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1447 if (getuser(&userbuf, recipient) == 0) {
1448 MailboxName(actual_rm, &userbuf, MAILROOM);
1449 if (lgetroom(&qtemp, actual_rm) == 0) {
1451 AddMessageToRoom(&qtemp, newmsgid);
1453 ++successful_local_recipients;
1457 /* If we've posted in a room other than the current room, then we
1458 * have to now go back to the current room...
1460 if (strlen(hold_rm) > 0) {
1461 usergoto(hold_rm, 0);
1464 /* Write a supplemental message info record. This doesn't have to
1465 * be a critical section because nobody else knows about this message
1468 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1469 smi.smi_msgnum = newmsgid;
1470 smi.smi_refcount = successful_local_recipients;
1471 safestrncpy(smi.smi_content_type, content_type, 64);
1472 PutSuppMsgInfo(&smi);
1474 /* Perform "after save" hooks */
1475 PerformMessageHooks(msg, EVT_AFTERSAVE);
1481 * Convenience function for generating small administrative messages.
1483 void quickie_message(char *from, char *to, char *room, char *text)
1485 struct CtdlMessage *msg;
1487 msg = mallok(sizeof(struct CtdlMessage));
1488 memset(msg, 0, sizeof(struct CtdlMessage));
1489 msg->cm_magic = CTDLMESSAGE_MAGIC;
1490 msg->cm_anon_type = MES_NORMAL;
1491 msg->cm_format_type = 0;
1492 msg->cm_fields['A'] = strdoop(from);
1493 msg->cm_fields['O'] = strdoop(room);
1494 msg->cm_fields['N'] = strdoop(NODENAME);
1496 msg->cm_fields['R'] = strdoop(to);
1497 msg->cm_fields['M'] = strdoop(text);
1499 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1500 CtdlFreeMessage(msg);
1501 syslog(LOG_NOTICE, text);
1506 * Build a binary message to be saved on disk.
1509 struct CtdlMessage *make_message(
1510 struct usersupp *author, /* author's usersupp structure */
1511 char *recipient, /* NULL if it's not mail */
1512 char *room, /* room where it's going */
1513 int type, /* see MES_ types in header file */
1514 int net_type, /* see MES_ types in header file */
1515 int format_type, /* local or remote (see citadel.h) */
1516 char *fake_name) /* who we're masquerading as */
1522 size_t message_len = 0;
1523 size_t buffer_len = 0;
1525 struct CtdlMessage *msg;
1527 msg = mallok(sizeof(struct CtdlMessage));
1528 memset(msg, 0, sizeof(struct CtdlMessage));
1529 msg->cm_magic = CTDLMESSAGE_MAGIC;
1530 msg->cm_anon_type = type;
1531 msg->cm_format_type = format_type;
1533 /* Don't confuse the poor folks if it's not routed mail. */
1534 strcpy(dest_node, "");
1536 /* If net_type is MES_BINARY, split out the destination node. */
1537 if (net_type == MES_BINARY) {
1538 strcpy(dest_node, NODENAME);
1539 for (a = 0; a < strlen(recipient); ++a) {
1540 if (recipient[a] == '@') {
1542 strcpy(dest_node, &recipient[a + 1]);
1547 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1548 if (net_type == MES_INTERNET) {
1549 strcpy(dest_node, "internet");
1552 while (isspace(recipient[strlen(recipient) - 1]))
1553 recipient[strlen(recipient) - 1] = 0;
1555 sprintf(buf, "cit%ld", author->usernum); /* Path */
1556 msg->cm_fields['P'] = strdoop(buf);
1558 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1559 msg->cm_fields['T'] = strdoop(buf);
1561 if (fake_name[0]) /* author */
1562 msg->cm_fields['A'] = strdoop(fake_name);
1564 msg->cm_fields['A'] = strdoop(author->fullname);
1566 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1567 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1569 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1571 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1572 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1574 if (recipient[0] != 0)
1575 msg->cm_fields['R'] = strdoop(recipient);
1576 if (dest_node[0] != 0)
1577 msg->cm_fields['D'] = strdoop(dest_node);
1579 msg->cm_fields['M'] = mallok(4096);
1580 if (msg->cm_fields['M'] == NULL) {
1581 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1585 msg->cm_fields['M'][0] = 0;
1589 /* read in the lines of message text one by one */
1590 while (client_gets(buf), strcmp(buf, "000")) {
1592 /* augment the buffer if we have to */
1593 if ((message_len + strlen(buf) + 2) > buffer_len) {
1594 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1595 if (ptr == NULL) { /* flush if can't allocate */
1596 while (client_gets(buf), strcmp(buf, "000")) ;;
1599 buffer_len = (buffer_len * 2);
1600 msg->cm_fields['M'] = ptr;
1604 strcat(msg->cm_fields['M'], buf);
1605 strcat(msg->cm_fields['M'], "\n");
1607 /* if we've hit the max msg length, flush the rest */
1608 if (message_len >= config.c_maxmsglen) {
1609 while (client_gets(buf), strcmp(buf, "000")) ;;
1622 * message entry - mode 0 (normal)
1624 void cmd_ent0(char *entargs)
1627 char recipient[256];
1629 int format_type = 0;
1630 char newusername[256];
1631 struct CtdlMessage *msg;
1635 struct usersupp tempUS;
1638 post = extract_int(entargs, 0);
1639 extract(recipient, entargs, 1);
1640 anon_flag = extract_int(entargs, 2);
1641 format_type = extract_int(entargs, 3);
1643 /* first check to make sure the request is valid. */
1645 if (!(CC->logged_in)) {
1646 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1649 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1650 cprintf("%d Need to be validated to enter ",
1651 ERROR + HIGHER_ACCESS_REQUIRED);
1652 cprintf("(except in %s> to sysop)\n", MAILROOM);
1655 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1656 cprintf("%d Need net privileges to enter here.\n",
1657 ERROR + HIGHER_ACCESS_REQUIRED);
1660 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1661 cprintf("%d Sorry, this is a read-only room.\n",
1662 ERROR + HIGHER_ACCESS_REQUIRED);
1669 if (CC->usersupp.axlevel < 6) {
1670 cprintf("%d You don't have permission to masquerade.\n",
1671 ERROR + HIGHER_ACCESS_REQUIRED);
1674 extract(newusername, entargs, 4);
1675 memset(CC->fake_postname, 0, 32);
1676 strcpy(CC->fake_postname, newusername);
1677 cprintf("%d Ok\n", OK);
1680 CC->cs_flags |= CS_POSTING;
1683 if (CC->quickroom.QRflags & QR_MAILBOX) {
1684 if (CC->usersupp.axlevel >= 2) {
1685 strcpy(buf, recipient);
1687 strcpy(buf, "sysop");
1688 e = alias(buf); /* alias and mail type */
1689 if ((buf[0] == 0) || (e == MES_ERROR)) {
1690 cprintf("%d Unknown address - cannot send message.\n",
1691 ERROR + NO_SUCH_USER);
1694 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1695 cprintf("%d Net privileges required for network mail.\n",
1696 ERROR + HIGHER_ACCESS_REQUIRED);
1699 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1700 && ((CC->usersupp.flags & US_INTERNET) == 0)
1701 && (!CC->internal_pgm)) {
1702 cprintf("%d You don't have access to Internet mail.\n",
1703 ERROR + HIGHER_ACCESS_REQUIRED);
1706 if (!strcasecmp(buf, "sysop")) {
1711 goto SKFALL; /* don't search local file */
1712 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1713 cprintf("%d Can't send mail to yourself!\n",
1714 ERROR + NO_SUCH_USER);
1717 /* Check to make sure the user exists; also get the correct
1718 * upper/lower casing of the name.
1720 a = getuser(&tempUS, buf);
1722 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1725 strcpy(buf, tempUS.fullname);
1728 SKFALL: b = MES_NORMAL;
1729 if (CC->quickroom.QRflags & QR_ANONONLY)
1731 if (CC->quickroom.QRflags & QR_ANONOPT) {
1735 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1738 /* If we're only checking the validity of the request, return
1739 * success without creating the message.
1742 cprintf("%d %s\n", OK, buf);
1746 cprintf("%d send message\n", SEND_LISTING);
1748 /* Read in the message from the client. */
1749 if (CC->fake_postname[0])
1750 msg = make_message(&CC->usersupp, buf,
1751 CC->quickroom.QRname, b, e, format_type,
1753 else if (CC->fake_username[0])
1754 msg = make_message(&CC->usersupp, buf,
1755 CC->quickroom.QRname, b, e, format_type,
1758 msg = make_message(&CC->usersupp, buf,
1759 CC->quickroom.QRname, b, e, format_type, "");
1762 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1763 CtdlFreeMessage(msg);
1764 CC->fake_postname[0] = '\0';
1771 * message entry - mode 3 (raw)
1773 void cmd_ent3(char *entargs)
1778 unsigned char ch, which_field;
1779 struct usersupp tempUS;
1781 struct CtdlMessage *msg;
1784 if (CC->internal_pgm == 0) {
1785 cprintf("%d This command is for internal programs only.\n",
1790 /* See if there's a recipient, but make sure it's a real one */
1791 extract(recp, entargs, 1);
1792 for (a = 0; a < strlen(recp); ++a)
1793 if (!isprint(recp[a]))
1794 strcpy(&recp[a], &recp[a + 1]);
1795 while (isspace(recp[0]))
1796 strcpy(recp, &recp[1]);
1797 while (isspace(recp[strlen(recp) - 1]))
1798 recp[strlen(recp) - 1] = 0;
1800 /* If we're in Mail, check the recipient */
1801 if (strlen(recp) > 0) {
1802 e = alias(recp); /* alias and mail type */
1803 if ((recp[0] == 0) || (e == MES_ERROR)) {
1804 cprintf("%d Unknown address - cannot send message.\n",
1805 ERROR + NO_SUCH_USER);
1808 if (e == MES_LOCAL) {
1809 a = getuser(&tempUS, recp);
1811 cprintf("%d No such user.\n",
1812 ERROR + NO_SUCH_USER);
1818 /* At this point, message has been approved. */
1819 if (extract_int(entargs, 0) == 0) {
1820 cprintf("%d OK to send\n", OK);
1824 msglen = extract_long(entargs, 2);
1825 msg = mallok(sizeof(struct CtdlMessage));
1827 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1831 memset(msg, 0, sizeof(struct CtdlMessage));
1832 tempbuf = mallok(msglen);
1833 if (tempbuf == NULL) {
1834 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1839 cprintf("%d %ld\n", SEND_BINARY, msglen);
1841 client_read(&ch, 1); /* 0xFF magic number */
1842 msg->cm_magic = CTDLMESSAGE_MAGIC;
1843 client_read(&ch, 1); /* anon type */
1844 msg->cm_anon_type = ch;
1845 client_read(&ch, 1); /* format type */
1846 msg->cm_format_type = ch;
1847 msglen = msglen - 3;
1849 while (msglen > 0) {
1850 client_read(&which_field, 1);
1854 client_read(&ch, 1);
1856 a = strlen(tempbuf);
1859 } while ( (ch != 0) && (msglen > 0) );
1860 msg->cm_fields[which_field] = strdoop(tempbuf);
1863 msg->cm_flags = CM_SKIP_HOOKS;
1864 CtdlSaveMsg(msg, recp, "", e, 0);
1865 CtdlFreeMessage(msg);
1871 * API function to delete messages which match a set of criteria
1872 * (returns the actual number of messages deleted)
1874 int CtdlDeleteMessages(char *room_name, /* which room */
1875 long dmsgnum, /* or "0" for any */
1876 char *content_type /* or NULL for any */
1880 struct quickroom qrbuf;
1881 struct cdbdata *cdbfr;
1882 long *msglist = NULL;
1885 int num_deleted = 0;
1887 struct SuppMsgInfo smi;
1889 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1890 room_name, dmsgnum, content_type);
1892 /* get room record, obtaining a lock... */
1893 if (lgetroom(&qrbuf, room_name) != 0) {
1894 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1896 return (0); /* room not found */
1898 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1900 if (cdbfr != NULL) {
1901 msglist = mallok(cdbfr->len);
1902 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1903 num_msgs = cdbfr->len / sizeof(long);
1907 for (i = 0; i < num_msgs; ++i) {
1910 /* Set/clear a bit for each criterion */
1912 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1913 delete_this |= 0x01;
1915 if (content_type == NULL) {
1916 delete_this |= 0x02;
1918 GetSuppMsgInfo(&smi, msglist[i]);
1919 if (!strcasecmp(smi.smi_content_type,
1921 delete_this |= 0x02;
1925 /* Delete message only if all bits are set */
1926 if (delete_this == 0x03) {
1927 AdjRefCount(msglist[i], -1);
1933 num_msgs = sort_msglist(msglist, num_msgs);
1934 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1935 msglist, (num_msgs * sizeof(long)));
1937 qrbuf.QRhighest = msglist[num_msgs - 1];
1941 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1942 return (num_deleted);
1948 * Delete message from current room
1950 void cmd_dele(char *delstr)
1955 getuser(&CC->usersupp, CC->curr_user);
1956 if ((CC->usersupp.axlevel < 6)
1957 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1958 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1959 && (!(CC->internal_pgm))) {
1960 cprintf("%d Higher access required.\n",
1961 ERROR + HIGHER_ACCESS_REQUIRED);
1964 delnum = extract_long(delstr, 0);
1966 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1969 cprintf("%d %d message%s deleted.\n", OK,
1970 num_deleted, ((num_deleted != 1) ? "s" : ""));
1972 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1978 * move a message to another room
1980 void cmd_move(char *args)
1984 struct quickroom qtemp;
1987 num = extract_long(args, 0);
1988 extract(targ, args, 1);
1990 getuser(&CC->usersupp, CC->curr_user);
1991 if ((CC->usersupp.axlevel < 6)
1992 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1993 cprintf("%d Higher access required.\n",
1994 ERROR + HIGHER_ACCESS_REQUIRED);
1997 if (getroom(&qtemp, targ) != 0) {
1998 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2001 /* Bump the reference count, otherwise the message will be deleted
2002 * from disk when we remove it from the source room.
2004 AdjRefCount(num, 1);
2006 /* yank the message out of the current room... */
2007 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
2010 /* put the message into the target room */
2011 lgetroom(&qtemp, targ);
2012 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
2014 cprintf("%d Message moved.\n", OK);
2016 AdjRefCount(num, (-1)); /* oops */
2017 cprintf("%d msg %ld does not exist.\n", ERROR, num);
2024 * GetSuppMsgInfo() - Get the supplementary record for a message
2026 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2029 struct cdbdata *cdbsmi;
2032 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2033 smibuf->smi_msgnum = msgnum;
2034 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2036 /* Use the negative of the message number for its supp record index */
2037 TheIndex = (0L - msgnum);
2039 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2040 if (cdbsmi == NULL) {
2041 return; /* record not found; go with defaults */
2043 memcpy(smibuf, cdbsmi->ptr,
2044 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2045 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2052 * PutSuppMsgInfo() - (re)write supplementary record for a message
2054 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2058 /* Use the negative of the message number for its supp record index */
2059 TheIndex = (0L - smibuf->smi_msgnum);
2061 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2062 smibuf->smi_msgnum, smibuf->smi_refcount);
2064 cdb_store(CDB_MSGMAIN,
2065 &TheIndex, sizeof(long),
2066 smibuf, sizeof(struct SuppMsgInfo));
2071 * AdjRefCount - change the reference count for a message;
2072 * delete the message if it reaches zero
2074 void AdjRefCount(long msgnum, int incr)
2077 struct SuppMsgInfo smi;
2080 /* This is a *tight* critical section; please keep it that way, as
2081 * it may get called while nested in other critical sections.
2082 * Complicating this any further will surely cause deadlock!
2084 begin_critical_section(S_SUPPMSGMAIN);
2085 GetSuppMsgInfo(&smi, msgnum);
2086 smi.smi_refcount += incr;
2087 PutSuppMsgInfo(&smi);
2088 end_critical_section(S_SUPPMSGMAIN);
2090 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2091 msgnum, smi.smi_refcount);
2093 /* If the reference count is now zero, delete the message
2094 * (and its supplementary record as well).
2096 if (smi.smi_refcount == 0) {
2097 lprintf(9, "Deleting message <%ld>\n", msgnum);
2099 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2100 delnum = (0L - msgnum);
2101 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2106 * Write a generic object to this room
2108 * Note: this could be much more efficient. Right now we use two temporary
2109 * files, and still pull the message into memory as with all others.
2111 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2112 char *content_type, /* MIME type of this object */
2113 char *tempfilename, /* Where to fetch it from */
2114 struct usersupp *is_mailbox, /* Mailbox room? */
2115 int is_binary, /* Is encoding necessary? */
2116 int is_unique, /* Del others of this type? */
2117 unsigned int flags /* Internal save flags */
2122 char filename[PATH_MAX];
2125 struct quickroom qrbuf;
2126 char roomname[ROOMNAMELEN];
2127 struct CtdlMessage *msg;
2130 if (is_mailbox != NULL)
2131 MailboxName(roomname, is_mailbox, req_room);
2133 safestrncpy(roomname, req_room, sizeof(roomname));
2134 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2136 strcpy(filename, tmpnam(NULL));
2137 fp = fopen(filename, "w");
2141 tempfp = fopen(tempfilename, "r");
2142 if (tempfp == NULL) {
2148 fprintf(fp, "Content-type: %s\n", content_type);
2149 lprintf(9, "Content-type: %s\n", content_type);
2151 if (is_binary == 0) {
2152 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2153 while (ch = getc(tempfp), ch > 0)
2159 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2162 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2163 tempfilename, filename);
2167 lprintf(9, "Allocating\n");
2168 msg = mallok(sizeof(struct CtdlMessage));
2169 memset(msg, 0, sizeof(struct CtdlMessage));
2170 msg->cm_magic = CTDLMESSAGE_MAGIC;
2171 msg->cm_anon_type = MES_NORMAL;
2172 msg->cm_format_type = 4;
2173 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2174 msg->cm_fields['O'] = strdoop(req_room);
2175 msg->cm_fields['N'] = strdoop(config.c_nodename);
2176 msg->cm_fields['H'] = strdoop(config.c_humannode);
2177 msg->cm_flags = flags;
2179 lprintf(9, "Loading\n");
2180 fp = fopen(filename, "rb");
2181 fseek(fp, 0L, SEEK_END);
2184 msg->cm_fields['M'] = mallok(len);
2185 fread(msg->cm_fields['M'], len, 1, fp);
2189 /* Create the requested room if we have to. */
2190 if (getroom(&qrbuf, roomname) != 0) {
2191 create_room(roomname, 4, "", 0);
2193 /* If the caller specified this object as unique, delete all
2194 * other objects of this type that are currently in the room.
2197 lprintf(9, "Deleted %d other msgs of this type\n",
2198 CtdlDeleteMessages(roomname, 0L, content_type));
2200 /* Now write the data */
2201 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2202 CtdlFreeMessage(msg);