19 #include "sysdep_decls.h"
20 #include "citserver.h"
25 #include "dynloader.h"
27 #include "mime_parser.h"
30 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
31 #define ma ((struct ma_info *)CtdlGetUserData(SYM_MA_INFO))
32 #define msg_repl ((struct repl *)CtdlGetUserData(SYM_REPL))
34 extern struct config config;
37 "", "", "", "", "", "", "", "",
38 "", "", "", "", "", "", "", "",
39 "", "", "", "", "", "", "", "",
40 "", "", "", "", "", "", "", "",
41 "", "", "", "", "", "", "", "",
42 "", "", "", "", "", "", "", "",
43 "", "", "", "", "", "", "", "",
44 "", "", "", "", "", "", "", "",
70 * This function is self explanatory.
71 * (What can I say, I'm in a weird mood today...)
73 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
77 for (i = 0; i < strlen(name); ++i)
80 if (isspace(name[i - 1])) {
81 strcpy(&name[i - 1], &name[i]);
84 while (isspace(name[i + 1])) {
85 strcpy(&name[i + 1], &name[i + 2]);
92 * Aliasing for network mail.
93 * (Error messages have been commented out, because this is a server.)
96 { /* process alias and routing info for mail */
99 char aaa[300], bbb[300];
101 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
103 fp = fopen("network/mail.aliases", "r");
105 fp = fopen("/dev/null", "r");
110 while (fgets(aaa, sizeof aaa, fp) != NULL) {
111 while (isspace(name[0]))
112 strcpy(name, &name[1]);
113 aaa[strlen(aaa) - 1] = 0;
115 for (a = 0; a < strlen(aaa); ++a) {
117 strcpy(bbb, &aaa[a + 1]);
121 if (!strcasecmp(name, aaa))
125 lprintf(7, "Mail is being forwarded to %s\n", name);
127 /* determine local or remote type, see citadel.h */
128 for (a = 0; a < strlen(name); ++a)
130 return (MES_INTERNET);
131 for (a = 0; a < strlen(name); ++a)
133 for (b = a; b < strlen(name); ++b)
135 return (MES_INTERNET);
137 for (a = 0; a < strlen(name); ++a)
141 lprintf(7, "Too many @'s in address\n");
145 for (a = 0; a < strlen(name); ++a)
147 strcpy(bbb, &name[a + 1]);
149 strcpy(bbb, &bbb[1]);
150 fp = fopen("network/mail.sysinfo", "r");
154 a = getstring(fp, aaa);
155 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
156 a = getstring(fp, aaa);
157 if (!strncmp(aaa, "use ", 4)) {
158 strcpy(bbb, &aaa[4]);
163 if (!strncmp(aaa, "uum", 3)) {
165 for (a = 0; a < strlen(bbb); ++a) {
171 while (bbb[strlen(bbb) - 1] == '_')
172 bbb[strlen(bbb) - 1] = 0;
173 sprintf(name, &aaa[4], bbb);
174 return (MES_INTERNET);
176 if (!strncmp(aaa, "bin", 3)) {
179 while (aaa[strlen(aaa) - 1] != '@')
180 aaa[strlen(aaa) - 1] = 0;
181 aaa[strlen(aaa) - 1] = 0;
182 while (aaa[strlen(aaa) - 1] == ' ')
183 aaa[strlen(aaa) - 1] = 0;
184 while (bbb[0] != '@')
185 strcpy(bbb, &bbb[1]);
186 strcpy(bbb, &bbb[1]);
187 while (bbb[0] == ' ')
188 strcpy(bbb, &bbb[1]);
189 sprintf(name, "%s @%s", aaa, bbb);
202 fp = fopen("citadel.control", "r");
203 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
209 void simple_listing(long msgnum)
211 cprintf("%ld\n", msgnum);
216 /* Determine if a given message matches the fields in a message template.
217 * Return 0 for a successful match.
219 int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
222 /* If there aren't any fields in the template, all messages will
225 if (template == NULL) return(0);
227 /* Null messages are bogus. */
228 if (msg == NULL) return(1);
230 for (i='A'; i<='Z'; ++i) {
231 if (template->cm_fields[i] != NULL) {
232 if (msg->cm_fields[i] == NULL) {
235 if (strcasecmp(msg->cm_fields[i],
236 template->cm_fields[i])) return 1;
240 /* All compares succeeded: we have a match! */
248 * API function to perform an operation for each qualifying message in the
251 void CtdlForEachMessage(int mode, long ref,
253 struct CtdlMessage *compare,
254 void (*CallBack) (long msgnum))
259 struct cdbdata *cdbfr;
260 long *msglist = NULL;
263 struct SuppMsgInfo smi;
264 struct CtdlMessage *msg;
266 /* Learn about the user and room in question */
268 getuser(&CC->usersupp, CC->curr_user);
269 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
271 /* Load the message list */
272 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
274 msglist = mallok(cdbfr->len);
275 memcpy(msglist, cdbfr->ptr, cdbfr->len);
276 num_msgs = cdbfr->len / sizeof(long);
279 return; /* No messages at all? No further action. */
283 /* If the caller is looking for a specific MIME type, then filter
284 * out all messages which are not of the type requested.
287 if (content_type != NULL)
288 if (strlen(content_type) > 0)
289 for (a = 0; a < num_msgs; ++a) {
290 GetSuppMsgInfo(&smi, msglist[a]);
291 if (strcasecmp(smi.smi_content_type, content_type)) {
296 num_msgs = sort_msglist(msglist, num_msgs);
298 /* If a template was supplied, filter out the messages which
299 * don't match. (This could induce some delays!)
302 if (compare != NULL) {
303 for (a = 0; a < num_msgs; ++a) {
304 msg = CtdlFetchMessage(msglist[a]);
306 if (CtdlMsgCmp(msg, compare)) {
309 CtdlFreeMessage(msg);
317 * Now iterate through the message list, according to the
318 * criteria supplied by the caller.
321 for (a = 0; a < num_msgs; ++a) {
322 thismsg = msglist[a];
327 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
328 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
329 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
330 && (CC->usersupp.flags & US_LASTOLD))
331 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
332 || ((mode == MSGS_FIRST) && (a < ref))
333 || ((mode == MSGS_GT) && (thismsg > ref))
339 phree(msglist); /* Clean up */
345 * cmd_msgs() - get list of message #'s in this room
346 * implements the MSGS server command using CtdlForEachMessage()
348 void cmd_msgs(char *cmdbuf)
357 int with_template = 0;
358 struct CtdlMessage *template = NULL;
360 extract(which, cmdbuf, 0);
361 cm_ref = extract_int(cmdbuf, 1);
362 with_template = extract_int(cmdbuf, 2);
366 if (!strncasecmp(which, "OLD", 3))
368 else if (!strncasecmp(which, "NEW", 3))
370 else if (!strncasecmp(which, "FIRST", 5))
372 else if (!strncasecmp(which, "LAST", 4))
374 else if (!strncasecmp(which, "GT", 2))
377 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
378 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
383 cprintf("%d Send template then receive message list\n",
385 template = (struct CtdlMessage *)
386 mallok(sizeof(struct CtdlMessage));
387 memset(template, 0, sizeof(struct CtdlMessage));
388 while(client_gets(buf), strcmp(buf,"000")) {
389 extract(tfield, buf, 0);
390 extract(tvalue, buf, 1);
391 for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
392 if (!strcasecmp(tfield, msgkeys[i])) {
393 template->cm_fields[i] =
400 cprintf("%d Message list...\n", LISTING_FOLLOWS);
403 CtdlForEachMessage(mode, cm_ref, NULL, template, simple_listing);
404 if (template != NULL) CtdlFreeMessage(template);
412 * help_subst() - support routine for help file viewer
414 void help_subst(char *strbuf, char *source, char *dest)
419 while (p = pattern2(strbuf, source), (p >= 0)) {
420 strcpy(workbuf, &strbuf[p + strlen(source)]);
421 strcpy(&strbuf[p], dest);
422 strcat(strbuf, workbuf);
427 void do_help_subst(char *buffer)
431 help_subst(buffer, "^nodename", config.c_nodename);
432 help_subst(buffer, "^humannode", config.c_humannode);
433 help_subst(buffer, "^fqdn", config.c_fqdn);
434 help_subst(buffer, "^username", CC->usersupp.fullname);
435 sprintf(buf2, "%ld", CC->usersupp.usernum);
436 help_subst(buffer, "^usernum", buf2);
437 help_subst(buffer, "^sysadm", config.c_sysadm);
438 help_subst(buffer, "^variantname", CITADEL);
439 sprintf(buf2, "%d", config.c_maxsessions);
440 help_subst(buffer, "^maxsessions", buf2);
446 * memfmout() - Citadel text formatter and paginator.
447 * Although the original purpose of this routine was to format
448 * text to the reader's screen width, all we're really using it
449 * for here is to format text out to 80 columns before sending it
450 * to the client. The client software may reformat it again.
452 void memfmout(int width, char *mptr, char subst)
453 /* screen width to use */
454 /* where are we going to get our text from? */
455 /* nonzero if we should use hypertext mode */
467 c = 1; /* c is the current pos */
470 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
472 buffer[strlen(buffer) + 1] = 0;
473 buffer[strlen(buffer)] = ch;
476 if (buffer[0] == '^')
477 do_help_subst(buffer);
479 buffer[strlen(buffer) + 1] = 0;
481 strcpy(buffer, &buffer[1]);
491 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
493 if (((old == 13) || (old == 10)) && (isspace(real))) {
501 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
502 cprintf("\n%s", aaa);
511 if ((strlen(aaa) + c) > (width - 5)) {
521 if ((ch == 13) || (ch == 10)) {
522 cprintf("%s\n", aaa);
529 FMTEND: cprintf("%s\n", aaa);
535 * Callback function for mime parser that simply lists the part
537 void list_this_part(char *name, char *filename, char *partnum, char *disp,
538 void *content, char *cbtype, size_t length)
541 cprintf("part=%s|%s|%s|%s|%s|%d\n",
542 name, filename, partnum, disp, cbtype, length);
547 * Callback function for mime parser that wants to display text
549 void fixed_output(char *name, char *filename, char *partnum, char *disp,
550 void *content, char *cbtype, size_t length)
554 if (!strcasecmp(cbtype, "multipart/alternative")) {
555 strcpy(ma->prefix, partnum);
556 strcat(ma->prefix, ".");
562 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
564 && (ma->did_print == 1) ) {
565 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
571 if (!strcasecmp(cbtype, "text/plain")) {
572 client_write(content, length);
574 else if (!strcasecmp(cbtype, "text/html")) {
575 ptr = html_to_ascii(content, 80, 0);
576 client_write(ptr, strlen(ptr));
579 else if (strncasecmp(cbtype, "multipart/", 10)) {
580 cprintf("Part %s: %s (%s) (%d bytes)\n",
581 partnum, filename, cbtype, length);
587 * Callback function for mime parser that opens a section for downloading
589 void mime_download(char *name, char *filename, char *partnum, char *disp,
590 void *content, char *cbtype, size_t length)
593 /* Silently go away if there's already a download open... */
594 if (CC->download_fp != NULL)
597 /* ...or if this is not the desired section */
598 if (strcasecmp(desired_section, partnum))
601 CC->download_fp = tmpfile();
602 if (CC->download_fp == NULL)
605 fwrite(content, length, 1, CC->download_fp);
606 fflush(CC->download_fp);
607 rewind(CC->download_fp);
609 OpenCmdResult(filename, cbtype);
615 * Load a message from disk into memory.
616 * This is used by output_message() and other fetch functions.
618 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
619 * using the CtdlMessageFree() function.
621 struct CtdlMessage *CtdlFetchMessage(long msgnum)
623 struct cdbdata *dmsgtext;
624 struct CtdlMessage *ret = NULL;
627 CIT_UBYTE field_header;
631 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
632 if (dmsgtext == NULL) {
635 mptr = dmsgtext->ptr;
637 /* Parse the three bytes that begin EVERY message on disk.
638 * The first is always 0xFF, the on-disk magic number.
639 * The second is the anonymous/public type byte.
640 * The third is the format type byte (vari, fixed, or MIME).
644 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
648 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
649 memset(ret, 0, sizeof(struct CtdlMessage));
651 ret->cm_magic = CTDLMESSAGE_MAGIC;
652 ret->cm_anon_type = *mptr++; /* Anon type byte */
653 ret->cm_format_type = *mptr++; /* Format type byte */
656 * The rest is zero or more arbitrary fields. Load them in.
657 * We're done when we encounter either a zero-length field or
658 * have just processed the 'M' (message text) field.
661 field_length = strlen(mptr);
662 if (field_length == 0)
664 field_header = *mptr++;
665 ret->cm_fields[field_header] = mallok(field_length);
666 strcpy(ret->cm_fields[field_header], mptr);
668 while (*mptr++ != 0); /* advance to next field */
670 } while ((field_length > 0) && (field_header != 'M'));
674 /* Perform "before read" hooks (aborting if any return nonzero) */
675 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
676 CtdlFreeMessage(ret);
685 * Returns 1 if the supplied pointer points to a valid Citadel message.
686 * If the pointer is NULL or the magic number check fails, returns 0.
688 int is_valid_message(struct CtdlMessage *msg) {
691 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
692 lprintf(3, "is_valid_message() -- self-check failed\n");
700 * 'Destructor' for struct CtdlMessage
702 void CtdlFreeMessage(struct CtdlMessage *msg)
706 if (is_valid_message(msg) == 0) return;
708 for (i = 0; i < 256; ++i)
709 if (msg->cm_fields[i] != NULL)
710 phree(msg->cm_fields[i]);
712 msg->cm_magic = 0; /* just in case */
719 * Get a message off disk. (return value is the message's timestamp)
722 void output_message(char *msgid, int mode, int headers_only)
730 char display_name[256];
732 struct CtdlMessage *TheMessage = NULL;
736 /* buffers needed for RFC822 translation */
744 msg_num = atol(msgid);
746 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
747 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
751 /* FIX ... small security issue
752 * We need to check to make sure the requested message is actually
753 * in the current room, and set msg_ok to 1 only if it is. This
754 * functionality is currently missing because I'm in a hurry to replace
755 * broken production code with nonbroken pre-beta code. :( -- ajc
758 cprintf("%d Message %ld is not in this room.\n",
765 * Fetch the message from disk
767 TheMessage = CtdlFetchMessage(msg_num);
768 if (TheMessage == NULL) {
769 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
773 /* Are we downloading a MIME component? */
774 if (mode == MT_DOWNLOAD) {
775 if (TheMessage->cm_format_type != 4) {
776 cprintf("%d This is not a MIME message.\n",
778 } else if (CC->download_fp != NULL) {
779 cprintf("%d You already have a download open.\n",
782 /* Parse the message text component */
783 mptr = TheMessage->cm_fields['M'];
784 mime_parser(mptr, NULL, *mime_download);
785 /* If there's no file open by this time, the requested
786 * section wasn't found, so print an error
788 if (CC->download_fp == NULL) {
789 cprintf("%d Section %s not found.\n",
790 ERROR + FILE_NOT_FOUND,
794 CtdlFreeMessage(TheMessage);
798 /* now for the user-mode message reading loops */
799 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
801 /* Tell the client which format type we're using. If this is a
802 * MIME message, *lie* about it and tell the user it's fixed-format.
804 if (mode == MT_CITADEL) {
805 if (TheMessage->cm_format_type == 4)
808 cprintf("type=%d\n", TheMessage->cm_format_type);
811 /* nhdr=yes means that we're only displaying headers, no body */
812 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
813 cprintf("nhdr=yes\n");
816 /* begin header processing loop for Citadel message format */
818 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
820 strcpy(display_name, "<unknown>");
821 if (TheMessage->cm_fields['A']) {
822 strcpy(buf, TheMessage->cm_fields['A']);
823 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
824 if (TheMessage->cm_anon_type == MES_ANON)
825 strcpy(display_name, "****");
826 else if (TheMessage->cm_anon_type == MES_AN2)
827 strcpy(display_name, "anonymous");
829 strcpy(display_name, buf);
831 && ((TheMessage->cm_anon_type == MES_ANON)
832 || (TheMessage->cm_anon_type == MES_AN2))) {
833 sprintf(&display_name[strlen(display_name)],
838 strcpy(allkeys, FORDER);
839 for (i=0; i<strlen(allkeys); ++i) {
840 k = (int) allkeys[i];
842 if (TheMessage->cm_fields[k] != NULL) {
851 TheMessage->cm_fields[k]
860 /* begin header processing loop for RFC822 transfer format */
864 strcpy(snode, NODENAME);
865 strcpy(lnode, HUMANNODE);
866 if (mode == MT_RFC822) {
867 for (i = 0; i < 256; ++i) {
868 if (TheMessage->cm_fields[i]) {
869 mptr = TheMessage->cm_fields[i];
873 } else if (i == 'P') {
874 cprintf("Path: %s\n", mptr);
875 for (a = 0; a < strlen(mptr); ++a) {
876 if (mptr[a] == '!') {
877 strcpy(mptr, &mptr[a + 1]);
883 cprintf("Subject: %s\n", mptr);
889 cprintf("X-Citadel-Room: %s\n", mptr);
893 cprintf("To: %s\n", mptr);
896 cprintf("Date: %s", asctime(localtime(&xtime)));
902 if (mode == MT_RFC822) {
903 if (!strcasecmp(snode, NODENAME)) {
906 cprintf("Message-ID: <%s@%s>\n", mid, snode);
907 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
908 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
909 cprintf("Organization: %s\n", lnode);
912 /* end header processing loop ... at this point, we're in the text */
914 mptr = TheMessage->cm_fields['M'];
916 /* Tell the client about the MIME parts in this message */
917 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
918 if (mode == MT_CITADEL) {
919 mime_parser(mptr, NULL, *list_this_part);
921 else if (mode == MT_MIME) { /* list parts only */
922 mime_parser(mptr, NULL, *list_this_part);
924 CtdlFreeMessage(TheMessage);
931 CtdlFreeMessage(TheMessage);
935 /* signify start of msg text */
936 if (mode == MT_CITADEL)
938 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
941 /* If the format type on disk is 1 (fixed-format), then we want
942 * everything to be output completely literally ... regardless of
943 * what message transfer format is in use.
945 if (TheMessage->cm_format_type == 1) {
947 while (ch = *mptr++, ch > 0) {
950 if ((ch == 10) || (strlen(buf) > 250)) {
951 cprintf("%s\n", buf);
954 buf[strlen(buf) + 1] = 0;
955 buf[strlen(buf)] = ch;
959 cprintf("%s\n", buf);
962 /* If the message on disk is format 0 (Citadel vari-format), we
963 * output using the formatter at 80 columns. This is the final output
964 * form if the transfer format is RFC822, but if the transfer format
965 * is Citadel proprietary, it'll still work, because the indentation
966 * for new paragraphs is correct and the client will reformat the
967 * message to the reader's screen width.
969 if (TheMessage->cm_format_type == 0) {
970 memfmout(80, mptr, 0);
973 /* If the message on disk is format 4 (MIME), we've gotta hand it
974 * off to the MIME parser. The client has already been told that
975 * this message is format 1 (fixed format), so the callback function
976 * we use will display those parts as-is.
978 if (TheMessage->cm_format_type == 4) {
979 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
980 memset(ma, 0, sizeof(struct ma_info));
981 mime_parser(mptr, NULL, *fixed_output);
986 CtdlFreeMessage(TheMessage);
993 * display a message (mode 0 - Citadel proprietary)
995 void cmd_msg0(char *cmdbuf)
998 int headers_only = 0;
1000 extract(msgid, cmdbuf, 0);
1001 headers_only = extract_int(cmdbuf, 1);
1003 output_message(msgid, MT_CITADEL, headers_only);
1009 * display a message (mode 2 - RFC822)
1011 void cmd_msg2(char *cmdbuf)
1014 int headers_only = 0;
1016 extract(msgid, cmdbuf, 0);
1017 headers_only = extract_int(cmdbuf, 1);
1019 output_message(msgid, MT_RFC822, headers_only);
1025 * display a message (mode 3 - IGnet raw format - internal programs only)
1027 void cmd_msg3(char *cmdbuf)
1030 struct CtdlMessage *msg;
1033 if (CC->internal_pgm == 0) {
1034 cprintf("%d This command is for internal programs only.\n",
1039 msgnum = extract_long(cmdbuf, 0);
1040 msg = CtdlFetchMessage(msgnum);
1042 cprintf("%d Message %ld not found.\n",
1047 serialize_message(&smr, msg);
1048 CtdlFreeMessage(msg);
1051 cprintf("%d Unable to serialize message\n",
1052 ERROR+INTERNAL_ERROR);
1056 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1057 client_write(smr.ser, smr.len);
1064 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
1066 void cmd_msg4(char *cmdbuf)
1070 extract(msgid, cmdbuf, 0);
1072 output_message(msgid, MT_MIME, 0);
1076 * Open a component of a MIME message as a download file
1078 void cmd_opna(char *cmdbuf)
1082 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
1084 extract(msgid, cmdbuf, 0);
1085 extract(desired_section, cmdbuf, 1);
1087 output_message(msgid, MT_DOWNLOAD, 0);
1092 * Save a message pointer into a specified room
1093 * (Returns 0 for success, nonzero for failure)
1094 * roomname may be NULL to use the current room
1096 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
1098 char hold_rm[ROOMNAMELEN];
1099 struct cdbdata *cdbfr;
1102 long highest_msg = 0L;
1103 struct CtdlMessage *msg = NULL;
1105 lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n",
1106 roomname, msgid, flags);
1108 strcpy(hold_rm, CC->quickroom.QRname);
1110 /* We may need to check to see if this message is real */
1111 if ( (flags & SM_VERIFY_GOODNESS)
1112 || (flags & SM_DO_REPL_CHECK)
1114 msg = CtdlFetchMessage(msgid);
1115 if (msg == NULL) return(ERROR + ILLEGAL_VALUE);
1118 /* Perform replication checks if necessary */
1119 if ( (flags & SM_DO_REPL_CHECK) && (msg != NULL) ) {
1121 if (getroom(&CC->quickroom,
1122 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1124 lprintf(9, "No such room <%s>\n", roomname);
1125 if (msg != NULL) CtdlFreeMessage(msg);
1126 return(ERROR + ROOM_NOT_FOUND);
1129 if (ReplicationChecks(msg) != 0) {
1130 getroom(&CC->quickroom, hold_rm);
1131 if (msg != NULL) CtdlFreeMessage(msg);
1132 lprintf(9, "Did replication, and newer exists\n");
1137 /* Now the regular stuff */
1138 if (lgetroom(&CC->quickroom,
1139 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1141 lprintf(9, "No such room <%s>\n", roomname);
1142 if (msg != NULL) CtdlFreeMessage(msg);
1143 return(ERROR + ROOM_NOT_FOUND);
1146 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
1147 if (cdbfr == NULL) {
1151 msglist = mallok(cdbfr->len);
1152 if (msglist == NULL)
1153 lprintf(3, "ERROR malloc msglist!\n");
1154 num_msgs = cdbfr->len / sizeof(long);
1155 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1160 /* Make sure the message doesn't already exist in this room. It
1161 * is absolutely taboo to have more than one reference to the same
1162 * message in a room.
1164 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1165 if (msglist[i] == msgid) {
1166 lputroom(&CC->quickroom); /* unlock the room */
1167 getroom(&CC->quickroom, hold_rm);
1168 if (msg != NULL) CtdlFreeMessage(msg);
1169 return(ERROR + ALREADY_EXISTS);
1173 /* Now add the new message */
1175 msglist = reallok(msglist,
1176 (num_msgs * sizeof(long)));
1178 if (msglist == NULL) {
1179 lprintf(3, "ERROR: can't realloc message list!\n");
1181 msglist[num_msgs - 1] = msgid;
1183 /* Sort the message list, so all the msgid's are in order */
1184 num_msgs = sort_msglist(msglist, num_msgs);
1186 /* Determine the highest message number */
1187 highest_msg = msglist[num_msgs - 1];
1189 /* Write it back to disk. */
1190 cdb_store(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long),
1191 msglist, num_msgs * sizeof(long));
1193 /* Free up the memory we used. */
1196 /* Update the highest-message pointer and unlock the room. */
1197 CC->quickroom.QRhighest = highest_msg;
1198 lputroom(&CC->quickroom);
1199 getroom(&CC->quickroom, hold_rm);
1201 /* Bump the reference count for this message. */
1202 AdjRefCount(msgid, +1);
1204 /* Return success. */
1205 if (msg != NULL) CtdlFreeMessage(msg);
1212 * Message base operation to send a message to the master file
1213 * (returns new message number)
1215 * This is the back end for CtdlSaveMsg() and should not be directly
1216 * called by server-side modules.
1219 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1220 int generate_id, /* generate 'I' field? */
1221 FILE *save_a_copy) /* save a copy to disk? */
1228 /* Get a new message number */
1229 newmsgid = get_new_message_number();
1230 sprintf(msgidbuf, "%ld", newmsgid);
1233 msg->cm_fields['I'] = strdoop(msgidbuf);
1236 serialize_message(&smr, msg);
1239 cprintf("%d Unable to serialize message\n",
1240 ERROR+INTERNAL_ERROR);
1244 /* Write our little bundle of joy into the message base */
1245 begin_critical_section(S_MSGMAIN);
1246 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1247 smr.ser, smr.len) < 0) {
1248 lprintf(2, "Can't store message\n");
1253 end_critical_section(S_MSGMAIN);
1255 /* If the caller specified that a copy should be saved to a particular
1256 * file handle, do that now too.
1258 if (save_a_copy != NULL) {
1259 fwrite(smr.ser, smr.len, 1, save_a_copy);
1262 /* Free the memory we used for the serialized message */
1265 /* Return the *local* message ID to the caller
1266 * (even if we're storing an incoming network message)
1274 * Serialize a struct CtdlMessage into the format used on disk and network.
1276 * This function loads up a "struct ser_ret" (defined in server.h) which
1277 * contains the length of the serialized message and a pointer to the
1278 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1280 void serialize_message(struct ser_ret *ret, /* return values */
1281 struct CtdlMessage *msg) /* unserialized msg */
1285 static char *forder = FORDER;
1287 if (is_valid_message(msg) == 0) return; /* self check */
1290 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1291 ret->len = ret->len +
1292 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1294 lprintf(9, "calling malloc\n");
1295 ret->ser = mallok(ret->len);
1296 if (ret->ser == NULL) {
1302 ret->ser[1] = msg->cm_anon_type;
1303 ret->ser[2] = msg->cm_format_type;
1306 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1307 ret->ser[wlen++] = (char)forder[i];
1308 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1309 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1311 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1320 * Back end for the ReplicationChecks() function
1322 void check_repl(long msgnum) {
1323 struct CtdlMessage *msg;
1324 time_t timestamp = (-1L);
1326 lprintf(9, "check_repl() found message %ld\n", msgnum);
1327 msg = CtdlFetchMessage(msgnum);
1328 if (msg == NULL) return;
1329 if (msg->cm_fields['T'] != NULL) {
1330 timestamp = atol(msg->cm_fields['T']);
1332 CtdlFreeMessage(msg);
1334 if (timestamp > msg_repl->highest) {
1335 msg_repl->highest = timestamp; /* newer! */
1336 lprintf(9, "newer!\n");
1339 lprintf(9, "older!\n");
1341 /* Existing isn't newer? Then delete the old one(s). */
1342 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, NULL);
1347 * Check to see if any messages already exist which carry the same Extended ID
1351 * -> With older timestamps: delete them and return 0. Message will be saved.
1352 * -> With newer timestamps: return 1. Message save will be aborted.
1354 int ReplicationChecks(struct CtdlMessage *msg) {
1355 struct CtdlMessage *template;
1358 lprintf(9, "ReplicationChecks() started\n");
1359 /* No extended id? Don't do anything. */
1360 if (msg->cm_fields['E'] == NULL) return 0;
1361 if (strlen(msg->cm_fields['E']) == 0) return 0;
1362 lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1364 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1365 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1366 msg_repl->highest = atol(msg->cm_fields['T']);
1368 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1369 memset(template, 0, sizeof(struct CtdlMessage));
1370 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1372 CtdlForEachMessage(MSGS_ALL, 0L, NULL, template, check_repl);
1374 /* If a newer message exists with the same Extended ID, abort
1377 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1381 CtdlFreeMessage(template);
1382 lprintf(9, "ReplicationChecks() returning %d\n", abort_this);
1390 * Save a message to disk
1392 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1393 char *rec, /* Recipient (mail) */
1394 char *force, /* force a particular room? */
1395 int mailtype, /* local or remote type */
1396 int generate_id) /* 1 = generate 'I' field */
1399 char hold_rm[ROOMNAMELEN];
1400 char actual_rm[ROOMNAMELEN];
1401 char force_room[ROOMNAMELEN];
1402 char content_type[256]; /* We have to learn this */
1403 char recipient[256];
1406 struct usersupp userbuf;
1408 struct SuppMsgInfo smi;
1409 FILE *network_fp = NULL;
1410 static int seqnum = 1;
1412 lprintf(9, "CtdlSaveMsg() called\n");
1413 if (is_valid_message(msg) == 0) return; /* self check */
1415 /* If this message has no timestamp, we take the liberty of
1416 * giving it one, right now.
1418 if (msg->cm_fields['T'] == NULL) {
1419 sprintf(aaa, "%ld", time(NULL));
1420 msg->cm_fields['T'] = strdoop(aaa);
1423 /* If this message has no path, we generate one.
1425 if (msg->cm_fields['P'] == NULL) {
1426 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1427 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1428 if (isspace(msg->cm_fields['P'][a])) {
1429 msg->cm_fields['P'][a] = ' ';
1434 strcpy(force_room, force);
1436 /* Strip non-printable characters out of the recipient name */
1437 strcpy(recipient, rec);
1438 for (a = 0; a < strlen(recipient); ++a)
1439 if (!isprint(recipient[a]))
1440 strcpy(&recipient[a], &recipient[a + 1]);
1442 /* Learn about what's inside, because it's what's inside that counts */
1444 switch (msg->cm_format_type) {
1446 strcpy(content_type, "text/x-citadel-variformat");
1449 strcpy(content_type, "text/plain");
1452 strcpy(content_type, "text/plain");
1453 /* advance past header fields */
1454 mptr = msg->cm_fields['M'];
1457 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1458 safestrncpy(content_type, mptr,
1459 sizeof(content_type));
1460 strcpy(content_type, &content_type[14]);
1461 for (a = 0; a < strlen(content_type); ++a)
1462 if ((content_type[a] == ';')
1463 || (content_type[a] == ' ')
1464 || (content_type[a] == 13)
1465 || (content_type[a] == 10))
1466 content_type[a] = 0;
1473 /* Goto the correct room */
1474 strcpy(hold_rm, CC->quickroom.QRname);
1475 strcpy(actual_rm, CC->quickroom.QRname);
1477 /* If the user is a twit, move to the twit room for posting */
1479 if (CC->usersupp.axlevel == 2) {
1480 strcpy(hold_rm, actual_rm);
1481 strcpy(actual_rm, config.c_twitroom);
1485 /* ...or if this message is destined for Aide> then go there. */
1486 if (strlen(force_room) > 0) {
1487 strcpy(actual_rm, force_room);
1490 if (strcasecmp(actual_rm, CC->quickroom.QRname))
1491 getroom(&CC->quickroom, actual_rm);
1493 /* Perform "before save" hooks (aborting if any return nonzero) */
1494 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1496 /* If this message has an Extended ID, perform replication checks */
1497 if (ReplicationChecks(msg) > 0) return;
1499 /* Network mail - send a copy to the network program. */
1500 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1501 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1502 (long) getpid(), CC->cs_pid, ++seqnum);
1503 lprintf(9, "Saving a copy to %s\n", aaa);
1504 network_fp = fopen(aaa, "ab+");
1505 if (network_fp == NULL)
1506 lprintf(2, "ERROR: %s\n", strerror(errno));
1509 /* Save it to disk */
1510 newmsgid = send_message(msg, generate_id, network_fp);
1511 if (network_fp != NULL) {
1513 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1516 if (newmsgid <= 0L) return;
1518 /* Write a supplemental message info record. This doesn't have to
1519 * be a critical section because nobody else knows about this message
1522 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1523 smi.smi_msgnum = newmsgid;
1524 smi.smi_refcount = 0;
1525 safestrncpy(smi.smi_content_type, content_type, 64);
1526 PutSuppMsgInfo(&smi);
1528 /* Now figure out where to store the pointers */
1531 /* If this is being done by the networker delivering a private
1532 * message, we want to BYPASS saving the sender's copy (because there
1533 * is no local sender; it would otherwise go to the Trashcan).
1535 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1536 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1539 /* Bump this user's messages posted counter. */
1540 lgetuser(&CC->usersupp, CC->curr_user);
1541 CC->usersupp.posted = CC->usersupp.posted + 1;
1542 lputuser(&CC->usersupp);
1544 /* If this is private, local mail, make a copy in the
1545 * recipient's mailbox and bump the reference count.
1547 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1548 if (getuser(&userbuf, recipient) == 0) {
1549 MailboxName(actual_rm, &userbuf, MAILROOM);
1550 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1554 /* Perform "after save" hooks */
1555 PerformMessageHooks(msg, EVT_AFTERSAVE);
1558 if (strcasecmp(hold_rm, CC->quickroom.QRname))
1559 getroom(&CC->quickroom, hold_rm);
1565 * Convenience function for generating small administrative messages.
1567 void quickie_message(char *from, char *to, char *room, char *text)
1569 struct CtdlMessage *msg;
1571 msg = mallok(sizeof(struct CtdlMessage));
1572 memset(msg, 0, sizeof(struct CtdlMessage));
1573 msg->cm_magic = CTDLMESSAGE_MAGIC;
1574 msg->cm_anon_type = MES_NORMAL;
1575 msg->cm_format_type = 0;
1576 msg->cm_fields['A'] = strdoop(from);
1577 msg->cm_fields['O'] = strdoop(room);
1578 msg->cm_fields['N'] = strdoop(NODENAME);
1580 msg->cm_fields['R'] = strdoop(to);
1581 msg->cm_fields['M'] = strdoop(text);
1583 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1584 CtdlFreeMessage(msg);
1585 syslog(LOG_NOTICE, text);
1590 * Build a binary message to be saved on disk.
1593 struct CtdlMessage *make_message(
1594 struct usersupp *author, /* author's usersupp structure */
1595 char *recipient, /* NULL if it's not mail */
1596 char *room, /* room where it's going */
1597 int type, /* see MES_ types in header file */
1598 int net_type, /* see MES_ types in header file */
1599 int format_type, /* local or remote (see citadel.h) */
1600 char *fake_name) /* who we're masquerading as */
1606 size_t message_len = 0;
1607 size_t buffer_len = 0;
1609 struct CtdlMessage *msg;
1611 msg = mallok(sizeof(struct CtdlMessage));
1612 memset(msg, 0, sizeof(struct CtdlMessage));
1613 msg->cm_magic = CTDLMESSAGE_MAGIC;
1614 msg->cm_anon_type = type;
1615 msg->cm_format_type = format_type;
1617 /* Don't confuse the poor folks if it's not routed mail. */
1618 strcpy(dest_node, "");
1620 /* If net_type is MES_BINARY, split out the destination node. */
1621 if (net_type == MES_BINARY) {
1622 strcpy(dest_node, NODENAME);
1623 for (a = 0; a < strlen(recipient); ++a) {
1624 if (recipient[a] == '@') {
1626 strcpy(dest_node, &recipient[a + 1]);
1631 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1632 if (net_type == MES_INTERNET) {
1633 strcpy(dest_node, "internet");
1636 while (isspace(recipient[strlen(recipient) - 1]))
1637 recipient[strlen(recipient) - 1] = 0;
1639 sprintf(buf, "cit%ld", author->usernum); /* Path */
1640 msg->cm_fields['P'] = strdoop(buf);
1642 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1643 msg->cm_fields['T'] = strdoop(buf);
1645 if (fake_name[0]) /* author */
1646 msg->cm_fields['A'] = strdoop(fake_name);
1648 msg->cm_fields['A'] = strdoop(author->fullname);
1650 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1651 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1653 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1655 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1656 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1658 if (recipient[0] != 0)
1659 msg->cm_fields['R'] = strdoop(recipient);
1660 if (dest_node[0] != 0)
1661 msg->cm_fields['D'] = strdoop(dest_node);
1663 msg->cm_fields['M'] = mallok(4096);
1664 if (msg->cm_fields['M'] == NULL) {
1665 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1669 msg->cm_fields['M'][0] = 0;
1673 /* read in the lines of message text one by one */
1675 while (client_gets(buf), strcmp(buf, "000")) {
1677 /* augment the buffer if we have to */
1678 if ((message_len + strlen(buf) + 2) > buffer_len) {
1679 lprintf(9, "realloking\n");
1680 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1681 if (ptr == NULL) { /* flush if can't allocate */
1682 while (client_gets(buf), strcmp(buf, "000")) ;;
1685 buffer_len = (buffer_len * 2);
1686 msg->cm_fields['M'] = ptr;
1688 lprintf(9, "buffer_len is %d\n", buffer_len);
1692 if (append == NULL) append = msg->cm_fields['M'];
1693 while (strlen(append) > 0) ++append;
1694 strcpy(append, buf);
1695 strcat(append, "\n");
1696 message_len = message_len + strlen(buf) + 1;
1698 /* if we've hit the max msg length, flush the rest */
1699 if (message_len >= config.c_maxmsglen) {
1700 while (client_gets(buf), strcmp(buf, "000")) ;;
1713 * message entry - mode 0 (normal)
1715 void cmd_ent0(char *entargs)
1718 char recipient[256];
1720 int format_type = 0;
1721 char newusername[256];
1722 struct CtdlMessage *msg;
1726 struct usersupp tempUS;
1729 post = extract_int(entargs, 0);
1730 extract(recipient, entargs, 1);
1731 anon_flag = extract_int(entargs, 2);
1732 format_type = extract_int(entargs, 3);
1734 /* first check to make sure the request is valid. */
1736 if (!(CC->logged_in)) {
1737 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1740 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1741 cprintf("%d Need to be validated to enter ",
1742 ERROR + HIGHER_ACCESS_REQUIRED);
1743 cprintf("(except in %s> to sysop)\n", MAILROOM);
1746 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1747 cprintf("%d Need net privileges to enter here.\n",
1748 ERROR + HIGHER_ACCESS_REQUIRED);
1751 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1752 cprintf("%d Sorry, this is a read-only room.\n",
1753 ERROR + HIGHER_ACCESS_REQUIRED);
1760 if (CC->usersupp.axlevel < 6) {
1761 cprintf("%d You don't have permission to masquerade.\n",
1762 ERROR + HIGHER_ACCESS_REQUIRED);
1765 extract(newusername, entargs, 4);
1766 memset(CC->fake_postname, 0, 32);
1767 strcpy(CC->fake_postname, newusername);
1768 cprintf("%d Ok\n", OK);
1771 CC->cs_flags |= CS_POSTING;
1774 if (CC->quickroom.QRflags & QR_MAILBOX) {
1775 if (CC->usersupp.axlevel >= 2) {
1776 strcpy(buf, recipient);
1778 strcpy(buf, "sysop");
1779 e = alias(buf); /* alias and mail type */
1780 if ((buf[0] == 0) || (e == MES_ERROR)) {
1781 cprintf("%d Unknown address - cannot send message.\n",
1782 ERROR + NO_SUCH_USER);
1785 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1786 cprintf("%d Net privileges required for network mail.\n",
1787 ERROR + HIGHER_ACCESS_REQUIRED);
1790 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1791 && ((CC->usersupp.flags & US_INTERNET) == 0)
1792 && (!CC->internal_pgm)) {
1793 cprintf("%d You don't have access to Internet mail.\n",
1794 ERROR + HIGHER_ACCESS_REQUIRED);
1797 if (!strcasecmp(buf, "sysop")) {
1802 goto SKFALL; /* don't search local file */
1803 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1804 cprintf("%d Can't send mail to yourself!\n",
1805 ERROR + NO_SUCH_USER);
1808 /* Check to make sure the user exists; also get the correct
1809 * upper/lower casing of the name.
1811 a = getuser(&tempUS, buf);
1813 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1816 strcpy(buf, tempUS.fullname);
1819 SKFALL: b = MES_NORMAL;
1820 if (CC->quickroom.QRflags & QR_ANONONLY)
1822 if (CC->quickroom.QRflags & QR_ANONOPT) {
1826 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1829 /* If we're only checking the validity of the request, return
1830 * success without creating the message.
1833 cprintf("%d %s\n", OK, buf);
1837 cprintf("%d send message\n", SEND_LISTING);
1839 /* Read in the message from the client. */
1840 if (CC->fake_postname[0])
1841 msg = make_message(&CC->usersupp, buf,
1842 CC->quickroom.QRname, b, e, format_type,
1844 else if (CC->fake_username[0])
1845 msg = make_message(&CC->usersupp, buf,
1846 CC->quickroom.QRname, b, e, format_type,
1849 msg = make_message(&CC->usersupp, buf,
1850 CC->quickroom.QRname, b, e, format_type, "");
1853 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1854 CtdlFreeMessage(msg);
1855 CC->fake_postname[0] = '\0';
1862 * message entry - mode 3 (raw)
1864 void cmd_ent3(char *entargs)
1869 unsigned char ch, which_field;
1870 struct usersupp tempUS;
1872 struct CtdlMessage *msg;
1875 if (CC->internal_pgm == 0) {
1876 cprintf("%d This command is for internal programs only.\n",
1881 /* See if there's a recipient, but make sure it's a real one */
1882 extract(recp, entargs, 1);
1883 for (a = 0; a < strlen(recp); ++a)
1884 if (!isprint(recp[a]))
1885 strcpy(&recp[a], &recp[a + 1]);
1886 while (isspace(recp[0]))
1887 strcpy(recp, &recp[1]);
1888 while (isspace(recp[strlen(recp) - 1]))
1889 recp[strlen(recp) - 1] = 0;
1891 /* If we're in Mail, check the recipient */
1892 if (strlen(recp) > 0) {
1893 e = alias(recp); /* alias and mail type */
1894 if ((recp[0] == 0) || (e == MES_ERROR)) {
1895 cprintf("%d Unknown address - cannot send message.\n",
1896 ERROR + NO_SUCH_USER);
1899 if (e == MES_LOCAL) {
1900 a = getuser(&tempUS, recp);
1902 cprintf("%d No such user.\n",
1903 ERROR + NO_SUCH_USER);
1909 /* At this point, message has been approved. */
1910 if (extract_int(entargs, 0) == 0) {
1911 cprintf("%d OK to send\n", OK);
1915 msglen = extract_long(entargs, 2);
1916 msg = mallok(sizeof(struct CtdlMessage));
1918 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1922 memset(msg, 0, sizeof(struct CtdlMessage));
1923 tempbuf = mallok(msglen);
1924 if (tempbuf == NULL) {
1925 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1930 cprintf("%d %ld\n", SEND_BINARY, msglen);
1932 client_read(&ch, 1); /* 0xFF magic number */
1933 msg->cm_magic = CTDLMESSAGE_MAGIC;
1934 client_read(&ch, 1); /* anon type */
1935 msg->cm_anon_type = ch;
1936 client_read(&ch, 1); /* format type */
1937 msg->cm_format_type = ch;
1938 msglen = msglen - 3;
1940 while (msglen > 0) {
1941 client_read(&which_field, 1);
1945 client_read(&ch, 1);
1947 a = strlen(tempbuf);
1950 } while ( (ch != 0) && (msglen > 0) );
1951 msg->cm_fields[which_field] = strdoop(tempbuf);
1954 msg->cm_flags = CM_SKIP_HOOKS;
1955 CtdlSaveMsg(msg, recp, "", e, 0);
1956 CtdlFreeMessage(msg);
1962 * API function to delete messages which match a set of criteria
1963 * (returns the actual number of messages deleted)
1965 int CtdlDeleteMessages(char *room_name, /* which room */
1966 long dmsgnum, /* or "0" for any */
1967 char *content_type /* or NULL for any */
1971 struct quickroom qrbuf;
1972 struct cdbdata *cdbfr;
1973 long *msglist = NULL;
1976 int num_deleted = 0;
1978 struct SuppMsgInfo smi;
1980 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1981 room_name, dmsgnum, content_type);
1983 /* get room record, obtaining a lock... */
1984 if (lgetroom(&qrbuf, room_name) != 0) {
1985 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1987 return (0); /* room not found */
1989 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1991 if (cdbfr != NULL) {
1992 msglist = mallok(cdbfr->len);
1993 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1994 num_msgs = cdbfr->len / sizeof(long);
1998 for (i = 0; i < num_msgs; ++i) {
2001 /* Set/clear a bit for each criterion */
2003 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
2004 delete_this |= 0x01;
2006 if (content_type == NULL) {
2007 delete_this |= 0x02;
2009 GetSuppMsgInfo(&smi, msglist[i]);
2010 if (!strcasecmp(smi.smi_content_type,
2012 delete_this |= 0x02;
2016 /* Delete message only if all bits are set */
2017 if (delete_this == 0x03) {
2018 AdjRefCount(msglist[i], -1);
2024 num_msgs = sort_msglist(msglist, num_msgs);
2025 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
2026 msglist, (num_msgs * sizeof(long)));
2028 qrbuf.QRhighest = msglist[num_msgs - 1];
2032 lprintf(9, "%d message(s) deleted.\n", num_deleted);
2033 return (num_deleted);
2039 * Delete message from current room
2041 void cmd_dele(char *delstr)
2046 getuser(&CC->usersupp, CC->curr_user);
2047 if ((CC->usersupp.axlevel < 6)
2048 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2049 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2050 && (!(CC->internal_pgm))) {
2051 cprintf("%d Higher access required.\n",
2052 ERROR + HIGHER_ACCESS_REQUIRED);
2055 delnum = extract_long(delstr, 0);
2057 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
2060 cprintf("%d %d message%s deleted.\n", OK,
2061 num_deleted, ((num_deleted != 1) ? "s" : ""));
2063 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2069 * move or copy a message to another room
2071 void cmd_move(char *args)
2075 struct quickroom qtemp;
2079 num = extract_long(args, 0);
2080 extract(targ, args, 1);
2081 is_copy = extract_int(args, 2);
2083 getuser(&CC->usersupp, CC->curr_user);
2084 if ((CC->usersupp.axlevel < 6)
2085 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2086 cprintf("%d Higher access required.\n",
2087 ERROR + HIGHER_ACCESS_REQUIRED);
2091 if (getroom(&qtemp, targ) != 0) {
2092 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2096 err = CtdlSaveMsgPointerInRoom(targ, num,
2097 (SM_VERIFY_GOODNESS | SM_DO_REPL_CHECK) );
2099 cprintf("%d Cannot store message in %s: error %d\n",
2104 /* Now delete the message from the source room,
2105 * if this is a 'move' rather than a 'copy' operation.
2107 if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
2109 cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") );
2115 * GetSuppMsgInfo() - Get the supplementary record for a message
2117 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2120 struct cdbdata *cdbsmi;
2123 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2124 smibuf->smi_msgnum = msgnum;
2125 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2127 /* Use the negative of the message number for its supp record index */
2128 TheIndex = (0L - msgnum);
2130 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2131 if (cdbsmi == NULL) {
2132 return; /* record not found; go with defaults */
2134 memcpy(smibuf, cdbsmi->ptr,
2135 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2136 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2143 * PutSuppMsgInfo() - (re)write supplementary record for a message
2145 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2149 /* Use the negative of the message number for its supp record index */
2150 TheIndex = (0L - smibuf->smi_msgnum);
2152 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2153 smibuf->smi_msgnum, smibuf->smi_refcount);
2155 cdb_store(CDB_MSGMAIN,
2156 &TheIndex, sizeof(long),
2157 smibuf, sizeof(struct SuppMsgInfo));
2162 * AdjRefCount - change the reference count for a message;
2163 * delete the message if it reaches zero
2165 void AdjRefCount(long msgnum, int incr)
2168 struct SuppMsgInfo smi;
2171 /* This is a *tight* critical section; please keep it that way, as
2172 * it may get called while nested in other critical sections.
2173 * Complicating this any further will surely cause deadlock!
2175 begin_critical_section(S_SUPPMSGMAIN);
2176 GetSuppMsgInfo(&smi, msgnum);
2177 smi.smi_refcount += incr;
2178 PutSuppMsgInfo(&smi);
2179 end_critical_section(S_SUPPMSGMAIN);
2181 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2182 msgnum, smi.smi_refcount);
2184 /* If the reference count is now zero, delete the message
2185 * (and its supplementary record as well).
2187 if (smi.smi_refcount == 0) {
2188 lprintf(9, "Deleting message <%ld>\n", msgnum);
2190 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2191 delnum = (0L - msgnum);
2192 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2197 * Write a generic object to this room
2199 * Note: this could be much more efficient. Right now we use two temporary
2200 * files, and still pull the message into memory as with all others.
2202 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2203 char *content_type, /* MIME type of this object */
2204 char *tempfilename, /* Where to fetch it from */
2205 struct usersupp *is_mailbox, /* Mailbox room? */
2206 int is_binary, /* Is encoding necessary? */
2207 int is_unique, /* Del others of this type? */
2208 unsigned int flags /* Internal save flags */
2213 char filename[PATH_MAX];
2216 struct quickroom qrbuf;
2217 char roomname[ROOMNAMELEN];
2218 struct CtdlMessage *msg;
2221 if (is_mailbox != NULL)
2222 MailboxName(roomname, is_mailbox, req_room);
2224 safestrncpy(roomname, req_room, sizeof(roomname));
2225 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2227 strcpy(filename, tmpnam(NULL));
2228 fp = fopen(filename, "w");
2232 tempfp = fopen(tempfilename, "r");
2233 if (tempfp == NULL) {
2239 fprintf(fp, "Content-type: %s\n", content_type);
2240 lprintf(9, "Content-type: %s\n", content_type);
2242 if (is_binary == 0) {
2243 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2244 while (ch = getc(tempfp), ch > 0)
2250 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2253 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2254 tempfilename, filename);
2258 lprintf(9, "Allocating\n");
2259 msg = mallok(sizeof(struct CtdlMessage));
2260 memset(msg, 0, sizeof(struct CtdlMessage));
2261 msg->cm_magic = CTDLMESSAGE_MAGIC;
2262 msg->cm_anon_type = MES_NORMAL;
2263 msg->cm_format_type = 4;
2264 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2265 msg->cm_fields['O'] = strdoop(req_room);
2266 msg->cm_fields['N'] = strdoop(config.c_nodename);
2267 msg->cm_fields['H'] = strdoop(config.c_humannode);
2268 msg->cm_flags = flags;
2270 lprintf(9, "Loading\n");
2271 fp = fopen(filename, "rb");
2272 fseek(fp, 0L, SEEK_END);
2275 msg->cm_fields['M'] = mallok(len);
2276 fread(msg->cm_fields['M'], len, 1, fp);
2280 /* Create the requested room if we have to. */
2281 if (getroom(&qrbuf, roomname) != 0) {
2282 create_room(roomname, 4, "", 0);
2284 /* If the caller specified this object as unique, delete all
2285 * other objects of this type that are currently in the room.
2288 lprintf(9, "Deleted %d other msgs of this type\n",
2289 CtdlDeleteMessages(roomname, 0L, content_type));
2291 /* Now write the data */
2292 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2293 CtdlFreeMessage(msg);