22 #include "sysdep_decls.h"
23 #include "citserver.h"
28 #include "dynloader.h"
30 #include "mime_parser.h"
33 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
34 #define ma ((struct ma_info *)CtdlGetUserData(SYM_MA_INFO))
35 #define msg_repl ((struct repl *)CtdlGetUserData(SYM_REPL))
37 extern struct config config;
40 "", "", "", "", "", "", "", "",
41 "", "", "", "", "", "", "", "",
42 "", "", "", "", "", "", "", "",
43 "", "", "", "", "", "", "", "",
44 "", "", "", "", "", "", "", "",
45 "", "", "", "", "", "", "", "",
46 "", "", "", "", "", "", "", "",
47 "", "", "", "", "", "", "", "",
73 * This function is self explanatory.
74 * (What can I say, I'm in a weird mood today...)
76 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
80 for (i = 0; i < strlen(name); ++i)
83 if (isspace(name[i - 1])) {
84 strcpy(&name[i - 1], &name[i]);
87 while (isspace(name[i + 1])) {
88 strcpy(&name[i + 1], &name[i + 2]);
95 * Aliasing for network mail.
96 * (Error messages have been commented out, because this is a server.)
99 { /* process alias and routing info for mail */
102 char aaa[300], bbb[300];
104 lprintf(9, "alias() called for <%s>\n", name);
106 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
108 fp = fopen("network/mail.aliases", "r");
110 fp = fopen("/dev/null", "r");
115 while (fgets(aaa, sizeof aaa, fp) != NULL) {
116 while (isspace(name[0]))
117 strcpy(name, &name[1]);
118 aaa[strlen(aaa) - 1] = 0;
120 for (a = 0; a < strlen(aaa); ++a) {
122 strcpy(bbb, &aaa[a + 1]);
126 if (!strcasecmp(name, aaa))
130 lprintf(7, "Mail is being forwarded to %s\n", name);
132 /* determine local or remote type, see citadel.h */
133 for (a = 0; a < strlen(name); ++a)
135 return (MES_INTERNET);
136 for (a = 0; a < strlen(name); ++a)
138 for (b = a; b < strlen(name); ++b)
140 return (MES_INTERNET);
142 for (a = 0; a < strlen(name); ++a)
146 lprintf(7, "Too many @'s in address\n");
150 for (a = 0; a < strlen(name); ++a)
152 strcpy(bbb, &name[a + 1]);
154 strcpy(bbb, &bbb[1]);
155 fp = fopen("network/mail.sysinfo", "r");
159 a = getstring(fp, aaa);
160 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
161 a = getstring(fp, aaa);
162 if (!strncmp(aaa, "use ", 4)) {
163 strcpy(bbb, &aaa[4]);
168 if (!strncmp(aaa, "uum", 3)) {
170 for (a = 0; a < strlen(bbb); ++a) {
176 while (bbb[strlen(bbb) - 1] == '_')
177 bbb[strlen(bbb) - 1] = 0;
178 sprintf(name, &aaa[4], bbb);
179 return (MES_INTERNET);
181 if (!strncmp(aaa, "bin", 3)) {
184 while (aaa[strlen(aaa) - 1] != '@')
185 aaa[strlen(aaa) - 1] = 0;
186 aaa[strlen(aaa) - 1] = 0;
187 while (aaa[strlen(aaa) - 1] == ' ')
188 aaa[strlen(aaa) - 1] = 0;
189 while (bbb[0] != '@')
190 strcpy(bbb, &bbb[1]);
191 strcpy(bbb, &bbb[1]);
192 while (bbb[0] == ' ')
193 strcpy(bbb, &bbb[1]);
194 sprintf(name, "%s @%s", aaa, bbb);
207 fp = fopen("citadel.control", "r");
208 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
214 void simple_listing(long msgnum)
216 cprintf("%ld\n", msgnum);
221 /* Determine if a given message matches the fields in a message template.
222 * Return 0 for a successful match.
224 int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
227 /* If there aren't any fields in the template, all messages will
230 if (template == NULL) return(0);
232 /* Null messages are bogus. */
233 if (msg == NULL) return(1);
235 for (i='A'; i<='Z'; ++i) {
236 if (template->cm_fields[i] != NULL) {
237 if (msg->cm_fields[i] == NULL) {
240 if (strcasecmp(msg->cm_fields[i],
241 template->cm_fields[i])) return 1;
245 /* All compares succeeded: we have a match! */
253 * API function to perform an operation for each qualifying message in the
256 void CtdlForEachMessage(int mode, long ref,
258 struct CtdlMessage *compare,
259 void (*CallBack) (long msgnum))
264 struct cdbdata *cdbfr;
265 long *msglist = NULL;
268 struct SuppMsgInfo smi;
269 struct CtdlMessage *msg;
271 /* Learn about the user and room in question */
273 getuser(&CC->usersupp, CC->curr_user);
274 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
276 /* Load the message list */
277 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
279 msglist = mallok(cdbfr->len);
280 memcpy(msglist, cdbfr->ptr, cdbfr->len);
281 num_msgs = cdbfr->len / sizeof(long);
284 return; /* No messages at all? No further action. */
288 /* If the caller is looking for a specific MIME type, then filter
289 * out all messages which are not of the type requested.
292 if (content_type != NULL)
293 if (strlen(content_type) > 0)
294 for (a = 0; a < num_msgs; ++a) {
295 GetSuppMsgInfo(&smi, msglist[a]);
296 if (strcasecmp(smi.smi_content_type, content_type)) {
301 num_msgs = sort_msglist(msglist, num_msgs);
303 /* If a template was supplied, filter out the messages which
304 * don't match. (This could induce some delays!)
307 if (compare != NULL) {
308 for (a = 0; a < num_msgs; ++a) {
309 msg = CtdlFetchMessage(msglist[a]);
311 if (CtdlMsgCmp(msg, compare)) {
314 CtdlFreeMessage(msg);
322 * Now iterate through the message list, according to the
323 * criteria supplied by the caller.
326 for (a = 0; a < num_msgs; ++a) {
327 thismsg = msglist[a];
332 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
333 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
334 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
335 && (CC->usersupp.flags & US_LASTOLD))
336 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
337 || ((mode == MSGS_FIRST) && (a < ref))
338 || ((mode == MSGS_GT) && (thismsg > ref))
344 phree(msglist); /* Clean up */
350 * cmd_msgs() - get list of message #'s in this room
351 * implements the MSGS server command using CtdlForEachMessage()
353 void cmd_msgs(char *cmdbuf)
362 int with_template = 0;
363 struct CtdlMessage *template = NULL;
365 extract(which, cmdbuf, 0);
366 cm_ref = extract_int(cmdbuf, 1);
367 with_template = extract_int(cmdbuf, 2);
371 if (!strncasecmp(which, "OLD", 3))
373 else if (!strncasecmp(which, "NEW", 3))
375 else if (!strncasecmp(which, "FIRST", 5))
377 else if (!strncasecmp(which, "LAST", 4))
379 else if (!strncasecmp(which, "GT", 2))
382 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
383 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
388 cprintf("%d Send template then receive message list\n",
390 template = (struct CtdlMessage *)
391 mallok(sizeof(struct CtdlMessage));
392 memset(template, 0, sizeof(struct CtdlMessage));
393 while(client_gets(buf), strcmp(buf,"000")) {
394 extract(tfield, buf, 0);
395 extract(tvalue, buf, 1);
396 for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
397 if (!strcasecmp(tfield, msgkeys[i])) {
398 template->cm_fields[i] =
405 cprintf("%d Message list...\n", LISTING_FOLLOWS);
408 CtdlForEachMessage(mode, cm_ref, NULL, template, simple_listing);
409 if (template != NULL) CtdlFreeMessage(template);
417 * help_subst() - support routine for help file viewer
419 void help_subst(char *strbuf, char *source, char *dest)
424 while (p = pattern2(strbuf, source), (p >= 0)) {
425 strcpy(workbuf, &strbuf[p + strlen(source)]);
426 strcpy(&strbuf[p], dest);
427 strcat(strbuf, workbuf);
432 void do_help_subst(char *buffer)
436 help_subst(buffer, "^nodename", config.c_nodename);
437 help_subst(buffer, "^humannode", config.c_humannode);
438 help_subst(buffer, "^fqdn", config.c_fqdn);
439 help_subst(buffer, "^username", CC->usersupp.fullname);
440 sprintf(buf2, "%ld", CC->usersupp.usernum);
441 help_subst(buffer, "^usernum", buf2);
442 help_subst(buffer, "^sysadm", config.c_sysadm);
443 help_subst(buffer, "^variantname", CITADEL);
444 sprintf(buf2, "%d", config.c_maxsessions);
445 help_subst(buffer, "^maxsessions", buf2);
451 * memfmout() - Citadel text formatter and paginator.
452 * Although the original purpose of this routine was to format
453 * text to the reader's screen width, all we're really using it
454 * for here is to format text out to 80 columns before sending it
455 * to the client. The client software may reformat it again.
457 void memfmout(int width, char *mptr, char subst)
458 /* screen width to use */
459 /* where are we going to get our text from? */
460 /* nonzero if we should use hypertext mode */
472 c = 1; /* c is the current pos */
475 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
477 buffer[strlen(buffer) + 1] = 0;
478 buffer[strlen(buffer)] = ch;
481 if (buffer[0] == '^')
482 do_help_subst(buffer);
484 buffer[strlen(buffer) + 1] = 0;
486 strcpy(buffer, &buffer[1]);
496 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
498 if (((old == 13) || (old == 10)) && (isspace(real))) {
506 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
507 cprintf("\n%s", aaa);
516 if ((strlen(aaa) + c) > (width - 5)) {
526 if ((ch == 13) || (ch == 10)) {
527 cprintf("%s\n", aaa);
534 FMTEND: cprintf("%s\n", aaa);
540 * Callback function for mime parser that simply lists the part
542 void list_this_part(char *name, char *filename, char *partnum, char *disp,
543 void *content, char *cbtype, size_t length)
546 cprintf("part=%s|%s|%s|%s|%s|%d\n",
547 name, filename, partnum, disp, cbtype, length);
552 * Callback function for mime parser that wants to display text
554 void fixed_output(char *name, char *filename, char *partnum, char *disp,
555 void *content, char *cbtype, size_t length)
559 if (!strcasecmp(cbtype, "multipart/alternative")) {
560 strcpy(ma->prefix, partnum);
561 strcat(ma->prefix, ".");
567 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
569 && (ma->did_print == 1) ) {
570 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
576 if (!strcasecmp(cbtype, "text/plain")) {
577 client_write(content, length);
579 else if (!strcasecmp(cbtype, "text/html")) {
580 ptr = html_to_ascii(content, 80, 0);
581 client_write(ptr, strlen(ptr));
584 else if (strncasecmp(cbtype, "multipart/", 10)) {
585 cprintf("Part %s: %s (%s) (%d bytes)\n",
586 partnum, filename, cbtype, length);
592 * Callback function for mime parser that opens a section for downloading
594 void mime_download(char *name, char *filename, char *partnum, char *disp,
595 void *content, char *cbtype, size_t length)
598 /* Silently go away if there's already a download open... */
599 if (CC->download_fp != NULL)
602 /* ...or if this is not the desired section */
603 if (strcasecmp(desired_section, partnum))
606 CC->download_fp = tmpfile();
607 if (CC->download_fp == NULL)
610 fwrite(content, length, 1, CC->download_fp);
611 fflush(CC->download_fp);
612 rewind(CC->download_fp);
614 OpenCmdResult(filename, cbtype);
620 * Load a message from disk into memory.
621 * This is used by output_message() and other fetch functions.
623 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
624 * using the CtdlMessageFree() function.
626 struct CtdlMessage *CtdlFetchMessage(long msgnum)
628 struct cdbdata *dmsgtext;
629 struct CtdlMessage *ret = NULL;
632 CIT_UBYTE field_header;
636 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
637 if (dmsgtext == NULL) {
638 lprintf(9, "CtdlFetchMessage(%ld) failed.\n");
641 mptr = dmsgtext->ptr;
643 /* Parse the three bytes that begin EVERY message on disk.
644 * The first is always 0xFF, the on-disk magic number.
645 * The second is the anonymous/public type byte.
646 * The third is the format type byte (vari, fixed, or MIME).
650 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
654 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
655 memset(ret, 0, sizeof(struct CtdlMessage));
657 ret->cm_magic = CTDLMESSAGE_MAGIC;
658 ret->cm_anon_type = *mptr++; /* Anon type byte */
659 ret->cm_format_type = *mptr++; /* Format type byte */
662 * The rest is zero or more arbitrary fields. Load them in.
663 * We're done when we encounter either a zero-length field or
664 * have just processed the 'M' (message text) field.
667 field_length = strlen(mptr);
668 if (field_length == 0)
670 field_header = *mptr++;
671 ret->cm_fields[field_header] = mallok(field_length);
672 strcpy(ret->cm_fields[field_header], mptr);
674 while (*mptr++ != 0); /* advance to next field */
676 } while ((field_length > 0) && (field_header != 'M'));
680 /* Perform "before read" hooks (aborting if any return nonzero) */
681 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
682 CtdlFreeMessage(ret);
691 * Returns 1 if the supplied pointer points to a valid Citadel message.
692 * If the pointer is NULL or the magic number check fails, returns 0.
694 int is_valid_message(struct CtdlMessage *msg) {
697 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
698 lprintf(3, "is_valid_message() -- self-check failed\n");
706 * 'Destructor' for struct CtdlMessage
708 void CtdlFreeMessage(struct CtdlMessage *msg)
712 if (is_valid_message(msg) == 0) return;
714 for (i = 0; i < 256; ++i)
715 if (msg->cm_fields[i] != NULL)
716 phree(msg->cm_fields[i]);
718 msg->cm_magic = 0; /* just in case */
725 * Get a message off disk. (return value is the message's timestamp)
728 void output_message(char *msgid, int mode, int headers_only)
736 char display_name[256];
738 struct CtdlMessage *TheMessage = NULL;
742 /* buffers needed for RFC822 translation */
750 msg_num = atol(msgid);
752 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
753 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
757 /* FIX ... small security issue
758 * We need to check to make sure the requested message is actually
759 * in the current room, and set msg_ok to 1 only if it is. This
760 * functionality is currently missing because I'm in a hurry to replace
761 * broken production code with nonbroken pre-beta code. :( -- ajc
764 cprintf("%d Message %ld is not in this room.\n",
771 * Fetch the message from disk
773 TheMessage = CtdlFetchMessage(msg_num);
774 if (TheMessage == NULL) {
775 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
779 /* Are we downloading a MIME component? */
780 if (mode == MT_DOWNLOAD) {
781 if (TheMessage->cm_format_type != 4) {
782 cprintf("%d This is not a MIME message.\n",
784 } else if (CC->download_fp != NULL) {
785 cprintf("%d You already have a download open.\n",
788 /* Parse the message text component */
789 mptr = TheMessage->cm_fields['M'];
790 mime_parser(mptr, NULL, *mime_download);
791 /* If there's no file open by this time, the requested
792 * section wasn't found, so print an error
794 if (CC->download_fp == NULL) {
795 cprintf("%d Section %s not found.\n",
796 ERROR + FILE_NOT_FOUND,
800 CtdlFreeMessage(TheMessage);
804 /* now for the user-mode message reading loops */
805 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
807 /* Tell the client which format type we're using. If this is a
808 * MIME message, *lie* about it and tell the user it's fixed-format.
810 if (mode == MT_CITADEL) {
811 if (TheMessage->cm_format_type == 4)
814 cprintf("type=%d\n", TheMessage->cm_format_type);
817 /* nhdr=yes means that we're only displaying headers, no body */
818 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
819 cprintf("nhdr=yes\n");
822 /* begin header processing loop for Citadel message format */
824 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
826 strcpy(display_name, "<unknown>");
827 if (TheMessage->cm_fields['A']) {
828 strcpy(buf, TheMessage->cm_fields['A']);
829 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
830 if (TheMessage->cm_anon_type == MES_ANON)
831 strcpy(display_name, "****");
832 else if (TheMessage->cm_anon_type == MES_AN2)
833 strcpy(display_name, "anonymous");
835 strcpy(display_name, buf);
837 && ((TheMessage->cm_anon_type == MES_ANON)
838 || (TheMessage->cm_anon_type == MES_AN2))) {
839 sprintf(&display_name[strlen(display_name)],
844 strcpy(allkeys, FORDER);
845 for (i=0; i<strlen(allkeys); ++i) {
846 k = (int) allkeys[i];
848 if (TheMessage->cm_fields[k] != NULL) {
857 TheMessage->cm_fields[k]
866 /* begin header processing loop for RFC822 transfer format */
870 strcpy(snode, NODENAME);
871 strcpy(lnode, HUMANNODE);
872 if (mode == MT_RFC822) {
873 for (i = 0; i < 256; ++i) {
874 if (TheMessage->cm_fields[i]) {
875 mptr = TheMessage->cm_fields[i];
879 } else if (i == 'P') {
880 cprintf("Path: %s\n", mptr);
881 for (a = 0; a < strlen(mptr); ++a) {
882 if (mptr[a] == '!') {
883 strcpy(mptr, &mptr[a + 1]);
889 cprintf("Subject: %s\n", mptr);
895 cprintf("X-Citadel-Room: %s\n", mptr);
899 cprintf("To: %s\n", mptr);
902 cprintf("Date: %s", asctime(localtime(&xtime)));
908 if (mode == MT_RFC822) {
909 if (!strcasecmp(snode, NODENAME)) {
912 cprintf("Message-ID: <%s@%s>\n", mid, snode);
913 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
914 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
915 cprintf("Organization: %s\n", lnode);
918 /* end header processing loop ... at this point, we're in the text */
920 mptr = TheMessage->cm_fields['M'];
922 /* Tell the client about the MIME parts in this message */
923 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
924 if (mode == MT_CITADEL) {
925 mime_parser(mptr, NULL, *list_this_part);
927 else if (mode == MT_MIME) { /* list parts only */
928 mime_parser(mptr, NULL, *list_this_part);
930 CtdlFreeMessage(TheMessage);
937 CtdlFreeMessage(TheMessage);
941 /* signify start of msg text */
942 if (mode == MT_CITADEL)
944 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
947 /* If the format type on disk is 1 (fixed-format), then we want
948 * everything to be output completely literally ... regardless of
949 * what message transfer format is in use.
951 if (TheMessage->cm_format_type == 1) {
953 while (ch = *mptr++, ch > 0) {
956 if ((ch == 10) || (strlen(buf) > 250)) {
957 cprintf("%s\n", buf);
960 buf[strlen(buf) + 1] = 0;
961 buf[strlen(buf)] = ch;
965 cprintf("%s\n", buf);
968 /* If the message on disk is format 0 (Citadel vari-format), we
969 * output using the formatter at 80 columns. This is the final output
970 * form if the transfer format is RFC822, but if the transfer format
971 * is Citadel proprietary, it'll still work, because the indentation
972 * for new paragraphs is correct and the client will reformat the
973 * message to the reader's screen width.
975 if (TheMessage->cm_format_type == 0) {
976 memfmout(80, mptr, 0);
979 /* If the message on disk is format 4 (MIME), we've gotta hand it
980 * off to the MIME parser. The client has already been told that
981 * this message is format 1 (fixed format), so the callback function
982 * we use will display those parts as-is.
984 if (TheMessage->cm_format_type == 4) {
985 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
986 memset(ma, 0, sizeof(struct ma_info));
987 mime_parser(mptr, NULL, *fixed_output);
992 CtdlFreeMessage(TheMessage);
999 * display a message (mode 0 - Citadel proprietary)
1001 void cmd_msg0(char *cmdbuf)
1004 int headers_only = 0;
1006 extract(msgid, cmdbuf, 0);
1007 headers_only = extract_int(cmdbuf, 1);
1009 output_message(msgid, MT_CITADEL, headers_only);
1015 * display a message (mode 2 - RFC822)
1017 void cmd_msg2(char *cmdbuf)
1020 int headers_only = 0;
1022 extract(msgid, cmdbuf, 0);
1023 headers_only = extract_int(cmdbuf, 1);
1025 output_message(msgid, MT_RFC822, headers_only);
1031 * display a message (mode 3 - IGnet raw format - internal programs only)
1033 void cmd_msg3(char *cmdbuf)
1036 struct CtdlMessage *msg;
1039 if (CC->internal_pgm == 0) {
1040 cprintf("%d This command is for internal programs only.\n",
1045 msgnum = extract_long(cmdbuf, 0);
1046 msg = CtdlFetchMessage(msgnum);
1048 cprintf("%d Message %ld not found.\n",
1053 serialize_message(&smr, msg);
1054 CtdlFreeMessage(msg);
1057 cprintf("%d Unable to serialize message\n",
1058 ERROR+INTERNAL_ERROR);
1062 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1063 client_write(smr.ser, smr.len);
1070 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
1072 void cmd_msg4(char *cmdbuf)
1076 extract(msgid, cmdbuf, 0);
1078 output_message(msgid, MT_MIME, 0);
1082 * Open a component of a MIME message as a download file
1084 void cmd_opna(char *cmdbuf)
1088 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
1090 extract(msgid, cmdbuf, 0);
1091 extract(desired_section, cmdbuf, 1);
1093 output_message(msgid, MT_DOWNLOAD, 0);
1099 * Save a message pointer into a specified room
1100 * (Returns 0 for success, nonzero for failure)
1102 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
1104 struct quickroom qrbuf;
1105 struct cdbdata *cdbfr;
1108 long highest_msg = 0L;
1110 lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n",
1111 roomname, msgid, flags);
1113 if (lgetroom(&qrbuf, roomname) != 0) {
1114 lprintf(9, "No such room <%s>\n", roomname);
1115 return(ERROR + ROOM_NOT_FOUND);
1118 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1119 if (cdbfr == NULL) {
1123 msglist = mallok(cdbfr->len);
1124 if (msglist == NULL)
1125 lprintf(3, "ERROR malloc msglist!\n");
1126 num_msgs = cdbfr->len / sizeof(long);
1127 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1132 /* Make sure the message doesn't already exist in this room. It
1133 * is absolutely taboo to have more than one reference to the same
1134 * message in a room.
1136 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1137 if (msglist[i] == msgid) {
1138 lputroom(&qrbuf); /* unlock the room */
1139 return(ERROR + ALREADY_EXISTS);
1143 /* Now add the new message */
1145 msglist = reallok(msglist,
1146 (num_msgs * sizeof(long)));
1148 if (msglist == NULL) {
1149 lprintf(3, "ERROR: can't realloc message list!\n");
1151 msglist[num_msgs - 1] = msgid;
1153 /* Sort the message list, so all the msgid's are in order */
1154 num_msgs = sort_msglist(msglist, num_msgs);
1156 /* Determine the highest message number */
1157 highest_msg = msglist[num_msgs - 1];
1159 /* Write it back to disk. */
1160 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1161 msglist, num_msgs * sizeof(long));
1163 /* Free up the memory we used. */
1166 /* Update the highest-message pointer and unlock the room. */
1167 qrbuf.QRhighest = highest_msg;
1170 /* Bump the reference count for this message. */
1171 AdjRefCount(msgid, +1);
1173 /* Return success. */
1174 lprintf(9, "CtdlSaveMsgPointerInRoom() succeeded\n");
1181 * Message base operation to send a message to the master file
1182 * (returns new message number)
1184 * This is the back end for CtdlSaveMsg() and should not be directly
1185 * called by server-side modules.
1188 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1189 int generate_id, /* generate 'I' field? */
1190 FILE *save_a_copy) /* save a copy to disk? */
1197 /* Get a new message number */
1198 newmsgid = get_new_message_number();
1199 sprintf(msgidbuf, "%ld", newmsgid);
1202 msg->cm_fields['I'] = strdoop(msgidbuf);
1205 serialize_message(&smr, msg);
1208 cprintf("%d Unable to serialize message\n",
1209 ERROR+INTERNAL_ERROR);
1213 /* Write our little bundle of joy into the message base */
1214 begin_critical_section(S_MSGMAIN);
1215 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1216 smr.ser, smr.len) < 0) {
1217 lprintf(2, "Can't store message\n");
1222 end_critical_section(S_MSGMAIN);
1224 /* If the caller specified that a copy should be saved to a particular
1225 * file handle, do that now too.
1227 if (save_a_copy != NULL) {
1228 fwrite(smr.ser, smr.len, 1, save_a_copy);
1231 /* Free the memory we used for the serialized message */
1234 /* Return the *local* message ID to the caller
1235 * (even if we're storing an incoming network message)
1243 * Serialize a struct CtdlMessage into the format used on disk and network.
1245 * This function loads up a "struct ser_ret" (defined in server.h) which
1246 * contains the length of the serialized message and a pointer to the
1247 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1249 void serialize_message(struct ser_ret *ret, /* return values */
1250 struct CtdlMessage *msg) /* unserialized msg */
1254 static char *forder = FORDER;
1256 lprintf(9, "serialize_message() called\n");
1258 if (is_valid_message(msg) == 0) return; /* self check */
1260 lprintf(9, "magic number check OK.\n");
1263 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1264 ret->len = ret->len +
1265 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1267 lprintf(9, "message is %d bytes\n", ret->len);
1269 lprintf(9, "calling malloc\n");
1270 ret->ser = mallok(ret->len);
1271 if (ret->ser == NULL) {
1277 ret->ser[1] = msg->cm_anon_type;
1278 ret->ser[2] = msg->cm_format_type;
1281 lprintf(9, "stuff\n");
1282 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1283 ret->ser[wlen++] = (char)forder[i];
1284 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1285 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1287 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1289 lprintf(9, "done serializing\n");
1297 * Back end for the ReplicationChecks() function
1299 void check_repl(long msgnum) {
1300 struct CtdlMessage *msg;
1301 time_t timestamp = (-1L);
1303 lprintf(9, "check_repl() found message %ld\n", msgnum);
1304 msg = CtdlFetchMessage(msgnum);
1305 if (msg == NULL) return;
1306 if (msg->cm_fields['T'] != NULL) {
1307 timestamp = atol(msg->cm_fields['T']);
1309 CtdlFreeMessage(msg);
1311 if (timestamp > msg_repl->highest) {
1312 msg_repl->highest = timestamp; /* newer! */
1313 lprintf(9, "newer!\n");
1316 lprintf(9, "older!\n");
1318 /* Existing isn't newer? Then delete the old one(s). */
1319 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, NULL);
1324 * Check to see if any messages already exist which carry the same Extended ID
1328 * -> With older timestamps: delete them and return 0. Message will be saved.
1329 * -> With newer timestamps: return 1. Message save will be aborted.
1331 int ReplicationChecks(struct CtdlMessage *msg) {
1332 struct CtdlMessage *template;
1335 lprintf(9, "ReplicationChecks() started\n");
1336 /* No extended id? Don't do anything. */
1337 if (msg->cm_fields['E'] == NULL) return 0;
1338 if (strlen(msg->cm_fields['E']) == 0) return 0;
1339 lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1341 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1342 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1343 msg_repl->highest = atol(msg->cm_fields['T']);
1345 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1346 memset(template, 0, sizeof(struct CtdlMessage));
1347 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1349 CtdlForEachMessage(MSGS_ALL, 0L, NULL, template, check_repl);
1351 /* If a newer message exists with the same Extended ID, abort
1354 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1358 CtdlFreeMessage(template);
1359 lprintf(9, "Returning %d\n", abort_this);
1367 * Save a message to disk
1369 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1370 char *rec, /* Recipient (mail) */
1371 char *force, /* force a particular room? */
1372 int mailtype, /* local or remote type */
1373 int generate_id) /* 1 = generate 'I' field */
1376 char hold_rm[ROOMNAMELEN];
1377 char actual_rm[ROOMNAMELEN];
1378 char force_room[ROOMNAMELEN];
1379 char content_type[256]; /* We have to learn this */
1380 char recipient[256];
1383 struct usersupp userbuf;
1385 struct SuppMsgInfo smi;
1386 FILE *network_fp = NULL;
1387 static int seqnum = 1;
1389 lprintf(9, "CtdlSaveMsg() called\n");
1390 if (is_valid_message(msg) == 0) return; /* self check */
1392 /* If this message has no timestamp, we take the liberty of
1393 * giving it one, right now.
1395 if (msg->cm_fields['T'] == NULL) {
1396 sprintf(aaa, "%ld", time(NULL));
1397 msg->cm_fields['T'] = strdoop(aaa);
1400 /* If this message has no path, we generate one.
1402 if (msg->cm_fields['P'] == NULL) {
1403 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1404 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1405 if (isspace(msg->cm_fields['P'][a])) {
1406 msg->cm_fields['P'][a] = ' ';
1411 strcpy(force_room, force);
1413 /* Strip non-printable characters out of the recipient name */
1414 strcpy(recipient, rec);
1415 for (a = 0; a < strlen(recipient); ++a)
1416 if (!isprint(recipient[a]))
1417 strcpy(&recipient[a], &recipient[a + 1]);
1419 /* Learn about what's inside, because it's what's inside that counts */
1421 switch (msg->cm_format_type) {
1423 strcpy(content_type, "text/x-citadel-variformat");
1426 strcpy(content_type, "text/plain");
1429 strcpy(content_type, "text/plain");
1430 /* advance past header fields */
1431 mptr = msg->cm_fields['M'];
1434 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1435 safestrncpy(content_type, mptr,
1436 sizeof(content_type));
1437 strcpy(content_type, &content_type[14]);
1438 for (a = 0; a < strlen(content_type); ++a)
1439 if ((content_type[a] == ';')
1440 || (content_type[a] == ' ')
1441 || (content_type[a] == 13)
1442 || (content_type[a] == 10))
1443 content_type[a] = 0;
1450 /* Perform "before save" hooks (aborting if any return nonzero) */
1451 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1453 /* If this message has an Extended ID, perform replication checks */
1454 if (ReplicationChecks(msg) > 0) return;
1456 /* Network mail - send a copy to the network program. */
1457 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1458 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1459 (long) getpid(), CC->cs_pid, ++seqnum);
1460 lprintf(9, "Saving a copy to %s\n", aaa);
1461 network_fp = fopen(aaa, "ab+");
1462 if (network_fp == NULL)
1463 lprintf(2, "ERROR: %s\n", strerror(errno));
1466 /* Save it to disk */
1467 newmsgid = send_message(msg, generate_id, network_fp);
1468 if (network_fp != NULL) {
1470 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1473 if (newmsgid <= 0L) return;
1475 /* Write a supplemental message info record. This doesn't have to
1476 * be a critical section because nobody else knows about this message
1479 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1480 smi.smi_msgnum = newmsgid;
1481 smi.smi_refcount = 0;
1482 safestrncpy(smi.smi_content_type, content_type, 64);
1483 PutSuppMsgInfo(&smi);
1485 /* Now figure out where to store the pointers */
1487 strcpy(actual_rm, CC->quickroom.QRname);
1489 /* If this is being done by the networker delivering a private
1490 * message, we want to BYPASS saving the sender's copy (because there
1491 * is no local sender; it would otherwise go to the Trashcan).
1493 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1494 /* If the user is a twit, move to the twit room for posting */
1496 if (CC->usersupp.axlevel == 2) {
1497 strcpy(hold_rm, actual_rm);
1498 strcpy(actual_rm, config.c_twitroom);
1501 /* ...or if this message is destined for Aide> then go there. */
1502 if (strlen(force_room) > 0) {
1503 strcpy(actual_rm, force_room);
1505 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1508 /* Bump this user's messages posted counter. */
1509 lgetuser(&CC->usersupp, CC->curr_user);
1510 CC->usersupp.posted = CC->usersupp.posted + 1;
1511 lputuser(&CC->usersupp);
1513 /* If this is private, local mail, make a copy in the
1514 * recipient's mailbox and bump the reference count.
1516 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1517 if (getuser(&userbuf, recipient) == 0) {
1518 MailboxName(actual_rm, &userbuf, MAILROOM);
1519 CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1523 /* Perform "after save" hooks */
1524 PerformMessageHooks(msg, EVT_AFTERSAVE);
1530 * Convenience function for generating small administrative messages.
1532 void quickie_message(char *from, char *to, char *room, char *text)
1534 struct CtdlMessage *msg;
1536 msg = mallok(sizeof(struct CtdlMessage));
1537 memset(msg, 0, sizeof(struct CtdlMessage));
1538 msg->cm_magic = CTDLMESSAGE_MAGIC;
1539 msg->cm_anon_type = MES_NORMAL;
1540 msg->cm_format_type = 0;
1541 msg->cm_fields['A'] = strdoop(from);
1542 msg->cm_fields['O'] = strdoop(room);
1543 msg->cm_fields['N'] = strdoop(NODENAME);
1545 msg->cm_fields['R'] = strdoop(to);
1546 msg->cm_fields['M'] = strdoop(text);
1548 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1549 CtdlFreeMessage(msg);
1550 syslog(LOG_NOTICE, text);
1555 * Build a binary message to be saved on disk.
1558 struct CtdlMessage *make_message(
1559 struct usersupp *author, /* author's usersupp structure */
1560 char *recipient, /* NULL if it's not mail */
1561 char *room, /* room where it's going */
1562 int type, /* see MES_ types in header file */
1563 int net_type, /* see MES_ types in header file */
1564 int format_type, /* local or remote (see citadel.h) */
1565 char *fake_name) /* who we're masquerading as */
1571 size_t message_len = 0;
1572 size_t buffer_len = 0;
1574 struct CtdlMessage *msg;
1576 msg = mallok(sizeof(struct CtdlMessage));
1577 memset(msg, 0, sizeof(struct CtdlMessage));
1578 msg->cm_magic = CTDLMESSAGE_MAGIC;
1579 msg->cm_anon_type = type;
1580 msg->cm_format_type = format_type;
1582 /* Don't confuse the poor folks if it's not routed mail. */
1583 strcpy(dest_node, "");
1585 /* If net_type is MES_BINARY, split out the destination node. */
1586 if (net_type == MES_BINARY) {
1587 strcpy(dest_node, NODENAME);
1588 for (a = 0; a < strlen(recipient); ++a) {
1589 if (recipient[a] == '@') {
1591 strcpy(dest_node, &recipient[a + 1]);
1596 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1597 if (net_type == MES_INTERNET) {
1598 strcpy(dest_node, "internet");
1601 while (isspace(recipient[strlen(recipient) - 1]))
1602 recipient[strlen(recipient) - 1] = 0;
1604 sprintf(buf, "cit%ld", author->usernum); /* Path */
1605 msg->cm_fields['P'] = strdoop(buf);
1607 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1608 msg->cm_fields['T'] = strdoop(buf);
1610 if (fake_name[0]) /* author */
1611 msg->cm_fields['A'] = strdoop(fake_name);
1613 msg->cm_fields['A'] = strdoop(author->fullname);
1615 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1616 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1618 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1620 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1621 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1623 if (recipient[0] != 0)
1624 msg->cm_fields['R'] = strdoop(recipient);
1625 if (dest_node[0] != 0)
1626 msg->cm_fields['D'] = strdoop(dest_node);
1628 msg->cm_fields['M'] = mallok(4096);
1629 if (msg->cm_fields['M'] == NULL) {
1630 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1634 msg->cm_fields['M'][0] = 0;
1638 /* read in the lines of message text one by one */
1639 while (client_gets(buf), strcmp(buf, "000")) {
1641 /* augment the buffer if we have to */
1642 if ((message_len + strlen(buf) + 2) > buffer_len) {
1643 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1644 if (ptr == NULL) { /* flush if can't allocate */
1645 while (client_gets(buf), strcmp(buf, "000")) ;;
1648 buffer_len = (buffer_len * 2);
1649 msg->cm_fields['M'] = ptr;
1653 strcat(msg->cm_fields['M'], buf);
1654 strcat(msg->cm_fields['M'], "\n");
1656 /* if we've hit the max msg length, flush the rest */
1657 if (message_len >= config.c_maxmsglen) {
1658 while (client_gets(buf), strcmp(buf, "000")) ;;
1671 * message entry - mode 0 (normal)
1673 void cmd_ent0(char *entargs)
1676 char recipient[256];
1678 int format_type = 0;
1679 char newusername[256];
1680 struct CtdlMessage *msg;
1684 struct usersupp tempUS;
1687 post = extract_int(entargs, 0);
1688 extract(recipient, entargs, 1);
1689 anon_flag = extract_int(entargs, 2);
1690 format_type = extract_int(entargs, 3);
1692 /* first check to make sure the request is valid. */
1694 if (!(CC->logged_in)) {
1695 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1698 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1699 cprintf("%d Need to be validated to enter ",
1700 ERROR + HIGHER_ACCESS_REQUIRED);
1701 cprintf("(except in %s> to sysop)\n", MAILROOM);
1704 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1705 cprintf("%d Need net privileges to enter here.\n",
1706 ERROR + HIGHER_ACCESS_REQUIRED);
1709 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1710 cprintf("%d Sorry, this is a read-only room.\n",
1711 ERROR + HIGHER_ACCESS_REQUIRED);
1718 if (CC->usersupp.axlevel < 6) {
1719 cprintf("%d You don't have permission to masquerade.\n",
1720 ERROR + HIGHER_ACCESS_REQUIRED);
1723 extract(newusername, entargs, 4);
1724 memset(CC->fake_postname, 0, 32);
1725 strcpy(CC->fake_postname, newusername);
1726 cprintf("%d Ok\n", OK);
1729 CC->cs_flags |= CS_POSTING;
1732 if (CC->quickroom.QRflags & QR_MAILBOX) {
1733 if (CC->usersupp.axlevel >= 2) {
1734 strcpy(buf, recipient);
1736 strcpy(buf, "sysop");
1737 e = alias(buf); /* alias and mail type */
1738 if ((buf[0] == 0) || (e == MES_ERROR)) {
1739 cprintf("%d Unknown address - cannot send message.\n",
1740 ERROR + NO_SUCH_USER);
1743 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1744 cprintf("%d Net privileges required for network mail.\n",
1745 ERROR + HIGHER_ACCESS_REQUIRED);
1748 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1749 && ((CC->usersupp.flags & US_INTERNET) == 0)
1750 && (!CC->internal_pgm)) {
1751 cprintf("%d You don't have access to Internet mail.\n",
1752 ERROR + HIGHER_ACCESS_REQUIRED);
1755 if (!strcasecmp(buf, "sysop")) {
1760 goto SKFALL; /* don't search local file */
1761 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1762 cprintf("%d Can't send mail to yourself!\n",
1763 ERROR + NO_SUCH_USER);
1766 /* Check to make sure the user exists; also get the correct
1767 * upper/lower casing of the name.
1769 a = getuser(&tempUS, buf);
1771 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1774 strcpy(buf, tempUS.fullname);
1777 SKFALL: b = MES_NORMAL;
1778 if (CC->quickroom.QRflags & QR_ANONONLY)
1780 if (CC->quickroom.QRflags & QR_ANONOPT) {
1784 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1787 /* If we're only checking the validity of the request, return
1788 * success without creating the message.
1791 cprintf("%d %s\n", OK, buf);
1795 cprintf("%d send message\n", SEND_LISTING);
1797 /* Read in the message from the client. */
1798 if (CC->fake_postname[0])
1799 msg = make_message(&CC->usersupp, buf,
1800 CC->quickroom.QRname, b, e, format_type,
1802 else if (CC->fake_username[0])
1803 msg = make_message(&CC->usersupp, buf,
1804 CC->quickroom.QRname, b, e, format_type,
1807 msg = make_message(&CC->usersupp, buf,
1808 CC->quickroom.QRname, b, e, format_type, "");
1811 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1812 CtdlFreeMessage(msg);
1813 CC->fake_postname[0] = '\0';
1820 * message entry - mode 3 (raw)
1822 void cmd_ent3(char *entargs)
1827 unsigned char ch, which_field;
1828 struct usersupp tempUS;
1830 struct CtdlMessage *msg;
1833 if (CC->internal_pgm == 0) {
1834 cprintf("%d This command is for internal programs only.\n",
1839 /* See if there's a recipient, but make sure it's a real one */
1840 extract(recp, entargs, 1);
1841 for (a = 0; a < strlen(recp); ++a)
1842 if (!isprint(recp[a]))
1843 strcpy(&recp[a], &recp[a + 1]);
1844 while (isspace(recp[0]))
1845 strcpy(recp, &recp[1]);
1846 while (isspace(recp[strlen(recp) - 1]))
1847 recp[strlen(recp) - 1] = 0;
1849 /* If we're in Mail, check the recipient */
1850 if (strlen(recp) > 0) {
1851 e = alias(recp); /* alias and mail type */
1852 if ((recp[0] == 0) || (e == MES_ERROR)) {
1853 cprintf("%d Unknown address - cannot send message.\n",
1854 ERROR + NO_SUCH_USER);
1857 if (e == MES_LOCAL) {
1858 a = getuser(&tempUS, recp);
1860 cprintf("%d No such user.\n",
1861 ERROR + NO_SUCH_USER);
1867 /* At this point, message has been approved. */
1868 if (extract_int(entargs, 0) == 0) {
1869 cprintf("%d OK to send\n", OK);
1873 msglen = extract_long(entargs, 2);
1874 msg = mallok(sizeof(struct CtdlMessage));
1876 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1880 memset(msg, 0, sizeof(struct CtdlMessage));
1881 tempbuf = mallok(msglen);
1882 if (tempbuf == NULL) {
1883 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1888 cprintf("%d %ld\n", SEND_BINARY, msglen);
1890 client_read(&ch, 1); /* 0xFF magic number */
1891 msg->cm_magic = CTDLMESSAGE_MAGIC;
1892 client_read(&ch, 1); /* anon type */
1893 msg->cm_anon_type = ch;
1894 client_read(&ch, 1); /* format type */
1895 msg->cm_format_type = ch;
1896 msglen = msglen - 3;
1898 while (msglen > 0) {
1899 client_read(&which_field, 1);
1903 client_read(&ch, 1);
1905 a = strlen(tempbuf);
1908 } while ( (ch != 0) && (msglen > 0) );
1909 msg->cm_fields[which_field] = strdoop(tempbuf);
1912 msg->cm_flags = CM_SKIP_HOOKS;
1913 CtdlSaveMsg(msg, recp, "", e, 0);
1914 CtdlFreeMessage(msg);
1920 * API function to delete messages which match a set of criteria
1921 * (returns the actual number of messages deleted)
1923 int CtdlDeleteMessages(char *room_name, /* which room */
1924 long dmsgnum, /* or "0" for any */
1925 char *content_type /* or NULL for any */
1929 struct quickroom qrbuf;
1930 struct cdbdata *cdbfr;
1931 long *msglist = NULL;
1934 int num_deleted = 0;
1936 struct SuppMsgInfo smi;
1938 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1939 room_name, dmsgnum, content_type);
1941 /* get room record, obtaining a lock... */
1942 if (lgetroom(&qrbuf, room_name) != 0) {
1943 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1945 return (0); /* room not found */
1947 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1949 if (cdbfr != NULL) {
1950 msglist = mallok(cdbfr->len);
1951 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1952 num_msgs = cdbfr->len / sizeof(long);
1956 for (i = 0; i < num_msgs; ++i) {
1959 /* Set/clear a bit for each criterion */
1961 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1962 delete_this |= 0x01;
1964 if (content_type == NULL) {
1965 delete_this |= 0x02;
1967 GetSuppMsgInfo(&smi, msglist[i]);
1968 if (!strcasecmp(smi.smi_content_type,
1970 delete_this |= 0x02;
1974 /* Delete message only if all bits are set */
1975 if (delete_this == 0x03) {
1976 AdjRefCount(msglist[i], -1);
1982 num_msgs = sort_msglist(msglist, num_msgs);
1983 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1984 msglist, (num_msgs * sizeof(long)));
1986 qrbuf.QRhighest = msglist[num_msgs - 1];
1990 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1991 return (num_deleted);
1997 * Delete message from current room
1999 void cmd_dele(char *delstr)
2004 getuser(&CC->usersupp, CC->curr_user);
2005 if ((CC->usersupp.axlevel < 6)
2006 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2007 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2008 && (!(CC->internal_pgm))) {
2009 cprintf("%d Higher access required.\n",
2010 ERROR + HIGHER_ACCESS_REQUIRED);
2013 delnum = extract_long(delstr, 0);
2015 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
2018 cprintf("%d %d message%s deleted.\n", OK,
2019 num_deleted, ((num_deleted != 1) ? "s" : ""));
2021 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2027 * move a message to another room
2029 void cmd_move(char *args)
2033 struct quickroom qtemp;
2037 num = extract_long(args, 0);
2038 extract(targ, args, 1);
2040 getuser(&CC->usersupp, CC->curr_user);
2041 if ((CC->usersupp.axlevel < 6)
2042 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2043 cprintf("%d Higher access required.\n",
2044 ERROR + HIGHER_ACCESS_REQUIRED);
2048 if (getroom(&qtemp, targ) != 0) {
2049 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2053 /* Temporarily bump the reference count to avoid having the message
2054 * deleted while it's in transit.
2056 AdjRefCount(num, 1);
2058 /* yank the message out of the current room... */
2059 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
2062 /* put the message into the target room */
2063 if (err = CtdlSaveMsgPointerInRoom(targ, num, 0), (err !=0) ) {
2064 cprintf("%d Could not save message %ld in %s.\n",
2068 cprintf("%d Message moved.\n", OK);
2072 cprintf("%d No such message.\n", ERROR);
2075 /* Fix the reference count. */
2076 AdjRefCount(num, (-1));
2082 * GetSuppMsgInfo() - Get the supplementary record for a message
2084 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2087 struct cdbdata *cdbsmi;
2090 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2091 smibuf->smi_msgnum = msgnum;
2092 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2094 /* Use the negative of the message number for its supp record index */
2095 TheIndex = (0L - msgnum);
2097 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2098 if (cdbsmi == NULL) {
2099 return; /* record not found; go with defaults */
2101 memcpy(smibuf, cdbsmi->ptr,
2102 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2103 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2110 * PutSuppMsgInfo() - (re)write supplementary record for a message
2112 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2116 /* Use the negative of the message number for its supp record index */
2117 TheIndex = (0L - smibuf->smi_msgnum);
2119 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2120 smibuf->smi_msgnum, smibuf->smi_refcount);
2122 cdb_store(CDB_MSGMAIN,
2123 &TheIndex, sizeof(long),
2124 smibuf, sizeof(struct SuppMsgInfo));
2129 * AdjRefCount - change the reference count for a message;
2130 * delete the message if it reaches zero
2132 void AdjRefCount(long msgnum, int incr)
2135 struct SuppMsgInfo smi;
2138 /* This is a *tight* critical section; please keep it that way, as
2139 * it may get called while nested in other critical sections.
2140 * Complicating this any further will surely cause deadlock!
2142 begin_critical_section(S_SUPPMSGMAIN);
2143 GetSuppMsgInfo(&smi, msgnum);
2144 smi.smi_refcount += incr;
2145 PutSuppMsgInfo(&smi);
2146 end_critical_section(S_SUPPMSGMAIN);
2148 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2149 msgnum, smi.smi_refcount);
2151 /* If the reference count is now zero, delete the message
2152 * (and its supplementary record as well).
2154 if (smi.smi_refcount == 0) {
2155 lprintf(9, "Deleting message <%ld>\n", msgnum);
2157 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2158 delnum = (0L - msgnum);
2159 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2164 * Write a generic object to this room
2166 * Note: this could be much more efficient. Right now we use two temporary
2167 * files, and still pull the message into memory as with all others.
2169 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2170 char *content_type, /* MIME type of this object */
2171 char *tempfilename, /* Where to fetch it from */
2172 struct usersupp *is_mailbox, /* Mailbox room? */
2173 int is_binary, /* Is encoding necessary? */
2174 int is_unique, /* Del others of this type? */
2175 unsigned int flags /* Internal save flags */
2180 char filename[PATH_MAX];
2183 struct quickroom qrbuf;
2184 char roomname[ROOMNAMELEN];
2185 struct CtdlMessage *msg;
2188 if (is_mailbox != NULL)
2189 MailboxName(roomname, is_mailbox, req_room);
2191 safestrncpy(roomname, req_room, sizeof(roomname));
2192 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2194 strcpy(filename, tmpnam(NULL));
2195 fp = fopen(filename, "w");
2199 tempfp = fopen(tempfilename, "r");
2200 if (tempfp == NULL) {
2206 fprintf(fp, "Content-type: %s\n", content_type);
2207 lprintf(9, "Content-type: %s\n", content_type);
2209 if (is_binary == 0) {
2210 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2211 while (ch = getc(tempfp), ch > 0)
2217 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2220 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2221 tempfilename, filename);
2225 lprintf(9, "Allocating\n");
2226 msg = mallok(sizeof(struct CtdlMessage));
2227 memset(msg, 0, sizeof(struct CtdlMessage));
2228 msg->cm_magic = CTDLMESSAGE_MAGIC;
2229 msg->cm_anon_type = MES_NORMAL;
2230 msg->cm_format_type = 4;
2231 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2232 msg->cm_fields['O'] = strdoop(req_room);
2233 msg->cm_fields['N'] = strdoop(config.c_nodename);
2234 msg->cm_fields['H'] = strdoop(config.c_humannode);
2235 msg->cm_flags = flags;
2237 lprintf(9, "Loading\n");
2238 fp = fopen(filename, "rb");
2239 fseek(fp, 0L, SEEK_END);
2242 msg->cm_fields['M'] = mallok(len);
2243 fread(msg->cm_fields['M'], len, 1, fp);
2247 /* Create the requested room if we have to. */
2248 if (getroom(&qrbuf, roomname) != 0) {
2249 create_room(roomname, 4, "", 0);
2251 /* If the caller specified this object as unique, delete all
2252 * other objects of this type that are currently in the room.
2255 lprintf(9, "Deleted %d other msgs of this type\n",
2256 CtdlDeleteMessages(roomname, 0L, content_type));
2258 /* Now write the data */
2259 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2260 CtdlFreeMessage(msg);