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;
38 "", "", "", "", "", "", "", "",
39 "", "", "", "", "", "", "", "",
40 "", "", "", "", "", "", "", "",
41 "", "", "", "", "", "", "", "",
42 "", "", "", "", "", "", "", "",
43 "", "", "", "", "", "", "", "",
44 "", "", "", "", "", "", "", "",
45 "", "", "", "", "", "", "", "",
71 * This function is self explanatory.
72 * (What can I say, I'm in a weird mood today...)
74 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
78 for (i = 0; i < strlen(name); ++i)
81 if (isspace(name[i - 1])) {
82 strcpy(&name[i - 1], &name[i]);
85 while (isspace(name[i + 1])) {
86 strcpy(&name[i + 1], &name[i + 2]);
93 * Aliasing for network mail.
94 * (Error messages have been commented out, because this is a server.)
97 { /* process alias and routing info for mail */
100 char aaa[300], bbb[300];
102 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
104 fp = fopen("network/mail.aliases", "r");
106 fp = fopen("/dev/null", "r");
111 while (fgets(aaa, sizeof aaa, fp) != NULL) {
112 while (isspace(name[0]))
113 strcpy(name, &name[1]);
114 aaa[strlen(aaa) - 1] = 0;
116 for (a = 0; a < strlen(aaa); ++a) {
118 strcpy(bbb, &aaa[a + 1]);
122 if (!strcasecmp(name, aaa))
126 lprintf(7, "Mail is being forwarded to %s\n", name);
128 /* determine local or remote type, see citadel.h */
129 for (a = 0; a < strlen(name); ++a)
131 return (MES_INTERNET);
132 for (a = 0; a < strlen(name); ++a)
134 for (b = a; b < strlen(name); ++b)
136 return (MES_INTERNET);
138 for (a = 0; a < strlen(name); ++a)
142 lprintf(7, "Too many @'s in address\n");
146 for (a = 0; a < strlen(name); ++a)
148 strcpy(bbb, &name[a + 1]);
150 strcpy(bbb, &bbb[1]);
151 fp = fopen("network/mail.sysinfo", "r");
155 a = getstring(fp, aaa);
156 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
157 a = getstring(fp, aaa);
158 if (!strncmp(aaa, "use ", 4)) {
159 strcpy(bbb, &aaa[4]);
164 if (!strncmp(aaa, "uum", 3)) {
166 for (a = 0; a < strlen(bbb); ++a) {
172 while (bbb[strlen(bbb) - 1] == '_')
173 bbb[strlen(bbb) - 1] = 0;
174 sprintf(name, &aaa[4], bbb);
175 return (MES_INTERNET);
177 if (!strncmp(aaa, "bin", 3)) {
180 while (aaa[strlen(aaa) - 1] != '@')
181 aaa[strlen(aaa) - 1] = 0;
182 aaa[strlen(aaa) - 1] = 0;
183 while (aaa[strlen(aaa) - 1] == ' ')
184 aaa[strlen(aaa) - 1] = 0;
185 while (bbb[0] != '@')
186 strcpy(bbb, &bbb[1]);
187 strcpy(bbb, &bbb[1]);
188 while (bbb[0] == ' ')
189 strcpy(bbb, &bbb[1]);
190 sprintf(name, "%s @%s", aaa, bbb);
203 fp = fopen("citadel.control", "r");
204 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
210 void simple_listing(long msgnum)
212 cprintf("%ld\n", msgnum);
217 /* Determine if a given message matches the fields in a message template.
218 * Return 0 for a successful match.
220 int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
223 /* If there aren't any fields in the template, all messages will
226 if (template == NULL) return(0);
228 /* Null messages are bogus. */
229 if (msg == NULL) return(1);
231 for (i='A'; i<='Z'; ++i) {
232 if (template->cm_fields[i] != NULL) {
233 if (msg->cm_fields[i] == NULL) {
236 if (strcasecmp(msg->cm_fields[i],
237 template->cm_fields[i])) return 1;
241 /* All compares succeeded: we have a match! */
249 * API function to perform an operation for each qualifying message in the
252 void CtdlForEachMessage(int mode, long ref,
254 struct CtdlMessage *compare,
255 void (*CallBack) (long msgnum))
260 struct cdbdata *cdbfr;
261 long *msglist = NULL;
264 struct SuppMsgInfo smi;
265 struct CtdlMessage *msg;
267 /* Learn about the user and room in question */
269 getuser(&CC->usersupp, CC->curr_user);
270 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
272 /* Load the message list */
273 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
275 msglist = mallok(cdbfr->len);
276 memcpy(msglist, cdbfr->ptr, cdbfr->len);
277 num_msgs = cdbfr->len / sizeof(long);
280 return; /* No messages at all? No further action. */
284 /* If the caller is looking for a specific MIME type, then filter
285 * out all messages which are not of the type requested.
288 if (content_type != NULL)
289 if (strlen(content_type) > 0)
290 for (a = 0; a < num_msgs; ++a) {
291 GetSuppMsgInfo(&smi, msglist[a]);
292 if (strcasecmp(smi.smi_content_type, content_type)) {
297 num_msgs = sort_msglist(msglist, num_msgs);
299 /* If a template was supplied, filter out the messages which
300 * don't match. (This could induce some delays!)
303 if (compare != NULL) {
304 for (a = 0; a < num_msgs; ++a) {
305 msg = CtdlFetchMessage(msglist[a]);
307 if (CtdlMsgCmp(msg, compare)) {
310 CtdlFreeMessage(msg);
318 * Now iterate through the message list, according to the
319 * criteria supplied by the caller.
322 for (a = 0; a < num_msgs; ++a) {
323 thismsg = msglist[a];
328 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
329 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
330 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
331 && (CC->usersupp.flags & US_LASTOLD))
332 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
333 || ((mode == MSGS_FIRST) && (a < ref))
334 || ((mode == MSGS_GT) && (thismsg > ref))
340 phree(msglist); /* Clean up */
346 * cmd_msgs() - get list of message #'s in this room
347 * implements the MSGS server command using CtdlForEachMessage()
349 void cmd_msgs(char *cmdbuf)
358 int with_template = 0;
359 struct CtdlMessage *template = NULL;
361 extract(which, cmdbuf, 0);
362 cm_ref = extract_int(cmdbuf, 1);
363 with_template = extract_int(cmdbuf, 2);
367 if (!strncasecmp(which, "OLD", 3))
369 else if (!strncasecmp(which, "NEW", 3))
371 else if (!strncasecmp(which, "FIRST", 5))
373 else if (!strncasecmp(which, "LAST", 4))
375 else if (!strncasecmp(which, "GT", 2))
378 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
379 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
384 cprintf("%d Send template then receive message list\n",
386 template = (struct CtdlMessage *)
387 mallok(sizeof(struct CtdlMessage));
388 memset(template, 0, sizeof(struct CtdlMessage));
389 while(client_gets(buf), strcmp(buf,"000")) {
390 extract(tfield, buf, 0);
391 extract(tvalue, buf, 1);
392 for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
393 if (!strcasecmp(tfield, msgkeys[i])) {
394 template->cm_fields[i] =
401 cprintf("%d Message list...\n", LISTING_FOLLOWS);
404 CtdlForEachMessage(mode, cm_ref, NULL, template, simple_listing);
405 if (template != NULL) CtdlFreeMessage(template);
413 * help_subst() - support routine for help file viewer
415 void help_subst(char *strbuf, char *source, char *dest)
420 while (p = pattern2(strbuf, source), (p >= 0)) {
421 strcpy(workbuf, &strbuf[p + strlen(source)]);
422 strcpy(&strbuf[p], dest);
423 strcat(strbuf, workbuf);
428 void do_help_subst(char *buffer)
432 help_subst(buffer, "^nodename", config.c_nodename);
433 help_subst(buffer, "^humannode", config.c_humannode);
434 help_subst(buffer, "^fqdn", config.c_fqdn);
435 help_subst(buffer, "^username", CC->usersupp.fullname);
436 sprintf(buf2, "%ld", CC->usersupp.usernum);
437 help_subst(buffer, "^usernum", buf2);
438 help_subst(buffer, "^sysadm", config.c_sysadm);
439 help_subst(buffer, "^variantname", CITADEL);
440 sprintf(buf2, "%d", config.c_maxsessions);
441 help_subst(buffer, "^maxsessions", buf2);
447 * memfmout() - Citadel text formatter and paginator.
448 * Although the original purpose of this routine was to format
449 * text to the reader's screen width, all we're really using it
450 * for here is to format text out to 80 columns before sending it
451 * to the client. The client software may reformat it again.
453 void memfmout(int width, char *mptr, char subst)
454 /* screen width to use */
455 /* where are we going to get our text from? */
456 /* nonzero if we should use hypertext mode */
468 c = 1; /* c is the current pos */
471 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
473 buffer[strlen(buffer) + 1] = 0;
474 buffer[strlen(buffer)] = ch;
477 if (buffer[0] == '^')
478 do_help_subst(buffer);
480 buffer[strlen(buffer) + 1] = 0;
482 strcpy(buffer, &buffer[1]);
492 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
494 if (((old == 13) || (old == 10)) && (isspace(real))) {
502 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
503 cprintf("\n%s", aaa);
512 if ((strlen(aaa) + c) > (width - 5)) {
522 if ((ch == 13) || (ch == 10)) {
523 cprintf("%s\n", aaa);
530 FMTEND: cprintf("%s\n", aaa);
536 * Callback function for mime parser that simply lists the part
538 void list_this_part(char *name, char *filename, char *partnum, char *disp,
539 void *content, char *cbtype, size_t length)
542 cprintf("part=%s|%s|%s|%s|%s|%d\n",
543 name, filename, partnum, disp, cbtype, length);
548 * Callback function for mime parser that wants to display text
550 void fixed_output(char *name, char *filename, char *partnum, char *disp,
551 void *content, char *cbtype, size_t length)
555 if (!strcasecmp(cbtype, "multipart/alternative")) {
556 strcpy(ma->prefix, partnum);
557 strcat(ma->prefix, ".");
563 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
565 && (ma->did_print == 1) ) {
566 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
572 if (!strcasecmp(cbtype, "text/plain")) {
573 client_write(content, length);
575 else if (!strcasecmp(cbtype, "text/html")) {
576 ptr = html_to_ascii(content, 80, 0);
577 client_write(ptr, strlen(ptr));
580 else if (strncasecmp(cbtype, "multipart/", 10)) {
581 cprintf("Part %s: %s (%s) (%d bytes)\n",
582 partnum, filename, cbtype, length);
588 * Callback function for mime parser that opens a section for downloading
590 void mime_download(char *name, char *filename, char *partnum, char *disp,
591 void *content, char *cbtype, size_t length)
594 /* Silently go away if there's already a download open... */
595 if (CC->download_fp != NULL)
598 /* ...or if this is not the desired section */
599 if (strcasecmp(desired_section, partnum))
602 CC->download_fp = tmpfile();
603 if (CC->download_fp == NULL)
606 fwrite(content, length, 1, CC->download_fp);
607 fflush(CC->download_fp);
608 rewind(CC->download_fp);
610 OpenCmdResult(filename, cbtype);
616 * Load a message from disk into memory.
617 * This is used by output_message() and other fetch functions.
619 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
620 * using the CtdlMessageFree() function.
622 struct CtdlMessage *CtdlFetchMessage(long msgnum)
624 struct cdbdata *dmsgtext;
625 struct CtdlMessage *ret = NULL;
628 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 /* Always make sure there's something in the msg text field */
675 if (ret->cm_fields['M'] == NULL)
676 ret->cm_fields['M'] = strdoop("<no text>\n");
678 /* Perform "before read" hooks (aborting if any return nonzero) */
679 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
680 CtdlFreeMessage(ret);
689 * Returns 1 if the supplied pointer points to a valid Citadel message.
690 * If the pointer is NULL or the magic number check fails, returns 0.
692 int is_valid_message(struct CtdlMessage *msg) {
695 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
696 lprintf(3, "is_valid_message() -- self-check failed\n");
704 * 'Destructor' for struct CtdlMessage
706 void CtdlFreeMessage(struct CtdlMessage *msg)
710 if (is_valid_message(msg) == 0) return;
712 for (i = 0; i < 256; ++i)
713 if (msg->cm_fields[i] != NULL)
714 phree(msg->cm_fields[i]);
716 msg->cm_magic = 0; /* just in case */
723 * Get a message off disk. (return value is the message's timestamp)
726 void output_message(char *msgid, int mode, int headers_only)
734 char display_name[256];
736 struct CtdlMessage *TheMessage = NULL;
740 /* buffers needed for RFC822 translation */
748 msg_num = atol(msgid);
749 safestrncpy(mid, msgid, sizeof mid);
751 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
752 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
756 /* FIX ... small security issue
757 * We need to check to make sure the requested message is actually
758 * in the current room, and set msg_ok to 1 only if it is. This
759 * functionality is currently missing because I'm in a hurry to replace
760 * broken production code with nonbroken pre-beta code. :( -- ajc
763 cprintf("%d Message %ld is not in this room.\n",
770 * Fetch the message from disk
772 TheMessage = CtdlFetchMessage(msg_num);
773 if (TheMessage == NULL) {
774 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
778 /* Are we downloading a MIME component? */
779 if (mode == MT_DOWNLOAD) {
780 if (TheMessage->cm_format_type != FMT_RFC822) {
781 cprintf("%d This is not a MIME message.\n",
783 } else if (CC->download_fp != NULL) {
784 cprintf("%d You already have a download open.\n",
787 /* Parse the message text component */
788 mptr = TheMessage->cm_fields['M'];
789 mime_parser(mptr, NULL, *mime_download);
790 /* If there's no file open by this time, the requested
791 * section wasn't found, so print an error
793 if (CC->download_fp == NULL) {
794 cprintf("%d Section %s not found.\n",
795 ERROR + FILE_NOT_FOUND,
799 CtdlFreeMessage(TheMessage);
803 /* now for the user-mode message reading loops */
804 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
806 /* Tell the client which format type we're using. If this is a
807 * MIME message, *lie* about it and tell the user it's fixed-format.
809 if (mode == MT_CITADEL) {
810 if (TheMessage->cm_format_type == FMT_RFC822)
813 cprintf("type=%d\n", TheMessage->cm_format_type);
816 /* nhdr=yes means that we're only displaying headers, no body */
817 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
818 cprintf("nhdr=yes\n");
821 /* begin header processing loop for Citadel message format */
823 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
825 strcpy(display_name, "<unknown>");
826 if (TheMessage->cm_fields['A']) {
827 strcpy(buf, TheMessage->cm_fields['A']);
828 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
829 if (TheMessage->cm_anon_type == MES_ANON)
830 strcpy(display_name, "****");
831 else if (TheMessage->cm_anon_type == MES_AN2)
832 strcpy(display_name, "anonymous");
834 strcpy(display_name, buf);
836 && ((TheMessage->cm_anon_type == MES_ANON)
837 || (TheMessage->cm_anon_type == MES_AN2))) {
838 sprintf(&display_name[strlen(display_name)],
843 strcpy(allkeys, FORDER);
844 for (i=0; i<strlen(allkeys); ++i) {
845 k = (int) allkeys[i];
847 if (TheMessage->cm_fields[k] != NULL) {
856 TheMessage->cm_fields[k]
865 /* begin header processing loop for RFC822 transfer format */
869 strcpy(snode, NODENAME);
870 strcpy(lnode, HUMANNODE);
871 if (mode == MT_RFC822) {
872 for (i = 0; i < 256; ++i) {
873 if (TheMessage->cm_fields[i]) {
874 mptr = TheMessage->cm_fields[i];
878 } else if (i == 'P') {
879 cprintf("Path: %s\n", mptr);
880 for (a = 0; a < strlen(mptr); ++a) {
881 if (mptr[a] == '!') {
882 strcpy(mptr, &mptr[a + 1]);
888 cprintf("Subject: %s\n", mptr);
894 cprintf("X-Citadel-Room: %s\n", mptr);
898 cprintf("To: %s\n", mptr);
901 cprintf("Date: %s", asctime(localtime(&xtime)));
907 if (mode == MT_RFC822) {
908 if (!strcasecmp(snode, NODENAME)) {
911 cprintf("Message-ID: <%s@%s>\n", mid, snode);
912 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
913 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
914 cprintf("Organization: %s\n", lnode);
917 /* end header processing loop ... at this point, we're in the text */
919 mptr = TheMessage->cm_fields['M'];
921 /* Tell the client about the MIME parts in this message */
922 if (TheMessage->cm_format_type == FMT_RFC822) { /* legacy text dump */
923 if (mode == MT_CITADEL) {
924 mime_parser(mptr, NULL, *list_this_part);
926 else if (mode == MT_MIME) { /* list parts only */
927 mime_parser(mptr, NULL, *list_this_part);
929 CtdlFreeMessage(TheMessage);
936 CtdlFreeMessage(TheMessage);
940 /* signify start of msg text */
941 if (mode == MT_CITADEL)
943 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != FMT_RFC822))
946 /* If the format type on disk is 1 (fixed-format), then we want
947 * everything to be output completely literally ... regardless of
948 * what message transfer format is in use.
950 if (TheMessage->cm_format_type == FMT_FIXED) {
952 while (ch = *mptr++, ch > 0) {
955 if ((ch == 10) || (strlen(buf) > 250)) {
956 cprintf("%s\n", buf);
959 buf[strlen(buf) + 1] = 0;
960 buf[strlen(buf)] = ch;
964 cprintf("%s\n", buf);
967 /* If the message on disk is format 0 (Citadel vari-format), we
968 * output using the formatter at 80 columns. This is the final output
969 * form if the transfer format is RFC822, but if the transfer format
970 * is Citadel proprietary, it'll still work, because the indentation
971 * for new paragraphs is correct and the client will reformat the
972 * message to the reader's screen width.
974 if (TheMessage->cm_format_type == FMT_CITADEL) {
975 memfmout(80, mptr, 0);
978 /* If the message on disk is format 4 (MIME), we've gotta hand it
979 * off to the MIME parser. The client has already been told that
980 * this message is format 1 (fixed format), so the callback function
981 * we use will display those parts as-is.
983 if (TheMessage->cm_format_type == FMT_RFC822) {
984 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
985 memset(ma, 0, sizeof(struct ma_info));
986 mime_parser(mptr, NULL, *fixed_output);
991 CtdlFreeMessage(TheMessage);
998 * display a message (mode 0 - Citadel proprietary)
1000 void cmd_msg0(char *cmdbuf)
1003 int headers_only = 0;
1005 extract(msgid, cmdbuf, 0);
1006 headers_only = extract_int(cmdbuf, 1);
1008 output_message(msgid, MT_CITADEL, headers_only);
1014 * display a message (mode 2 - RFC822)
1016 void cmd_msg2(char *cmdbuf)
1019 int headers_only = 0;
1021 extract(msgid, cmdbuf, 0);
1022 headers_only = extract_int(cmdbuf, 1);
1024 output_message(msgid, MT_RFC822, headers_only);
1030 * display a message (mode 3 - IGnet raw format - internal programs only)
1032 void cmd_msg3(char *cmdbuf)
1035 struct CtdlMessage *msg;
1038 if (CC->internal_pgm == 0) {
1039 cprintf("%d This command is for internal programs only.\n",
1044 msgnum = extract_long(cmdbuf, 0);
1045 msg = CtdlFetchMessage(msgnum);
1047 cprintf("%d Message %ld not found.\n",
1052 serialize_message(&smr, msg);
1053 CtdlFreeMessage(msg);
1056 cprintf("%d Unable to serialize message\n",
1057 ERROR+INTERNAL_ERROR);
1061 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1062 client_write(smr.ser, smr.len);
1069 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
1071 void cmd_msg4(char *cmdbuf)
1075 extract(msgid, cmdbuf, 0);
1077 output_message(msgid, MT_MIME, 0);
1081 * Open a component of a MIME message as a download file
1083 void cmd_opna(char *cmdbuf)
1087 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
1089 extract(msgid, cmdbuf, 0);
1090 extract(desired_section, cmdbuf, 1);
1092 output_message(msgid, MT_DOWNLOAD, 0);
1097 * Save a message pointer into a specified room
1098 * (Returns 0 for success, nonzero for failure)
1099 * roomname may be NULL to use the current room
1101 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
1103 char hold_rm[ROOMNAMELEN];
1104 struct cdbdata *cdbfr;
1107 long highest_msg = 0L;
1108 struct CtdlMessage *msg = NULL;
1110 lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n",
1111 roomname, msgid, flags);
1113 strcpy(hold_rm, CC->quickroom.QRname);
1115 /* We may need to check to see if this message is real */
1116 if ( (flags & SM_VERIFY_GOODNESS)
1117 || (flags & SM_DO_REPL_CHECK)
1119 msg = CtdlFetchMessage(msgid);
1120 if (msg == NULL) return(ERROR + ILLEGAL_VALUE);
1123 /* Perform replication checks if necessary */
1124 if ( (flags & SM_DO_REPL_CHECK) && (msg != NULL) ) {
1126 if (getroom(&CC->quickroom,
1127 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1129 lprintf(9, "No such room <%s>\n", roomname);
1130 if (msg != NULL) CtdlFreeMessage(msg);
1131 return(ERROR + ROOM_NOT_FOUND);
1134 if (ReplicationChecks(msg) != 0) {
1135 getroom(&CC->quickroom, hold_rm);
1136 if (msg != NULL) CtdlFreeMessage(msg);
1137 lprintf(9, "Did replication, and newer exists\n");
1142 /* Now the regular stuff */
1143 if (lgetroom(&CC->quickroom,
1144 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1146 lprintf(9, "No such room <%s>\n", roomname);
1147 if (msg != NULL) CtdlFreeMessage(msg);
1148 return(ERROR + ROOM_NOT_FOUND);
1151 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
1152 if (cdbfr == NULL) {
1156 msglist = mallok(cdbfr->len);
1157 if (msglist == NULL)
1158 lprintf(3, "ERROR malloc msglist!\n");
1159 num_msgs = cdbfr->len / sizeof(long);
1160 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1165 /* Make sure the message doesn't already exist in this room. It
1166 * is absolutely taboo to have more than one reference to the same
1167 * message in a room.
1169 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1170 if (msglist[i] == msgid) {
1171 lputroom(&CC->quickroom); /* unlock the room */
1172 getroom(&CC->quickroom, hold_rm);
1173 if (msg != NULL) CtdlFreeMessage(msg);
1174 return(ERROR + ALREADY_EXISTS);
1178 /* Now add the new message */
1180 msglist = reallok(msglist,
1181 (num_msgs * sizeof(long)));
1183 if (msglist == NULL) {
1184 lprintf(3, "ERROR: can't realloc message list!\n");
1186 msglist[num_msgs - 1] = msgid;
1188 /* Sort the message list, so all the msgid's are in order */
1189 num_msgs = sort_msglist(msglist, num_msgs);
1191 /* Determine the highest message number */
1192 highest_msg = msglist[num_msgs - 1];
1194 /* Write it back to disk. */
1195 cdb_store(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long),
1196 msglist, num_msgs * sizeof(long));
1198 /* Free up the memory we used. */
1201 /* Update the highest-message pointer and unlock the room. */
1202 CC->quickroom.QRhighest = highest_msg;
1203 lputroom(&CC->quickroom);
1204 getroom(&CC->quickroom, hold_rm);
1206 /* Bump the reference count for this message. */
1207 AdjRefCount(msgid, +1);
1209 /* Return success. */
1210 if (msg != NULL) CtdlFreeMessage(msg);
1217 * Message base operation to send a message to the master file
1218 * (returns new message number)
1220 * This is the back end for CtdlSaveMsg() and should not be directly
1221 * called by server-side modules.
1224 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1225 int generate_id, /* generate 'I' field? */
1226 FILE *save_a_copy) /* save a copy to disk? */
1233 /* Get a new message number */
1234 newmsgid = get_new_message_number();
1235 sprintf(msgidbuf, "%ld", newmsgid);
1238 msg->cm_fields['I'] = strdoop(msgidbuf);
1241 serialize_message(&smr, msg);
1244 cprintf("%d Unable to serialize message\n",
1245 ERROR+INTERNAL_ERROR);
1249 /* Write our little bundle of joy into the message base */
1250 begin_critical_section(S_MSGMAIN);
1251 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1252 smr.ser, smr.len) < 0) {
1253 lprintf(2, "Can't store message\n");
1258 end_critical_section(S_MSGMAIN);
1260 /* If the caller specified that a copy should be saved to a particular
1261 * file handle, do that now too.
1263 if (save_a_copy != NULL) {
1264 fwrite(smr.ser, smr.len, 1, save_a_copy);
1267 /* Free the memory we used for the serialized message */
1270 /* Return the *local* message ID to the caller
1271 * (even if we're storing an incoming network message)
1279 * Serialize a struct CtdlMessage into the format used on disk and network.
1281 * This function loads up a "struct ser_ret" (defined in server.h) which
1282 * contains the length of the serialized message and a pointer to the
1283 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1285 void serialize_message(struct ser_ret *ret, /* return values */
1286 struct CtdlMessage *msg) /* unserialized msg */
1290 static char *forder = FORDER;
1292 if (is_valid_message(msg) == 0) return; /* self check */
1295 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1296 ret->len = ret->len +
1297 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1299 lprintf(9, "calling malloc\n");
1300 ret->ser = mallok(ret->len);
1301 if (ret->ser == NULL) {
1307 ret->ser[1] = msg->cm_anon_type;
1308 ret->ser[2] = msg->cm_format_type;
1311 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1312 ret->ser[wlen++] = (char)forder[i];
1313 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1314 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1316 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1325 * Back end for the ReplicationChecks() function
1327 void check_repl(long msgnum) {
1328 struct CtdlMessage *msg;
1329 time_t timestamp = (-1L);
1331 lprintf(9, "check_repl() found message %ld\n", msgnum);
1332 msg = CtdlFetchMessage(msgnum);
1333 if (msg == NULL) return;
1334 if (msg->cm_fields['T'] != NULL) {
1335 timestamp = atol(msg->cm_fields['T']);
1337 CtdlFreeMessage(msg);
1339 if (timestamp > msg_repl->highest) {
1340 msg_repl->highest = timestamp; /* newer! */
1341 lprintf(9, "newer!\n");
1344 lprintf(9, "older!\n");
1346 /* Existing isn't newer? Then delete the old one(s). */
1347 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, NULL);
1352 * Check to see if any messages already exist which carry the same Extended ID
1356 * -> With older timestamps: delete them and return 0. Message will be saved.
1357 * -> With newer timestamps: return 1. Message save will be aborted.
1359 int ReplicationChecks(struct CtdlMessage *msg) {
1360 struct CtdlMessage *template;
1363 lprintf(9, "ReplicationChecks() started\n");
1364 /* No extended id? Don't do anything. */
1365 if (msg->cm_fields['E'] == NULL) return 0;
1366 if (strlen(msg->cm_fields['E']) == 0) return 0;
1367 lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1369 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1370 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1371 msg_repl->highest = atol(msg->cm_fields['T']);
1373 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1374 memset(template, 0, sizeof(struct CtdlMessage));
1375 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1377 CtdlForEachMessage(MSGS_ALL, 0L, NULL, template, check_repl);
1379 /* If a newer message exists with the same Extended ID, abort
1382 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1386 CtdlFreeMessage(template);
1387 lprintf(9, "ReplicationChecks() returning %d\n", abort_this);
1395 * Save a message to disk
1397 long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1398 char *rec, /* Recipient (mail) */
1399 char *force, /* force a particular room? */
1400 int mailtype, /* local or remote type */
1401 int generate_id) /* 1 = generate 'I' field */
1404 char hold_rm[ROOMNAMELEN];
1405 char actual_rm[ROOMNAMELEN];
1406 char force_room[ROOMNAMELEN];
1407 char content_type[256]; /* We have to learn this */
1408 char recipient[256];
1411 struct usersupp userbuf;
1413 struct SuppMsgInfo smi;
1414 FILE *network_fp = NULL;
1415 static int seqnum = 1;
1417 lprintf(9, "CtdlSaveMsg() called\n");
1418 if (is_valid_message(msg) == 0) return(-1); /* self check */
1420 /* If this message has no timestamp, we take the liberty of
1421 * giving it one, right now.
1423 if (msg->cm_fields['T'] == NULL) {
1424 lprintf(9, "Generating timestamp\n");
1425 sprintf(aaa, "%ld", time(NULL));
1426 msg->cm_fields['T'] = strdoop(aaa);
1429 /* If this message has no path, we generate one.
1431 if (msg->cm_fields['P'] == NULL) {
1432 lprintf(9, "Generating path\n");
1433 if (msg->cm_fields['A'] != NULL) {
1434 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1435 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1436 if (isspace(msg->cm_fields['P'][a])) {
1437 msg->cm_fields['P'][a] = ' ';
1442 msg->cm_fields['P'] = strdoop("unknown");
1446 strcpy(force_room, force);
1448 /* Strip non-printable characters out of the recipient name */
1449 strcpy(recipient, rec);
1450 for (a = 0; a < strlen(recipient); ++a)
1451 if (!isprint(recipient[a]))
1452 strcpy(&recipient[a], &recipient[a + 1]);
1454 /* Learn about what's inside, because it's what's inside that counts */
1455 lprintf(9, "Learning what's inside\n");
1456 if (msg->cm_fields['M'] == NULL) {
1457 lprintf(1, "ERROR: attempt to save message with NULL body\n");
1460 switch (msg->cm_format_type) {
1462 strcpy(content_type, "text/x-citadel-variformat");
1465 strcpy(content_type, "text/plain");
1468 strcpy(content_type, "text/plain");
1469 /* advance past header fields */
1470 mptr = msg->cm_fields['M'];
1473 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1474 safestrncpy(content_type, mptr,
1475 sizeof(content_type));
1476 strcpy(content_type, &content_type[14]);
1477 for (a = 0; a < strlen(content_type); ++a)
1478 if ((content_type[a] == ';')
1479 || (content_type[a] == ' ')
1480 || (content_type[a] == 13)
1481 || (content_type[a] == 10))
1482 content_type[a] = 0;
1489 /* Goto the correct room */
1490 lprintf(9, "Switching rooms\n");
1491 strcpy(hold_rm, CC->quickroom.QRname);
1492 strcpy(actual_rm, CC->quickroom.QRname);
1494 /* If the user is a twit, move to the twit room for posting */
1495 lprintf(9, "Handling twit stuff\n");
1497 if (CC->usersupp.axlevel == 2) {
1498 strcpy(hold_rm, actual_rm);
1499 strcpy(actual_rm, config.c_twitroom);
1503 /* ...or if this message is destined for Aide> then go there. */
1504 if (strlen(force_room) > 0) {
1505 strcpy(actual_rm, force_room);
1508 lprintf(9, "Possibly relocating\n");
1509 if (strcasecmp(actual_rm, CC->quickroom.QRname))
1510 getroom(&CC->quickroom, actual_rm);
1512 /* Perform "before save" hooks (aborting if any return nonzero) */
1513 lprintf(9, "Performing before-save hooks\n");
1514 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return(-1);
1516 /* If this message has an Extended ID, perform replication checks */
1517 lprintf(9, "Performing replication checks\n");
1518 if (ReplicationChecks(msg) > 0) return(-1);
1520 /* Network mail - send a copy to the network program. */
1521 if ((strlen(recipient) > 0) && (mailtype == MES_BINARY)) {
1522 lprintf(9, "Sending network spool\n");
1523 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1524 (long) getpid(), CC->cs_pid, ++seqnum);
1525 lprintf(9, "Saving a copy to %s\n", aaa);
1526 network_fp = fopen(aaa, "ab+");
1527 if (network_fp == NULL)
1528 lprintf(2, "ERROR: %s\n", strerror(errno));
1531 /* Save it to disk */
1532 lprintf(9, "Saving to disk\n");
1533 newmsgid = send_message(msg, generate_id, network_fp);
1534 if (network_fp != NULL) {
1536 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1539 if (newmsgid <= 0L) return(-1);
1541 /* Write a supplemental message info record. This doesn't have to
1542 * be a critical section because nobody else knows about this message
1545 lprintf(9, "Creating SuppMsgInfo record\n");
1546 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1547 smi.smi_msgnum = newmsgid;
1548 smi.smi_refcount = 0;
1549 safestrncpy(smi.smi_content_type, content_type, 64);
1550 PutSuppMsgInfo(&smi);
1552 /* Now figure out where to store the pointers */
1553 lprintf(9, "Storing pointers\n");
1556 /* If this is being done by the networker delivering a private
1557 * message, we want to BYPASS saving the sender's copy (because there
1558 * is no local sender; it would otherwise go to the Trashcan).
1560 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1561 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1564 /* For internet mail, drop a copy in the outbound queue room */
1565 /* FIX ... nothing's going to get delivered until we add
1566 some delivery instructions!!! */
1567 if (mailtype == MES_INTERNET) {
1568 CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0);
1571 /* Bump this user's messages posted counter. */
1572 lprintf(9, "Updating user\n");
1573 lgetuser(&CC->usersupp, CC->curr_user);
1574 CC->usersupp.posted = CC->usersupp.posted + 1;
1575 lputuser(&CC->usersupp);
1577 /* If this is private, local mail, make a copy in the
1578 * recipient's mailbox and bump the reference count.
1580 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1581 if (getuser(&userbuf, recipient) == 0) {
1582 lprintf(9, "Delivering private mail\n");
1583 MailboxName(actual_rm, &userbuf, MAILROOM);
1584 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1588 /* Perform "after save" hooks */
1589 lprintf(9, "Performing after-save hooks\n");
1590 PerformMessageHooks(msg, EVT_AFTERSAVE);
1593 lprintf(9, "Returning to original room\n");
1594 if (strcasecmp(hold_rm, CC->quickroom.QRname))
1595 getroom(&CC->quickroom, hold_rm);
1603 * Convenience function for generating small administrative messages.
1605 void quickie_message(char *from, char *to, char *room, char *text)
1607 struct CtdlMessage *msg;
1609 msg = mallok(sizeof(struct CtdlMessage));
1610 memset(msg, 0, sizeof(struct CtdlMessage));
1611 msg->cm_magic = CTDLMESSAGE_MAGIC;
1612 msg->cm_anon_type = MES_NORMAL;
1613 msg->cm_format_type = 0;
1614 msg->cm_fields['A'] = strdoop(from);
1615 msg->cm_fields['O'] = strdoop(room);
1616 msg->cm_fields['N'] = strdoop(NODENAME);
1618 msg->cm_fields['R'] = strdoop(to);
1619 msg->cm_fields['M'] = strdoop(text);
1621 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1622 CtdlFreeMessage(msg);
1623 syslog(LOG_NOTICE, text);
1629 * Back end function used by make_message() and similar functions
1631 char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */
1632 size_t maxlen, /* maximum message length */
1633 char *exist /* if non-null, append to it;
1634 exist is ALWAYS freed */
1637 size_t message_len = 0;
1638 size_t buffer_len = 0;
1642 if (exist == NULL) {
1646 m = reallok(exist, strlen(exist) + 4096);
1647 if (m == NULL) phree(exist);
1650 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) ;;
1657 /* read in the lines of message text one by one */
1659 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) {
1661 /* augment the buffer if we have to */
1662 if ((message_len + strlen(buf) + 2) > buffer_len) {
1663 lprintf(9, "realloking\n");
1664 ptr = reallok(m, (buffer_len * 2) );
1665 if (ptr == NULL) { /* flush if can't allocate */
1666 while ( (client_gets(buf)>0) &&
1667 strcmp(buf, terminator)) ;;
1670 buffer_len = (buffer_len * 2);
1673 lprintf(9, "buffer_len is %d\n", buffer_len);
1677 if (append == NULL) append = m;
1678 while (strlen(append) > 0) ++append;
1679 strcpy(append, buf);
1680 strcat(append, "\n");
1681 message_len = message_len + strlen(buf) + 1;
1683 /* if we've hit the max msg length, flush the rest */
1684 if (message_len >= maxlen) {
1685 while ( (client_gets(buf)>0) && strcmp(buf, terminator)) ;;
1696 * Build a binary message to be saved on disk.
1699 struct CtdlMessage *make_message(
1700 struct usersupp *author, /* author's usersupp structure */
1701 char *recipient, /* NULL if it's not mail */
1702 char *room, /* room where it's going */
1703 int type, /* see MES_ types in header file */
1704 int net_type, /* see MES_ types in header file */
1705 int format_type, /* local or remote (see citadel.h) */
1706 char *fake_name) /* who we're masquerading as */
1712 struct CtdlMessage *msg;
1714 msg = mallok(sizeof(struct CtdlMessage));
1715 memset(msg, 0, sizeof(struct CtdlMessage));
1716 msg->cm_magic = CTDLMESSAGE_MAGIC;
1717 msg->cm_anon_type = type;
1718 msg->cm_format_type = format_type;
1720 /* Don't confuse the poor folks if it's not routed mail. */
1721 strcpy(dest_node, "");
1723 /* If net_type is MES_BINARY, split out the destination node. */
1724 if (net_type == MES_BINARY) {
1725 strcpy(dest_node, NODENAME);
1726 for (a = 0; a < strlen(recipient); ++a) {
1727 if (recipient[a] == '@') {
1729 strcpy(dest_node, &recipient[a + 1]);
1734 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1735 if (net_type == MES_INTERNET) {
1736 strcpy(dest_node, "internet");
1739 while (isspace(recipient[strlen(recipient) - 1]))
1740 recipient[strlen(recipient) - 1] = 0;
1742 sprintf(buf, "cit%ld", author->usernum); /* Path */
1743 msg->cm_fields['P'] = strdoop(buf);
1745 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1746 msg->cm_fields['T'] = strdoop(buf);
1748 if (fake_name[0]) /* author */
1749 msg->cm_fields['A'] = strdoop(fake_name);
1751 msg->cm_fields['A'] = strdoop(author->fullname);
1753 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1754 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1756 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1758 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1759 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1761 if (recipient[0] != 0)
1762 msg->cm_fields['R'] = strdoop(recipient);
1763 if (dest_node[0] != 0)
1764 msg->cm_fields['D'] = strdoop(dest_node);
1767 msg->cm_fields['M'] = CtdlReadMessageBody("000",
1768 config.c_maxmsglen, NULL);
1779 * message entry - mode 0 (normal)
1781 void cmd_ent0(char *entargs)
1784 char recipient[256];
1786 int format_type = 0;
1787 char newusername[256];
1788 struct CtdlMessage *msg;
1792 struct usersupp tempUS;
1795 post = extract_int(entargs, 0);
1796 extract(recipient, entargs, 1);
1797 anon_flag = extract_int(entargs, 2);
1798 format_type = extract_int(entargs, 3);
1800 /* first check to make sure the request is valid. */
1802 if (!(CC->logged_in)) {
1803 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1806 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1807 cprintf("%d Need to be validated to enter ",
1808 ERROR + HIGHER_ACCESS_REQUIRED);
1809 cprintf("(except in %s> to sysop)\n", MAILROOM);
1812 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1813 cprintf("%d Need net privileges to enter here.\n",
1814 ERROR + HIGHER_ACCESS_REQUIRED);
1817 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1818 cprintf("%d Sorry, this is a read-only room.\n",
1819 ERROR + HIGHER_ACCESS_REQUIRED);
1826 if (CC->usersupp.axlevel < 6) {
1827 cprintf("%d You don't have permission to masquerade.\n",
1828 ERROR + HIGHER_ACCESS_REQUIRED);
1831 extract(newusername, entargs, 4);
1832 memset(CC->fake_postname, 0, 32);
1833 strcpy(CC->fake_postname, newusername);
1834 cprintf("%d Ok\n", OK);
1837 CC->cs_flags |= CS_POSTING;
1840 if (CC->quickroom.QRflags & QR_MAILBOX) {
1841 if (CC->usersupp.axlevel >= 2) {
1842 strcpy(buf, recipient);
1844 strcpy(buf, "sysop");
1845 e = alias(buf); /* alias and mail type */
1846 if ((buf[0] == 0) || (e == MES_ERROR)) {
1847 cprintf("%d Unknown address - cannot send message.\n",
1848 ERROR + NO_SUCH_USER);
1851 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1852 cprintf("%d Net privileges required for network mail.\n",
1853 ERROR + HIGHER_ACCESS_REQUIRED);
1856 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1857 && ((CC->usersupp.flags & US_INTERNET) == 0)
1858 && (!CC->internal_pgm)) {
1859 cprintf("%d You don't have access to Internet mail.\n",
1860 ERROR + HIGHER_ACCESS_REQUIRED);
1863 if (!strcasecmp(buf, "sysop")) {
1868 goto SKFALL; /* don't search local file */
1869 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1870 cprintf("%d Can't send mail to yourself!\n",
1871 ERROR + NO_SUCH_USER);
1874 /* Check to make sure the user exists; also get the correct
1875 * upper/lower casing of the name.
1877 a = getuser(&tempUS, buf);
1879 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1882 strcpy(buf, tempUS.fullname);
1885 SKFALL: b = MES_NORMAL;
1886 if (CC->quickroom.QRflags & QR_ANONONLY)
1888 if (CC->quickroom.QRflags & QR_ANONOPT) {
1892 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1895 /* If we're only checking the validity of the request, return
1896 * success without creating the message.
1899 cprintf("%d %s\n", OK, buf);
1903 cprintf("%d send message\n", SEND_LISTING);
1905 /* Read in the message from the client. */
1906 if (CC->fake_postname[0])
1907 msg = make_message(&CC->usersupp, buf,
1908 CC->quickroom.QRname, b, e, format_type,
1910 else if (CC->fake_username[0])
1911 msg = make_message(&CC->usersupp, buf,
1912 CC->quickroom.QRname, b, e, format_type,
1915 msg = make_message(&CC->usersupp, buf,
1916 CC->quickroom.QRname, b, e, format_type, "");
1919 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1920 CtdlFreeMessage(msg);
1921 CC->fake_postname[0] = '\0';
1928 * message entry - mode 3 (raw)
1930 void cmd_ent3(char *entargs)
1936 unsigned char ch, which_field;
1937 struct usersupp tempUS;
1939 struct CtdlMessage *msg;
1942 if (CC->internal_pgm == 0) {
1943 cprintf("%d This command is for internal programs only.\n",
1948 /* See if there's a recipient, but make sure it's a real one */
1949 extract(recp, entargs, 1);
1950 for (a = 0; a < strlen(recp); ++a)
1951 if (!isprint(recp[a]))
1952 strcpy(&recp[a], &recp[a + 1]);
1953 while (isspace(recp[0]))
1954 strcpy(recp, &recp[1]);
1955 while (isspace(recp[strlen(recp) - 1]))
1956 recp[strlen(recp) - 1] = 0;
1958 /* If we're in Mail, check the recipient */
1959 if (strlen(recp) > 0) {
1960 e = alias(recp); /* alias and mail type */
1961 if ((recp[0] == 0) || (e == MES_ERROR)) {
1962 cprintf("%d Unknown address - cannot send message.\n",
1963 ERROR + NO_SUCH_USER);
1966 if (e == MES_LOCAL) {
1967 a = getuser(&tempUS, recp);
1969 cprintf("%d No such user.\n",
1970 ERROR + NO_SUCH_USER);
1976 /* At this point, message has been approved. */
1977 if (extract_int(entargs, 0) == 0) {
1978 cprintf("%d OK to send\n", OK);
1982 msglen = extract_long(entargs, 2);
1983 msg = mallok(sizeof(struct CtdlMessage));
1985 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1989 memset(msg, 0, sizeof(struct CtdlMessage));
1990 tempbuf = mallok(msglen);
1991 if (tempbuf == NULL) {
1992 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1997 cprintf("%d %ld\n", SEND_BINARY, msglen);
1999 client_read(&ch, 1); /* 0xFF magic number */
2000 msg->cm_magic = CTDLMESSAGE_MAGIC;
2001 client_read(&ch, 1); /* anon type */
2002 msg->cm_anon_type = ch;
2003 client_read(&ch, 1); /* format type */
2004 msg->cm_format_type = ch;
2005 msglen = msglen - 3;
2007 while (msglen > 0) {
2008 client_read(&which_field, 1);
2009 if (!isalpha(which_field)) valid_msg = 0;
2013 client_read(&ch, 1);
2015 a = strlen(tempbuf);
2018 } while ( (ch != 0) && (msglen > 0) );
2020 msg->cm_fields[which_field] = strdoop(tempbuf);
2023 msg->cm_flags = CM_SKIP_HOOKS;
2024 if (valid_msg) CtdlSaveMsg(msg, recp, "", e, 0);
2025 CtdlFreeMessage(msg);
2031 * API function to delete messages which match a set of criteria
2032 * (returns the actual number of messages deleted)
2034 int CtdlDeleteMessages(char *room_name, /* which room */
2035 long dmsgnum, /* or "0" for any */
2036 char *content_type /* or NULL for any */
2040 struct quickroom qrbuf;
2041 struct cdbdata *cdbfr;
2042 long *msglist = NULL;
2045 int num_deleted = 0;
2047 struct SuppMsgInfo smi;
2049 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
2050 room_name, dmsgnum, content_type);
2052 /* get room record, obtaining a lock... */
2053 if (lgetroom(&qrbuf, room_name) != 0) {
2054 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
2056 return (0); /* room not found */
2058 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
2060 if (cdbfr != NULL) {
2061 msglist = mallok(cdbfr->len);
2062 memcpy(msglist, cdbfr->ptr, cdbfr->len);
2063 num_msgs = cdbfr->len / sizeof(long);
2067 for (i = 0; i < num_msgs; ++i) {
2070 /* Set/clear a bit for each criterion */
2072 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
2073 delete_this |= 0x01;
2075 if (content_type == NULL) {
2076 delete_this |= 0x02;
2078 GetSuppMsgInfo(&smi, msglist[i]);
2079 if (!strcasecmp(smi.smi_content_type,
2081 delete_this |= 0x02;
2085 /* Delete message only if all bits are set */
2086 if (delete_this == 0x03) {
2087 AdjRefCount(msglist[i], -1);
2093 num_msgs = sort_msglist(msglist, num_msgs);
2094 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
2095 msglist, (num_msgs * sizeof(long)));
2097 qrbuf.QRhighest = msglist[num_msgs - 1];
2101 lprintf(9, "%d message(s) deleted.\n", num_deleted);
2102 return (num_deleted);
2108 * Delete message from current room
2110 void cmd_dele(char *delstr)
2115 getuser(&CC->usersupp, CC->curr_user);
2116 if ((CC->usersupp.axlevel < 6)
2117 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2118 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2119 && (!(CC->internal_pgm))) {
2120 cprintf("%d Higher access required.\n",
2121 ERROR + HIGHER_ACCESS_REQUIRED);
2124 delnum = extract_long(delstr, 0);
2126 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
2129 cprintf("%d %d message%s deleted.\n", OK,
2130 num_deleted, ((num_deleted != 1) ? "s" : ""));
2132 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2138 * move or copy a message to another room
2140 void cmd_move(char *args)
2144 struct quickroom qtemp;
2148 num = extract_long(args, 0);
2149 extract(targ, args, 1);
2150 targ[ROOMNAMELEN - 1] = 0;
2151 is_copy = extract_int(args, 2);
2153 getuser(&CC->usersupp, CC->curr_user);
2154 if ((CC->usersupp.axlevel < 6)
2155 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2156 cprintf("%d Higher access required.\n",
2157 ERROR + HIGHER_ACCESS_REQUIRED);
2161 if (getroom(&qtemp, targ) != 0) {
2162 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2166 err = CtdlSaveMsgPointerInRoom(targ, num,
2167 (SM_VERIFY_GOODNESS | SM_DO_REPL_CHECK) );
2169 cprintf("%d Cannot store message in %s: error %d\n",
2174 /* Now delete the message from the source room,
2175 * if this is a 'move' rather than a 'copy' operation.
2177 if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
2179 cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") );
2185 * GetSuppMsgInfo() - Get the supplementary record for a message
2187 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2190 struct cdbdata *cdbsmi;
2193 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2194 smibuf->smi_msgnum = msgnum;
2195 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2197 /* Use the negative of the message number for its supp record index */
2198 TheIndex = (0L - msgnum);
2200 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2201 if (cdbsmi == NULL) {
2202 return; /* record not found; go with defaults */
2204 memcpy(smibuf, cdbsmi->ptr,
2205 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2206 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2213 * PutSuppMsgInfo() - (re)write supplementary record for a message
2215 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2219 /* Use the negative of the message number for its supp record index */
2220 TheIndex = (0L - smibuf->smi_msgnum);
2222 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2223 smibuf->smi_msgnum, smibuf->smi_refcount);
2225 cdb_store(CDB_MSGMAIN,
2226 &TheIndex, sizeof(long),
2227 smibuf, sizeof(struct SuppMsgInfo));
2232 * AdjRefCount - change the reference count for a message;
2233 * delete the message if it reaches zero
2235 void AdjRefCount(long msgnum, int incr)
2238 struct SuppMsgInfo smi;
2241 /* This is a *tight* critical section; please keep it that way, as
2242 * it may get called while nested in other critical sections.
2243 * Complicating this any further will surely cause deadlock!
2245 begin_critical_section(S_SUPPMSGMAIN);
2246 GetSuppMsgInfo(&smi, msgnum);
2247 smi.smi_refcount += incr;
2248 PutSuppMsgInfo(&smi);
2249 end_critical_section(S_SUPPMSGMAIN);
2251 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2252 msgnum, smi.smi_refcount);
2254 /* If the reference count is now zero, delete the message
2255 * (and its supplementary record as well).
2257 if (smi.smi_refcount == 0) {
2258 lprintf(9, "Deleting message <%ld>\n", msgnum);
2260 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2261 delnum = (0L - msgnum);
2262 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2267 * Write a generic object to this room
2269 * Note: this could be much more efficient. Right now we use two temporary
2270 * files, and still pull the message into memory as with all others.
2272 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2273 char *content_type, /* MIME type of this object */
2274 char *tempfilename, /* Where to fetch it from */
2275 struct usersupp *is_mailbox, /* Mailbox room? */
2276 int is_binary, /* Is encoding necessary? */
2277 int is_unique, /* Del others of this type? */
2278 unsigned int flags /* Internal save flags */
2283 char filename[PATH_MAX];
2286 struct quickroom qrbuf;
2287 char roomname[ROOMNAMELEN];
2288 struct CtdlMessage *msg;
2291 if (is_mailbox != NULL)
2292 MailboxName(roomname, is_mailbox, req_room);
2294 safestrncpy(roomname, req_room, sizeof(roomname));
2295 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2297 strcpy(filename, tmpnam(NULL));
2298 fp = fopen(filename, "w");
2302 tempfp = fopen(tempfilename, "r");
2303 if (tempfp == NULL) {
2309 fprintf(fp, "Content-type: %s\n", content_type);
2310 lprintf(9, "Content-type: %s\n", content_type);
2312 if (is_binary == 0) {
2313 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2314 while (ch = getc(tempfp), ch > 0)
2320 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2323 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2324 tempfilename, filename);
2328 lprintf(9, "Allocating\n");
2329 msg = mallok(sizeof(struct CtdlMessage));
2330 memset(msg, 0, sizeof(struct CtdlMessage));
2331 msg->cm_magic = CTDLMESSAGE_MAGIC;
2332 msg->cm_anon_type = MES_NORMAL;
2333 msg->cm_format_type = 4;
2334 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2335 msg->cm_fields['O'] = strdoop(req_room);
2336 msg->cm_fields['N'] = strdoop(config.c_nodename);
2337 msg->cm_fields['H'] = strdoop(config.c_humannode);
2338 msg->cm_flags = flags;
2340 lprintf(9, "Loading\n");
2341 fp = fopen(filename, "rb");
2342 fseek(fp, 0L, SEEK_END);
2345 msg->cm_fields['M'] = mallok(len);
2346 fread(msg->cm_fields['M'], len, 1, fp);
2350 /* Create the requested room if we have to. */
2351 if (getroom(&qrbuf, roomname) != 0) {
2352 create_room(roomname, 4, "", 0);
2354 /* If the caller specified this object as unique, delete all
2355 * other objects of this type that are currently in the room.
2358 lprintf(9, "Deleted %d other msgs of this type\n",
2359 CtdlDeleteMessages(roomname, 0L, content_type));
2361 /* Now write the data */
2362 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2363 CtdlFreeMessage(msg);
2371 void CtdlGetSysConfigBackend(long msgnum) {
2372 config_msgnum = msgnum;
2376 char *CtdlGetSysConfig(char *sysconfname) {
2377 char hold_rm[ROOMNAMELEN];
2380 struct CtdlMessage *msg;
2383 strcpy(hold_rm, CC->quickroom.QRname);
2384 if (getroom(&CC->quickroom, SYSCONFIGROOM) != 0) {
2385 getroom(&CC->quickroom, hold_rm);
2390 /* We want the last (and probably only) config in this room */
2391 begin_critical_section(S_CONFIG);
2392 config_msgnum = (-1L);
2393 CtdlForEachMessage(MSGS_LAST, 1, sysconfname, NULL,
2394 CtdlGetSysConfigBackend);
2395 msgnum = config_msgnum;
2396 end_critical_section(S_CONFIG);
2402 msg = CtdlFetchMessage(msgnum);
2404 conf = strdoop(msg->cm_fields['M']);
2405 CtdlFreeMessage(msg);
2412 getroom(&CC->quickroom, hold_rm);
2414 if (conf != NULL) do {
2415 extract_token(buf, conf, 0, '\n');
2416 strcpy(conf, &conf[strlen(buf)+1]);
2417 } while ( (strlen(conf)>0) && (strlen(buf)>0) );
2422 void CtdlPutSysConfig(char *sysconfname, char *sysconfdata) {
2423 char temp[PATH_MAX];
2426 strcpy(temp, tmpnam(NULL));
2428 fp = fopen(temp, "w");
2429 if (fp == NULL) return;
2430 fprintf(fp, "Content-type: %s\n\n", sysconfname);
2431 fprintf(fp, "%s", sysconfdata);
2434 /* this handy API function does all the work for us */
2435 CtdlWriteObject(SYSCONFIGROOM, sysconfname, temp,
2436 &CC->usersupp, 0, 1, 0);