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;
630 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
631 if (dmsgtext == NULL) {
634 mptr = dmsgtext->ptr;
636 /* Parse the three bytes that begin EVERY message on disk.
637 * The first is always 0xFF, the on-disk magic number.
638 * The second is the anonymous/public type byte.
639 * The third is the format type byte (vari, fixed, or MIME).
643 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
647 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
648 memset(ret, 0, sizeof(struct CtdlMessage));
650 ret->cm_magic = CTDLMESSAGE_MAGIC;
651 ret->cm_anon_type = *mptr++; /* Anon type byte */
652 ret->cm_format_type = *mptr++; /* Format type byte */
655 * The rest is zero or more arbitrary fields. Load them in.
656 * We're done when we encounter either a zero-length field or
657 * have just processed the 'M' (message text) field.
660 field_length = strlen(mptr);
661 if (field_length == 0)
663 field_header = *mptr++;
664 ret->cm_fields[field_header] = mallok(field_length);
665 strcpy(ret->cm_fields[field_header], mptr);
667 while (*mptr++ != 0); /* advance to next field */
669 } while ((field_length > 0) && (field_header != 'M'));
673 /* Always make sure there's something in the msg text field */
674 if (ret->cm_fields['M'] == NULL)
675 ret->cm_fields['M'] = strdoop("<no text>\n");
677 /* Perform "before read" hooks (aborting if any return nonzero) */
678 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
679 CtdlFreeMessage(ret);
688 * Returns 1 if the supplied pointer points to a valid Citadel message.
689 * If the pointer is NULL or the magic number check fails, returns 0.
691 int is_valid_message(struct CtdlMessage *msg) {
694 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
695 lprintf(3, "is_valid_message() -- self-check failed\n");
703 * 'Destructor' for struct CtdlMessage
705 void CtdlFreeMessage(struct CtdlMessage *msg)
709 if (is_valid_message(msg) == 0) return;
711 for (i = 0; i < 256; ++i)
712 if (msg->cm_fields[i] != NULL)
713 phree(msg->cm_fields[i]);
715 msg->cm_magic = 0; /* just in case */
722 * Get a message off disk. (return value is the message's timestamp)
725 void output_message(char *msgid, int mode, int headers_only)
733 char display_name[256];
735 struct CtdlMessage *TheMessage = NULL;
739 /* buffers needed for RFC822 translation */
747 msg_num = atol(msgid);
748 safestrncpy(mid, msgid, sizeof mid);
750 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
751 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
755 /* FIX ... small security issue
756 * We need to check to make sure the requested message is actually
757 * in the current room, and set msg_ok to 1 only if it is. This
758 * functionality is currently missing because I'm in a hurry to replace
759 * broken production code with nonbroken pre-beta code. :( -- ajc
762 cprintf("%d Message %ld is not in this room.\n",
769 * Fetch the message from disk
771 TheMessage = CtdlFetchMessage(msg_num);
772 if (TheMessage == NULL) {
773 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
777 /* Are we downloading a MIME component? */
778 if (mode == MT_DOWNLOAD) {
779 if (TheMessage->cm_format_type != FMT_RFC822) {
780 cprintf("%d This is not a MIME message.\n",
782 } else if (CC->download_fp != NULL) {
783 cprintf("%d You already have a download open.\n",
786 /* Parse the message text component */
787 mptr = TheMessage->cm_fields['M'];
788 mime_parser(mptr, NULL, *mime_download);
789 /* If there's no file open by this time, the requested
790 * section wasn't found, so print an error
792 if (CC->download_fp == NULL) {
793 cprintf("%d Section %s not found.\n",
794 ERROR + FILE_NOT_FOUND,
798 CtdlFreeMessage(TheMessage);
802 /* now for the user-mode message reading loops */
803 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
805 /* Tell the client which format type we're using. If this is a
806 * MIME message, *lie* about it and tell the user it's fixed-format.
808 if (mode == MT_CITADEL) {
809 if (TheMessage->cm_format_type == FMT_RFC822)
812 cprintf("type=%d\n", TheMessage->cm_format_type);
815 /* nhdr=yes means that we're only displaying headers, no body */
816 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
817 cprintf("nhdr=yes\n");
820 /* begin header processing loop for Citadel message format */
822 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
824 strcpy(display_name, "<unknown>");
825 if (TheMessage->cm_fields['A']) {
826 strcpy(buf, TheMessage->cm_fields['A']);
827 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
828 if (TheMessage->cm_anon_type == MES_ANON)
829 strcpy(display_name, "****");
830 else if (TheMessage->cm_anon_type == MES_AN2)
831 strcpy(display_name, "anonymous");
833 strcpy(display_name, buf);
835 && ((TheMessage->cm_anon_type == MES_ANON)
836 || (TheMessage->cm_anon_type == MES_AN2))) {
837 sprintf(&display_name[strlen(display_name)],
842 strcpy(allkeys, FORDER);
843 for (i=0; i<strlen(allkeys); ++i) {
844 k = (int) allkeys[i];
846 if (TheMessage->cm_fields[k] != NULL) {
855 TheMessage->cm_fields[k]
864 /* begin header processing loop for RFC822 transfer format */
868 strcpy(snode, NODENAME);
869 strcpy(lnode, HUMANNODE);
870 if (mode == MT_RFC822) {
871 for (i = 0; i < 256; ++i) {
872 if (TheMessage->cm_fields[i]) {
873 mptr = TheMessage->cm_fields[i];
877 } else if (i == 'P') {
878 cprintf("Path: %s\n", mptr);
879 for (a = 0; a < strlen(mptr); ++a) {
880 if (mptr[a] == '!') {
881 strcpy(mptr, &mptr[a + 1]);
887 cprintf("Subject: %s\n", mptr);
893 cprintf("X-Citadel-Room: %s\n", mptr);
897 cprintf("To: %s\n", mptr);
900 cprintf("Date: %s", asctime(localtime(&xtime)));
906 if (mode == MT_RFC822) {
907 if (!strcasecmp(snode, NODENAME)) {
910 cprintf("Message-ID: <%s@%s>\n", mid, snode);
911 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
912 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
913 cprintf("Organization: %s\n", lnode);
916 /* end header processing loop ... at this point, we're in the text */
918 mptr = TheMessage->cm_fields['M'];
920 /* Tell the client about the MIME parts in this message */
921 if (TheMessage->cm_format_type == FMT_RFC822) { /* legacy text dump */
922 if (mode == MT_CITADEL) {
923 mime_parser(mptr, NULL, *list_this_part);
925 else if (mode == MT_MIME) { /* list parts only */
926 mime_parser(mptr, NULL, *list_this_part);
928 CtdlFreeMessage(TheMessage);
935 CtdlFreeMessage(TheMessage);
939 /* signify start of msg text */
940 if (mode == MT_CITADEL)
942 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != FMT_RFC822))
945 /* If the format type on disk is 1 (fixed-format), then we want
946 * everything to be output completely literally ... regardless of
947 * what message transfer format is in use.
949 if (TheMessage->cm_format_type == FMT_FIXED) {
951 while (ch = *mptr++, ch > 0) {
954 if ((ch == 10) || (strlen(buf) > 250)) {
955 cprintf("%s\n", buf);
958 buf[strlen(buf) + 1] = 0;
959 buf[strlen(buf)] = ch;
963 cprintf("%s\n", buf);
966 /* If the message on disk is format 0 (Citadel vari-format), we
967 * output using the formatter at 80 columns. This is the final output
968 * form if the transfer format is RFC822, but if the transfer format
969 * is Citadel proprietary, it'll still work, because the indentation
970 * for new paragraphs is correct and the client will reformat the
971 * message to the reader's screen width.
973 if (TheMessage->cm_format_type == FMT_CITADEL) {
974 memfmout(80, mptr, 0);
977 /* If the message on disk is format 4 (MIME), we've gotta hand it
978 * off to the MIME parser. The client has already been told that
979 * this message is format 1 (fixed format), so the callback function
980 * we use will display those parts as-is.
982 if (TheMessage->cm_format_type == FMT_RFC822) {
983 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
984 memset(ma, 0, sizeof(struct ma_info));
985 mime_parser(mptr, NULL, *fixed_output);
990 CtdlFreeMessage(TheMessage);
997 * display a message (mode 0 - Citadel proprietary)
999 void cmd_msg0(char *cmdbuf)
1002 int headers_only = 0;
1004 extract(msgid, cmdbuf, 0);
1005 headers_only = extract_int(cmdbuf, 1);
1007 output_message(msgid, MT_CITADEL, headers_only);
1013 * display a message (mode 2 - RFC822)
1015 void cmd_msg2(char *cmdbuf)
1018 int headers_only = 0;
1020 extract(msgid, cmdbuf, 0);
1021 headers_only = extract_int(cmdbuf, 1);
1023 output_message(msgid, MT_RFC822, headers_only);
1029 * display a message (mode 3 - IGnet raw format - internal programs only)
1031 void cmd_msg3(char *cmdbuf)
1034 struct CtdlMessage *msg;
1037 if (CC->internal_pgm == 0) {
1038 cprintf("%d This command is for internal programs only.\n",
1043 msgnum = extract_long(cmdbuf, 0);
1044 msg = CtdlFetchMessage(msgnum);
1046 cprintf("%d Message %ld not found.\n",
1051 serialize_message(&smr, msg);
1052 CtdlFreeMessage(msg);
1055 cprintf("%d Unable to serialize message\n",
1056 ERROR+INTERNAL_ERROR);
1060 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1061 client_write(smr.ser, smr.len);
1068 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
1070 void cmd_msg4(char *cmdbuf)
1074 extract(msgid, cmdbuf, 0);
1076 output_message(msgid, MT_MIME, 0);
1080 * Open a component of a MIME message as a download file
1082 void cmd_opna(char *cmdbuf)
1086 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
1088 extract(msgid, cmdbuf, 0);
1089 extract(desired_section, cmdbuf, 1);
1091 output_message(msgid, MT_DOWNLOAD, 0);
1096 * Save a message pointer into a specified room
1097 * (Returns 0 for success, nonzero for failure)
1098 * roomname may be NULL to use the current room
1100 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
1102 char hold_rm[ROOMNAMELEN];
1103 struct cdbdata *cdbfr;
1106 long highest_msg = 0L;
1107 struct CtdlMessage *msg = NULL;
1109 lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n",
1110 roomname, msgid, flags);
1112 strcpy(hold_rm, CC->quickroom.QRname);
1114 /* We may need to check to see if this message is real */
1115 if ( (flags & SM_VERIFY_GOODNESS)
1116 || (flags & SM_DO_REPL_CHECK)
1118 msg = CtdlFetchMessage(msgid);
1119 if (msg == NULL) return(ERROR + ILLEGAL_VALUE);
1122 /* Perform replication checks if necessary */
1123 if ( (flags & SM_DO_REPL_CHECK) && (msg != NULL) ) {
1125 if (getroom(&CC->quickroom,
1126 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1128 lprintf(9, "No such room <%s>\n", roomname);
1129 if (msg != NULL) CtdlFreeMessage(msg);
1130 return(ERROR + ROOM_NOT_FOUND);
1133 if (ReplicationChecks(msg) != 0) {
1134 getroom(&CC->quickroom, hold_rm);
1135 if (msg != NULL) CtdlFreeMessage(msg);
1136 lprintf(9, "Did replication, and newer exists\n");
1141 /* Now the regular stuff */
1142 if (lgetroom(&CC->quickroom,
1143 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1145 lprintf(9, "No such room <%s>\n", roomname);
1146 if (msg != NULL) CtdlFreeMessage(msg);
1147 return(ERROR + ROOM_NOT_FOUND);
1150 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
1151 if (cdbfr == NULL) {
1155 msglist = mallok(cdbfr->len);
1156 if (msglist == NULL)
1157 lprintf(3, "ERROR malloc msglist!\n");
1158 num_msgs = cdbfr->len / sizeof(long);
1159 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1164 /* Make sure the message doesn't already exist in this room. It
1165 * is absolutely taboo to have more than one reference to the same
1166 * message in a room.
1168 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1169 if (msglist[i] == msgid) {
1170 lputroom(&CC->quickroom); /* unlock the room */
1171 getroom(&CC->quickroom, hold_rm);
1172 if (msg != NULL) CtdlFreeMessage(msg);
1173 return(ERROR + ALREADY_EXISTS);
1177 /* Now add the new message */
1179 msglist = reallok(msglist,
1180 (num_msgs * sizeof(long)));
1182 if (msglist == NULL) {
1183 lprintf(3, "ERROR: can't realloc message list!\n");
1185 msglist[num_msgs - 1] = msgid;
1187 /* Sort the message list, so all the msgid's are in order */
1188 num_msgs = sort_msglist(msglist, num_msgs);
1190 /* Determine the highest message number */
1191 highest_msg = msglist[num_msgs - 1];
1193 /* Write it back to disk. */
1194 cdb_store(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long),
1195 msglist, num_msgs * sizeof(long));
1197 /* Free up the memory we used. */
1200 /* Update the highest-message pointer and unlock the room. */
1201 CC->quickroom.QRhighest = highest_msg;
1202 lputroom(&CC->quickroom);
1203 getroom(&CC->quickroom, hold_rm);
1205 /* Bump the reference count for this message. */
1206 AdjRefCount(msgid, +1);
1208 /* Return success. */
1209 if (msg != NULL) CtdlFreeMessage(msg);
1216 * Message base operation to send a message to the master file
1217 * (returns new message number)
1219 * This is the back end for CtdlSaveMsg() and should not be directly
1220 * called by server-side modules.
1223 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1224 int generate_id, /* generate 'I' field? */
1225 FILE *save_a_copy) /* save a copy to disk? */
1232 /* Get a new message number */
1233 newmsgid = get_new_message_number();
1234 sprintf(msgidbuf, "%ld", newmsgid);
1237 msg->cm_fields['I'] = strdoop(msgidbuf);
1240 serialize_message(&smr, msg);
1243 cprintf("%d Unable to serialize message\n",
1244 ERROR+INTERNAL_ERROR);
1248 /* Write our little bundle of joy into the message base */
1249 begin_critical_section(S_MSGMAIN);
1250 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1251 smr.ser, smr.len) < 0) {
1252 lprintf(2, "Can't store message\n");
1257 end_critical_section(S_MSGMAIN);
1259 /* If the caller specified that a copy should be saved to a particular
1260 * file handle, do that now too.
1262 if (save_a_copy != NULL) {
1263 fwrite(smr.ser, smr.len, 1, save_a_copy);
1266 /* Free the memory we used for the serialized message */
1269 /* Return the *local* message ID to the caller
1270 * (even if we're storing an incoming network message)
1278 * Serialize a struct CtdlMessage into the format used on disk and network.
1280 * This function loads up a "struct ser_ret" (defined in server.h) which
1281 * contains the length of the serialized message and a pointer to the
1282 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1284 void serialize_message(struct ser_ret *ret, /* return values */
1285 struct CtdlMessage *msg) /* unserialized msg */
1289 static char *forder = FORDER;
1291 if (is_valid_message(msg) == 0) return; /* self check */
1294 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1295 ret->len = ret->len +
1296 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1298 lprintf(9, "calling malloc\n");
1299 ret->ser = mallok(ret->len);
1300 if (ret->ser == NULL) {
1306 ret->ser[1] = msg->cm_anon_type;
1307 ret->ser[2] = msg->cm_format_type;
1310 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1311 ret->ser[wlen++] = (char)forder[i];
1312 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1313 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1315 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1324 * Back end for the ReplicationChecks() function
1326 void check_repl(long msgnum) {
1327 struct CtdlMessage *msg;
1328 time_t timestamp = (-1L);
1330 lprintf(9, "check_repl() found message %ld\n", msgnum);
1331 msg = CtdlFetchMessage(msgnum);
1332 if (msg == NULL) return;
1333 if (msg->cm_fields['T'] != NULL) {
1334 timestamp = atol(msg->cm_fields['T']);
1336 CtdlFreeMessage(msg);
1338 if (timestamp > msg_repl->highest) {
1339 msg_repl->highest = timestamp; /* newer! */
1340 lprintf(9, "newer!\n");
1343 lprintf(9, "older!\n");
1345 /* Existing isn't newer? Then delete the old one(s). */
1346 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, NULL);
1351 * Check to see if any messages already exist which carry the same Extended ID
1355 * -> With older timestamps: delete them and return 0. Message will be saved.
1356 * -> With newer timestamps: return 1. Message save will be aborted.
1358 int ReplicationChecks(struct CtdlMessage *msg) {
1359 struct CtdlMessage *template;
1362 lprintf(9, "ReplicationChecks() started\n");
1363 /* No extended id? Don't do anything. */
1364 if (msg->cm_fields['E'] == NULL) return 0;
1365 if (strlen(msg->cm_fields['E']) == 0) return 0;
1366 lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1368 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1369 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1370 msg_repl->highest = atol(msg->cm_fields['T']);
1372 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1373 memset(template, 0, sizeof(struct CtdlMessage));
1374 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1376 CtdlForEachMessage(MSGS_ALL, 0L, NULL, template, check_repl);
1378 /* If a newer message exists with the same Extended ID, abort
1381 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1385 CtdlFreeMessage(template);
1386 lprintf(9, "ReplicationChecks() returning %d\n", abort_this);
1394 * Save a message to disk
1396 long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1397 char *rec, /* Recipient (mail) */
1398 char *force, /* force a particular room? */
1399 int mailtype, /* local or remote type */
1400 int generate_id) /* 1 = generate 'I' field */
1403 char hold_rm[ROOMNAMELEN];
1404 char actual_rm[ROOMNAMELEN];
1405 char force_room[ROOMNAMELEN];
1406 char content_type[256]; /* We have to learn this */
1407 char recipient[256];
1410 struct usersupp userbuf;
1412 struct SuppMsgInfo smi;
1413 FILE *network_fp = NULL;
1414 static int seqnum = 1;
1416 lprintf(9, "CtdlSaveMsg() called\n");
1417 if (is_valid_message(msg) == 0) return(-1); /* self check */
1419 /* If this message has no timestamp, we take the liberty of
1420 * giving it one, right now.
1422 if (msg->cm_fields['T'] == NULL) {
1423 lprintf(9, "Generating timestamp\n");
1424 sprintf(aaa, "%ld", time(NULL));
1425 msg->cm_fields['T'] = strdoop(aaa);
1428 /* If this message has no path, we generate one.
1430 if (msg->cm_fields['P'] == NULL) {
1431 lprintf(9, "Generating path\n");
1432 if (msg->cm_fields['A'] != NULL) {
1433 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1434 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1435 if (isspace(msg->cm_fields['P'][a])) {
1436 msg->cm_fields['P'][a] = ' ';
1441 msg->cm_fields['P'] = strdoop("unknown");
1445 strcpy(force_room, force);
1447 /* Strip non-printable characters out of the recipient name */
1448 strcpy(recipient, rec);
1449 for (a = 0; a < strlen(recipient); ++a)
1450 if (!isprint(recipient[a]))
1451 strcpy(&recipient[a], &recipient[a + 1]);
1453 /* Learn about what's inside, because it's what's inside that counts */
1454 lprintf(9, "Learning what's inside\n");
1455 if (msg->cm_fields['M'] == NULL) {
1456 lprintf(1, "ERROR: attempt to save message with NULL body\n");
1459 switch (msg->cm_format_type) {
1461 strcpy(content_type, "text/x-citadel-variformat");
1464 strcpy(content_type, "text/plain");
1467 strcpy(content_type, "text/plain");
1468 /* advance past header fields */
1469 mptr = msg->cm_fields['M'];
1472 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1473 safestrncpy(content_type, mptr,
1474 sizeof(content_type));
1475 strcpy(content_type, &content_type[14]);
1476 for (a = 0; a < strlen(content_type); ++a)
1477 if ((content_type[a] == ';')
1478 || (content_type[a] == ' ')
1479 || (content_type[a] == 13)
1480 || (content_type[a] == 10))
1481 content_type[a] = 0;
1488 /* Goto the correct room */
1489 lprintf(9, "Switching rooms\n");
1490 strcpy(hold_rm, CC->quickroom.QRname);
1491 strcpy(actual_rm, CC->quickroom.QRname);
1493 /* If the user is a twit, move to the twit room for posting */
1494 lprintf(9, "Handling twit stuff\n");
1496 if (CC->usersupp.axlevel == 2) {
1497 strcpy(hold_rm, actual_rm);
1498 strcpy(actual_rm, config.c_twitroom);
1502 /* ...or if this message is destined for Aide> then go there. */
1503 if (strlen(force_room) > 0) {
1504 strcpy(actual_rm, force_room);
1507 lprintf(9, "Possibly relocating\n");
1508 if (strcasecmp(actual_rm, CC->quickroom.QRname))
1509 getroom(&CC->quickroom, actual_rm);
1511 /* Perform "before save" hooks (aborting if any return nonzero) */
1512 lprintf(9, "Performing before-save hooks\n");
1513 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return(-1);
1515 /* If this message has an Extended ID, perform replication checks */
1516 lprintf(9, "Performing replication checks\n");
1517 if (ReplicationChecks(msg) > 0) return(-1);
1519 /* Network mail - send a copy to the network program. */
1520 if ((strlen(recipient) > 0) && (mailtype == MES_BINARY)) {
1521 lprintf(9, "Sending network spool\n");
1522 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1523 (long) getpid(), CC->cs_pid, ++seqnum);
1524 lprintf(9, "Saving a copy to %s\n", aaa);
1525 network_fp = fopen(aaa, "ab+");
1526 if (network_fp == NULL)
1527 lprintf(2, "ERROR: %s\n", strerror(errno));
1530 /* Save it to disk */
1531 lprintf(9, "Saving to disk\n");
1532 newmsgid = send_message(msg, generate_id, network_fp);
1533 if (network_fp != NULL) {
1535 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1538 if (newmsgid <= 0L) return(-1);
1540 /* Write a supplemental message info record. This doesn't have to
1541 * be a critical section because nobody else knows about this message
1544 lprintf(9, "Creating SuppMsgInfo record\n");
1545 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1546 smi.smi_msgnum = newmsgid;
1547 smi.smi_refcount = 0;
1548 safestrncpy(smi.smi_content_type, content_type, 64);
1549 PutSuppMsgInfo(&smi);
1551 /* Now figure out where to store the pointers */
1552 lprintf(9, "Storing pointers\n");
1555 /* If this is being done by the networker delivering a private
1556 * message, we want to BYPASS saving the sender's copy (because there
1557 * is no local sender; it would otherwise go to the Trashcan).
1559 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1560 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1563 /* For internet mail, drop a copy in the outbound queue room */
1564 /* FIX ... nothing's going to get delivered until we add
1565 some delivery instructions!!! */
1566 if (mailtype == MES_INTERNET) {
1567 CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0);
1570 /* Bump this user's messages posted counter. */
1571 lprintf(9, "Updating user\n");
1572 lgetuser(&CC->usersupp, CC->curr_user);
1573 CC->usersupp.posted = CC->usersupp.posted + 1;
1574 lputuser(&CC->usersupp);
1576 /* If this is private, local mail, make a copy in the
1577 * recipient's mailbox and bump the reference count.
1579 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1580 if (getuser(&userbuf, recipient) == 0) {
1581 lprintf(9, "Delivering private mail\n");
1582 MailboxName(actual_rm, &userbuf, MAILROOM);
1583 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1587 /* Perform "after save" hooks */
1588 lprintf(9, "Performing after-save hooks\n");
1589 PerformMessageHooks(msg, EVT_AFTERSAVE);
1592 lprintf(9, "Returning to original room\n");
1593 if (strcasecmp(hold_rm, CC->quickroom.QRname))
1594 getroom(&CC->quickroom, hold_rm);
1602 * Convenience function for generating small administrative messages.
1604 void quickie_message(char *from, char *to, char *room, char *text)
1606 struct CtdlMessage *msg;
1608 msg = mallok(sizeof(struct CtdlMessage));
1609 memset(msg, 0, sizeof(struct CtdlMessage));
1610 msg->cm_magic = CTDLMESSAGE_MAGIC;
1611 msg->cm_anon_type = MES_NORMAL;
1612 msg->cm_format_type = 0;
1613 msg->cm_fields['A'] = strdoop(from);
1614 msg->cm_fields['O'] = strdoop(room);
1615 msg->cm_fields['N'] = strdoop(NODENAME);
1617 msg->cm_fields['R'] = strdoop(to);
1618 msg->cm_fields['M'] = strdoop(text);
1620 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1621 CtdlFreeMessage(msg);
1622 syslog(LOG_NOTICE, text);
1628 * Back end function used by make_message() and similar functions
1630 char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */
1631 size_t maxlen, /* maximum message length */
1632 char *exist /* if non-null, append to it;
1633 exist is ALWAYS freed */
1636 size_t message_len = 0;
1637 size_t buffer_len = 0;
1641 if (exist == NULL) {
1645 m = reallok(exist, strlen(exist) + 4096);
1646 if (m == NULL) phree(exist);
1649 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) ;;
1656 /* read in the lines of message text one by one */
1658 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) {
1660 /* augment the buffer if we have to */
1661 if ((message_len + strlen(buf) + 2) > buffer_len) {
1662 lprintf(9, "realloking\n");
1663 ptr = reallok(m, (buffer_len * 2) );
1664 if (ptr == NULL) { /* flush if can't allocate */
1665 while ( (client_gets(buf)>0) &&
1666 strcmp(buf, terminator)) ;;
1669 buffer_len = (buffer_len * 2);
1672 lprintf(9, "buffer_len is %d\n", buffer_len);
1676 if (append == NULL) append = m;
1677 while (strlen(append) > 0) ++append;
1678 strcpy(append, buf);
1679 strcat(append, "\n");
1680 message_len = message_len + strlen(buf) + 1;
1682 /* if we've hit the max msg length, flush the rest */
1683 if (message_len >= maxlen) {
1684 while ( (client_gets(buf)>0) && strcmp(buf, terminator)) ;;
1695 * Build a binary message to be saved on disk.
1698 struct CtdlMessage *make_message(
1699 struct usersupp *author, /* author's usersupp structure */
1700 char *recipient, /* NULL if it's not mail */
1701 char *room, /* room where it's going */
1702 int type, /* see MES_ types in header file */
1703 int net_type, /* see MES_ types in header file */
1704 int format_type, /* local or remote (see citadel.h) */
1705 char *fake_name) /* who we're masquerading as */
1711 struct CtdlMessage *msg;
1713 msg = mallok(sizeof(struct CtdlMessage));
1714 memset(msg, 0, sizeof(struct CtdlMessage));
1715 msg->cm_magic = CTDLMESSAGE_MAGIC;
1716 msg->cm_anon_type = type;
1717 msg->cm_format_type = format_type;
1719 /* Don't confuse the poor folks if it's not routed mail. */
1720 strcpy(dest_node, "");
1722 /* If net_type is MES_BINARY, split out the destination node. */
1723 if (net_type == MES_BINARY) {
1724 strcpy(dest_node, NODENAME);
1725 for (a = 0; a < strlen(recipient); ++a) {
1726 if (recipient[a] == '@') {
1728 strcpy(dest_node, &recipient[a + 1]);
1733 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1734 if (net_type == MES_INTERNET) {
1735 strcpy(dest_node, "internet");
1738 while (isspace(recipient[strlen(recipient) - 1]))
1739 recipient[strlen(recipient) - 1] = 0;
1741 sprintf(buf, "cit%ld", author->usernum); /* Path */
1742 msg->cm_fields['P'] = strdoop(buf);
1744 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1745 msg->cm_fields['T'] = strdoop(buf);
1747 if (fake_name[0]) /* author */
1748 msg->cm_fields['A'] = strdoop(fake_name);
1750 msg->cm_fields['A'] = strdoop(author->fullname);
1752 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1753 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1755 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1757 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1758 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1760 if (recipient[0] != 0)
1761 msg->cm_fields['R'] = strdoop(recipient);
1762 if (dest_node[0] != 0)
1763 msg->cm_fields['D'] = strdoop(dest_node);
1766 msg->cm_fields['M'] = CtdlReadMessageBody("000",
1767 config.c_maxmsglen, NULL);
1778 * message entry - mode 0 (normal)
1780 void cmd_ent0(char *entargs)
1783 char recipient[256];
1785 int format_type = 0;
1786 char newusername[256];
1787 struct CtdlMessage *msg;
1791 struct usersupp tempUS;
1794 post = extract_int(entargs, 0);
1795 extract(recipient, entargs, 1);
1796 anon_flag = extract_int(entargs, 2);
1797 format_type = extract_int(entargs, 3);
1799 /* first check to make sure the request is valid. */
1801 if (!(CC->logged_in)) {
1802 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1805 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1806 cprintf("%d Need to be validated to enter ",
1807 ERROR + HIGHER_ACCESS_REQUIRED);
1808 cprintf("(except in %s> to sysop)\n", MAILROOM);
1811 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1812 cprintf("%d Need net privileges to enter here.\n",
1813 ERROR + HIGHER_ACCESS_REQUIRED);
1816 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1817 cprintf("%d Sorry, this is a read-only room.\n",
1818 ERROR + HIGHER_ACCESS_REQUIRED);
1825 if (CC->usersupp.axlevel < 6) {
1826 cprintf("%d You don't have permission to masquerade.\n",
1827 ERROR + HIGHER_ACCESS_REQUIRED);
1830 extract(newusername, entargs, 4);
1831 memset(CC->fake_postname, 0, 32);
1832 strcpy(CC->fake_postname, newusername);
1833 cprintf("%d Ok\n", OK);
1836 CC->cs_flags |= CS_POSTING;
1839 if (CC->quickroom.QRflags & QR_MAILBOX) {
1840 if (CC->usersupp.axlevel >= 2) {
1841 strcpy(buf, recipient);
1843 strcpy(buf, "sysop");
1844 e = alias(buf); /* alias and mail type */
1845 if ((buf[0] == 0) || (e == MES_ERROR)) {
1846 cprintf("%d Unknown address - cannot send message.\n",
1847 ERROR + NO_SUCH_USER);
1850 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1851 cprintf("%d Net privileges required for network mail.\n",
1852 ERROR + HIGHER_ACCESS_REQUIRED);
1855 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1856 && ((CC->usersupp.flags & US_INTERNET) == 0)
1857 && (!CC->internal_pgm)) {
1858 cprintf("%d You don't have access to Internet mail.\n",
1859 ERROR + HIGHER_ACCESS_REQUIRED);
1862 if (!strcasecmp(buf, "sysop")) {
1867 goto SKFALL; /* don't search local file */
1868 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1869 cprintf("%d Can't send mail to yourself!\n",
1870 ERROR + NO_SUCH_USER);
1873 /* Check to make sure the user exists; also get the correct
1874 * upper/lower casing of the name.
1876 a = getuser(&tempUS, buf);
1878 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1881 strcpy(buf, tempUS.fullname);
1884 SKFALL: b = MES_NORMAL;
1885 if (CC->quickroom.QRflags & QR_ANONONLY)
1887 if (CC->quickroom.QRflags & QR_ANONOPT) {
1891 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1894 /* If we're only checking the validity of the request, return
1895 * success without creating the message.
1898 cprintf("%d %s\n", OK, buf);
1902 cprintf("%d send message\n", SEND_LISTING);
1904 /* Read in the message from the client. */
1905 if (CC->fake_postname[0])
1906 msg = make_message(&CC->usersupp, buf,
1907 CC->quickroom.QRname, b, e, format_type,
1909 else if (CC->fake_username[0])
1910 msg = make_message(&CC->usersupp, buf,
1911 CC->quickroom.QRname, b, e, format_type,
1914 msg = make_message(&CC->usersupp, buf,
1915 CC->quickroom.QRname, b, e, format_type, "");
1918 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1919 CtdlFreeMessage(msg);
1920 CC->fake_postname[0] = '\0';
1927 * message entry - mode 3 (raw)
1929 void cmd_ent3(char *entargs)
1935 unsigned char ch, which_field;
1936 struct usersupp tempUS;
1938 struct CtdlMessage *msg;
1941 if (CC->internal_pgm == 0) {
1942 cprintf("%d This command is for internal programs only.\n",
1947 /* See if there's a recipient, but make sure it's a real one */
1948 extract(recp, entargs, 1);
1949 for (a = 0; a < strlen(recp); ++a)
1950 if (!isprint(recp[a]))
1951 strcpy(&recp[a], &recp[a + 1]);
1952 while (isspace(recp[0]))
1953 strcpy(recp, &recp[1]);
1954 while (isspace(recp[strlen(recp) - 1]))
1955 recp[strlen(recp) - 1] = 0;
1957 /* If we're in Mail, check the recipient */
1958 if (strlen(recp) > 0) {
1959 e = alias(recp); /* alias and mail type */
1960 if ((recp[0] == 0) || (e == MES_ERROR)) {
1961 cprintf("%d Unknown address - cannot send message.\n",
1962 ERROR + NO_SUCH_USER);
1965 if (e == MES_LOCAL) {
1966 a = getuser(&tempUS, recp);
1968 cprintf("%d No such user.\n",
1969 ERROR + NO_SUCH_USER);
1975 /* At this point, message has been approved. */
1976 if (extract_int(entargs, 0) == 0) {
1977 cprintf("%d OK to send\n", OK);
1981 msglen = extract_long(entargs, 2);
1982 msg = mallok(sizeof(struct CtdlMessage));
1984 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1988 memset(msg, 0, sizeof(struct CtdlMessage));
1989 tempbuf = mallok(msglen);
1990 if (tempbuf == NULL) {
1991 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1996 cprintf("%d %ld\n", SEND_BINARY, msglen);
1998 client_read(&ch, 1); /* 0xFF magic number */
1999 msg->cm_magic = CTDLMESSAGE_MAGIC;
2000 client_read(&ch, 1); /* anon type */
2001 msg->cm_anon_type = ch;
2002 client_read(&ch, 1); /* format type */
2003 msg->cm_format_type = ch;
2004 msglen = msglen - 3;
2006 while (msglen > 0) {
2007 client_read(&which_field, 1);
2008 if (!isalpha(which_field)) valid_msg = 0;
2012 client_read(&ch, 1);
2014 a = strlen(tempbuf);
2017 } while ( (ch != 0) && (msglen > 0) );
2019 msg->cm_fields[which_field] = strdoop(tempbuf);
2022 msg->cm_flags = CM_SKIP_HOOKS;
2023 if (valid_msg) CtdlSaveMsg(msg, recp, "", e, 0);
2024 CtdlFreeMessage(msg);
2030 * API function to delete messages which match a set of criteria
2031 * (returns the actual number of messages deleted)
2033 int CtdlDeleteMessages(char *room_name, /* which room */
2034 long dmsgnum, /* or "0" for any */
2035 char *content_type /* or NULL for any */
2039 struct quickroom qrbuf;
2040 struct cdbdata *cdbfr;
2041 long *msglist = NULL;
2044 int num_deleted = 0;
2046 struct SuppMsgInfo smi;
2048 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
2049 room_name, dmsgnum, content_type);
2051 /* get room record, obtaining a lock... */
2052 if (lgetroom(&qrbuf, room_name) != 0) {
2053 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
2055 return (0); /* room not found */
2057 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
2059 if (cdbfr != NULL) {
2060 msglist = mallok(cdbfr->len);
2061 memcpy(msglist, cdbfr->ptr, cdbfr->len);
2062 num_msgs = cdbfr->len / sizeof(long);
2066 for (i = 0; i < num_msgs; ++i) {
2069 /* Set/clear a bit for each criterion */
2071 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
2072 delete_this |= 0x01;
2074 if (content_type == NULL) {
2075 delete_this |= 0x02;
2077 GetSuppMsgInfo(&smi, msglist[i]);
2078 if (!strcasecmp(smi.smi_content_type,
2080 delete_this |= 0x02;
2084 /* Delete message only if all bits are set */
2085 if (delete_this == 0x03) {
2086 AdjRefCount(msglist[i], -1);
2092 num_msgs = sort_msglist(msglist, num_msgs);
2093 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
2094 msglist, (num_msgs * sizeof(long)));
2096 qrbuf.QRhighest = msglist[num_msgs - 1];
2100 lprintf(9, "%d message(s) deleted.\n", num_deleted);
2101 return (num_deleted);
2107 * Delete message from current room
2109 void cmd_dele(char *delstr)
2114 getuser(&CC->usersupp, CC->curr_user);
2115 if ((CC->usersupp.axlevel < 6)
2116 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2117 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2118 && (!(CC->internal_pgm))) {
2119 cprintf("%d Higher access required.\n",
2120 ERROR + HIGHER_ACCESS_REQUIRED);
2123 delnum = extract_long(delstr, 0);
2125 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
2128 cprintf("%d %d message%s deleted.\n", OK,
2129 num_deleted, ((num_deleted != 1) ? "s" : ""));
2131 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2137 * move or copy a message to another room
2139 void cmd_move(char *args)
2143 struct quickroom qtemp;
2147 num = extract_long(args, 0);
2148 extract(targ, args, 1);
2149 targ[ROOMNAMELEN - 1] = 0;
2150 is_copy = extract_int(args, 2);
2152 getuser(&CC->usersupp, CC->curr_user);
2153 if ((CC->usersupp.axlevel < 6)
2154 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2155 cprintf("%d Higher access required.\n",
2156 ERROR + HIGHER_ACCESS_REQUIRED);
2160 if (getroom(&qtemp, targ) != 0) {
2161 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2165 err = CtdlSaveMsgPointerInRoom(targ, num,
2166 (SM_VERIFY_GOODNESS | SM_DO_REPL_CHECK) );
2168 cprintf("%d Cannot store message in %s: error %d\n",
2173 /* Now delete the message from the source room,
2174 * if this is a 'move' rather than a 'copy' operation.
2176 if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
2178 cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") );
2184 * GetSuppMsgInfo() - Get the supplementary record for a message
2186 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2189 struct cdbdata *cdbsmi;
2192 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2193 smibuf->smi_msgnum = msgnum;
2194 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2196 /* Use the negative of the message number for its supp record index */
2197 TheIndex = (0L - msgnum);
2199 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2200 if (cdbsmi == NULL) {
2201 return; /* record not found; go with defaults */
2203 memcpy(smibuf, cdbsmi->ptr,
2204 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2205 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2212 * PutSuppMsgInfo() - (re)write supplementary record for a message
2214 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2218 /* Use the negative of the message number for its supp record index */
2219 TheIndex = (0L - smibuf->smi_msgnum);
2221 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2222 smibuf->smi_msgnum, smibuf->smi_refcount);
2224 cdb_store(CDB_MSGMAIN,
2225 &TheIndex, sizeof(long),
2226 smibuf, sizeof(struct SuppMsgInfo));
2231 * AdjRefCount - change the reference count for a message;
2232 * delete the message if it reaches zero
2234 void AdjRefCount(long msgnum, int incr)
2237 struct SuppMsgInfo smi;
2240 /* This is a *tight* critical section; please keep it that way, as
2241 * it may get called while nested in other critical sections.
2242 * Complicating this any further will surely cause deadlock!
2244 begin_critical_section(S_SUPPMSGMAIN);
2245 GetSuppMsgInfo(&smi, msgnum);
2246 smi.smi_refcount += incr;
2247 PutSuppMsgInfo(&smi);
2248 end_critical_section(S_SUPPMSGMAIN);
2250 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2251 msgnum, smi.smi_refcount);
2253 /* If the reference count is now zero, delete the message
2254 * (and its supplementary record as well).
2256 if (smi.smi_refcount == 0) {
2257 lprintf(9, "Deleting message <%ld>\n", msgnum);
2259 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2260 delnum = (0L - msgnum);
2261 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2266 * Write a generic object to this room
2268 * Note: this could be much more efficient. Right now we use two temporary
2269 * files, and still pull the message into memory as with all others.
2271 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2272 char *content_type, /* MIME type of this object */
2273 char *tempfilename, /* Where to fetch it from */
2274 struct usersupp *is_mailbox, /* Mailbox room? */
2275 int is_binary, /* Is encoding necessary? */
2276 int is_unique, /* Del others of this type? */
2277 unsigned int flags /* Internal save flags */
2282 char filename[PATH_MAX];
2285 struct quickroom qrbuf;
2286 char roomname[ROOMNAMELEN];
2287 struct CtdlMessage *msg;
2290 if (is_mailbox != NULL)
2291 MailboxName(roomname, is_mailbox, req_room);
2293 safestrncpy(roomname, req_room, sizeof(roomname));
2294 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2296 strcpy(filename, tmpnam(NULL));
2297 fp = fopen(filename, "w");
2301 tempfp = fopen(tempfilename, "r");
2302 if (tempfp == NULL) {
2308 fprintf(fp, "Content-type: %s\n", content_type);
2309 lprintf(9, "Content-type: %s\n", content_type);
2311 if (is_binary == 0) {
2312 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2313 while (ch = getc(tempfp), ch > 0)
2319 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2322 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2323 tempfilename, filename);
2327 lprintf(9, "Allocating\n");
2328 msg = mallok(sizeof(struct CtdlMessage));
2329 memset(msg, 0, sizeof(struct CtdlMessage));
2330 msg->cm_magic = CTDLMESSAGE_MAGIC;
2331 msg->cm_anon_type = MES_NORMAL;
2332 msg->cm_format_type = 4;
2333 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2334 msg->cm_fields['O'] = strdoop(req_room);
2335 msg->cm_fields['N'] = strdoop(config.c_nodename);
2336 msg->cm_fields['H'] = strdoop(config.c_humannode);
2337 msg->cm_flags = flags;
2339 lprintf(9, "Loading\n");
2340 fp = fopen(filename, "rb");
2341 fseek(fp, 0L, SEEK_END);
2344 msg->cm_fields['M'] = mallok(len);
2345 fread(msg->cm_fields['M'], len, 1, fp);
2349 /* Create the requested room if we have to. */
2350 if (getroom(&qrbuf, roomname) != 0) {
2351 create_room(roomname, 4, "", 0);
2353 /* If the caller specified this object as unique, delete all
2354 * other objects of this type that are currently in the room.
2357 lprintf(9, "Deleted %d other msgs of this type\n",
2358 CtdlDeleteMessages(roomname, 0L, content_type));
2360 /* Now write the data */
2361 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2362 CtdlFreeMessage(msg);