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);
1099 * Save a message pointer into a specified room
1100 * (Returns 0 for success, nonzero for failure)
1102 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid) {
1104 struct quickroom qrbuf;
1105 struct cdbdata *cdbfr;
1108 long highest_msg = 0L;
1110 lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld)\n", roomname, msgid);
1112 if (lgetroom(&qrbuf, roomname) != 0) {
1113 lprintf(9, "No such room <%s>\n", roomname);
1114 return(ERROR + ROOM_NOT_FOUND);
1117 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1118 if (cdbfr == NULL) {
1122 msglist = mallok(cdbfr->len);
1123 if (msglist == NULL)
1124 lprintf(3, "ERROR malloc msglist!\n");
1125 num_msgs = cdbfr->len / sizeof(long);
1126 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1131 /* Make sure the message doesn't already exist in this room. It
1132 * is absolutely taboo to have more than one reference to the same
1133 * message in a room.
1135 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1136 if (msglist[i] == msgid) {
1137 lputroom(&qrbuf); /* unlock the room */
1138 return(ERROR + ALREADY_EXISTS);
1142 /* Now add the new message */
1144 msglist = reallok(msglist,
1145 (num_msgs * sizeof(long)));
1147 if (msglist == NULL) {
1148 lprintf(3, "ERROR: can't realloc message list!\n");
1150 msglist[num_msgs - 1] = msgid;
1152 /* Sort the message list, so all the msgid's are in order */
1153 num_msgs = sort_msglist(msglist, num_msgs);
1155 /* Determine the highest message number */
1156 highest_msg = msglist[num_msgs - 1];
1158 /* Write it back to disk. */
1159 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1160 msglist, num_msgs * sizeof(long));
1162 /* Free up the memory we used. */
1165 /* Update the highest-message pointer and unlock the room. */
1166 qrbuf.QRhighest = highest_msg;
1169 /* Bump the reference count for this message. */
1170 AdjRefCount(msgid, +1);
1172 /* Return success. */
1173 lprintf(9, "CtdlSaveMsgPointerInRoom() succeeded\n");
1180 * Message base operation to send a message to the master file
1181 * (returns new message number)
1183 * This is the back end for CtdlSaveMsg() and should not be directly
1184 * called by server-side modules.
1187 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1188 int generate_id, /* generate 'I' field? */
1189 FILE *save_a_copy) /* save a copy to disk? */
1196 /* Get a new message number */
1197 newmsgid = get_new_message_number();
1198 sprintf(msgidbuf, "%ld", newmsgid);
1201 msg->cm_fields['I'] = strdoop(msgidbuf);
1204 serialize_message(&smr, msg);
1207 cprintf("%d Unable to serialize message\n",
1208 ERROR+INTERNAL_ERROR);
1212 /* Write our little bundle of joy into the message base */
1213 begin_critical_section(S_MSGMAIN);
1214 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1215 smr.ser, smr.len) < 0) {
1216 lprintf(2, "Can't store message\n");
1221 end_critical_section(S_MSGMAIN);
1223 /* If the caller specified that a copy should be saved to a particular
1224 * file handle, do that now too.
1226 if (save_a_copy != NULL) {
1227 fwrite(smr.ser, smr.len, 1, save_a_copy);
1230 /* Free the memory we used for the serialized message */
1233 /* Return the *local* message ID to the caller
1234 * (even if we're storing an incoming network message)
1242 * Serialize a struct CtdlMessage into the format used on disk and network.
1244 * This function loads up a "struct ser_ret" (defined in server.h) which
1245 * contains the length of the serialized message and a pointer to the
1246 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1248 void serialize_message(struct ser_ret *ret, /* return values */
1249 struct CtdlMessage *msg) /* unserialized msg */
1253 static char *forder = FORDER;
1255 lprintf(9, "serialize_message() called\n");
1257 if (is_valid_message(msg) == 0) return; /* self check */
1259 lprintf(9, "magic number check OK.\n");
1262 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1263 ret->len = ret->len +
1264 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1266 lprintf(9, "message is %d bytes\n", ret->len);
1268 lprintf(9, "calling malloc\n");
1269 ret->ser = mallok(ret->len);
1270 if (ret->ser == NULL) {
1276 ret->ser[1] = msg->cm_anon_type;
1277 ret->ser[2] = msg->cm_format_type;
1280 lprintf(9, "stuff\n");
1281 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1282 ret->ser[wlen++] = (char)forder[i];
1283 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1284 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1286 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1288 lprintf(9, "done serializing\n");
1296 * Back end for the ReplicationChecks() function
1298 void check_repl(long msgnum) {
1299 struct CtdlMessage *msg;
1300 time_t timestamp = (-1L);
1302 lprintf(9, "check_repl() found message %ld\n", msgnum);
1303 msg = CtdlFetchMessage(msgnum);
1304 if (msg == NULL) return;
1305 if (msg->cm_fields['T'] != NULL) {
1306 timestamp = atol(msg->cm_fields['T']);
1308 CtdlFreeMessage(msg);
1310 if (timestamp > msg_repl->highest) {
1311 msg_repl->highest = timestamp; /* newer! */
1312 lprintf(9, "newer!\n");
1315 lprintf(9, "older!\n");
1317 /* Existing isn't newer? Then delete the old one(s). */
1318 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, NULL);
1323 * Check to see if any messages already exist which carry the same Extended ID
1327 * -> With older timestamps: delete them and return 0. Message will be saved.
1328 * -> With newer timestamps: return 1. Message save will be aborted.
1330 int ReplicationChecks(struct CtdlMessage *msg) {
1331 struct CtdlMessage *template;
1334 lprintf(9, "ReplicationChecks() started\n");
1335 /* No extended id? Don't do anything. */
1336 if (msg->cm_fields['E'] == NULL) return 0;
1337 if (strlen(msg->cm_fields['E']) == 0) return 0;
1338 lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1340 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1341 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1342 msg_repl->highest = atol(msg->cm_fields['T']);
1344 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1345 memset(template, 0, sizeof(struct CtdlMessage));
1346 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1348 CtdlForEachMessage(MSGS_ALL, 0L, NULL, template, check_repl);
1350 /* If a newer message exists with the same Extended ID, abort
1353 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1357 CtdlFreeMessage(template);
1358 lprintf(9, "Returning %d\n", abort_this);
1370 * Save a message to disk
1372 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1373 char *rec, /* Recipient (mail) */
1374 char *force, /* force a particular room? */
1375 int mailtype, /* local or remote type */
1376 int generate_id) /* 1 = generate 'I' field */
1379 char hold_rm[ROOMNAMELEN];
1380 char actual_rm[ROOMNAMELEN];
1381 char force_room[ROOMNAMELEN];
1382 char content_type[256]; /* We have to learn this */
1383 char recipient[256];
1386 struct usersupp userbuf;
1388 struct SuppMsgInfo smi;
1389 FILE *network_fp = NULL;
1390 static int seqnum = 1;
1392 lprintf(9, "CtdlSaveMsg() called\n");
1393 if (is_valid_message(msg) == 0) return; /* self check */
1395 /* If this message has no timestamp, we take the liberty of
1396 * giving it one, right now.
1398 if (msg->cm_fields['T'] == NULL) {
1399 sprintf(aaa, "%ld", time(NULL));
1400 msg->cm_fields['T'] = strdoop(aaa);
1403 /* If this message has no path, we generate one.
1405 if (msg->cm_fields['P'] == NULL) {
1406 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1407 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1408 if (isspace(msg->cm_fields['P'][a])) {
1409 msg->cm_fields['P'][a] = ' ';
1414 strcpy(force_room, force);
1416 /* Strip non-printable characters out of the recipient name */
1417 strcpy(recipient, rec);
1418 for (a = 0; a < strlen(recipient); ++a)
1419 if (!isprint(recipient[a]))
1420 strcpy(&recipient[a], &recipient[a + 1]);
1422 /* Learn about what's inside, because it's what's inside that counts */
1424 switch (msg->cm_format_type) {
1426 strcpy(content_type, "text/x-citadel-variformat");
1429 strcpy(content_type, "text/plain");
1432 strcpy(content_type, "text/plain");
1433 /* advance past header fields */
1434 mptr = msg->cm_fields['M'];
1437 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1438 safestrncpy(content_type, mptr,
1439 sizeof(content_type));
1440 strcpy(content_type, &content_type[14]);
1441 for (a = 0; a < strlen(content_type); ++a)
1442 if ((content_type[a] == ';')
1443 || (content_type[a] == ' ')
1444 || (content_type[a] == 13)
1445 || (content_type[a] == 10))
1446 content_type[a] = 0;
1453 /* Perform "before save" hooks (aborting if any return nonzero) */
1454 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1456 /* If this message has an Extended ID, perform replication checks */
1457 if (ReplicationChecks(msg) > 0) return;
1459 /* Network mail - send a copy to the network program. */
1460 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1461 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1462 (long) getpid(), CC->cs_pid, ++seqnum);
1463 lprintf(9, "Saving a copy to %s\n", aaa);
1464 network_fp = fopen(aaa, "ab+");
1465 if (network_fp == NULL)
1466 lprintf(2, "ERROR: %s\n", strerror(errno));
1469 /* Save it to disk */
1470 newmsgid = send_message(msg, generate_id, network_fp);
1471 if (network_fp != NULL) {
1473 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1476 if (newmsgid <= 0L) return;
1478 /* Write a supplemental message info record. This doesn't have to
1479 * be a critical section because nobody else knows about this message
1482 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1483 smi.smi_msgnum = newmsgid;
1484 smi.smi_refcount = 0;
1485 safestrncpy(smi.smi_content_type, content_type, 64);
1486 PutSuppMsgInfo(&smi);
1488 /* Now figure out where to store the pointers */
1490 strcpy(actual_rm, CC->quickroom.QRname);
1492 /* If this is being done by the networker delivering a private
1493 * message, we want to BYPASS saving the sender's copy (because there
1494 * is no local sender; it would otherwise go to the Trashcan).
1496 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1497 /* If the user is a twit, move to the twit room for posting */
1499 if (CC->usersupp.axlevel == 2) {
1500 strcpy(hold_rm, actual_rm);
1501 strcpy(actual_rm, config.c_twitroom);
1504 /* ...or if this message is destined for Aide> then go there. */
1505 if (strlen(force_room) > 0) {
1506 strcpy(actual_rm, force_room);
1508 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid);
1511 /* Bump this user's messages posted counter. */
1512 lgetuser(&CC->usersupp, CC->curr_user);
1513 CC->usersupp.posted = CC->usersupp.posted + 1;
1514 lputuser(&CC->usersupp);
1516 /* If this is private, local mail, make a copy in the
1517 * recipient's mailbox and bump the reference count.
1519 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1520 if (getuser(&userbuf, recipient) == 0) {
1521 MailboxName(actual_rm, &userbuf, MAILROOM);
1522 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid);
1526 /* Perform "after save" hooks */
1527 PerformMessageHooks(msg, EVT_AFTERSAVE);
1533 * Convenience function for generating small administrative messages.
1535 void quickie_message(char *from, char *to, char *room, char *text)
1537 struct CtdlMessage *msg;
1539 msg = mallok(sizeof(struct CtdlMessage));
1540 memset(msg, 0, sizeof(struct CtdlMessage));
1541 msg->cm_magic = CTDLMESSAGE_MAGIC;
1542 msg->cm_anon_type = MES_NORMAL;
1543 msg->cm_format_type = 0;
1544 msg->cm_fields['A'] = strdoop(from);
1545 msg->cm_fields['O'] = strdoop(room);
1546 msg->cm_fields['N'] = strdoop(NODENAME);
1548 msg->cm_fields['R'] = strdoop(to);
1549 msg->cm_fields['M'] = strdoop(text);
1551 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1552 CtdlFreeMessage(msg);
1553 syslog(LOG_NOTICE, text);
1558 * Build a binary message to be saved on disk.
1561 struct CtdlMessage *make_message(
1562 struct usersupp *author, /* author's usersupp structure */
1563 char *recipient, /* NULL if it's not mail */
1564 char *room, /* room where it's going */
1565 int type, /* see MES_ types in header file */
1566 int net_type, /* see MES_ types in header file */
1567 int format_type, /* local or remote (see citadel.h) */
1568 char *fake_name) /* who we're masquerading as */
1574 size_t message_len = 0;
1575 size_t buffer_len = 0;
1577 struct CtdlMessage *msg;
1579 msg = mallok(sizeof(struct CtdlMessage));
1580 memset(msg, 0, sizeof(struct CtdlMessage));
1581 msg->cm_magic = CTDLMESSAGE_MAGIC;
1582 msg->cm_anon_type = type;
1583 msg->cm_format_type = format_type;
1585 /* Don't confuse the poor folks if it's not routed mail. */
1586 strcpy(dest_node, "");
1588 /* If net_type is MES_BINARY, split out the destination node. */
1589 if (net_type == MES_BINARY) {
1590 strcpy(dest_node, NODENAME);
1591 for (a = 0; a < strlen(recipient); ++a) {
1592 if (recipient[a] == '@') {
1594 strcpy(dest_node, &recipient[a + 1]);
1599 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1600 if (net_type == MES_INTERNET) {
1601 strcpy(dest_node, "internet");
1604 while (isspace(recipient[strlen(recipient) - 1]))
1605 recipient[strlen(recipient) - 1] = 0;
1607 sprintf(buf, "cit%ld", author->usernum); /* Path */
1608 msg->cm_fields['P'] = strdoop(buf);
1610 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1611 msg->cm_fields['T'] = strdoop(buf);
1613 if (fake_name[0]) /* author */
1614 msg->cm_fields['A'] = strdoop(fake_name);
1616 msg->cm_fields['A'] = strdoop(author->fullname);
1618 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1619 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1621 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1623 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1624 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1626 if (recipient[0] != 0)
1627 msg->cm_fields['R'] = strdoop(recipient);
1628 if (dest_node[0] != 0)
1629 msg->cm_fields['D'] = strdoop(dest_node);
1631 msg->cm_fields['M'] = mallok(4096);
1632 if (msg->cm_fields['M'] == NULL) {
1633 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1637 msg->cm_fields['M'][0] = 0;
1641 /* read in the lines of message text one by one */
1642 while (client_gets(buf), strcmp(buf, "000")) {
1644 /* augment the buffer if we have to */
1645 if ((message_len + strlen(buf) + 2) > buffer_len) {
1646 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1647 if (ptr == NULL) { /* flush if can't allocate */
1648 while (client_gets(buf), strcmp(buf, "000")) ;;
1651 buffer_len = (buffer_len * 2);
1652 msg->cm_fields['M'] = ptr;
1656 strcat(msg->cm_fields['M'], buf);
1657 strcat(msg->cm_fields['M'], "\n");
1659 /* if we've hit the max msg length, flush the rest */
1660 if (message_len >= config.c_maxmsglen) {
1661 while (client_gets(buf), strcmp(buf, "000")) ;;
1674 * message entry - mode 0 (normal)
1676 void cmd_ent0(char *entargs)
1679 char recipient[256];
1681 int format_type = 0;
1682 char newusername[256];
1683 struct CtdlMessage *msg;
1687 struct usersupp tempUS;
1690 post = extract_int(entargs, 0);
1691 extract(recipient, entargs, 1);
1692 anon_flag = extract_int(entargs, 2);
1693 format_type = extract_int(entargs, 3);
1695 /* first check to make sure the request is valid. */
1697 if (!(CC->logged_in)) {
1698 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1701 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1702 cprintf("%d Need to be validated to enter ",
1703 ERROR + HIGHER_ACCESS_REQUIRED);
1704 cprintf("(except in %s> to sysop)\n", MAILROOM);
1707 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1708 cprintf("%d Need net privileges to enter here.\n",
1709 ERROR + HIGHER_ACCESS_REQUIRED);
1712 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1713 cprintf("%d Sorry, this is a read-only room.\n",
1714 ERROR + HIGHER_ACCESS_REQUIRED);
1721 if (CC->usersupp.axlevel < 6) {
1722 cprintf("%d You don't have permission to masquerade.\n",
1723 ERROR + HIGHER_ACCESS_REQUIRED);
1726 extract(newusername, entargs, 4);
1727 memset(CC->fake_postname, 0, 32);
1728 strcpy(CC->fake_postname, newusername);
1729 cprintf("%d Ok\n", OK);
1732 CC->cs_flags |= CS_POSTING;
1735 if (CC->quickroom.QRflags & QR_MAILBOX) {
1736 if (CC->usersupp.axlevel >= 2) {
1737 strcpy(buf, recipient);
1739 strcpy(buf, "sysop");
1740 e = alias(buf); /* alias and mail type */
1741 if ((buf[0] == 0) || (e == MES_ERROR)) {
1742 cprintf("%d Unknown address - cannot send message.\n",
1743 ERROR + NO_SUCH_USER);
1746 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1747 cprintf("%d Net privileges required for network mail.\n",
1748 ERROR + HIGHER_ACCESS_REQUIRED);
1751 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1752 && ((CC->usersupp.flags & US_INTERNET) == 0)
1753 && (!CC->internal_pgm)) {
1754 cprintf("%d You don't have access to Internet mail.\n",
1755 ERROR + HIGHER_ACCESS_REQUIRED);
1758 if (!strcasecmp(buf, "sysop")) {
1763 goto SKFALL; /* don't search local file */
1764 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1765 cprintf("%d Can't send mail to yourself!\n",
1766 ERROR + NO_SUCH_USER);
1769 /* Check to make sure the user exists; also get the correct
1770 * upper/lower casing of the name.
1772 a = getuser(&tempUS, buf);
1774 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1777 strcpy(buf, tempUS.fullname);
1780 SKFALL: b = MES_NORMAL;
1781 if (CC->quickroom.QRflags & QR_ANONONLY)
1783 if (CC->quickroom.QRflags & QR_ANONOPT) {
1787 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1790 /* If we're only checking the validity of the request, return
1791 * success without creating the message.
1794 cprintf("%d %s\n", OK, buf);
1798 cprintf("%d send message\n", SEND_LISTING);
1800 /* Read in the message from the client. */
1801 if (CC->fake_postname[0])
1802 msg = make_message(&CC->usersupp, buf,
1803 CC->quickroom.QRname, b, e, format_type,
1805 else if (CC->fake_username[0])
1806 msg = make_message(&CC->usersupp, buf,
1807 CC->quickroom.QRname, b, e, format_type,
1810 msg = make_message(&CC->usersupp, buf,
1811 CC->quickroom.QRname, b, e, format_type, "");
1814 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1815 CtdlFreeMessage(msg);
1816 CC->fake_postname[0] = '\0';
1823 * message entry - mode 3 (raw)
1825 void cmd_ent3(char *entargs)
1830 unsigned char ch, which_field;
1831 struct usersupp tempUS;
1833 struct CtdlMessage *msg;
1836 if (CC->internal_pgm == 0) {
1837 cprintf("%d This command is for internal programs only.\n",
1842 /* See if there's a recipient, but make sure it's a real one */
1843 extract(recp, entargs, 1);
1844 for (a = 0; a < strlen(recp); ++a)
1845 if (!isprint(recp[a]))
1846 strcpy(&recp[a], &recp[a + 1]);
1847 while (isspace(recp[0]))
1848 strcpy(recp, &recp[1]);
1849 while (isspace(recp[strlen(recp) - 1]))
1850 recp[strlen(recp) - 1] = 0;
1852 /* If we're in Mail, check the recipient */
1853 if (strlen(recp) > 0) {
1854 e = alias(recp); /* alias and mail type */
1855 if ((recp[0] == 0) || (e == MES_ERROR)) {
1856 cprintf("%d Unknown address - cannot send message.\n",
1857 ERROR + NO_SUCH_USER);
1860 if (e == MES_LOCAL) {
1861 a = getuser(&tempUS, recp);
1863 cprintf("%d No such user.\n",
1864 ERROR + NO_SUCH_USER);
1870 /* At this point, message has been approved. */
1871 if (extract_int(entargs, 0) == 0) {
1872 cprintf("%d OK to send\n", OK);
1876 msglen = extract_long(entargs, 2);
1877 msg = mallok(sizeof(struct CtdlMessage));
1879 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1883 memset(msg, 0, sizeof(struct CtdlMessage));
1884 tempbuf = mallok(msglen);
1885 if (tempbuf == NULL) {
1886 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1891 cprintf("%d %ld\n", SEND_BINARY, msglen);
1893 client_read(&ch, 1); /* 0xFF magic number */
1894 msg->cm_magic = CTDLMESSAGE_MAGIC;
1895 client_read(&ch, 1); /* anon type */
1896 msg->cm_anon_type = ch;
1897 client_read(&ch, 1); /* format type */
1898 msg->cm_format_type = ch;
1899 msglen = msglen - 3;
1901 while (msglen > 0) {
1902 client_read(&which_field, 1);
1906 client_read(&ch, 1);
1908 a = strlen(tempbuf);
1911 } while ( (ch != 0) && (msglen > 0) );
1912 msg->cm_fields[which_field] = strdoop(tempbuf);
1915 msg->cm_flags = CM_SKIP_HOOKS;
1916 CtdlSaveMsg(msg, recp, "", e, 0);
1917 CtdlFreeMessage(msg);
1923 * API function to delete messages which match a set of criteria
1924 * (returns the actual number of messages deleted)
1926 int CtdlDeleteMessages(char *room_name, /* which room */
1927 long dmsgnum, /* or "0" for any */
1928 char *content_type /* or NULL for any */
1932 struct quickroom qrbuf;
1933 struct cdbdata *cdbfr;
1934 long *msglist = NULL;
1937 int num_deleted = 0;
1939 struct SuppMsgInfo smi;
1941 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1942 room_name, dmsgnum, content_type);
1944 /* get room record, obtaining a lock... */
1945 if (lgetroom(&qrbuf, room_name) != 0) {
1946 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1948 return (0); /* room not found */
1950 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1952 if (cdbfr != NULL) {
1953 msglist = mallok(cdbfr->len);
1954 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1955 num_msgs = cdbfr->len / sizeof(long);
1959 for (i = 0; i < num_msgs; ++i) {
1962 /* Set/clear a bit for each criterion */
1964 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1965 delete_this |= 0x01;
1967 if (content_type == NULL) {
1968 delete_this |= 0x02;
1970 GetSuppMsgInfo(&smi, msglist[i]);
1971 if (!strcasecmp(smi.smi_content_type,
1973 delete_this |= 0x02;
1977 /* Delete message only if all bits are set */
1978 if (delete_this == 0x03) {
1979 AdjRefCount(msglist[i], -1);
1985 num_msgs = sort_msglist(msglist, num_msgs);
1986 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1987 msglist, (num_msgs * sizeof(long)));
1989 qrbuf.QRhighest = msglist[num_msgs - 1];
1993 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1994 return (num_deleted);
2000 * Delete message from current room
2002 void cmd_dele(char *delstr)
2007 getuser(&CC->usersupp, CC->curr_user);
2008 if ((CC->usersupp.axlevel < 6)
2009 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2010 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2011 && (!(CC->internal_pgm))) {
2012 cprintf("%d Higher access required.\n",
2013 ERROR + HIGHER_ACCESS_REQUIRED);
2016 delnum = extract_long(delstr, 0);
2018 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
2021 cprintf("%d %d message%s deleted.\n", OK,
2022 num_deleted, ((num_deleted != 1) ? "s" : ""));
2024 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2030 * move a message to another room
2032 void cmd_move(char *args)
2036 struct quickroom qtemp;
2040 num = extract_long(args, 0);
2041 extract(targ, args, 1);
2043 getuser(&CC->usersupp, CC->curr_user);
2044 if ((CC->usersupp.axlevel < 6)
2045 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2046 cprintf("%d Higher access required.\n",
2047 ERROR + HIGHER_ACCESS_REQUIRED);
2051 if (getroom(&qtemp, targ) != 0) {
2052 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2056 /* Temporarily bump the reference count to avoid having the message
2057 * deleted while it's in transit.
2059 AdjRefCount(num, 1);
2061 /* yank the message out of the current room... */
2062 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
2065 /* put the message into the target room */
2066 if (err = CtdlSaveMsgPointerInRoom(targ, num), (err !=0) ) {
2067 cprintf("%d Could not save message %ld in %s.\n",
2071 cprintf("%d Message moved.\n", OK);
2075 cprintf("%d No such message.\n", ERROR);
2078 /* Fix the reference count. */
2079 AdjRefCount(num, (-1));
2085 * GetSuppMsgInfo() - Get the supplementary record for a message
2087 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2090 struct cdbdata *cdbsmi;
2093 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2094 smibuf->smi_msgnum = msgnum;
2095 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2097 /* Use the negative of the message number for its supp record index */
2098 TheIndex = (0L - msgnum);
2100 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2101 if (cdbsmi == NULL) {
2102 return; /* record not found; go with defaults */
2104 memcpy(smibuf, cdbsmi->ptr,
2105 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2106 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2113 * PutSuppMsgInfo() - (re)write supplementary record for a message
2115 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2119 /* Use the negative of the message number for its supp record index */
2120 TheIndex = (0L - smibuf->smi_msgnum);
2122 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2123 smibuf->smi_msgnum, smibuf->smi_refcount);
2125 cdb_store(CDB_MSGMAIN,
2126 &TheIndex, sizeof(long),
2127 smibuf, sizeof(struct SuppMsgInfo));
2132 * AdjRefCount - change the reference count for a message;
2133 * delete the message if it reaches zero
2135 void AdjRefCount(long msgnum, int incr)
2138 struct SuppMsgInfo smi;
2141 /* This is a *tight* critical section; please keep it that way, as
2142 * it may get called while nested in other critical sections.
2143 * Complicating this any further will surely cause deadlock!
2145 begin_critical_section(S_SUPPMSGMAIN);
2146 GetSuppMsgInfo(&smi, msgnum);
2147 smi.smi_refcount += incr;
2148 PutSuppMsgInfo(&smi);
2149 end_critical_section(S_SUPPMSGMAIN);
2151 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2152 msgnum, smi.smi_refcount);
2154 /* If the reference count is now zero, delete the message
2155 * (and its supplementary record as well).
2157 if (smi.smi_refcount == 0) {
2158 lprintf(9, "Deleting message <%ld>\n", msgnum);
2160 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2161 delnum = (0L - msgnum);
2162 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2167 * Write a generic object to this room
2169 * Note: this could be much more efficient. Right now we use two temporary
2170 * files, and still pull the message into memory as with all others.
2172 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2173 char *content_type, /* MIME type of this object */
2174 char *tempfilename, /* Where to fetch it from */
2175 struct usersupp *is_mailbox, /* Mailbox room? */
2176 int is_binary, /* Is encoding necessary? */
2177 int is_unique, /* Del others of this type? */
2178 unsigned int flags /* Internal save flags */
2183 char filename[PATH_MAX];
2186 struct quickroom qrbuf;
2187 char roomname[ROOMNAMELEN];
2188 struct CtdlMessage *msg;
2191 if (is_mailbox != NULL)
2192 MailboxName(roomname, is_mailbox, req_room);
2194 safestrncpy(roomname, req_room, sizeof(roomname));
2195 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2197 strcpy(filename, tmpnam(NULL));
2198 fp = fopen(filename, "w");
2202 tempfp = fopen(tempfilename, "r");
2203 if (tempfp == NULL) {
2209 fprintf(fp, "Content-type: %s\n", content_type);
2210 lprintf(9, "Content-type: %s\n", content_type);
2212 if (is_binary == 0) {
2213 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2214 while (ch = getc(tempfp), ch > 0)
2220 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2223 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2224 tempfilename, filename);
2228 lprintf(9, "Allocating\n");
2229 msg = mallok(sizeof(struct CtdlMessage));
2230 memset(msg, 0, sizeof(struct CtdlMessage));
2231 msg->cm_magic = CTDLMESSAGE_MAGIC;
2232 msg->cm_anon_type = MES_NORMAL;
2233 msg->cm_format_type = 4;
2234 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2235 msg->cm_fields['O'] = strdoop(req_room);
2236 msg->cm_fields['N'] = strdoop(config.c_nodename);
2237 msg->cm_fields['H'] = strdoop(config.c_humannode);
2238 msg->cm_flags = flags;
2240 lprintf(9, "Loading\n");
2241 fp = fopen(filename, "rb");
2242 fseek(fp, 0L, SEEK_END);
2245 msg->cm_fields['M'] = mallok(len);
2246 fread(msg->cm_fields['M'], len, 1, fp);
2250 /* Create the requested room if we have to. */
2251 if (getroom(&qrbuf, roomname) != 0) {
2252 create_room(roomname, 4, "", 0);
2254 /* If the caller specified this object as unique, delete all
2255 * other objects of this type that are currently in the room.
2258 lprintf(9, "Deleted %d other msgs of this type\n",
2259 CtdlDeleteMessages(roomname, 0L, content_type));
2261 /* Now write the data */
2262 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2263 CtdlFreeMessage(msg);