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;
1217 msg = NULL; /* FIX change to get */
1220 if (msg->cm_fields['T'] != NULL) {
1221 timestamp = atol(msg->cm_fields['T']);
1222 if (timestamp > msg_repl->highest) {
1223 msg_repl->highest = timestamp; /* newer! */
1228 /* Existing isn't newer? Then delete the old one(s). */
1229 CtdlDeleteMessages(&CC->quickroom.QRname, msgnum, NULL);
1234 * Check to see if any messages already exist which carry the same Extended ID
1238 * -> With older timestamps: delete them and return 0. Message will be saved.
1239 * -> With newer timestamps: return 1. Message save will be aborted.
1241 int ReplicationChecks(struct CtdlMessage *msg) {
1242 struct CtdlMessage *template;
1245 /* No extended id? Don't do anything. */
1246 if (msg->cm_fields['E'] == NULL) return 0;
1247 if (strlen(msg->cm_fields['E']) == 0) return 0;
1249 CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1250 strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1251 msg_repl->highest = (-1L);
1253 template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1254 memset(template, 0, sizeof(struct CtdlMessage));
1255 template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1257 CtdlForEachMessage(MSGS_ALL, 0L, NULL, template, check_repl);
1259 /* If a newer message exists with the same Extended ID, abort
1262 if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1266 CtdlFreeMessage(template);
1278 * Save a message to disk
1280 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1281 char *rec, /* Recipient (mail) */
1282 char *force, /* force a particular room? */
1283 int mailtype, /* local or remote type */
1284 int generate_id) /* 1 = generate 'I' field */
1287 char hold_rm[ROOMNAMELEN];
1288 char actual_rm[ROOMNAMELEN];
1289 char force_room[ROOMNAMELEN];
1290 char content_type[256]; /* We have to learn this */
1291 char recipient[256];
1294 struct usersupp userbuf;
1296 int successful_local_recipients = 0;
1297 struct quickroom qtemp;
1298 struct SuppMsgInfo smi;
1299 FILE *network_fp = NULL;
1300 static int seqnum = 1;
1302 lprintf(9, "CtdlSaveMsg() called\n");
1303 if (is_valid_message(msg) == 0) return; /* self check */
1305 /* If this message has no timestamp, we take the liberty of
1306 * giving it one, right now.
1308 if (msg->cm_fields['T'] == NULL) {
1309 sprintf(aaa, "%ld", time(NULL));
1310 msg->cm_fields['T'] = strdoop(aaa);
1313 /* If this message has no path, we generate one.
1315 if (msg->cm_fields['P'] == NULL) {
1316 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1317 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1318 if (isspace(msg->cm_fields['P'][a])) {
1319 msg->cm_fields['P'][a] = ' ';
1324 strcpy(force_room, force);
1326 /* Strip non-printable characters out of the recipient name */
1327 strcpy(recipient, rec);
1328 for (a = 0; a < strlen(recipient); ++a)
1329 if (!isprint(recipient[a]))
1330 strcpy(&recipient[a], &recipient[a + 1]);
1332 /* Learn about what's inside, because it's what's inside that counts */
1334 switch (msg->cm_format_type) {
1336 strcpy(content_type, "text/x-citadel-variformat");
1339 strcpy(content_type, "text/plain");
1342 strcpy(content_type, "text/plain");
1343 /* advance past header fields */
1344 mptr = msg->cm_fields['M'];
1347 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1348 safestrncpy(content_type, mptr,
1349 sizeof(content_type));
1350 strcpy(content_type, &content_type[14]);
1351 for (a = 0; a < strlen(content_type); ++a)
1352 if ((content_type[a] == ';')
1353 || (content_type[a] == ' ')
1354 || (content_type[a] == 13)
1355 || (content_type[a] == 10))
1356 content_type[a] = 0;
1363 /* Perform "before save" hooks (aborting if any return nonzero) */
1364 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1366 /* If this message has an Extended ID, perform replication checks */
1367 if (ReplicationChecks(msg) > 0) return;
1369 /* Network mail - send a copy to the network program. */
1370 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1371 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1372 (long) getpid(), CC->cs_pid, ++seqnum);
1373 lprintf(9, "Saving a copy to %s\n", aaa);
1374 network_fp = fopen(aaa, "ab+");
1375 if (network_fp == NULL)
1376 lprintf(2, "ERROR: %s\n", strerror(errno));
1379 /* Save it to disk */
1380 newmsgid = send_message(msg, generate_id, network_fp);
1381 if (network_fp != NULL) {
1383 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1388 strcpy(actual_rm, CC->quickroom.QRname);
1389 strcpy(hold_rm, "");
1391 /* If this is being done by the networker delivering a private
1392 * message, we want to BYPASS saving the sender's copy (because there
1393 * is no local sender; it would otherwise go to the Trashcan).
1395 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1396 /* If the user is a twit, move to the twit room for posting */
1398 if (CC->usersupp.axlevel == 2) {
1399 strcpy(hold_rm, actual_rm);
1400 strcpy(actual_rm, config.c_twitroom);
1402 /* ...or if this message is destined for Aide> then go there. */
1403 if (strlen(force_room) > 0) {
1404 strcpy(hold_rm, actual_rm);
1405 strcpy(actual_rm, force_room);
1407 /* This call to usergoto() changes rooms if necessary. It also
1408 * causes the latest message list to be read into memory.
1410 usergoto(actual_rm, 0);
1412 /* read in the quickroom record, obtaining a lock... */
1413 lgetroom(&CC->quickroom, actual_rm);
1415 /* Fix an obscure bug */
1416 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1417 CC->quickroom.QRflags =
1418 CC->quickroom.QRflags & ~QR_MAILBOX;
1420 /* Add the message pointer to the room */
1421 CC->quickroom.QRhighest =
1422 AddMessageToRoom(&CC->quickroom, newmsgid);
1424 /* update quickroom */
1425 lputroom(&CC->quickroom);
1426 ++successful_local_recipients;
1429 /* Bump this user's messages posted counter. */
1430 lgetuser(&CC->usersupp, CC->curr_user);
1431 CC->usersupp.posted = CC->usersupp.posted + 1;
1432 lputuser(&CC->usersupp);
1434 /* If this is private, local mail, make a copy in the
1435 * recipient's mailbox and bump the reference count.
1437 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1438 if (getuser(&userbuf, recipient) == 0) {
1439 MailboxName(actual_rm, &userbuf, MAILROOM);
1440 if (lgetroom(&qtemp, actual_rm) == 0) {
1442 AddMessageToRoom(&qtemp, newmsgid);
1444 ++successful_local_recipients;
1448 /* If we've posted in a room other than the current room, then we
1449 * have to now go back to the current room...
1451 if (strlen(hold_rm) > 0) {
1452 usergoto(hold_rm, 0);
1455 /* Write a supplemental message info record. This doesn't have to
1456 * be a critical section because nobody else knows about this message
1459 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1460 smi.smi_msgnum = newmsgid;
1461 smi.smi_refcount = successful_local_recipients;
1462 safestrncpy(smi.smi_content_type, content_type, 64);
1463 PutSuppMsgInfo(&smi);
1465 /* Perform "after save" hooks */
1466 PerformMessageHooks(msg, EVT_AFTERSAVE);
1472 * Convenience function for generating small administrative messages.
1474 void quickie_message(char *from, char *to, char *room, char *text)
1476 struct CtdlMessage *msg;
1478 msg = mallok(sizeof(struct CtdlMessage));
1479 memset(msg, 0, sizeof(struct CtdlMessage));
1480 msg->cm_magic = CTDLMESSAGE_MAGIC;
1481 msg->cm_anon_type = MES_NORMAL;
1482 msg->cm_format_type = 0;
1483 msg->cm_fields['A'] = strdoop(from);
1484 msg->cm_fields['O'] = strdoop(room);
1485 msg->cm_fields['N'] = strdoop(NODENAME);
1487 msg->cm_fields['R'] = strdoop(to);
1488 msg->cm_fields['M'] = strdoop(text);
1490 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1491 CtdlFreeMessage(msg);
1492 syslog(LOG_NOTICE, text);
1497 * Build a binary message to be saved on disk.
1500 struct CtdlMessage *make_message(
1501 struct usersupp *author, /* author's usersupp structure */
1502 char *recipient, /* NULL if it's not mail */
1503 char *room, /* room where it's going */
1504 int type, /* see MES_ types in header file */
1505 int net_type, /* see MES_ types in header file */
1506 int format_type, /* local or remote (see citadel.h) */
1507 char *fake_name) /* who we're masquerading as */
1513 size_t message_len = 0;
1514 size_t buffer_len = 0;
1516 struct CtdlMessage *msg;
1518 msg = mallok(sizeof(struct CtdlMessage));
1519 memset(msg, 0, sizeof(struct CtdlMessage));
1520 msg->cm_magic = CTDLMESSAGE_MAGIC;
1521 msg->cm_anon_type = type;
1522 msg->cm_format_type = format_type;
1524 /* Don't confuse the poor folks if it's not routed mail. */
1525 strcpy(dest_node, "");
1527 /* If net_type is MES_BINARY, split out the destination node. */
1528 if (net_type == MES_BINARY) {
1529 strcpy(dest_node, NODENAME);
1530 for (a = 0; a < strlen(recipient); ++a) {
1531 if (recipient[a] == '@') {
1533 strcpy(dest_node, &recipient[a + 1]);
1538 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1539 if (net_type == MES_INTERNET) {
1540 strcpy(dest_node, "internet");
1543 while (isspace(recipient[strlen(recipient) - 1]))
1544 recipient[strlen(recipient) - 1] = 0;
1546 sprintf(buf, "cit%ld", author->usernum); /* Path */
1547 msg->cm_fields['P'] = strdoop(buf);
1549 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1550 msg->cm_fields['T'] = strdoop(buf);
1552 if (fake_name[0]) /* author */
1553 msg->cm_fields['A'] = strdoop(fake_name);
1555 msg->cm_fields['A'] = strdoop(author->fullname);
1557 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1558 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1560 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1562 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1563 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1565 if (recipient[0] != 0)
1566 msg->cm_fields['R'] = strdoop(recipient);
1567 if (dest_node[0] != 0)
1568 msg->cm_fields['D'] = strdoop(dest_node);
1570 msg->cm_fields['M'] = mallok(4096);
1571 if (msg->cm_fields['M'] == NULL) {
1572 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1576 msg->cm_fields['M'][0] = 0;
1580 /* read in the lines of message text one by one */
1581 while (client_gets(buf), strcmp(buf, "000")) {
1583 /* augment the buffer if we have to */
1584 if ((message_len + strlen(buf) + 2) > buffer_len) {
1585 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1586 if (ptr == NULL) { /* flush if can't allocate */
1587 while (client_gets(buf), strcmp(buf, "000")) ;;
1590 buffer_len = (buffer_len * 2);
1591 msg->cm_fields['M'] = ptr;
1595 strcat(msg->cm_fields['M'], buf);
1596 strcat(msg->cm_fields['M'], "\n");
1598 /* if we've hit the max msg length, flush the rest */
1599 if (message_len >= config.c_maxmsglen) {
1600 while (client_gets(buf), strcmp(buf, "000")) ;;
1613 * message entry - mode 0 (normal)
1615 void cmd_ent0(char *entargs)
1618 char recipient[256];
1620 int format_type = 0;
1621 char newusername[256];
1622 struct CtdlMessage *msg;
1626 struct usersupp tempUS;
1629 post = extract_int(entargs, 0);
1630 extract(recipient, entargs, 1);
1631 anon_flag = extract_int(entargs, 2);
1632 format_type = extract_int(entargs, 3);
1634 /* first check to make sure the request is valid. */
1636 if (!(CC->logged_in)) {
1637 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1640 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1641 cprintf("%d Need to be validated to enter ",
1642 ERROR + HIGHER_ACCESS_REQUIRED);
1643 cprintf("(except in %s> to sysop)\n", MAILROOM);
1646 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1647 cprintf("%d Need net privileges to enter here.\n",
1648 ERROR + HIGHER_ACCESS_REQUIRED);
1651 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1652 cprintf("%d Sorry, this is a read-only room.\n",
1653 ERROR + HIGHER_ACCESS_REQUIRED);
1660 if (CC->usersupp.axlevel < 6) {
1661 cprintf("%d You don't have permission to masquerade.\n",
1662 ERROR + HIGHER_ACCESS_REQUIRED);
1665 extract(newusername, entargs, 4);
1666 memset(CC->fake_postname, 0, 32);
1667 strcpy(CC->fake_postname, newusername);
1668 cprintf("%d Ok\n", OK);
1671 CC->cs_flags |= CS_POSTING;
1674 if (CC->quickroom.QRflags & QR_MAILBOX) {
1675 if (CC->usersupp.axlevel >= 2) {
1676 strcpy(buf, recipient);
1678 strcpy(buf, "sysop");
1679 e = alias(buf); /* alias and mail type */
1680 if ((buf[0] == 0) || (e == MES_ERROR)) {
1681 cprintf("%d Unknown address - cannot send message.\n",
1682 ERROR + NO_SUCH_USER);
1685 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1686 cprintf("%d Net privileges required for network mail.\n",
1687 ERROR + HIGHER_ACCESS_REQUIRED);
1690 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1691 && ((CC->usersupp.flags & US_INTERNET) == 0)
1692 && (!CC->internal_pgm)) {
1693 cprintf("%d You don't have access to Internet mail.\n",
1694 ERROR + HIGHER_ACCESS_REQUIRED);
1697 if (!strcasecmp(buf, "sysop")) {
1702 goto SKFALL; /* don't search local file */
1703 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1704 cprintf("%d Can't send mail to yourself!\n",
1705 ERROR + NO_SUCH_USER);
1708 /* Check to make sure the user exists; also get the correct
1709 * upper/lower casing of the name.
1711 a = getuser(&tempUS, buf);
1713 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1716 strcpy(buf, tempUS.fullname);
1719 SKFALL: b = MES_NORMAL;
1720 if (CC->quickroom.QRflags & QR_ANONONLY)
1722 if (CC->quickroom.QRflags & QR_ANONOPT) {
1726 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1729 /* If we're only checking the validity of the request, return
1730 * success without creating the message.
1733 cprintf("%d %s\n", OK, buf);
1737 cprintf("%d send message\n", SEND_LISTING);
1739 /* Read in the message from the client. */
1740 if (CC->fake_postname[0])
1741 msg = make_message(&CC->usersupp, buf,
1742 CC->quickroom.QRname, b, e, format_type,
1744 else if (CC->fake_username[0])
1745 msg = make_message(&CC->usersupp, buf,
1746 CC->quickroom.QRname, b, e, format_type,
1749 msg = make_message(&CC->usersupp, buf,
1750 CC->quickroom.QRname, b, e, format_type, "");
1753 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1754 CtdlFreeMessage(msg);
1755 CC->fake_postname[0] = '\0';
1762 * message entry - mode 3 (raw)
1764 void cmd_ent3(char *entargs)
1769 unsigned char ch, which_field;
1770 struct usersupp tempUS;
1772 struct CtdlMessage *msg;
1775 if (CC->internal_pgm == 0) {
1776 cprintf("%d This command is for internal programs only.\n",
1781 /* See if there's a recipient, but make sure it's a real one */
1782 extract(recp, entargs, 1);
1783 for (a = 0; a < strlen(recp); ++a)
1784 if (!isprint(recp[a]))
1785 strcpy(&recp[a], &recp[a + 1]);
1786 while (isspace(recp[0]))
1787 strcpy(recp, &recp[1]);
1788 while (isspace(recp[strlen(recp) - 1]))
1789 recp[strlen(recp) - 1] = 0;
1791 /* If we're in Mail, check the recipient */
1792 if (strlen(recp) > 0) {
1793 e = alias(recp); /* alias and mail type */
1794 if ((recp[0] == 0) || (e == MES_ERROR)) {
1795 cprintf("%d Unknown address - cannot send message.\n",
1796 ERROR + NO_SUCH_USER);
1799 if (e == MES_LOCAL) {
1800 a = getuser(&tempUS, recp);
1802 cprintf("%d No such user.\n",
1803 ERROR + NO_SUCH_USER);
1809 /* At this point, message has been approved. */
1810 if (extract_int(entargs, 0) == 0) {
1811 cprintf("%d OK to send\n", OK);
1815 msglen = extract_long(entargs, 2);
1816 msg = mallok(sizeof(struct CtdlMessage));
1818 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1822 memset(msg, 0, sizeof(struct CtdlMessage));
1823 tempbuf = mallok(msglen);
1824 if (tempbuf == NULL) {
1825 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1830 cprintf("%d %ld\n", SEND_BINARY, msglen);
1832 client_read(&ch, 1); /* 0xFF magic number */
1833 msg->cm_magic = CTDLMESSAGE_MAGIC;
1834 client_read(&ch, 1); /* anon type */
1835 msg->cm_anon_type = ch;
1836 client_read(&ch, 1); /* format type */
1837 msg->cm_format_type = ch;
1838 msglen = msglen - 3;
1840 while (msglen > 0) {
1841 client_read(&which_field, 1);
1845 client_read(&ch, 1);
1847 a = strlen(tempbuf);
1850 } while ( (ch != 0) && (msglen > 0) );
1851 msg->cm_fields[which_field] = strdoop(tempbuf);
1854 msg->cm_flags = CM_SKIP_HOOKS;
1855 CtdlSaveMsg(msg, recp, "", e, 0);
1856 CtdlFreeMessage(msg);
1862 * API function to delete messages which match a set of criteria
1863 * (returns the actual number of messages deleted)
1865 int CtdlDeleteMessages(char *room_name, /* which room */
1866 long dmsgnum, /* or "0" for any */
1867 char *content_type /* or NULL for any */
1871 struct quickroom qrbuf;
1872 struct cdbdata *cdbfr;
1873 long *msglist = NULL;
1876 int num_deleted = 0;
1878 struct SuppMsgInfo smi;
1880 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1881 room_name, dmsgnum, content_type);
1883 /* get room record, obtaining a lock... */
1884 if (lgetroom(&qrbuf, room_name) != 0) {
1885 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1887 return (0); /* room not found */
1889 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1891 if (cdbfr != NULL) {
1892 msglist = mallok(cdbfr->len);
1893 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1894 num_msgs = cdbfr->len / sizeof(long);
1898 for (i = 0; i < num_msgs; ++i) {
1901 /* Set/clear a bit for each criterion */
1903 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1904 delete_this |= 0x01;
1906 if (content_type == NULL) {
1907 delete_this |= 0x02;
1909 GetSuppMsgInfo(&smi, msglist[i]);
1910 if (!strcasecmp(smi.smi_content_type,
1912 delete_this |= 0x02;
1916 /* Delete message only if all bits are set */
1917 if (delete_this == 0x03) {
1918 AdjRefCount(msglist[i], -1);
1924 num_msgs = sort_msglist(msglist, num_msgs);
1925 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1926 msglist, (num_msgs * sizeof(long)));
1928 qrbuf.QRhighest = msglist[num_msgs - 1];
1932 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1933 return (num_deleted);
1939 * Delete message from current room
1941 void cmd_dele(char *delstr)
1946 getuser(&CC->usersupp, CC->curr_user);
1947 if ((CC->usersupp.axlevel < 6)
1948 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1949 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1950 && (!(CC->internal_pgm))) {
1951 cprintf("%d Higher access required.\n",
1952 ERROR + HIGHER_ACCESS_REQUIRED);
1955 delnum = extract_long(delstr, 0);
1957 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1960 cprintf("%d %d message%s deleted.\n", OK,
1961 num_deleted, ((num_deleted != 1) ? "s" : ""));
1963 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1969 * move a message to another room
1971 void cmd_move(char *args)
1975 struct quickroom qtemp;
1978 num = extract_long(args, 0);
1979 extract(targ, args, 1);
1981 getuser(&CC->usersupp, CC->curr_user);
1982 if ((CC->usersupp.axlevel < 6)
1983 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1984 cprintf("%d Higher access required.\n",
1985 ERROR + HIGHER_ACCESS_REQUIRED);
1988 if (getroom(&qtemp, targ) != 0) {
1989 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1992 /* Bump the reference count, otherwise the message will be deleted
1993 * from disk when we remove it from the source room.
1995 AdjRefCount(num, 1);
1997 /* yank the message out of the current room... */
1998 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
2001 /* put the message into the target room */
2002 lgetroom(&qtemp, targ);
2003 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
2005 cprintf("%d Message moved.\n", OK);
2007 AdjRefCount(num, (-1)); /* oops */
2008 cprintf("%d msg %ld does not exist.\n", ERROR, num);
2015 * GetSuppMsgInfo() - Get the supplementary record for a message
2017 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2020 struct cdbdata *cdbsmi;
2023 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2024 smibuf->smi_msgnum = msgnum;
2025 smibuf->smi_refcount = 1; /* Default reference count is 1 */
2027 /* Use the negative of the message number for its supp record index */
2028 TheIndex = (0L - msgnum);
2030 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2031 if (cdbsmi == NULL) {
2032 return; /* record not found; go with defaults */
2034 memcpy(smibuf, cdbsmi->ptr,
2035 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2036 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2043 * PutSuppMsgInfo() - (re)write supplementary record for a message
2045 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2049 /* Use the negative of the message number for its supp record index */
2050 TheIndex = (0L - smibuf->smi_msgnum);
2052 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2053 smibuf->smi_msgnum, smibuf->smi_refcount);
2055 cdb_store(CDB_MSGMAIN,
2056 &TheIndex, sizeof(long),
2057 smibuf, sizeof(struct SuppMsgInfo));
2062 * AdjRefCount - change the reference count for a message;
2063 * delete the message if it reaches zero
2065 void AdjRefCount(long msgnum, int incr)
2068 struct SuppMsgInfo smi;
2071 /* This is a *tight* critical section; please keep it that way, as
2072 * it may get called while nested in other critical sections.
2073 * Complicating this any further will surely cause deadlock!
2075 begin_critical_section(S_SUPPMSGMAIN);
2076 GetSuppMsgInfo(&smi, msgnum);
2077 smi.smi_refcount += incr;
2078 PutSuppMsgInfo(&smi);
2079 end_critical_section(S_SUPPMSGMAIN);
2081 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2082 msgnum, smi.smi_refcount);
2084 /* If the reference count is now zero, delete the message
2085 * (and its supplementary record as well).
2087 if (smi.smi_refcount == 0) {
2088 lprintf(9, "Deleting message <%ld>\n", msgnum);
2090 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2091 delnum = (0L - msgnum);
2092 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2097 * Write a generic object to this room
2099 * Note: this could be much more efficient. Right now we use two temporary
2100 * files, and still pull the message into memory as with all others.
2102 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
2103 char *content_type, /* MIME type of this object */
2104 char *tempfilename, /* Where to fetch it from */
2105 struct usersupp *is_mailbox, /* Mailbox room? */
2106 int is_binary, /* Is encoding necessary? */
2107 int is_unique, /* Del others of this type? */
2108 unsigned int flags /* Internal save flags */
2113 char filename[PATH_MAX];
2116 struct quickroom qrbuf;
2117 char roomname[ROOMNAMELEN];
2118 struct CtdlMessage *msg;
2121 if (is_mailbox != NULL)
2122 MailboxName(roomname, is_mailbox, req_room);
2124 safestrncpy(roomname, req_room, sizeof(roomname));
2125 lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2127 strcpy(filename, tmpnam(NULL));
2128 fp = fopen(filename, "w");
2132 tempfp = fopen(tempfilename, "r");
2133 if (tempfp == NULL) {
2139 fprintf(fp, "Content-type: %s\n", content_type);
2140 lprintf(9, "Content-type: %s\n", content_type);
2142 if (is_binary == 0) {
2143 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2144 while (ch = getc(tempfp), ch > 0)
2150 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2153 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2154 tempfilename, filename);
2158 lprintf(9, "Allocating\n");
2159 msg = mallok(sizeof(struct CtdlMessage));
2160 memset(msg, 0, sizeof(struct CtdlMessage));
2161 msg->cm_magic = CTDLMESSAGE_MAGIC;
2162 msg->cm_anon_type = MES_NORMAL;
2163 msg->cm_format_type = 4;
2164 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2165 msg->cm_fields['O'] = strdoop(req_room);
2166 msg->cm_fields['N'] = strdoop(config.c_nodename);
2167 msg->cm_fields['H'] = strdoop(config.c_humannode);
2168 msg->cm_flags = flags;
2170 lprintf(9, "Loading\n");
2171 fp = fopen(filename, "rb");
2172 fseek(fp, 0L, SEEK_END);
2175 msg->cm_fields['M'] = mallok(len);
2176 fread(msg->cm_fields['M'], len, 1, fp);
2180 /* Create the requested room if we have to. */
2181 if (getroom(&qrbuf, roomname) != 0) {
2182 create_room(roomname, 4, "", 0);
2184 /* If the caller specified this object as unique, delete all
2185 * other objects of this type that are currently in the room.
2188 lprintf(9, "Deleted %d other msgs of this type\n",
2189 CtdlDeleteMessages(roomname, 0L, content_type));
2191 /* Now write the data */
2192 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2193 CtdlFreeMessage(msg);