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 "", "", "", "", "", "", "", "",
50 "", "", "", "", "", "",
71 * This function is self explanatory.
72 * (What can I say, I'm in a weird mood today...)
74 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
78 for (i = 0; i < strlen(name); ++i)
81 if (isspace(name[i - 1])) {
82 strcpy(&name[i - 1], &name[i]);
85 while (isspace(name[i + 1])) {
86 strcpy(&name[i + 1], &name[i + 2]);
93 * Aliasing for network mail.
94 * (Error messages have been commented out, because this is a server.)
97 { /* process alias and routing info for mail */
100 char aaa[300], bbb[300];
102 lprintf(9, "alias() called for <%s>\n", name);
104 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
106 fp = fopen("network/mail.aliases", "r");
108 fp = fopen("/dev/null", "r");
113 while (fgets(aaa, sizeof aaa, fp) != NULL) {
114 while (isspace(name[0]))
115 strcpy(name, &name[1]);
116 aaa[strlen(aaa) - 1] = 0;
118 for (a = 0; a < strlen(aaa); ++a) {
120 strcpy(bbb, &aaa[a + 1]);
124 if (!strcasecmp(name, aaa))
128 lprintf(7, "Mail is being forwarded to %s\n", name);
130 /* determine local or remote type, see citadel.h */
131 for (a = 0; a < strlen(name); ++a)
133 return (MES_INTERNET);
134 for (a = 0; a < strlen(name); ++a)
136 for (b = a; b < strlen(name); ++b)
138 return (MES_INTERNET);
140 for (a = 0; a < strlen(name); ++a)
144 lprintf(7, "Too many @'s in address\n");
148 for (a = 0; a < strlen(name); ++a)
150 strcpy(bbb, &name[a + 1]);
152 strcpy(bbb, &bbb[1]);
153 fp = fopen("network/mail.sysinfo", "r");
157 a = getstring(fp, aaa);
158 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
159 a = getstring(fp, aaa);
160 if (!strncmp(aaa, "use ", 4)) {
161 strcpy(bbb, &aaa[4]);
166 if (!strncmp(aaa, "uum", 3)) {
168 for (a = 0; a < strlen(bbb); ++a) {
174 while (bbb[strlen(bbb) - 1] == '_')
175 bbb[strlen(bbb) - 1] = 0;
176 sprintf(name, &aaa[4], bbb);
177 return (MES_INTERNET);
179 if (!strncmp(aaa, "bin", 3)) {
182 while (aaa[strlen(aaa) - 1] != '@')
183 aaa[strlen(aaa) - 1] = 0;
184 aaa[strlen(aaa) - 1] = 0;
185 while (aaa[strlen(aaa) - 1] == ' ')
186 aaa[strlen(aaa) - 1] = 0;
187 while (bbb[0] != '@')
188 strcpy(bbb, &bbb[1]);
189 strcpy(bbb, &bbb[1]);
190 while (bbb[0] == ' ')
191 strcpy(bbb, &bbb[1]);
192 sprintf(name, "%s @%s", aaa, bbb);
205 fp = fopen("citadel.control", "r");
206 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
212 void simple_listing(long msgnum)
214 cprintf("%ld\n", msgnum);
219 /* Determine if a given message matches the fields in a message template.
220 * Return 0 for a successful match.
222 int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
225 /* If there aren't any fields in the template, all messages will
228 if (template == NULL) return(0);
230 /* Null messages are bogus. */
231 if (msg == NULL) return(1);
233 for (i='A'; i<='Z'; ++i) {
234 if (template->cm_fields[i] != NULL) {
235 if (msg->cm_fields[i] == NULL) {
238 if (strcasecmp(msg->cm_fields[i],
239 template->cm_fields[i])) return 1;
243 /* All compares succeeded: we have a match! */
251 * API function to perform an operation for each qualifying message in the
254 void CtdlForEachMessage(int mode, long ref,
256 struct CtdlMessage *compare,
257 void (*CallBack) (long msgnum))
262 struct cdbdata *cdbfr;
263 long *msglist = NULL;
266 struct SuppMsgInfo smi;
267 struct CtdlMessage *msg;
269 /* Learn about the user and room in question */
271 getuser(&CC->usersupp, CC->curr_user);
272 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
274 /* Load the message list */
275 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
277 msglist = mallok(cdbfr->len);
278 memcpy(msglist, cdbfr->ptr, cdbfr->len);
279 num_msgs = cdbfr->len / sizeof(long);
282 return; /* No messages at all? No further action. */
286 /* If the caller is looking for a specific MIME type, then filter
287 * out all messages which are not of the type requested.
290 if (content_type != NULL)
291 if (strlen(content_type) > 0)
292 for (a = 0; a < num_msgs; ++a) {
293 GetSuppMsgInfo(&smi, msglist[a]);
294 if (strcasecmp(smi.smi_content_type, content_type)) {
299 num_msgs = sort_msglist(msglist, num_msgs);
301 /* If a template was supplied, filter out the messages which
302 * don't match. (This could induce some delays!)
305 if (compare != NULL) {
306 for (a = 0; a < num_msgs; ++a) {
307 msg = CtdlFetchMessage(msglist[a]);
309 if (CtdlMsgCmp(msg, compare)) {
312 CtdlFreeMessage(msg);
320 * Now iterate through the message list, according to the
321 * criteria supplied by the caller.
324 for (a = 0; a < num_msgs; ++a) {
325 thismsg = msglist[a];
330 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
331 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
332 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
333 && (CC->usersupp.flags & US_LASTOLD))
334 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
335 || ((mode == MSGS_FIRST) && (a < ref))
336 || ((mode == MSGS_GT) && (thismsg > ref))
342 phree(msglist); /* Clean up */
348 * cmd_msgs() - get list of message #'s in this room
349 * implements the MSGS server command using CtdlForEachMessage()
351 void cmd_msgs(char *cmdbuf)
360 int with_template = 0;
361 struct CtdlMessage *template = NULL;
363 extract(which, cmdbuf, 0);
364 cm_ref = extract_int(cmdbuf, 1);
365 with_template = extract_int(cmdbuf, 2);
369 if (!strncasecmp(which, "OLD", 3))
371 else if (!strncasecmp(which, "NEW", 3))
373 else if (!strncasecmp(which, "FIRST", 5))
375 else if (!strncasecmp(which, "LAST", 4))
377 else if (!strncasecmp(which, "GT", 2))
380 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
381 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
386 cprintf("%d Send template then receive message list\n",
388 template = (struct CtdlMessage *)
389 mallok(sizeof(struct CtdlMessage));
390 memset(template, 0, sizeof(struct CtdlMessage));
391 while(client_gets(buf), strcmp(buf,"000")) {
392 extract(tfield, buf, 0);
393 extract(tvalue, buf, 1);
394 for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
395 if (!strcasecmp(tfield, msgkeys[i])) {
396 template->cm_fields[i] =
403 cprintf("%d Message list...\n", LISTING_FOLLOWS);
406 CtdlForEachMessage(mode, cm_ref, NULL, template, simple_listing);
407 if (template != NULL) CtdlFreeMessage(template);
415 * help_subst() - support routine for help file viewer
417 void help_subst(char *strbuf, char *source, char *dest)
422 while (p = pattern2(strbuf, source), (p >= 0)) {
423 strcpy(workbuf, &strbuf[p + strlen(source)]);
424 strcpy(&strbuf[p], dest);
425 strcat(strbuf, workbuf);
430 void do_help_subst(char *buffer)
434 help_subst(buffer, "^nodename", config.c_nodename);
435 help_subst(buffer, "^humannode", config.c_humannode);
436 help_subst(buffer, "^fqdn", config.c_fqdn);
437 help_subst(buffer, "^username", CC->usersupp.fullname);
438 sprintf(buf2, "%ld", CC->usersupp.usernum);
439 help_subst(buffer, "^usernum", buf2);
440 help_subst(buffer, "^sysadm", config.c_sysadm);
441 help_subst(buffer, "^variantname", CITADEL);
442 sprintf(buf2, "%d", config.c_maxsessions);
443 help_subst(buffer, "^maxsessions", buf2);
449 * memfmout() - Citadel text formatter and paginator.
450 * Although the original purpose of this routine was to format
451 * text to the reader's screen width, all we're really using it
452 * for here is to format text out to 80 columns before sending it
453 * to the client. The client software may reformat it again.
455 void memfmout(int width, char *mptr, char subst)
456 /* screen width to use */
457 /* where are we going to get our text from? */
458 /* nonzero if we should use hypertext mode */
470 c = 1; /* c is the current pos */
473 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
475 buffer[strlen(buffer) + 1] = 0;
476 buffer[strlen(buffer)] = ch;
479 if (buffer[0] == '^')
480 do_help_subst(buffer);
482 buffer[strlen(buffer) + 1] = 0;
484 strcpy(buffer, &buffer[1]);
494 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
496 if (((old == 13) || (old == 10)) && (isspace(real))) {
504 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
505 cprintf("\n%s", aaa);
514 if ((strlen(aaa) + c) > (width - 5)) {
524 if ((ch == 13) || (ch == 10)) {
525 cprintf("%s\n", aaa);
532 FMTEND: cprintf("%s\n", aaa);
538 * Callback function for mime parser that simply lists the part
540 void list_this_part(char *name, char *filename, char *partnum, char *disp,
541 void *content, char *cbtype, size_t length)
544 cprintf("part=%s|%s|%s|%s|%s|%d\n",
545 name, filename, partnum, disp, cbtype, length);
550 * Callback function for mime parser that wants to display text
552 void fixed_output(char *name, char *filename, char *partnum, char *disp,
553 void *content, char *cbtype, size_t length)
557 if (!strcasecmp(cbtype, "multipart/alternative")) {
558 strcpy(ma->prefix, partnum);
559 strcat(ma->prefix, ".");
565 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
567 && (ma->did_print == 1) ) {
568 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
574 if (!strcasecmp(cbtype, "text/plain")) {
575 client_write(content, length);
577 else if (!strcasecmp(cbtype, "text/html")) {
578 ptr = html_to_ascii(content, 80, 0);
579 client_write(ptr, strlen(ptr));
582 else if (strncasecmp(cbtype, "multipart/", 10)) {
583 cprintf("Part %s: %s (%s) (%d bytes)\n",
584 partnum, filename, cbtype, length);
590 * Callback function for mime parser that opens a section for downloading
592 void mime_download(char *name, char *filename, char *partnum, char *disp,
593 void *content, char *cbtype, size_t length)
596 /* Silently go away if there's already a download open... */
597 if (CC->download_fp != NULL)
600 /* ...or if this is not the desired section */
601 if (strcasecmp(desired_section, partnum))
604 CC->download_fp = tmpfile();
605 if (CC->download_fp == NULL)
608 fwrite(content, length, 1, CC->download_fp);
609 fflush(CC->download_fp);
610 rewind(CC->download_fp);
612 OpenCmdResult(filename, cbtype);
618 * Load a message from disk into memory.
619 * This is used by output_message() and other fetch functions.
621 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
622 * using the CtdlMessageFree() function.
624 struct CtdlMessage *CtdlFetchMessage(long msgnum)
626 struct cdbdata *dmsgtext;
627 struct CtdlMessage *ret = NULL;
630 CIT_UBYTE field_header;
634 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
635 if (dmsgtext == NULL) {
636 lprintf(9, "CtdlFetchMessage(%ld) failed.\n");
639 mptr = dmsgtext->ptr;
641 /* Parse the three bytes that begin EVERY message on disk.
642 * The first is always 0xFF, the on-disk magic number.
643 * The second is the anonymous/public type byte.
644 * The third is the format type byte (vari, fixed, or MIME).
648 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
652 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
653 memset(ret, 0, sizeof(struct CtdlMessage));
655 ret->cm_magic = CTDLMESSAGE_MAGIC;
656 ret->cm_anon_type = *mptr++; /* Anon type byte */
657 ret->cm_format_type = *mptr++; /* Format type byte */
660 * The rest is zero or more arbitrary fields. Load them in.
661 * We're done when we encounter either a zero-length field or
662 * have just processed the 'M' (message text) field.
665 field_length = strlen(mptr);
666 if (field_length == 0)
668 field_header = *mptr++;
669 ret->cm_fields[field_header] = mallok(field_length);
670 strcpy(ret->cm_fields[field_header], mptr);
672 while (*mptr++ != 0); /* advance to next field */
674 } while ((field_length > 0) && (field_header != 'M'));
678 /* Perform "before read" hooks (aborting if any return nonzero) */
679 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
680 CtdlFreeMessage(ret);
689 * Returns 1 if the supplied pointer points to a valid Citadel message.
690 * If the pointer is NULL or the magic number check fails, returns 0.
692 int is_valid_message(struct CtdlMessage *msg) {
695 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
696 lprintf(3, "is_valid_message() -- self-check failed\n");
704 * 'Destructor' for struct CtdlMessage
706 void CtdlFreeMessage(struct CtdlMessage *msg)
710 if (is_valid_message(msg) == 0) return;
712 for (i = 0; i < 256; ++i)
713 if (msg->cm_fields[i] != NULL)
714 phree(msg->cm_fields[i]);
716 msg->cm_magic = 0; /* just in case */
723 * Get a message off disk. (return value is the message's timestamp)
726 void output_message(char *msgid, int mode, int headers_only)
734 char display_name[256];
736 struct CtdlMessage *TheMessage = NULL;
740 /* buffers needed for RFC822 translation */
748 msg_num = atol(msgid);
750 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
751 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
755 /* FIX ... small security issue
756 * We need to check to make sure the requested message is actually
757 * in the current room, and set msg_ok to 1 only if it is. This
758 * functionality is currently missing because I'm in a hurry to replace
759 * broken production code with nonbroken pre-beta code. :( -- ajc
762 cprintf("%d Message %ld is not in this room.\n",
769 * Fetch the message from disk
771 TheMessage = CtdlFetchMessage(msg_num);
772 if (TheMessage == NULL) {
773 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
777 /* Are we downloading a MIME component? */
778 if (mode == MT_DOWNLOAD) {
779 if (TheMessage->cm_format_type != 4) {
780 cprintf("%d This is not a MIME message.\n",
782 } else if (CC->download_fp != NULL) {
783 cprintf("%d You already have a download open.\n",
786 /* Parse the message text component */
787 mptr = TheMessage->cm_fields['M'];
788 mime_parser(mptr, NULL, *mime_download);
789 /* If there's no file open by this time, the requested
790 * section wasn't found, so print an error
792 if (CC->download_fp == NULL) {
793 cprintf("%d Section %s not found.\n",
794 ERROR + FILE_NOT_FOUND,
798 CtdlFreeMessage(TheMessage);
802 /* now for the user-mode message reading loops */
803 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
805 /* Tell the client which format type we're using. If this is a
806 * MIME message, *lie* about it and tell the user it's fixed-format.
808 if (mode == MT_CITADEL) {
809 if (TheMessage->cm_format_type == 4)
812 cprintf("type=%d\n", TheMessage->cm_format_type);
815 /* nhdr=yes means that we're only displaying headers, no body */
816 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
817 cprintf("nhdr=yes\n");
820 /* begin header processing loop for Citadel message format */
822 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
824 strcpy(display_name, "<unknown>");
825 if (TheMessage->cm_fields['A']) {
826 strcpy(buf, TheMessage->cm_fields['A']);
827 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
828 if (TheMessage->cm_anon_type == MES_ANON)
829 strcpy(display_name, "****");
830 else if (TheMessage->cm_anon_type == MES_AN2)
831 strcpy(display_name, "anonymous");
833 strcpy(display_name, buf);
835 && ((TheMessage->cm_anon_type == MES_ANON)
836 || (TheMessage->cm_anon_type == MES_AN2))) {
837 sprintf(&display_name[strlen(display_name)],
842 strcpy(allkeys, FORDER);
843 for (i=0; i<strlen(allkeys); ++i) {
844 k = (int) allkeys[i];
846 if (TheMessage->cm_fields[k] != NULL) {
855 TheMessage->cm_fields[k]
864 /* begin header processing loop for RFC822 transfer format */
868 strcpy(snode, NODENAME);
869 strcpy(lnode, HUMANNODE);
870 if (mode == MT_RFC822) {
871 for (i = 0; i < 256; ++i) {
872 if (TheMessage->cm_fields[i]) {
873 mptr = TheMessage->cm_fields[i];
877 } else if (i == 'P') {
878 cprintf("Path: %s\n", mptr);
879 for (a = 0; a < strlen(mptr); ++a) {
880 if (mptr[a] == '!') {
881 strcpy(mptr, &mptr[a + 1]);
887 cprintf("Subject: %s\n", mptr);
893 cprintf("X-Citadel-Room: %s\n", mptr);
897 cprintf("To: %s\n", mptr);
900 cprintf("Date: %s", asctime(localtime(&xtime)));
906 if (mode == MT_RFC822) {
907 if (!strcasecmp(snode, NODENAME)) {
910 cprintf("Message-ID: <%s@%s>\n", mid, snode);
911 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
912 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
913 cprintf("Organization: %s\n", lnode);
916 /* end header processing loop ... at this point, we're in the text */
918 mptr = TheMessage->cm_fields['M'];
920 /* Tell the client about the MIME parts in this message */
921 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
922 if (mode == MT_CITADEL) {
923 mime_parser(mptr, NULL, *list_this_part);
925 else if (mode == MT_MIME) { /* list parts only */
926 mime_parser(mptr, NULL, *list_this_part);
928 CtdlFreeMessage(TheMessage);
935 CtdlFreeMessage(TheMessage);
939 /* signify start of msg text */
940 if (mode == MT_CITADEL)
942 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
945 /* If the format type on disk is 1 (fixed-format), then we want
946 * everything to be output completely literally ... regardless of
947 * what message transfer format is in use.
949 if (TheMessage->cm_format_type == 1) {
951 while (ch = *mptr++, ch > 0) {
954 if ((ch == 10) || (strlen(buf) > 250)) {
955 cprintf("%s\n", buf);
958 buf[strlen(buf) + 1] = 0;
959 buf[strlen(buf)] = ch;
963 cprintf("%s\n", buf);
966 /* If the message on disk is format 0 (Citadel vari-format), we
967 * output using the formatter at 80 columns. This is the final output
968 * form if the transfer format is RFC822, but if the transfer format
969 * is Citadel proprietary, it'll still work, because the indentation
970 * for new paragraphs is correct and the client will reformat the
971 * message to the reader's screen width.
973 if (TheMessage->cm_format_type == 0) {
974 memfmout(80, mptr, 0);
977 /* If the message on disk is format 4 (MIME), we've gotta hand it
978 * off to the MIME parser. The client has already been told that
979 * this message is format 1 (fixed format), so the callback function
980 * we use will display those parts as-is.
982 if (TheMessage->cm_format_type == 4) {
983 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
984 memset(ma, 0, sizeof(struct ma_info));
985 mime_parser(mptr, NULL, *fixed_output);
990 CtdlFreeMessage(TheMessage);
997 * display a message (mode 0 - Citadel proprietary)
999 void cmd_msg0(char *cmdbuf)
1002 int headers_only = 0;
1004 extract(msgid, cmdbuf, 0);
1005 headers_only = extract_int(cmdbuf, 1);
1007 output_message(msgid, MT_CITADEL, headers_only);
1013 * display a message (mode 2 - RFC822)
1015 void cmd_msg2(char *cmdbuf)
1018 int headers_only = 0;
1020 extract(msgid, cmdbuf, 0);
1021 headers_only = extract_int(cmdbuf, 1);
1023 output_message(msgid, MT_RFC822, headers_only);
1029 * display a message (mode 3 - IGnet raw format - internal programs only)
1031 void cmd_msg3(char *cmdbuf)
1034 struct CtdlMessage *msg;
1037 if (CC->internal_pgm == 0) {
1038 cprintf("%d This command is for internal programs only.\n",
1043 msgnum = extract_long(cmdbuf, 0);
1044 msg = CtdlFetchMessage(msgnum);
1046 cprintf("%d Message %ld not found.\n",
1051 serialize_message(&smr, msg);
1052 CtdlFreeMessage(msg);
1055 cprintf("%d Unable to serialize message\n",
1056 ERROR+INTERNAL_ERROR);
1060 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1061 client_write(smr.ser, smr.len);
1068 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
1070 void cmd_msg4(char *cmdbuf)
1074 extract(msgid, cmdbuf, 0);
1076 output_message(msgid, MT_MIME, 0);
1080 * Open a component of a MIME message as a download file
1082 void cmd_opna(char *cmdbuf)
1086 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
1088 extract(msgid, cmdbuf, 0);
1089 extract(desired_section, cmdbuf, 1);
1091 output_message(msgid, MT_DOWNLOAD, 0);
1095 * Message base operation to send a message to the master file
1096 * (returns new message number)
1098 * This is the back end for CtdlSaveMsg() and should not be directly
1099 * called by server-side modules.
1102 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
1103 int generate_id, /* generate 'I' field? */
1104 FILE *save_a_copy) /* save a copy to disk? */
1111 /* Get a new message number */
1112 newmsgid = get_new_message_number();
1113 sprintf(msgidbuf, "%ld", newmsgid);
1116 msg->cm_fields['I'] = strdoop(msgidbuf);
1119 serialize_message(&smr, msg);
1122 cprintf("%d Unable to serialize message\n",
1123 ERROR+INTERNAL_ERROR);
1127 /* Write our little bundle of joy into the message base */
1128 begin_critical_section(S_MSGMAIN);
1129 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1130 smr.ser, smr.len) < 0) {
1131 lprintf(2, "Can't store message\n");
1136 end_critical_section(S_MSGMAIN);
1138 /* If the caller specified that a copy should be saved to a particular
1139 * file handle, do that now too.
1141 if (save_a_copy != NULL) {
1142 fwrite(smr.ser, smr.len, 1, save_a_copy);
1145 /* Free the memory we used for the serialized message */
1148 /* Return the *local* message ID to the caller
1149 * (even if we're storing an incoming network message)
1157 * Serialize a struct CtdlMessage into the format used on disk and network.
1159 * This function loads up a "struct ser_ret" (defined in server.h) which
1160 * contains the length of the serialized message and a pointer to the
1161 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1163 void serialize_message(struct ser_ret *ret, /* return values */
1164 struct CtdlMessage *msg) /* unserialized msg */
1168 static char *forder = FORDER;
1170 lprintf(9, "serialize_message() called\n");
1172 if (is_valid_message(msg) == 0) return; /* self check */
1174 lprintf(9, "magic number check OK.\n");
1177 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1178 ret->len = ret->len +
1179 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1181 lprintf(9, "message is %d bytes\n", ret->len);
1183 lprintf(9, "calling malloc\n");
1184 ret->ser = mallok(ret->len);
1185 if (ret->ser == NULL) {
1191 ret->ser[1] = msg->cm_anon_type;
1192 ret->ser[2] = msg->cm_format_type;
1195 lprintf(9, "stuff\n");
1196 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1197 ret->ser[wlen++] = (char)forder[i];
1198 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1199 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1201 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1203 lprintf(9, "done serializing\n");
1211 * Back end for the ReplicationChecks() function
1213 void check_repl(long msgnum) {
1214 struct CtdlMessage *msg;
1215 time_t timestamp = (-1L);
1217 msg = CtdlFetchMessage(msgnum);
1218 if (msg == NULL) return;
1219 if (msg->cm_fields['T'] != NULL) {
1220 timestamp = atol(msg->cm_fields['T']);
1222 CtdlFreeMessage(msg);
1224 if (timestamp > msg_repl->highest) {
1225 msg_repl->highest = timestamp; /* newer! */
1229 /* Existing isn't newer? Then delete the old one(s). */
1230 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, NULL);
1235 * Check to see if any messages already exist which carry the same Extended ID
1239 * -> With older timestamps: delete them and return 0. Message will be saved.
1240 * -> With newer timestamps: return 1. Message save will be aborted.
1242 int ReplicationChecks(struct CtdlMessage *msg) {
1243 struct CtdlMessage *template;
1246 /* No extended id? Don't do anything. */
1247 if (msg->cm_fields['E'] == NULL) return 0;
1248 if (strlen(msg->cm_fields['E']) == 0) return 0;
1250 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1251 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1252 msg_repl->highest = (-1L);
1254 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1255 memset(template, 0, sizeof(struct CtdlMessage));
1256 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1258 CtdlForEachMessage(MSGS_ALL, 0L, NULL, template, check_repl);
1260 /* If a newer message exists with the same Extended ID, abort
1263 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1267 CtdlFreeMessage(template);
1279 * Save a message to disk
1281 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1282 char *rec, /* Recipient (mail) */
1283 char *force, /* force a particular room? */
1284 int mailtype, /* local or remote type */
1285 int generate_id) /* 1 = generate 'I' field */
1288 char hold_rm[ROOMNAMELEN];
1289 char actual_rm[ROOMNAMELEN];
1290 char force_room[ROOMNAMELEN];
1291 char content_type[256]; /* We have to learn this */
1292 char recipient[256];
1295 struct usersupp userbuf;
1297 int successful_local_recipients = 0;
1298 struct quickroom qtemp;
1299 struct SuppMsgInfo smi;
1300 FILE *network_fp = NULL;
1301 static int seqnum = 1;
1303 lprintf(9, "CtdlSaveMsg() called\n");
1304 if (is_valid_message(msg) == 0) return; /* self check */
1306 /* If this message has no timestamp, we take the liberty of
1307 * giving it one, right now.
1309 if (msg->cm_fields['T'] == NULL) {
1310 sprintf(aaa, "%ld", time(NULL));
1311 msg->cm_fields['T'] = strdoop(aaa);
1314 /* If this message has no path, we generate one.
1316 if (msg->cm_fields['P'] == NULL) {
1317 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1318 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1319 if (isspace(msg->cm_fields['P'][a])) {
1320 msg->cm_fields['P'][a] = ' ';
1325 strcpy(force_room, force);
1327 /* Strip non-printable characters out of the recipient name */
1328 strcpy(recipient, rec);
1329 for (a = 0; a < strlen(recipient); ++a)
1330 if (!isprint(recipient[a]))
1331 strcpy(&recipient[a], &recipient[a + 1]);
1333 /* Learn about what's inside, because it's what's inside that counts */
1335 switch (msg->cm_format_type) {
1337 strcpy(content_type, "text/x-citadel-variformat");
1340 strcpy(content_type, "text/plain");
1343 strcpy(content_type, "text/plain");
1344 /* advance past header fields */
1345 mptr = msg->cm_fields['M'];
1348 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1349 safestrncpy(content_type, mptr,
1350 sizeof(content_type));
1351 strcpy(content_type, &content_type[14]);
1352 for (a = 0; a < strlen(content_type); ++a)
1353 if ((content_type[a] == ';')
1354 || (content_type[a] == ' ')
1355 || (content_type[a] == 13)
1356 || (content_type[a] == 10))
1357 content_type[a] = 0;
1364 /* Perform "before save" hooks (aborting if any return nonzero) */
1365 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1367 /* If this message has an Extended ID, perform replication checks */
1368 if (ReplicationChecks(msg) > 0) return;
1370 /* Network mail - send a copy to the network program. */
1371 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1372 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1373 (long) getpid(), CC->cs_pid, ++seqnum);
1374 lprintf(9, "Saving a copy to %s\n", aaa);
1375 network_fp = fopen(aaa, "ab+");
1376 if (network_fp == NULL)
1377 lprintf(2, "ERROR: %s\n", strerror(errno));
1380 /* Save it to disk */
1381 newmsgid = send_message(msg, generate_id, network_fp);
1382 if (network_fp != NULL) {
1384 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1389 strcpy(actual_rm, CC->quickroom.QRname);
1390 strcpy(hold_rm, "");
1392 /* If this is being done by the networker delivering a private
1393 * message, we want to BYPASS saving the sender's copy (because there
1394 * is no local sender; it would otherwise go to the Trashcan).
1396 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1397 /* If the user is a twit, move to the twit room for posting */
1399 if (CC->usersupp.axlevel == 2) {
1400 strcpy(hold_rm, actual_rm);
1401 strcpy(actual_rm, config.c_twitroom);
1403 /* ...or if this message is destined for Aide> then go there. */
1404 if (strlen(force_room) > 0) {
1405 strcpy(hold_rm, actual_rm);
1406 strcpy(actual_rm, force_room);
1408 /* This call to usergoto() changes rooms if necessary. It also
1409 * causes the latest message list to be read into memory.
1411 usergoto(actual_rm, 0);
1413 /* read in the quickroom record, obtaining a lock... */
1414 lgetroom(&CC->quickroom, actual_rm);
1416 /* Fix an obscure bug */
1417 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1418 CC->quickroom.QRflags =
1419 CC->quickroom.QRflags & ~QR_MAILBOX;
1421 /* Add the message pointer to the room */
1422 CC->quickroom.QRhighest =
1423 AddMessageToRoom(&CC->quickroom, newmsgid);
1425 /* update quickroom */
1426 lputroom(&CC->quickroom);
1427 ++successful_local_recipients;
1430 /* Bump this user's messages posted counter. */
1431 lgetuser(&CC->usersupp, CC->curr_user);
1432 CC->usersupp.posted = CC->usersupp.posted + 1;
1433 lputuser(&CC->usersupp);
1435 /* If this is private, local mail, make a copy in the
1436 * recipient's mailbox and bump the reference count.
1438 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1439 if (getuser(&userbuf, recipient) == 0) {
1440 MailboxName(actual_rm, &userbuf, MAILROOM);
1441 if (lgetroom(&qtemp, actual_rm) == 0) {
1443 AddMessageToRoom(&qtemp, newmsgid);
1445 ++successful_local_recipients;
1449 /* If we've posted in a room other than the current room, then we
1450 * have to now go back to the current room...
1452 if (strlen(hold_rm) > 0) {
1453 usergoto(hold_rm, 0);
1456 /* Write a supplemental message info record. This doesn't have to
1457 * be a critical section because nobody else knows about this message
1460 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1461 smi.smi_msgnum = newmsgid;
1462 smi.smi_refcount = successful_local_recipients;
1463 safestrncpy(smi.smi_content_type, content_type, 64);
1464 PutSuppMsgInfo(&smi);
1466 /* Perform "after save" hooks */
1467 PerformMessageHooks(msg, EVT_AFTERSAVE);
1473 * Convenience function for generating small administrative messages.
1475 void quickie_message(char *from, char *to, char *room, char *text)
1477 struct CtdlMessage *msg;
1479 msg = mallok(sizeof(struct CtdlMessage));
1480 memset(msg, 0, sizeof(struct CtdlMessage));
1481 msg->cm_magic = CTDLMESSAGE_MAGIC;
1482 msg->cm_anon_type = MES_NORMAL;
1483 msg->cm_format_type = 0;
1484 msg->cm_fields['A'] = strdoop(from);
1485 msg->cm_fields['O'] = strdoop(room);
1486 msg->cm_fields['N'] = strdoop(NODENAME);
1488 msg->cm_fields['R'] = strdoop(to);
1489 msg->cm_fields['M'] = strdoop(text);
1491 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1492 CtdlFreeMessage(msg);
1493 syslog(LOG_NOTICE, text);
1498 * Build a binary message to be saved on disk.
1501 struct CtdlMessage *make_message(
1502 struct usersupp *author, /* author's usersupp structure */
1503 char *recipient, /* NULL if it's not mail */
1504 char *room, /* room where it's going */
1505 int type, /* see MES_ types in header file */
1506 int net_type, /* see MES_ types in header file */
1507 int format_type, /* local or remote (see citadel.h) */
1508 char *fake_name) /* who we're masquerading as */
1514 size_t message_len = 0;
1515 size_t buffer_len = 0;
1517 struct CtdlMessage *msg;
1519 msg = mallok(sizeof(struct CtdlMessage));
1520 memset(msg, 0, sizeof(struct CtdlMessage));
1521 msg->cm_magic = CTDLMESSAGE_MAGIC;
1522 msg->cm_anon_type = type;
1523 msg->cm_format_type = format_type;
1525 /* Don't confuse the poor folks if it's not routed mail. */
1526 strcpy(dest_node, "");
1528 /* If net_type is MES_BINARY, split out the destination node. */
1529 if (net_type == MES_BINARY) {
1530 strcpy(dest_node, NODENAME);
1531 for (a = 0; a < strlen(recipient); ++a) {
1532 if (recipient[a] == '@') {
1534 strcpy(dest_node, &recipient[a + 1]);
1539 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1540 if (net_type == MES_INTERNET) {
1541 strcpy(dest_node, "internet");
1544 while (isspace(recipient[strlen(recipient) - 1]))
1545 recipient[strlen(recipient) - 1] = 0;
1547 sprintf(buf, "cit%ld", author->usernum); /* Path */
1548 msg->cm_fields['P'] = strdoop(buf);
1550 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1551 msg->cm_fields['T'] = strdoop(buf);
1553 if (fake_name[0]) /* author */
1554 msg->cm_fields['A'] = strdoop(fake_name);
1556 msg->cm_fields['A'] = strdoop(author->fullname);
1558 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1559 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1561 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1563 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1564 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1566 if (recipient[0] != 0)
1567 msg->cm_fields['R'] = strdoop(recipient);
1568 if (dest_node[0] != 0)
1569 msg->cm_fields['D'] = strdoop(dest_node);
1571 msg->cm_fields['M'] = mallok(4096);
1572 if (msg->cm_fields['M'] == NULL) {
1573 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1577 msg->cm_fields['M'][0] = 0;
1581 /* read in the lines of message text one by one */
1582 while (client_gets(buf), strcmp(buf, "000")) {
1584 /* augment the buffer if we have to */
1585 if ((message_len + strlen(buf) + 2) > buffer_len) {
1586 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1587 if (ptr == NULL) { /* flush if can't allocate */
1588 while (client_gets(buf), strcmp(buf, "000")) ;;
1591 buffer_len = (buffer_len * 2);
1592 msg->cm_fields['M'] = ptr;
1596 strcat(msg->cm_fields['M'], buf);
1597 strcat(msg->cm_fields['M'], "\n");
1599 /* if we've hit the max msg length, flush the rest */
1600 if (message_len >= config.c_maxmsglen) {
1601 while (client_gets(buf), strcmp(buf, "000")) ;;
1614 * message entry - mode 0 (normal)
1616 void cmd_ent0(char *entargs)
1619 char recipient[256];
1621 int format_type = 0;
1622 char newusername[256];
1623 struct CtdlMessage *msg;
1627 struct usersupp tempUS;
1630 post = extract_int(entargs, 0);
1631 extract(recipient, entargs, 1);
1632 anon_flag = extract_int(entargs, 2);
1633 format_type = extract_int(entargs, 3);
1635 /* first check to make sure the request is valid. */
1637 if (!(CC->logged_in)) {
1638 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1641 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1642 cprintf("%d Need to be validated to enter ",
1643 ERROR + HIGHER_ACCESS_REQUIRED);
1644 cprintf("(except in %s> to sysop)\n", MAILROOM);
1647 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1648 cprintf("%d Need net privileges to enter here.\n",
1649 ERROR + HIGHER_ACCESS_REQUIRED);
1652 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1653 cprintf("%d Sorry, this is a read-only room.\n",
1654 ERROR + HIGHER_ACCESS_REQUIRED);
1661 if (CC->usersupp.axlevel < 6) {
1662 cprintf("%d You don't have permission to masquerade.\n",
1663 ERROR + HIGHER_ACCESS_REQUIRED);
1666 extract(newusername, entargs, 4);
1667 memset(CC->fake_postname, 0, 32);
1668 strcpy(CC->fake_postname, newusername);
1669 cprintf("%d Ok\n", OK);
1672 CC->cs_flags |= CS_POSTING;
1675 if (CC->quickroom.QRflags & QR_MAILBOX) {
1676 if (CC->usersupp.axlevel >= 2) {
1677 strcpy(buf, recipient);
1679 strcpy(buf, "sysop");
1680 e = alias(buf); /* alias and mail type */
1681 if ((buf[0] == 0) || (e == MES_ERROR)) {
1682 cprintf("%d Unknown address - cannot send message.\n",
1683 ERROR + NO_SUCH_USER);
1686 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1687 cprintf("%d Net privileges required for network mail.\n",
1688 ERROR + HIGHER_ACCESS_REQUIRED);
1691 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1692 && ((CC->usersupp.flags & US_INTERNET) == 0)
1693 && (!CC->internal_pgm)) {
1694 cprintf("%d You don't have access to Internet mail.\n",
1695 ERROR + HIGHER_ACCESS_REQUIRED);
1698 if (!strcasecmp(buf, "sysop")) {
1703 goto SKFALL; /* don't search local file */
1704 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1705 cprintf("%d Can't send mail to yourself!\n",
1706 ERROR + NO_SUCH_USER);
1709 /* Check to make sure the user exists; also get the correct
1710 * upper/lower casing of the name.
1712 a = getuser(&tempUS, buf);
1714 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1717 strcpy(buf, tempUS.fullname);
1720 SKFALL: b = MES_NORMAL;
1721 if (CC->quickroom.QRflags & QR_ANONONLY)
1723 if (CC->quickroom.QRflags & QR_ANONOPT) {
1727 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1730 /* If we're only checking the validity of the request, return
1731 * success without creating the message.
1734 cprintf("%d %s\n", OK, buf);
1738 cprintf("%d send message\n", SEND_LISTING);
1740 /* Read in the message from the client. */
1741 if (CC->fake_postname[0])
1742 msg = make_message(&CC->usersupp, buf,
1743 CC->quickroom.QRname, b, e, format_type,
1745 else if (CC->fake_username[0])
1746 msg = make_message(&CC->usersupp, buf,
1747 CC->quickroom.QRname, b, e, format_type,
1750 msg = make_message(&CC->usersupp, buf,
1751 CC->quickroom.QRname, b, e, format_type, "");
1754 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1755 CtdlFreeMessage(msg);
1756 CC->fake_postname[0] = '\0';
1763 * message entry - mode 3 (raw)
1765 void cmd_ent3(char *entargs)
1770 unsigned char ch, which_field;
1771 struct usersupp tempUS;
1773 struct CtdlMessage *msg;
1776 if (CC->internal_pgm == 0) {
1777 cprintf("%d This command is for internal programs only.\n",
1782 /* See if there's a recipient, but make sure it's a real one */
1783 extract(recp, entargs, 1);
1784 for (a = 0; a < strlen(recp); ++a)
1785 if (!isprint(recp[a]))
1786 strcpy(&recp[a], &recp[a + 1]);
1787 while (isspace(recp[0]))
1788 strcpy(recp, &recp[1]);
1789 while (isspace(recp[strlen(recp) - 1]))
1790 recp[strlen(recp) - 1] = 0;
1792 /* If we're in Mail, check the recipient */
1793 if (strlen(recp) > 0) {
1794 e = alias(recp); /* alias and mail type */
1795 if ((recp[0] == 0) || (e == MES_ERROR)) {
1796 cprintf("%d Unknown address - cannot send message.\n",
1797 ERROR + NO_SUCH_USER);
1800 if (e == MES_LOCAL) {
1801 a = getuser(&tempUS, recp);
1803 cprintf("%d No such user.\n",
1804 ERROR + NO_SUCH_USER);
1810 /* At this point, message has been approved. */
1811 if (extract_int(entargs, 0) == 0) {
1812 cprintf("%d OK to send\n", OK);
1816 msglen = extract_long(entargs, 2);
1817 msg = mallok(sizeof(struct CtdlMessage));
1819 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1823 memset(msg, 0, sizeof(struct CtdlMessage));
1824 tempbuf = mallok(msglen);
1825 if (tempbuf == NULL) {
1826 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1831 cprintf("%d %ld\n", SEND_BINARY, msglen);
1833 client_read(&ch, 1); /* 0xFF magic number */
1834 msg->cm_magic = CTDLMESSAGE_MAGIC;
1835 client_read(&ch, 1); /* anon type */
1836 msg->cm_anon_type = ch;
1837 client_read(&ch, 1); /* format type */
1838 msg->cm_format_type = ch;
1839 msglen = msglen - 3;
1841 while (msglen > 0) {
1842 client_read(&which_field, 1);
1846 client_read(&ch, 1);
1848 a = strlen(tempbuf);
1851 } while ( (ch != 0) && (msglen > 0) );
1852 msg->cm_fields[which_field] = strdoop(tempbuf);
1855 msg->cm_flags = CM_SKIP_HOOKS;
1856 CtdlSaveMsg(msg, recp, "", e, 0);
1857 CtdlFreeMessage(msg);
1863 * API function to delete messages which match a set of criteria
1864 * (returns the actual number of messages deleted)
1866 int CtdlDeleteMessages(char *room_name, /* which room */
1867 long dmsgnum, /* or "0" for any */
1868 char *content_type /* or NULL for any */
1872 struct quickroom qrbuf;
1873 struct cdbdata *cdbfr;
1874 long *msglist = NULL;
1877 int num_deleted = 0;
1879 struct SuppMsgInfo smi;
1881 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1882 room_name, dmsgnum, content_type);
1884 /* get room record, obtaining a lock... */
1885 if (lgetroom(&qrbuf, room_name) != 0) {
1886 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1888 return (0); /* room not found */
1890 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1892 if (cdbfr != NULL) {
1893 msglist = mallok(cdbfr->len);
1894 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1895 num_msgs = cdbfr->len / sizeof(long);
1899 for (i = 0; i < num_msgs; ++i) {
1902 /* Set/clear a bit for each criterion */
1904 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1905 delete_this |= 0x01;
1907 if (content_type == NULL) {
1908 delete_this |= 0x02;
1910 GetSuppMsgInfo(&smi, msglist[i]);
1911 if (!strcasecmp(smi.smi_content_type,
1913 delete_this |= 0x02;
1917 /* Delete message only if all bits are set */
1918 if (delete_this == 0x03) {
1919 AdjRefCount(msglist[i], -1);
1925 num_msgs = sort_msglist(msglist, num_msgs);
1926 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1927 msglist, (num_msgs * sizeof(long)));
1929 qrbuf.QRhighest = msglist[num_msgs - 1];
1933 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1934 return (num_deleted);
1940 * Delete message from current room
1942 void cmd_dele(char *delstr)
1947 getuser(&CC->usersupp, CC->curr_user);
1948 if ((CC->usersupp.axlevel < 6)
1949 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1950 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1951 && (!(CC->internal_pgm))) {
1952 cprintf("%d Higher access required.\n",
1953 ERROR + HIGHER_ACCESS_REQUIRED);
1956 delnum = extract_long(delstr, 0);
1958 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1961 cprintf("%d %d message%s deleted.\n", OK,
1962 num_deleted, ((num_deleted != 1) ? "s" : ""));
1964 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1970 * move a message to another room
1972 void cmd_move(char *args)
1976 struct quickroom qtemp;
1979 num = extract_long(args, 0);
1980 extract(targ, args, 1);
1982 getuser(&CC->usersupp, CC->curr_user);
1983 if ((CC->usersupp.axlevel < 6)
1984 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1985 cprintf("%d Higher access required.\n",
1986 ERROR + HIGHER_ACCESS_REQUIRED);
1989 if (getroom(&qtemp, targ) != 0) {
1990 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1993 /* Bump the reference count, otherwise the message will be deleted
1994 * from disk when we remove it from the source room.
1996 AdjRefCount(num, 1);
1998 /* yank the message out of the current room... */
1999 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
2002 /* put the message into the target room */
2003 lgetroom(&qtemp, targ);
2004 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
2006 cprintf("%d Message moved.\n", OK);
2008 AdjRefCount(num, (-1)); /* oops */
2009 cprintf("%d msg %ld does not exist.\n", ERROR, num);
2016 * GetSuppMsgInfo() - Get the supplementary record for a message
2018 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2021 struct cdbdata *cdbsmi;
2024 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2025 smibuf->smi_msgnum = msgnum;
2026 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2028 /* Use the negative of the message number for its supp record index */
2029 TheIndex = (0L - msgnum);
2031 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2032 if (cdbsmi == NULL) {
2033 return; /* record not found; go with defaults */
2035 memcpy(smibuf, cdbsmi->ptr,
2036 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2037 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2044 * PutSuppMsgInfo() - (re)write supplementary record for a message
2046 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2050 /* Use the negative of the message number for its supp record index */
2051 TheIndex = (0L - smibuf->smi_msgnum);
2053 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2054 smibuf->smi_msgnum, smibuf->smi_refcount);
2056 cdb_store(CDB_MSGMAIN,
2057 &TheIndex, sizeof(long),
2058 smibuf, sizeof(struct SuppMsgInfo));
2063 * AdjRefCount - change the reference count for a message;
2064 * delete the message if it reaches zero
2066 void AdjRefCount(long msgnum, int incr)
2069 struct SuppMsgInfo smi;
2072 /* This is a *tight* critical section; please keep it that way, as
2073 * it may get called while nested in other critical sections.
2074 * Complicating this any further will surely cause deadlock!
2076 begin_critical_section(S_SUPPMSGMAIN);
2077 GetSuppMsgInfo(&smi, msgnum);
2078 smi.smi_refcount += incr;
2079 PutSuppMsgInfo(&smi);
2080 end_critical_section(S_SUPPMSGMAIN);
2082 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2083 msgnum, smi.smi_refcount);
2085 /* If the reference count is now zero, delete the message
2086 * (and its supplementary record as well).
2088 if (smi.smi_refcount == 0) {
2089 lprintf(9, "Deleting message <%ld>\n", msgnum);
2091 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2092 delnum = (0L - msgnum);
2093 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2098 * Write a generic object to this room
2100 * Note: this could be much more efficient. Right now we use two temporary
2101 * files, and still pull the message into memory as with all others.
2103 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2104 char *content_type, /* MIME type of this object */
2105 char *tempfilename, /* Where to fetch it from */
2106 struct usersupp *is_mailbox, /* Mailbox room? */
2107 int is_binary, /* Is encoding necessary? */
2108 int is_unique, /* Del others of this type? */
2109 unsigned int flags /* Internal save flags */
2114 char filename[PATH_MAX];
2117 struct quickroom qrbuf;
2118 char roomname[ROOMNAMELEN];
2119 struct CtdlMessage *msg;
2122 if (is_mailbox != NULL)
2123 MailboxName(roomname, is_mailbox, req_room);
2125 safestrncpy(roomname, req_room, sizeof(roomname));
2126 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2128 strcpy(filename, tmpnam(NULL));
2129 fp = fopen(filename, "w");
2133 tempfp = fopen(tempfilename, "r");
2134 if (tempfp == NULL) {
2140 fprintf(fp, "Content-type: %s\n", content_type);
2141 lprintf(9, "Content-type: %s\n", content_type);
2143 if (is_binary == 0) {
2144 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2145 while (ch = getc(tempfp), ch > 0)
2151 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2154 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2155 tempfilename, filename);
2159 lprintf(9, "Allocating\n");
2160 msg = mallok(sizeof(struct CtdlMessage));
2161 memset(msg, 0, sizeof(struct CtdlMessage));
2162 msg->cm_magic = CTDLMESSAGE_MAGIC;
2163 msg->cm_anon_type = MES_NORMAL;
2164 msg->cm_format_type = 4;
2165 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2166 msg->cm_fields['O'] = strdoop(req_room);
2167 msg->cm_fields['N'] = strdoop(config.c_nodename);
2168 msg->cm_fields['H'] = strdoop(config.c_humannode);
2169 msg->cm_flags = flags;
2171 lprintf(9, "Loading\n");
2172 fp = fopen(filename, "rb");
2173 fseek(fp, 0L, SEEK_END);
2176 msg->cm_fields['M'] = mallok(len);
2177 fread(msg->cm_fields['M'], len, 1, fp);
2181 /* Create the requested room if we have to. */
2182 if (getroom(&qrbuf, roomname) != 0) {
2183 create_room(roomname, 4, "", 0);
2185 /* If the caller specified this object as unique, delete all
2186 * other objects of this type that are currently in the room.
2189 lprintf(9, "Deleted %d other msgs of this type\n",
2190 CtdlDeleteMessages(roomname, 0L, content_type));
2192 /* Now write the data */
2193 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2194 CtdlFreeMessage(msg);