22 #include "sysdep_decls.h"
23 #include "citserver.h"
28 #include "dynloader.h"
30 #include "mime_parser.h"
33 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
34 #define ma ((struct ma_info *)CtdlGetUserData(SYM_MA_INFO))
35 #define msg_repl ((struct repl *)CtdlGetUserData(SYM_REPL))
37 extern struct config config;
40 "", "", "", "", "", "", "", "",
41 "", "", "", "", "", "", "", "",
42 "", "", "", "", "", "", "", "",
43 "", "", "", "", "", "", "", "",
44 "", "", "", "", "", "", "", "",
45 "", "", "", "", "", "", "", "",
46 "", "", "", "", "", "", "", "",
47 "", "", "", "", "", "", "", "",
73 * This function is self explanatory.
74 * (What can I say, I'm in a weird mood today...)
76 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
80 for (i = 0; i < strlen(name); ++i)
83 if (isspace(name[i - 1])) {
84 strcpy(&name[i - 1], &name[i]);
87 while (isspace(name[i + 1])) {
88 strcpy(&name[i + 1], &name[i + 2]);
95 * Aliasing for network mail.
96 * (Error messages have been commented out, because this is a server.)
99 { /* process alias and routing info for mail */
102 char aaa[300], bbb[300];
104 lprintf(9, "alias() called for <%s>\n", name);
106 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
108 fp = fopen("network/mail.aliases", "r");
110 fp = fopen("/dev/null", "r");
115 while (fgets(aaa, sizeof aaa, fp) != NULL) {
116 while (isspace(name[0]))
117 strcpy(name, &name[1]);
118 aaa[strlen(aaa) - 1] = 0;
120 for (a = 0; a < strlen(aaa); ++a) {
122 strcpy(bbb, &aaa[a + 1]);
126 if (!strcasecmp(name, aaa))
130 lprintf(7, "Mail is being forwarded to %s\n", name);
132 /* determine local or remote type, see citadel.h */
133 for (a = 0; a < strlen(name); ++a)
135 return (MES_INTERNET);
136 for (a = 0; a < strlen(name); ++a)
138 for (b = a; b < strlen(name); ++b)
140 return (MES_INTERNET);
142 for (a = 0; a < strlen(name); ++a)
146 lprintf(7, "Too many @'s in address\n");
150 for (a = 0; a < strlen(name); ++a)
152 strcpy(bbb, &name[a + 1]);
154 strcpy(bbb, &bbb[1]);
155 fp = fopen("network/mail.sysinfo", "r");
159 a = getstring(fp, aaa);
160 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
161 a = getstring(fp, aaa);
162 if (!strncmp(aaa, "use ", 4)) {
163 strcpy(bbb, &aaa[4]);
168 if (!strncmp(aaa, "uum", 3)) {
170 for (a = 0; a < strlen(bbb); ++a) {
176 while (bbb[strlen(bbb) - 1] == '_')
177 bbb[strlen(bbb) - 1] = 0;
178 sprintf(name, &aaa[4], bbb);
179 return (MES_INTERNET);
181 if (!strncmp(aaa, "bin", 3)) {
184 while (aaa[strlen(aaa) - 1] != '@')
185 aaa[strlen(aaa) - 1] = 0;
186 aaa[strlen(aaa) - 1] = 0;
187 while (aaa[strlen(aaa) - 1] == ' ')
188 aaa[strlen(aaa) - 1] = 0;
189 while (bbb[0] != '@')
190 strcpy(bbb, &bbb[1]);
191 strcpy(bbb, &bbb[1]);
192 while (bbb[0] == ' ')
193 strcpy(bbb, &bbb[1]);
194 sprintf(name, "%s @%s", aaa, bbb);
207 fp = fopen("citadel.control", "r");
208 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
214 void simple_listing(long msgnum)
216 cprintf("%ld\n", msgnum);
221 /* Determine if a given message matches the fields in a message template.
222 * Return 0 for a successful match.
224 int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
227 /* If there aren't any fields in the template, all messages will
230 if (template == NULL) return(0);
232 /* Null messages are bogus. */
233 if (msg == NULL) return(1);
235 for (i='A'; i<='Z'; ++i) {
236 if (template->cm_fields[i] != NULL) {
237 if (msg->cm_fields[i] == NULL) {
240 if (strcasecmp(msg->cm_fields[i],
241 template->cm_fields[i])) return 1;
245 /* All compares succeeded: we have a match! */
253 * API function to perform an operation for each qualifying message in the
256 void CtdlForEachMessage(int mode, long ref,
258 struct CtdlMessage *compare,
259 void (*CallBack) (long msgnum))
264 struct cdbdata *cdbfr;
265 long *msglist = NULL;
268 struct SuppMsgInfo smi;
269 struct CtdlMessage *msg;
271 /* Learn about the user and room in question */
273 getuser(&CC->usersupp, CC->curr_user);
274 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
276 /* Load the message list */
277 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
279 msglist = mallok(cdbfr->len);
280 memcpy(msglist, cdbfr->ptr, cdbfr->len);
281 num_msgs = cdbfr->len / sizeof(long);
284 return; /* No messages at all? No further action. */
288 /* If the caller is looking for a specific MIME type, then filter
289 * out all messages which are not of the type requested.
292 if (content_type != NULL)
293 if (strlen(content_type) > 0)
294 for (a = 0; a < num_msgs; ++a) {
295 GetSuppMsgInfo(&smi, msglist[a]);
296 if (strcasecmp(smi.smi_content_type, content_type)) {
301 num_msgs = sort_msglist(msglist, num_msgs);
303 /* If a template was supplied, filter out the messages which
304 * don't match. (This could induce some delays!)
307 if (compare != NULL) {
308 for (a = 0; a < num_msgs; ++a) {
309 msg = CtdlFetchMessage(msglist[a]);
311 if (CtdlMsgCmp(msg, compare)) {
314 CtdlFreeMessage(msg);
322 * Now iterate through the message list, according to the
323 * criteria supplied by the caller.
326 for (a = 0; a < num_msgs; ++a) {
327 thismsg = msglist[a];
332 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
333 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
334 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
335 && (CC->usersupp.flags & US_LASTOLD))
336 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
337 || ((mode == MSGS_FIRST) && (a < ref))
338 || ((mode == MSGS_GT) && (thismsg > ref))
344 phree(msglist); /* Clean up */
350 * cmd_msgs() - get list of message #'s in this room
351 * implements the MSGS server command using CtdlForEachMessage()
353 void cmd_msgs(char *cmdbuf)
362 int with_template = 0;
363 struct CtdlMessage *template = NULL;
365 extract(which, cmdbuf, 0);
366 cm_ref = extract_int(cmdbuf, 1);
367 with_template = extract_int(cmdbuf, 2);
371 if (!strncasecmp(which, "OLD", 3))
373 else if (!strncasecmp(which, "NEW", 3))
375 else if (!strncasecmp(which, "FIRST", 5))
377 else if (!strncasecmp(which, "LAST", 4))
379 else if (!strncasecmp(which, "GT", 2))
382 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
383 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
388 cprintf("%d Send template then receive message list\n",
390 template = (struct CtdlMessage *)
391 mallok(sizeof(struct CtdlMessage));
392 memset(template, 0, sizeof(struct CtdlMessage));
393 while(client_gets(buf), strcmp(buf,"000")) {
394 extract(tfield, buf, 0);
395 extract(tvalue, buf, 1);
396 for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
397 if (!strcasecmp(tfield, msgkeys[i])) {
398 template->cm_fields[i] =
405 cprintf("%d Message list...\n", LISTING_FOLLOWS);
408 CtdlForEachMessage(mode, cm_ref, NULL, template, simple_listing);
409 if (template != NULL) CtdlFreeMessage(template);
417 * help_subst() - support routine for help file viewer
419 void help_subst(char *strbuf, char *source, char *dest)
424 while (p = pattern2(strbuf, source), (p >= 0)) {
425 strcpy(workbuf, &strbuf[p + strlen(source)]);
426 strcpy(&strbuf[p], dest);
427 strcat(strbuf, workbuf);
432 void do_help_subst(char *buffer)
436 help_subst(buffer, "^nodename", config.c_nodename);
437 help_subst(buffer, "^humannode", config.c_humannode);
438 help_subst(buffer, "^fqdn", config.c_fqdn);
439 help_subst(buffer, "^username", CC->usersupp.fullname);
440 sprintf(buf2, "%ld", CC->usersupp.usernum);
441 help_subst(buffer, "^usernum", buf2);
442 help_subst(buffer, "^sysadm", config.c_sysadm);
443 help_subst(buffer, "^variantname", CITADEL);
444 sprintf(buf2, "%d", config.c_maxsessions);
445 help_subst(buffer, "^maxsessions", buf2);
451 * memfmout() - Citadel text formatter and paginator.
452 * Although the original purpose of this routine was to format
453 * text to the reader's screen width, all we're really using it
454 * for here is to format text out to 80 columns before sending it
455 * to the client. The client software may reformat it again.
457 void memfmout(int width, char *mptr, char subst)
458 /* screen width to use */
459 /* where are we going to get our text from? */
460 /* nonzero if we should use hypertext mode */
472 c = 1; /* c is the current pos */
475 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
477 buffer[strlen(buffer) + 1] = 0;
478 buffer[strlen(buffer)] = ch;
481 if (buffer[0] == '^')
482 do_help_subst(buffer);
484 buffer[strlen(buffer) + 1] = 0;
486 strcpy(buffer, &buffer[1]);
496 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
498 if (((old == 13) || (old == 10)) && (isspace(real))) {
506 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
507 cprintf("\n%s", aaa);
516 if ((strlen(aaa) + c) > (width - 5)) {
526 if ((ch == 13) || (ch == 10)) {
527 cprintf("%s\n", aaa);
534 FMTEND: cprintf("%s\n", aaa);
540 * Callback function for mime parser that simply lists the part
542 void list_this_part(char *name, char *filename, char *partnum, char *disp,
543 void *content, char *cbtype, size_t length)
546 cprintf("part=%s|%s|%s|%s|%s|%d\n",
547 name, filename, partnum, disp, cbtype, length);
552 * Callback function for mime parser that wants to display text
554 void fixed_output(char *name, char *filename, char *partnum, char *disp,
555 void *content, char *cbtype, size_t length)
559 if (!strcasecmp(cbtype, "multipart/alternative")) {
560 strcpy(ma->prefix, partnum);
561 strcat(ma->prefix, ".");
567 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
569 && (ma->did_print == 1) ) {
570 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
576 if (!strcasecmp(cbtype, "text/plain")) {
577 client_write(content, length);
579 else if (!strcasecmp(cbtype, "text/html")) {
580 ptr = html_to_ascii(content, 80, 0);
581 client_write(ptr, strlen(ptr));
584 else if (strncasecmp(cbtype, "multipart/", 10)) {
585 cprintf("Part %s: %s (%s) (%d bytes)\n",
586 partnum, filename, cbtype, length);
592 * Callback function for mime parser that opens a section for downloading
594 void mime_download(char *name, char *filename, char *partnum, char *disp,
595 void *content, char *cbtype, size_t length)
598 /* Silently go away if there's already a download open... */
599 if (CC->download_fp != NULL)
602 /* ...or if this is not the desired section */
603 if (strcasecmp(desired_section, partnum))
606 CC->download_fp = tmpfile();
607 if (CC->download_fp == NULL)
610 fwrite(content, length, 1, CC->download_fp);
611 fflush(CC->download_fp);
612 rewind(CC->download_fp);
614 OpenCmdResult(filename, cbtype);
620 * Load a message from disk into memory.
621 * This is used by output_message() and other fetch functions.
623 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
624 * using the CtdlMessageFree() function.
626 struct CtdlMessage *CtdlFetchMessage(long msgnum)
628 struct cdbdata *dmsgtext;
629 struct CtdlMessage *ret = NULL;
632 CIT_UBYTE field_header;
636 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
637 if (dmsgtext == NULL) {
638 lprintf(9, "CtdlFetchMessage(%ld) failed.\n");
641 mptr = dmsgtext->ptr;
643 /* Parse the three bytes that begin EVERY message on disk.
644 * The first is always 0xFF, the on-disk magic number.
645 * The second is the anonymous/public type byte.
646 * The third is the format type byte (vari, fixed, or MIME).
650 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
654 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
655 memset(ret, 0, sizeof(struct CtdlMessage));
657 ret->cm_magic = CTDLMESSAGE_MAGIC;
658 ret->cm_anon_type = *mptr++; /* Anon type byte */
659 ret->cm_format_type = *mptr++; /* Format type byte */
662 * The rest is zero or more arbitrary fields. Load them in.
663 * We're done when we encounter either a zero-length field or
664 * have just processed the 'M' (message text) field.
667 field_length = strlen(mptr);
668 if (field_length == 0)
670 field_header = *mptr++;
671 ret->cm_fields[field_header] = mallok(field_length);
672 strcpy(ret->cm_fields[field_header], mptr);
674 while (*mptr++ != 0); /* advance to next field */
676 } while ((field_length > 0) && (field_header != 'M'));
680 /* Perform "before read" hooks (aborting if any return nonzero) */
681 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
682 CtdlFreeMessage(ret);
691 * Returns 1 if the supplied pointer points to a valid Citadel message.
692 * If the pointer is NULL or the magic number check fails, returns 0.
694 int is_valid_message(struct CtdlMessage *msg) {
697 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
698 lprintf(3, "is_valid_message() -- self-check failed\n");
706 * 'Destructor' for struct CtdlMessage
708 void CtdlFreeMessage(struct CtdlMessage *msg)
712 if (is_valid_message(msg) == 0) return;
714 for (i = 0; i < 256; ++i)
715 if (msg->cm_fields[i] != NULL)
716 phree(msg->cm_fields[i]);
718 msg->cm_magic = 0; /* just in case */
725 * Get a message off disk. (return value is the message's timestamp)
728 void output_message(char *msgid, int mode, int headers_only)
736 char display_name[256];
738 struct CtdlMessage *TheMessage = NULL;
742 /* buffers needed for RFC822 translation */
750 msg_num = atol(msgid);
752 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
753 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
757 /* FIX ... small security issue
758 * We need to check to make sure the requested message is actually
759 * in the current room, and set msg_ok to 1 only if it is. This
760 * functionality is currently missing because I'm in a hurry to replace
761 * broken production code with nonbroken pre-beta code. :( -- ajc
764 cprintf("%d Message %ld is not in this room.\n",
771 * Fetch the message from disk
773 TheMessage = CtdlFetchMessage(msg_num);
774 if (TheMessage == NULL) {
775 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
779 /* Are we downloading a MIME component? */
780 if (mode == MT_DOWNLOAD) {
781 if (TheMessage->cm_format_type != 4) {
782 cprintf("%d This is not a MIME message.\n",
784 } else if (CC->download_fp != NULL) {
785 cprintf("%d You already have a download open.\n",
788 /* Parse the message text component */
789 mptr = TheMessage->cm_fields['M'];
790 mime_parser(mptr, NULL, *mime_download);
791 /* If there's no file open by this time, the requested
792 * section wasn't found, so print an error
794 if (CC->download_fp == NULL) {
795 cprintf("%d Section %s not found.\n",
796 ERROR + FILE_NOT_FOUND,
800 CtdlFreeMessage(TheMessage);
804 /* now for the user-mode message reading loops */
805 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
807 /* Tell the client which format type we're using. If this is a
808 * MIME message, *lie* about it and tell the user it's fixed-format.
810 if (mode == MT_CITADEL) {
811 if (TheMessage->cm_format_type == 4)
814 cprintf("type=%d\n", TheMessage->cm_format_type);
817 /* nhdr=yes means that we're only displaying headers, no body */
818 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
819 cprintf("nhdr=yes\n");
822 /* begin header processing loop for Citadel message format */
824 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
826 strcpy(display_name, "<unknown>");
827 if (TheMessage->cm_fields['A']) {
828 strcpy(buf, TheMessage->cm_fields['A']);
829 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
830 if (TheMessage->cm_anon_type == MES_ANON)
831 strcpy(display_name, "****");
832 else if (TheMessage->cm_anon_type == MES_AN2)
833 strcpy(display_name, "anonymous");
835 strcpy(display_name, buf);
837 && ((TheMessage->cm_anon_type == MES_ANON)
838 || (TheMessage->cm_anon_type == MES_AN2))) {
839 sprintf(&display_name[strlen(display_name)],
844 strcpy(allkeys, FORDER);
845 for (i=0; i<strlen(allkeys); ++i) {
846 k = (int) allkeys[i];
848 if (TheMessage->cm_fields[k] != NULL) {
857 TheMessage->cm_fields[k]
866 /* begin header processing loop for RFC822 transfer format */
870 strcpy(snode, NODENAME);
871 strcpy(lnode, HUMANNODE);
872 if (mode == MT_RFC822) {
873 for (i = 0; i < 256; ++i) {
874 if (TheMessage->cm_fields[i]) {
875 mptr = TheMessage->cm_fields[i];
879 } else if (i == 'P') {
880 cprintf("Path: %s\n", mptr);
881 for (a = 0; a < strlen(mptr); ++a) {
882 if (mptr[a] == '!') {
883 strcpy(mptr, &mptr[a + 1]);
889 cprintf("Subject: %s\n", mptr);
895 cprintf("X-Citadel-Room: %s\n", mptr);
899 cprintf("To: %s\n", mptr);
902 cprintf("Date: %s", asctime(localtime(&xtime)));
908 if (mode == MT_RFC822) {
909 if (!strcasecmp(snode, NODENAME)) {
912 cprintf("Message-ID: <%s@%s>\n", mid, snode);
913 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
914 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
915 cprintf("Organization: %s\n", lnode);
918 /* end header processing loop ... at this point, we're in the text */
920 mptr = TheMessage->cm_fields['M'];
922 /* Tell the client about the MIME parts in this message */
923 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
924 if (mode == MT_CITADEL) {
925 mime_parser(mptr, NULL, *list_this_part);
927 else if (mode == MT_MIME) { /* list parts only */
928 mime_parser(mptr, NULL, *list_this_part);
930 CtdlFreeMessage(TheMessage);
937 CtdlFreeMessage(TheMessage);
941 /* signify start of msg text */
942 if (mode == MT_CITADEL)
944 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
947 /* If the format type on disk is 1 (fixed-format), then we want
948 * everything to be output completely literally ... regardless of
949 * what message transfer format is in use.
951 if (TheMessage->cm_format_type == 1) {
953 while (ch = *mptr++, ch > 0) {
956 if ((ch == 10) || (strlen(buf) > 250)) {
957 cprintf("%s\n", buf);
960 buf[strlen(buf) + 1] = 0;
961 buf[strlen(buf)] = ch;
965 cprintf("%s\n", buf);
968 /* If the message on disk is format 0 (Citadel vari-format), we
969 * output using the formatter at 80 columns. This is the final output
970 * form if the transfer format is RFC822, but if the transfer format
971 * is Citadel proprietary, it'll still work, because the indentation
972 * for new paragraphs is correct and the client will reformat the
973 * message to the reader's screen width.
975 if (TheMessage->cm_format_type == 0) {
976 memfmout(80, mptr, 0);
979 /* If the message on disk is format 4 (MIME), we've gotta hand it
980 * off to the MIME parser. The client has already been told that
981 * this message is format 1 (fixed format), so the callback function
982 * we use will display those parts as-is.
984 if (TheMessage->cm_format_type == 4) {
985 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
986 memset(ma, 0, sizeof(struct ma_info));
987 mime_parser(mptr, NULL, *fixed_output);
992 CtdlFreeMessage(TheMessage);
999 * display a message (mode 0 - Citadel proprietary)
1001 void cmd_msg0(char *cmdbuf)
1004 int headers_only = 0;
1006 extract(msgid, cmdbuf, 0);
1007 headers_only = extract_int(cmdbuf, 1);
1009 output_message(msgid, MT_CITADEL, headers_only);
1015 * display a message (mode 2 - RFC822)
1017 void cmd_msg2(char *cmdbuf)
1020 int headers_only = 0;
1022 extract(msgid, cmdbuf, 0);
1023 headers_only = extract_int(cmdbuf, 1);
1025 output_message(msgid, MT_RFC822, headers_only);
1031 * display a message (mode 3 - IGnet raw format - internal programs only)
1033 void cmd_msg3(char *cmdbuf)
1036 struct CtdlMessage *msg;
1039 if (CC->internal_pgm == 0) {
1040 cprintf("%d This command is for internal programs only.\n",
1045 msgnum = extract_long(cmdbuf, 0);
1046 msg = CtdlFetchMessage(msgnum);
1048 cprintf("%d Message %ld not found.\n",
1053 serialize_message(&smr, msg);
1054 CtdlFreeMessage(msg);
1057 cprintf("%d Unable to serialize message\n",
1058 ERROR+INTERNAL_ERROR);
1062 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1063 client_write(smr.ser, smr.len);
1070 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
1072 void cmd_msg4(char *cmdbuf)
1076 extract(msgid, cmdbuf, 0);
1078 output_message(msgid, MT_MIME, 0);
1082 * Open a component of a MIME message as a download file
1084 void cmd_opna(char *cmdbuf)
1088 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
1090 extract(msgid, cmdbuf, 0);
1091 extract(desired_section, cmdbuf, 1);
1093 output_message(msgid, MT_DOWNLOAD, 0);
1098 * Save a message pointer into a specified room
1099 * (Returns 0 for success, nonzero for failure)
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 if (lgetroom(&CC->quickroom,
1124 ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1126 lprintf(9, "No such room <%s>\n", roomname);
1127 if (msg != NULL) CtdlFreeMessage(msg);
1128 return(ERROR + ROOM_NOT_FOUND);
1131 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
1132 if (cdbfr == NULL) {
1136 msglist = mallok(cdbfr->len);
1137 if (msglist == NULL)
1138 lprintf(3, "ERROR malloc msglist!\n");
1139 num_msgs = cdbfr->len / sizeof(long);
1140 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1145 /* Make sure the message doesn't already exist in this room. It
1146 * is absolutely taboo to have more than one reference to the same
1147 * message in a room.
1149 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1150 if (msglist[i] == msgid) {
1151 lputroom(&CC->quickroom); /* unlock the room */
1152 getroom(&CC->quickroom, hold_rm);
1153 if (msg != NULL) CtdlFreeMessage(msg);
1154 return(ERROR + ALREADY_EXISTS);
1158 /* Perform replication checks if necessary */
1159 if ( (flags & SM_DO_REPL_CHECK) && (msg != NULL) ) {
1160 if (ReplicationChecks(msg) != 0) {
1161 lputroom(&CC->quickroom); /* unlock the room */
1162 getroom(&CC->quickroom, hold_rm);
1163 if (msg != NULL) CtdlFreeMessage(msg);
1164 lprintf(9, "Did replication, and newer exists\n");
1169 /* Now add the new message */
1171 msglist = reallok(msglist,
1172 (num_msgs * sizeof(long)));
1174 if (msglist == NULL) {
1175 lprintf(3, "ERROR: can't realloc message list!\n");
1177 msglist[num_msgs - 1] = msgid;
1179 /* Sort the message list, so all the msgid's are in order */
1180 num_msgs = sort_msglist(msglist, num_msgs);
1182 /* Determine the highest message number */
1183 highest_msg = msglist[num_msgs - 1];
1185 /* Write it back to disk. */
1186 cdb_store(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long),
1187 msglist, num_msgs * sizeof(long));
1189 /* Free up the memory we used. */
1192 /* Update the highest-message pointer and unlock the room. */
1193 CC->quickroom.QRhighest = highest_msg;
1194 lputroom(&CC->quickroom);
1195 getroom(&CC->quickroom, hold_rm);
1197 /* Bump the reference count for this message. */
1198 AdjRefCount(msgid, +1);
1200 /* Return success. */
1201 lprintf(9, "CtdlSaveMsgPointerInRoom() succeeded\n");
1202 if (msg != NULL) CtdlFreeMessage(msg);
1209 * Message base operation to send a message to the master file
1210 * (returns new message number)
1212 * This is the back end for CtdlSaveMsg() and should not be directly
1213 * called by server-side modules.
1216 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1217 int generate_id, /* generate 'I' field? */
1218 FILE *save_a_copy) /* save a copy to disk? */
1225 /* Get a new message number */
1226 newmsgid = get_new_message_number();
1227 sprintf(msgidbuf, "%ld", newmsgid);
1230 msg->cm_fields['I'] = strdoop(msgidbuf);
1233 serialize_message(&smr, msg);
1236 cprintf("%d Unable to serialize message\n",
1237 ERROR+INTERNAL_ERROR);
1241 /* Write our little bundle of joy into the message base */
1242 begin_critical_section(S_MSGMAIN);
1243 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1244 smr.ser, smr.len) < 0) {
1245 lprintf(2, "Can't store message\n");
1250 end_critical_section(S_MSGMAIN);
1252 /* If the caller specified that a copy should be saved to a particular
1253 * file handle, do that now too.
1255 if (save_a_copy != NULL) {
1256 fwrite(smr.ser, smr.len, 1, save_a_copy);
1259 /* Free the memory we used for the serialized message */
1262 /* Return the *local* message ID to the caller
1263 * (even if we're storing an incoming network message)
1271 * Serialize a struct CtdlMessage into the format used on disk and network.
1273 * This function loads up a "struct ser_ret" (defined in server.h) which
1274 * contains the length of the serialized message and a pointer to the
1275 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1277 void serialize_message(struct ser_ret *ret, /* return values */
1278 struct CtdlMessage *msg) /* unserialized msg */
1282 static char *forder = FORDER;
1284 lprintf(9, "serialize_message() called\n");
1286 if (is_valid_message(msg) == 0) return; /* self check */
1288 lprintf(9, "magic number check OK.\n");
1291 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1292 ret->len = ret->len +
1293 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1295 lprintf(9, "message is %d bytes\n", ret->len);
1297 lprintf(9, "calling malloc\n");
1298 ret->ser = mallok(ret->len);
1299 if (ret->ser == NULL) {
1305 ret->ser[1] = msg->cm_anon_type;
1306 ret->ser[2] = msg->cm_format_type;
1309 lprintf(9, "stuff\n");
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",
1317 lprintf(9, "done serializing\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, "Returning %d\n", abort_this);
1395 * Save a message to disk
1397 void 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; /* 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 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 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1432 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1433 if (isspace(msg->cm_fields['P'][a])) {
1434 msg->cm_fields['P'][a] = ' ';
1439 strcpy(force_room, force);
1441 /* Strip non-printable characters out of the recipient name */
1442 strcpy(recipient, rec);
1443 for (a = 0; a < strlen(recipient); ++a)
1444 if (!isprint(recipient[a]))
1445 strcpy(&recipient[a], &recipient[a + 1]);
1447 /* Learn about what's inside, because it's what's inside that counts */
1449 switch (msg->cm_format_type) {
1451 strcpy(content_type, "text/x-citadel-variformat");
1454 strcpy(content_type, "text/plain");
1457 strcpy(content_type, "text/plain");
1458 /* advance past header fields */
1459 mptr = msg->cm_fields['M'];
1462 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1463 safestrncpy(content_type, mptr,
1464 sizeof(content_type));
1465 strcpy(content_type, &content_type[14]);
1466 for (a = 0; a < strlen(content_type); ++a)
1467 if ((content_type[a] == ';')
1468 || (content_type[a] == ' ')
1469 || (content_type[a] == 13)
1470 || (content_type[a] == 10))
1471 content_type[a] = 0;
1478 /* Perform "before save" hooks (aborting if any return nonzero) */
1479 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1481 /* If this message has an Extended ID, perform replication checks */
1482 if (ReplicationChecks(msg) > 0) return;
1484 /* Network mail - send a copy to the network program. */
1485 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1486 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1487 (long) getpid(), CC->cs_pid, ++seqnum);
1488 lprintf(9, "Saving a copy to %s\n", aaa);
1489 network_fp = fopen(aaa, "ab+");
1490 if (network_fp == NULL)
1491 lprintf(2, "ERROR: %s\n", strerror(errno));
1494 /* Save it to disk */
1495 newmsgid = send_message(msg, generate_id, network_fp);
1496 if (network_fp != NULL) {
1498 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1501 if (newmsgid <= 0L) return;
1503 /* Write a supplemental message info record. This doesn't have to
1504 * be a critical section because nobody else knows about this message
1507 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1508 smi.smi_msgnum = newmsgid;
1509 smi.smi_refcount = 0;
1510 safestrncpy(smi.smi_content_type, content_type, 64);
1511 PutSuppMsgInfo(&smi);
1513 /* Now figure out where to store the pointers */
1515 strcpy(actual_rm, CC->quickroom.QRname);
1517 /* If this is being done by the networker delivering a private
1518 * message, we want to BYPASS saving the sender's copy (because there
1519 * is no local sender; it would otherwise go to the Trashcan).
1521 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1522 /* If the user is a twit, move to the twit room for posting */
1524 if (CC->usersupp.axlevel == 2) {
1525 strcpy(hold_rm, actual_rm);
1526 strcpy(actual_rm, config.c_twitroom);
1529 /* ...or if this message is destined for Aide> then go there. */
1530 if (strlen(force_room) > 0) {
1531 strcpy(actual_rm, force_room);
1533 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1536 /* Bump this user's messages posted counter. */
1537 lgetuser(&CC->usersupp, CC->curr_user);
1538 CC->usersupp.posted = CC->usersupp.posted + 1;
1539 lputuser(&CC->usersupp);
1541 /* If this is private, local mail, make a copy in the
1542 * recipient's mailbox and bump the reference count.
1544 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1545 if (getuser(&userbuf, recipient) == 0) {
1546 MailboxName(actual_rm, &userbuf, MAILROOM);
1547 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1551 /* Perform "after save" hooks */
1552 PerformMessageHooks(msg, EVT_AFTERSAVE);
1558 * Convenience function for generating small administrative messages.
1560 void quickie_message(char *from, char *to, char *room, char *text)
1562 struct CtdlMessage *msg;
1564 msg = mallok(sizeof(struct CtdlMessage));
1565 memset(msg, 0, sizeof(struct CtdlMessage));
1566 msg->cm_magic = CTDLMESSAGE_MAGIC;
1567 msg->cm_anon_type = MES_NORMAL;
1568 msg->cm_format_type = 0;
1569 msg->cm_fields['A'] = strdoop(from);
1570 msg->cm_fields['O'] = strdoop(room);
1571 msg->cm_fields['N'] = strdoop(NODENAME);
1573 msg->cm_fields['R'] = strdoop(to);
1574 msg->cm_fields['M'] = strdoop(text);
1576 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1577 CtdlFreeMessage(msg);
1578 syslog(LOG_NOTICE, text);
1583 * Build a binary message to be saved on disk.
1586 struct CtdlMessage *make_message(
1587 struct usersupp *author, /* author's usersupp structure */
1588 char *recipient, /* NULL if it's not mail */
1589 char *room, /* room where it's going */
1590 int type, /* see MES_ types in header file */
1591 int net_type, /* see MES_ types in header file */
1592 int format_type, /* local or remote (see citadel.h) */
1593 char *fake_name) /* who we're masquerading as */
1599 size_t message_len = 0;
1600 size_t buffer_len = 0;
1602 struct CtdlMessage *msg;
1604 msg = mallok(sizeof(struct CtdlMessage));
1605 memset(msg, 0, sizeof(struct CtdlMessage));
1606 msg->cm_magic = CTDLMESSAGE_MAGIC;
1607 msg->cm_anon_type = type;
1608 msg->cm_format_type = format_type;
1610 /* Don't confuse the poor folks if it's not routed mail. */
1611 strcpy(dest_node, "");
1613 /* If net_type is MES_BINARY, split out the destination node. */
1614 if (net_type == MES_BINARY) {
1615 strcpy(dest_node, NODENAME);
1616 for (a = 0; a < strlen(recipient); ++a) {
1617 if (recipient[a] == '@') {
1619 strcpy(dest_node, &recipient[a + 1]);
1624 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1625 if (net_type == MES_INTERNET) {
1626 strcpy(dest_node, "internet");
1629 while (isspace(recipient[strlen(recipient) - 1]))
1630 recipient[strlen(recipient) - 1] = 0;
1632 sprintf(buf, "cit%ld", author->usernum); /* Path */
1633 msg->cm_fields['P'] = strdoop(buf);
1635 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1636 msg->cm_fields['T'] = strdoop(buf);
1638 if (fake_name[0]) /* author */
1639 msg->cm_fields['A'] = strdoop(fake_name);
1641 msg->cm_fields['A'] = strdoop(author->fullname);
1643 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1644 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1646 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1648 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1649 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1651 if (recipient[0] != 0)
1652 msg->cm_fields['R'] = strdoop(recipient);
1653 if (dest_node[0] != 0)
1654 msg->cm_fields['D'] = strdoop(dest_node);
1656 msg->cm_fields['M'] = mallok(4096);
1657 if (msg->cm_fields['M'] == NULL) {
1658 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1662 msg->cm_fields['M'][0] = 0;
1666 /* read in the lines of message text one by one */
1667 while (client_gets(buf), strcmp(buf, "000")) {
1669 /* augment the buffer if we have to */
1670 if ((message_len + strlen(buf) + 2) > buffer_len) {
1671 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1672 if (ptr == NULL) { /* flush if can't allocate */
1673 while (client_gets(buf), strcmp(buf, "000")) ;;
1676 buffer_len = (buffer_len * 2);
1677 msg->cm_fields['M'] = ptr;
1681 strcat(msg->cm_fields['M'], buf);
1682 strcat(msg->cm_fields['M'], "\n");
1684 /* if we've hit the max msg length, flush the rest */
1685 if (message_len >= config.c_maxmsglen) {
1686 while (client_gets(buf), strcmp(buf, "000")) ;;
1699 * message entry - mode 0 (normal)
1701 void cmd_ent0(char *entargs)
1704 char recipient[256];
1706 int format_type = 0;
1707 char newusername[256];
1708 struct CtdlMessage *msg;
1712 struct usersupp tempUS;
1715 post = extract_int(entargs, 0);
1716 extract(recipient, entargs, 1);
1717 anon_flag = extract_int(entargs, 2);
1718 format_type = extract_int(entargs, 3);
1720 /* first check to make sure the request is valid. */
1722 if (!(CC->logged_in)) {
1723 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1726 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1727 cprintf("%d Need to be validated to enter ",
1728 ERROR + HIGHER_ACCESS_REQUIRED);
1729 cprintf("(except in %s> to sysop)\n", MAILROOM);
1732 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1733 cprintf("%d Need net privileges to enter here.\n",
1734 ERROR + HIGHER_ACCESS_REQUIRED);
1737 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1738 cprintf("%d Sorry, this is a read-only room.\n",
1739 ERROR + HIGHER_ACCESS_REQUIRED);
1746 if (CC->usersupp.axlevel < 6) {
1747 cprintf("%d You don't have permission to masquerade.\n",
1748 ERROR + HIGHER_ACCESS_REQUIRED);
1751 extract(newusername, entargs, 4);
1752 memset(CC->fake_postname, 0, 32);
1753 strcpy(CC->fake_postname, newusername);
1754 cprintf("%d Ok\n", OK);
1757 CC->cs_flags |= CS_POSTING;
1760 if (CC->quickroom.QRflags & QR_MAILBOX) {
1761 if (CC->usersupp.axlevel >= 2) {
1762 strcpy(buf, recipient);
1764 strcpy(buf, "sysop");
1765 e = alias(buf); /* alias and mail type */
1766 if ((buf[0] == 0) || (e == MES_ERROR)) {
1767 cprintf("%d Unknown address - cannot send message.\n",
1768 ERROR + NO_SUCH_USER);
1771 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1772 cprintf("%d Net privileges required for network mail.\n",
1773 ERROR + HIGHER_ACCESS_REQUIRED);
1776 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1777 && ((CC->usersupp.flags & US_INTERNET) == 0)
1778 && (!CC->internal_pgm)) {
1779 cprintf("%d You don't have access to Internet mail.\n",
1780 ERROR + HIGHER_ACCESS_REQUIRED);
1783 if (!strcasecmp(buf, "sysop")) {
1788 goto SKFALL; /* don't search local file */
1789 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1790 cprintf("%d Can't send mail to yourself!\n",
1791 ERROR + NO_SUCH_USER);
1794 /* Check to make sure the user exists; also get the correct
1795 * upper/lower casing of the name.
1797 a = getuser(&tempUS, buf);
1799 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1802 strcpy(buf, tempUS.fullname);
1805 SKFALL: b = MES_NORMAL;
1806 if (CC->quickroom.QRflags & QR_ANONONLY)
1808 if (CC->quickroom.QRflags & QR_ANONOPT) {
1812 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1815 /* If we're only checking the validity of the request, return
1816 * success without creating the message.
1819 cprintf("%d %s\n", OK, buf);
1823 cprintf("%d send message\n", SEND_LISTING);
1825 /* Read in the message from the client. */
1826 if (CC->fake_postname[0])
1827 msg = make_message(&CC->usersupp, buf,
1828 CC->quickroom.QRname, b, e, format_type,
1830 else if (CC->fake_username[0])
1831 msg = make_message(&CC->usersupp, buf,
1832 CC->quickroom.QRname, b, e, format_type,
1835 msg = make_message(&CC->usersupp, buf,
1836 CC->quickroom.QRname, b, e, format_type, "");
1839 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1840 CtdlFreeMessage(msg);
1841 CC->fake_postname[0] = '\0';
1848 * message entry - mode 3 (raw)
1850 void cmd_ent3(char *entargs)
1855 unsigned char ch, which_field;
1856 struct usersupp tempUS;
1858 struct CtdlMessage *msg;
1861 if (CC->internal_pgm == 0) {
1862 cprintf("%d This command is for internal programs only.\n",
1867 /* See if there's a recipient, but make sure it's a real one */
1868 extract(recp, entargs, 1);
1869 for (a = 0; a < strlen(recp); ++a)
1870 if (!isprint(recp[a]))
1871 strcpy(&recp[a], &recp[a + 1]);
1872 while (isspace(recp[0]))
1873 strcpy(recp, &recp[1]);
1874 while (isspace(recp[strlen(recp) - 1]))
1875 recp[strlen(recp) - 1] = 0;
1877 /* If we're in Mail, check the recipient */
1878 if (strlen(recp) > 0) {
1879 e = alias(recp); /* alias and mail type */
1880 if ((recp[0] == 0) || (e == MES_ERROR)) {
1881 cprintf("%d Unknown address - cannot send message.\n",
1882 ERROR + NO_SUCH_USER);
1885 if (e == MES_LOCAL) {
1886 a = getuser(&tempUS, recp);
1888 cprintf("%d No such user.\n",
1889 ERROR + NO_SUCH_USER);
1895 /* At this point, message has been approved. */
1896 if (extract_int(entargs, 0) == 0) {
1897 cprintf("%d OK to send\n", OK);
1901 msglen = extract_long(entargs, 2);
1902 msg = mallok(sizeof(struct CtdlMessage));
1904 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1908 memset(msg, 0, sizeof(struct CtdlMessage));
1909 tempbuf = mallok(msglen);
1910 if (tempbuf == NULL) {
1911 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1916 cprintf("%d %ld\n", SEND_BINARY, msglen);
1918 client_read(&ch, 1); /* 0xFF magic number */
1919 msg->cm_magic = CTDLMESSAGE_MAGIC;
1920 client_read(&ch, 1); /* anon type */
1921 msg->cm_anon_type = ch;
1922 client_read(&ch, 1); /* format type */
1923 msg->cm_format_type = ch;
1924 msglen = msglen - 3;
1926 while (msglen > 0) {
1927 client_read(&which_field, 1);
1931 client_read(&ch, 1);
1933 a = strlen(tempbuf);
1936 } while ( (ch != 0) && (msglen > 0) );
1937 msg->cm_fields[which_field] = strdoop(tempbuf);
1940 msg->cm_flags = CM_SKIP_HOOKS;
1941 CtdlSaveMsg(msg, recp, "", e, 0);
1942 CtdlFreeMessage(msg);
1948 * API function to delete messages which match a set of criteria
1949 * (returns the actual number of messages deleted)
1951 int CtdlDeleteMessages(char *room_name, /* which room */
1952 long dmsgnum, /* or "0" for any */
1953 char *content_type /* or NULL for any */
1957 struct quickroom qrbuf;
1958 struct cdbdata *cdbfr;
1959 long *msglist = NULL;
1962 int num_deleted = 0;
1964 struct SuppMsgInfo smi;
1966 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1967 room_name, dmsgnum, content_type);
1969 /* get room record, obtaining a lock... */
1970 if (lgetroom(&qrbuf, room_name) != 0) {
1971 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1973 return (0); /* room not found */
1975 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1977 if (cdbfr != NULL) {
1978 msglist = mallok(cdbfr->len);
1979 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1980 num_msgs = cdbfr->len / sizeof(long);
1984 for (i = 0; i < num_msgs; ++i) {
1987 /* Set/clear a bit for each criterion */
1989 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1990 delete_this |= 0x01;
1992 if (content_type == NULL) {
1993 delete_this |= 0x02;
1995 GetSuppMsgInfo(&smi, msglist[i]);
1996 if (!strcasecmp(smi.smi_content_type,
1998 delete_this |= 0x02;
2002 /* Delete message only if all bits are set */
2003 if (delete_this == 0x03) {
2004 AdjRefCount(msglist[i], -1);
2010 num_msgs = sort_msglist(msglist, num_msgs);
2011 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
2012 msglist, (num_msgs * sizeof(long)));
2014 qrbuf.QRhighest = msglist[num_msgs - 1];
2018 lprintf(9, "%d message(s) deleted.\n", num_deleted);
2019 return (num_deleted);
2025 * Delete message from current room
2027 void cmd_dele(char *delstr)
2032 getuser(&CC->usersupp, CC->curr_user);
2033 if ((CC->usersupp.axlevel < 6)
2034 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2035 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2036 && (!(CC->internal_pgm))) {
2037 cprintf("%d Higher access required.\n",
2038 ERROR + HIGHER_ACCESS_REQUIRED);
2041 delnum = extract_long(delstr, 0);
2043 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
2046 cprintf("%d %d message%s deleted.\n", OK,
2047 num_deleted, ((num_deleted != 1) ? "s" : ""));
2049 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2055 * move or copy a message to another room
2057 void cmd_move(char *args)
2061 struct quickroom qtemp;
2065 num = extract_long(args, 0);
2066 extract(targ, args, 1);
2067 is_copy = extract_int(args, 2);
2069 getuser(&CC->usersupp, CC->curr_user);
2070 if ((CC->usersupp.axlevel < 6)
2071 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2072 cprintf("%d Higher access required.\n",
2073 ERROR + HIGHER_ACCESS_REQUIRED);
2077 if (getroom(&qtemp, targ) != 0) {
2078 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2082 err = CtdlSaveMsgPointerInRoom(targ, num,
2083 (SM_VERIFY_GOODNESS | SM_DO_REPL_CHECK) );
2085 cprintf("%d Cannot store message in %s: error %d\n",
2090 /* Now delete the message from the source room,
2091 * if this is a 'move' rather than a 'copy' operation.
2093 if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
2095 cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") );
2101 * GetSuppMsgInfo() - Get the supplementary record for a message
2103 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2106 struct cdbdata *cdbsmi;
2109 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2110 smibuf->smi_msgnum = msgnum;
2111 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2113 /* Use the negative of the message number for its supp record index */
2114 TheIndex = (0L - msgnum);
2116 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2117 if (cdbsmi == NULL) {
2118 return; /* record not found; go with defaults */
2120 memcpy(smibuf, cdbsmi->ptr,
2121 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2122 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2129 * PutSuppMsgInfo() - (re)write supplementary record for a message
2131 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2135 /* Use the negative of the message number for its supp record index */
2136 TheIndex = (0L - smibuf->smi_msgnum);
2138 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2139 smibuf->smi_msgnum, smibuf->smi_refcount);
2141 cdb_store(CDB_MSGMAIN,
2142 &TheIndex, sizeof(long),
2143 smibuf, sizeof(struct SuppMsgInfo));
2148 * AdjRefCount - change the reference count for a message;
2149 * delete the message if it reaches zero
2151 void AdjRefCount(long msgnum, int incr)
2154 struct SuppMsgInfo smi;
2157 /* This is a *tight* critical section; please keep it that way, as
2158 * it may get called while nested in other critical sections.
2159 * Complicating this any further will surely cause deadlock!
2161 begin_critical_section(S_SUPPMSGMAIN);
2162 GetSuppMsgInfo(&smi, msgnum);
2163 smi.smi_refcount += incr;
2164 PutSuppMsgInfo(&smi);
2165 end_critical_section(S_SUPPMSGMAIN);
2167 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2168 msgnum, smi.smi_refcount);
2170 /* If the reference count is now zero, delete the message
2171 * (and its supplementary record as well).
2173 if (smi.smi_refcount == 0) {
2174 lprintf(9, "Deleting message <%ld>\n", msgnum);
2176 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2177 delnum = (0L - msgnum);
2178 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2183 * Write a generic object to this room
2185 * Note: this could be much more efficient. Right now we use two temporary
2186 * files, and still pull the message into memory as with all others.
2188 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2189 char *content_type, /* MIME type of this object */
2190 char *tempfilename, /* Where to fetch it from */
2191 struct usersupp *is_mailbox, /* Mailbox room? */
2192 int is_binary, /* Is encoding necessary? */
2193 int is_unique, /* Del others of this type? */
2194 unsigned int flags /* Internal save flags */
2199 char filename[PATH_MAX];
2202 struct quickroom qrbuf;
2203 char roomname[ROOMNAMELEN];
2204 struct CtdlMessage *msg;
2207 if (is_mailbox != NULL)
2208 MailboxName(roomname, is_mailbox, req_room);
2210 safestrncpy(roomname, req_room, sizeof(roomname));
2211 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2213 strcpy(filename, tmpnam(NULL));
2214 fp = fopen(filename, "w");
2218 tempfp = fopen(tempfilename, "r");
2219 if (tempfp == NULL) {
2225 fprintf(fp, "Content-type: %s\n", content_type);
2226 lprintf(9, "Content-type: %s\n", content_type);
2228 if (is_binary == 0) {
2229 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2230 while (ch = getc(tempfp), ch > 0)
2236 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2239 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2240 tempfilename, filename);
2244 lprintf(9, "Allocating\n");
2245 msg = mallok(sizeof(struct CtdlMessage));
2246 memset(msg, 0, sizeof(struct CtdlMessage));
2247 msg->cm_magic = CTDLMESSAGE_MAGIC;
2248 msg->cm_anon_type = MES_NORMAL;
2249 msg->cm_format_type = 4;
2250 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2251 msg->cm_fields['O'] = strdoop(req_room);
2252 msg->cm_fields['N'] = strdoop(config.c_nodename);
2253 msg->cm_fields['H'] = strdoop(config.c_humannode);
2254 msg->cm_flags = flags;
2256 lprintf(9, "Loading\n");
2257 fp = fopen(filename, "rb");
2258 fseek(fp, 0L, SEEK_END);
2261 msg->cm_fields['M'] = mallok(len);
2262 fread(msg->cm_fields['M'], len, 1, fp);
2266 /* Create the requested room if we have to. */
2267 if (getroom(&qrbuf, roomname) != 0) {
2268 create_room(roomname, 4, "", 0);
2270 /* If the caller specified this object as unique, delete all
2271 * other objects of this type that are currently in the room.
2274 lprintf(9, "Deleted %d other msgs of this type\n",
2275 CtdlDeleteMessages(roomname, 0L, content_type));
2277 /* Now write the data */
2278 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2279 CtdlFreeMessage(msg);