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))
36 extern struct config config;
39 * This function is self explanatory.
40 * (What can I say, I'm in a weird mood today...)
42 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
46 for (i = 0; i < strlen(name); ++i)
49 if (isspace(name[i - 1])) {
50 strcpy(&name[i - 1], &name[i]);
53 while (isspace(name[i + 1])) {
54 strcpy(&name[i + 1], &name[i + 2]);
61 * Aliasing for network mail.
62 * (Error messages have been commented out, because this is a server.)
65 { /* process alias and routing info for mail */
68 char aaa[300], bbb[300];
70 lprintf(9, "alias() called for <%s>\n", name);
72 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
74 fp = fopen("network/mail.aliases", "r");
76 fp = fopen("/dev/null", "r");
81 while (fgets(aaa, sizeof aaa, fp) != NULL) {
82 while (isspace(name[0]))
83 strcpy(name, &name[1]);
84 aaa[strlen(aaa) - 1] = 0;
86 for (a = 0; a < strlen(aaa); ++a) {
88 strcpy(bbb, &aaa[a + 1]);
92 if (!strcasecmp(name, aaa))
96 lprintf(7, "Mail is being forwarded to %s\n", name);
98 /* determine local or remote type, see citadel.h */
99 for (a = 0; a < strlen(name); ++a)
101 return (MES_INTERNET);
102 for (a = 0; a < strlen(name); ++a)
104 for (b = a; b < strlen(name); ++b)
106 return (MES_INTERNET);
108 for (a = 0; a < strlen(name); ++a)
112 lprintf(7, "Too many @'s in address\n");
116 for (a = 0; a < strlen(name); ++a)
118 strcpy(bbb, &name[a + 1]);
120 strcpy(bbb, &bbb[1]);
121 fp = fopen("network/mail.sysinfo", "r");
125 a = getstring(fp, aaa);
126 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
127 a = getstring(fp, aaa);
128 if (!strncmp(aaa, "use ", 4)) {
129 strcpy(bbb, &aaa[4]);
134 if (!strncmp(aaa, "uum", 3)) {
136 for (a = 0; a < strlen(bbb); ++a) {
142 while (bbb[strlen(bbb) - 1] == '_')
143 bbb[strlen(bbb) - 1] = 0;
144 sprintf(name, &aaa[4], bbb);
145 return (MES_INTERNET);
147 if (!strncmp(aaa, "bin", 3)) {
150 while (aaa[strlen(aaa) - 1] != '@')
151 aaa[strlen(aaa) - 1] = 0;
152 aaa[strlen(aaa) - 1] = 0;
153 while (aaa[strlen(aaa) - 1] == ' ')
154 aaa[strlen(aaa) - 1] = 0;
155 while (bbb[0] != '@')
156 strcpy(bbb, &bbb[1]);
157 strcpy(bbb, &bbb[1]);
158 while (bbb[0] == ' ')
159 strcpy(bbb, &bbb[1]);
160 sprintf(name, "%s @%s", aaa, bbb);
173 fp = fopen("citadel.control", "r");
174 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
180 void simple_listing(long msgnum)
182 cprintf("%ld\n", msgnum);
187 * API function to perform an operation for each qualifying message in the
190 void CtdlForEachMessage(int mode, long ref,
192 void (*CallBack) (long msgnum))
197 struct cdbdata *cdbfr;
198 long *msglist = NULL;
201 struct SuppMsgInfo smi;
203 /* Learn about the user and room in question */
205 getuser(&CC->usersupp, CC->curr_user);
206 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
208 /* Load the message list */
209 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
211 msglist = mallok(cdbfr->len);
212 memcpy(msglist, cdbfr->ptr, cdbfr->len);
213 num_msgs = cdbfr->len / sizeof(long);
216 return; /* No messages at all? No further action. */
220 /* If the caller is looking for a specific MIME type, then filter
221 * out all messages which are not of the type requested.
224 if (content_type != NULL)
225 if (strlen(content_type) > 0)
226 for (a = 0; a < num_msgs; ++a) {
227 GetSuppMsgInfo(&smi, msglist[a]);
228 if (strcasecmp(smi.smi_content_type, content_type)) {
233 num_msgs = sort_msglist(msglist, num_msgs);
236 * Now iterate through the message list, according to the
237 * criteria supplied by the caller.
240 for (a = 0; a < num_msgs; ++a) {
241 thismsg = msglist[a];
246 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
247 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
248 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
249 && (CC->usersupp.flags & US_LASTOLD))
250 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
251 || ((mode == MSGS_FIRST) && (a < ref))
252 || ((mode == MSGS_GT) && (thismsg > ref))
258 phree(msglist); /* Clean up */
264 * cmd_msgs() - get list of message #'s in this room
265 * implements the MSGS server command using CtdlForEachMessage()
267 void cmd_msgs(char *cmdbuf)
273 extract(which, cmdbuf, 0);
274 cm_ref = extract_int(cmdbuf, 1);
278 if (!strncasecmp(which, "OLD", 3))
280 else if (!strncasecmp(which, "NEW", 3))
282 else if (!strncasecmp(which, "FIRST", 5))
284 else if (!strncasecmp(which, "LAST", 4))
286 else if (!strncasecmp(which, "GT", 2))
289 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
290 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
293 cprintf("%d Message list...\n", LISTING_FOLLOWS);
294 CtdlForEachMessage(mode, cm_ref, NULL, simple_listing);
302 * help_subst() - support routine for help file viewer
304 void help_subst(char *strbuf, char *source, char *dest)
309 while (p = pattern2(strbuf, source), (p >= 0)) {
310 strcpy(workbuf, &strbuf[p + strlen(source)]);
311 strcpy(&strbuf[p], dest);
312 strcat(strbuf, workbuf);
317 void do_help_subst(char *buffer)
321 help_subst(buffer, "^nodename", config.c_nodename);
322 help_subst(buffer, "^humannode", config.c_humannode);
323 help_subst(buffer, "^fqdn", config.c_fqdn);
324 help_subst(buffer, "^username", CC->usersupp.fullname);
325 sprintf(buf2, "%ld", CC->usersupp.usernum);
326 help_subst(buffer, "^usernum", buf2);
327 help_subst(buffer, "^sysadm", config.c_sysadm);
328 help_subst(buffer, "^variantname", CITADEL);
329 sprintf(buf2, "%d", config.c_maxsessions);
330 help_subst(buffer, "^maxsessions", buf2);
336 * memfmout() - Citadel text formatter and paginator.
337 * Although the original purpose of this routine was to format
338 * text to the reader's screen width, all we're really using it
339 * for here is to format text out to 80 columns before sending it
340 * to the client. The client software may reformat it again.
342 void memfmout(int width, char *mptr, char subst)
343 /* screen width to use */
344 /* where are we going to get our text from? */
345 /* nonzero if we should use hypertext mode */
357 c = 1; /* c is the current pos */
360 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
362 buffer[strlen(buffer) + 1] = 0;
363 buffer[strlen(buffer)] = ch;
366 if (buffer[0] == '^')
367 do_help_subst(buffer);
369 buffer[strlen(buffer) + 1] = 0;
371 strcpy(buffer, &buffer[1]);
381 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
383 if (((old == 13) || (old == 10)) && (isspace(real))) {
391 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
392 cprintf("\n%s", aaa);
401 if ((strlen(aaa) + c) > (width - 5)) {
411 if ((ch == 13) || (ch == 10)) {
412 cprintf("%s\n", aaa);
419 FMTEND: cprintf("%s\n", aaa);
425 * Callback function for mime parser that simply lists the part
427 void list_this_part(char *name, char *filename, char *partnum, char *disp,
428 void *content, char *cbtype, size_t length)
431 cprintf("part=%s|%s|%s|%s|%s|%d\n",
432 name, filename, partnum, disp, cbtype, length);
437 * Callback function for mime parser that wants to display text
439 void fixed_output(char *name, char *filename, char *partnum, char *disp,
440 void *content, char *cbtype, size_t length)
444 if (!strcasecmp(cbtype, "multipart/alternative")) {
445 strcpy(ma->prefix, partnum);
446 strcat(ma->prefix, ".");
452 if ( (!strncasecmp(partnum, ma->prefix, strlen(ma->prefix)))
454 && (ma->did_print == 1) ) {
455 lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
461 if (!strcasecmp(cbtype, "text/plain")) {
462 client_write(content, length);
464 else if (!strcasecmp(cbtype, "text/html")) {
465 ptr = html_to_ascii(content, 80, 0);
466 client_write(ptr, strlen(ptr));
469 else if (strncasecmp(cbtype, "multipart/", 10)) {
470 cprintf("Part %s: %s (%s) (%d bytes)\n",
471 partnum, filename, cbtype, length);
477 * Callback function for mime parser that opens a section for downloading
479 void mime_download(char *name, char *filename, char *partnum, char *disp,
480 void *content, char *cbtype, size_t length)
483 /* Silently go away if there's already a download open... */
484 if (CC->download_fp != NULL)
487 /* ...or if this is not the desired section */
488 if (strcasecmp(desired_section, partnum))
491 CC->download_fp = tmpfile();
492 if (CC->download_fp == NULL)
495 fwrite(content, length, 1, CC->download_fp);
496 fflush(CC->download_fp);
497 rewind(CC->download_fp);
499 OpenCmdResult(filename, cbtype);
505 * Load a message from disk into memory.
506 * This is used by output_message() and other fetch functions.
508 * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
509 * using the CtdlMessageFree() function.
511 struct CtdlMessage *CtdlFetchMessage(long msgnum)
513 struct cdbdata *dmsgtext;
514 struct CtdlMessage *ret = NULL;
517 CIT_UBYTE field_header;
521 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
522 if (dmsgtext == NULL) {
523 lprintf(9, "CtdlFetchMessage(%ld) failed.\n");
526 mptr = dmsgtext->ptr;
528 /* Parse the three bytes that begin EVERY message on disk.
529 * The first is always 0xFF, the on-disk magic number.
530 * The second is the anonymous/public type byte.
531 * The third is the format type byte (vari, fixed, or MIME).
535 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
539 ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
540 memset(ret, 0, sizeof(struct CtdlMessage));
542 ret->cm_magic = CTDLMESSAGE_MAGIC;
543 ret->cm_anon_type = *mptr++; /* Anon type byte */
544 ret->cm_format_type = *mptr++; /* Format type byte */
547 * The rest is zero or more arbitrary fields. Load them in.
548 * We're done when we encounter either a zero-length field or
549 * have just processed the 'M' (message text) field.
552 field_length = strlen(mptr);
553 if (field_length == 0)
555 field_header = *mptr++;
556 ret->cm_fields[field_header] = mallok(field_length);
557 strcpy(ret->cm_fields[field_header], mptr);
559 while (*mptr++ != 0); /* advance to next field */
561 } while ((field_length > 0) && (field_header != 'M'));
565 /* Perform "before read" hooks (aborting if any return nonzero) */
566 if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
567 CtdlFreeMessage(ret);
576 * Returns 1 if the supplied pointer points to a valid Citadel message.
577 * If the pointer is NULL or the magic number check fails, returns 0.
579 int is_valid_message(struct CtdlMessage *msg) {
582 if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
583 lprintf(3, "is_valid_message() -- self-check failed\n");
591 * 'Destructor' for struct CtdlMessage
593 void CtdlFreeMessage(struct CtdlMessage *msg)
597 if (is_valid_message(msg) == 0) return;
599 for (i = 0; i < 256; ++i)
600 if (msg->cm_fields[i] != NULL)
601 phree(msg->cm_fields[i]);
603 msg->cm_magic = 0; /* just in case */
610 * Get a message off disk. (return value is the message's timestamp)
613 void output_message(char *msgid, int mode, int headers_only)
621 struct CtdlMessage *TheMessage = NULL;
625 /* buffers needed for RFC822 translation */
633 msg_num = atol(msgid);
635 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
636 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
640 /* FIX ... small security issue
641 * We need to check to make sure the requested message is actually
642 * in the current room, and set msg_ok to 1 only if it is. This
643 * functionality is currently missing because I'm in a hurry to replace
644 * broken production code with nonbroken pre-beta code. :( -- ajc
647 cprintf("%d Message %ld is not in this room.\n",
654 * Fetch the message from disk
656 TheMessage = CtdlFetchMessage(msg_num);
657 if (TheMessage == NULL) {
658 cprintf("%d Can't locate msg %ld on disk\n", ERROR, msg_num);
662 /* Are we downloading a MIME component? */
663 if (mode == MT_DOWNLOAD) {
664 if (TheMessage->cm_format_type != 4) {
665 cprintf("%d This is not a MIME message.\n",
667 } else if (CC->download_fp != NULL) {
668 cprintf("%d You already have a download open.\n",
671 /* Parse the message text component */
672 mptr = TheMessage->cm_fields['M'];
673 mime_parser(mptr, NULL, *mime_download);
674 /* If there's no file open by this time, the requested
675 * section wasn't found, so print an error
677 if (CC->download_fp == NULL) {
678 cprintf("%d Section %s not found.\n",
679 ERROR + FILE_NOT_FOUND,
683 CtdlFreeMessage(TheMessage);
687 /* now for the user-mode message reading loops */
688 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
690 /* Tell the client which format type we're using. If this is a
691 * MIME message, *lie* about it and tell the user it's fixed-format.
693 if (mode == MT_CITADEL) {
694 if (TheMessage->cm_format_type == 4)
697 cprintf("type=%d\n", TheMessage->cm_format_type);
700 /* nhdr=yes means that we're only displaying headers, no body */
701 if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
702 cprintf("nhdr=yes\n");
705 /* begin header processing loop for Citadel message format */
707 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
709 if (TheMessage->cm_fields['P']) {
710 cprintf("path=%s\n", TheMessage->cm_fields['P']);
712 if (TheMessage->cm_fields['I']) {
713 cprintf("msgn=%s\n", TheMessage->cm_fields['I']);
715 if (TheMessage->cm_fields['T']) {
716 cprintf("time=%s\n", TheMessage->cm_fields['T']);
718 if (TheMessage->cm_fields['A']) {
719 strcpy(buf, TheMessage->cm_fields['A']);
720 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
721 if (TheMessage->cm_anon_type == MES_ANON)
722 cprintf("from=****");
723 else if (TheMessage->cm_anon_type == MES_AN2)
724 cprintf("from=anonymous");
726 cprintf("from=%s", buf);
728 && ((TheMessage->cm_anon_type == MES_ANON)
729 || (TheMessage->cm_anon_type == MES_AN2))) {
730 cprintf(" [%s]", buf);
734 if (TheMessage->cm_fields['O']) {
735 cprintf("room=%s\n", TheMessage->cm_fields['O']);
737 if (TheMessage->cm_fields['N']) {
738 cprintf("node=%s\n", TheMessage->cm_fields['N']);
740 if (TheMessage->cm_fields['H']) {
741 cprintf("hnod=%s\n", TheMessage->cm_fields['H']);
743 if (TheMessage->cm_fields['R']) {
744 cprintf("rcpt=%s\n", TheMessage->cm_fields['R']);
746 if (TheMessage->cm_fields['U']) {
747 cprintf("subj=%s\n", TheMessage->cm_fields['U']);
749 if (TheMessage->cm_fields['Z']) {
750 cprintf("zaps=%s\n", TheMessage->cm_fields['Z']);
754 /* begin header processing loop for RFC822 transfer format */
758 strcpy(snode, NODENAME);
759 strcpy(lnode, HUMANNODE);
760 if (mode == MT_RFC822) {
761 for (i = 0; i < 256; ++i) {
762 if (TheMessage->cm_fields[i]) {
763 mptr = TheMessage->cm_fields[i];
767 } else if (i == 'P') {
768 cprintf("Path: %s\n", mptr);
769 for (a = 0; a < strlen(mptr); ++a) {
770 if (mptr[a] == '!') {
771 strcpy(mptr, &mptr[a + 1]);
777 cprintf("Subject: %s\n", mptr);
783 cprintf("X-Citadel-Room: %s\n", mptr);
787 cprintf("To: %s\n", mptr);
790 cprintf("Date: %s", asctime(localtime(&xtime)));
796 if (mode == MT_RFC822) {
797 if (!strcasecmp(snode, NODENAME)) {
800 cprintf("Message-ID: <%s@%s>\n", mid, snode);
801 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
802 cprintf("From: %s@%s (%s)\n", suser, snode, luser);
803 cprintf("Organization: %s\n", lnode);
806 /* end header processing loop ... at this point, we're in the text */
808 mptr = TheMessage->cm_fields['M'];
810 /* Tell the client about the MIME parts in this message */
811 if (TheMessage->cm_format_type == 4) { /* legacy textual dump */
812 if (mode == MT_CITADEL) {
813 mime_parser(mptr, NULL, *list_this_part);
815 else if (mode == MT_MIME) { /* list parts only */
816 mime_parser(mptr, NULL, *list_this_part);
818 CtdlFreeMessage(TheMessage);
825 CtdlFreeMessage(TheMessage);
829 /* signify start of msg text */
830 if (mode == MT_CITADEL)
832 if ((mode == MT_RFC822) && (TheMessage->cm_format_type != 4))
835 /* If the format type on disk is 1 (fixed-format), then we want
836 * everything to be output completely literally ... regardless of
837 * what message transfer format is in use.
839 if (TheMessage->cm_format_type == 1) {
841 while (ch = *mptr++, ch > 0) {
844 if ((ch == 10) || (strlen(buf) > 250)) {
845 cprintf("%s\n", buf);
848 buf[strlen(buf) + 1] = 0;
849 buf[strlen(buf)] = ch;
853 cprintf("%s\n", buf);
856 /* If the message on disk is format 0 (Citadel vari-format), we
857 * output using the formatter at 80 columns. This is the final output
858 * form if the transfer format is RFC822, but if the transfer format
859 * is Citadel proprietary, it'll still work, because the indentation
860 * for new paragraphs is correct and the client will reformat the
861 * message to the reader's screen width.
863 if (TheMessage->cm_format_type == 0) {
864 memfmout(80, mptr, 0);
867 /* If the message on disk is format 4 (MIME), we've gotta hand it
868 * off to the MIME parser. The client has already been told that
869 * this message is format 1 (fixed format), so the callback function
870 * we use will display those parts as-is.
872 if (TheMessage->cm_format_type == 4) {
873 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
874 memset(ma, 0, sizeof(struct ma_info));
875 mime_parser(mptr, NULL, *fixed_output);
880 CtdlFreeMessage(TheMessage);
887 * display a message (mode 0 - Citadel proprietary)
889 void cmd_msg0(char *cmdbuf)
892 int headers_only = 0;
894 extract(msgid, cmdbuf, 0);
895 headers_only = extract_int(cmdbuf, 1);
897 output_message(msgid, MT_CITADEL, headers_only);
903 * display a message (mode 2 - RFC822)
905 void cmd_msg2(char *cmdbuf)
908 int headers_only = 0;
910 extract(msgid, cmdbuf, 0);
911 headers_only = extract_int(cmdbuf, 1);
913 output_message(msgid, MT_RFC822, headers_only);
919 * display a message (mode 3 - IGnet raw format - internal programs only)
921 void cmd_msg3(char *cmdbuf)
924 struct CtdlMessage *msg;
927 if (CC->internal_pgm == 0) {
928 cprintf("%d This command is for internal programs only.\n",
933 msgnum = extract_long(cmdbuf, 0);
934 msg = CtdlFetchMessage(msgnum);
936 cprintf("%d Message %ld not found.\n",
941 serialize_message(&smr, msg);
942 CtdlFreeMessage(msg);
945 cprintf("%d Unable to serialize message\n",
946 ERROR+INTERNAL_ERROR);
950 cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
951 client_write(smr.ser, smr.len);
958 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
960 void cmd_msg4(char *cmdbuf)
964 extract(msgid, cmdbuf, 0);
966 output_message(msgid, MT_MIME, 0);
970 * Open a component of a MIME message as a download file
972 void cmd_opna(char *cmdbuf)
976 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
978 extract(msgid, cmdbuf, 0);
979 extract(desired_section, cmdbuf, 1);
981 output_message(msgid, MT_DOWNLOAD, 0);
985 * Message base operation to send a message to the master file
986 * (returns new message number)
988 * This is the back end for CtdlSaveMsg() and should not be directly
989 * called by server-side modules.
992 long send_message(struct CtdlMessage *msg, /* pointer to buffer */
993 int generate_id, /* generate 'I' field? */
994 FILE *save_a_copy) /* save a copy to disk? */
1001 /* Get a new message number */
1002 newmsgid = get_new_message_number();
1003 sprintf(msgidbuf, "%ld", newmsgid);
1006 msg->cm_fields['I'] = strdoop(msgidbuf);
1009 serialize_message(&smr, msg);
1012 cprintf("%d Unable to serialize message\n",
1013 ERROR+INTERNAL_ERROR);
1017 /* Write our little bundle of joy into the message base */
1018 begin_critical_section(S_MSGMAIN);
1019 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1020 smr.ser, smr.len) < 0) {
1021 lprintf(2, "Can't store message\n");
1026 end_critical_section(S_MSGMAIN);
1028 /* If the caller specified that a copy should be saved to a particular
1029 * file handle, do that now too.
1031 if (save_a_copy != NULL) {
1032 fwrite(smr.ser, smr.len, 1, save_a_copy);
1035 /* Free the memory we used for the serialized message */
1038 /* Return the *local* message ID to the caller
1039 * (even if we're storing an incoming network message)
1047 * Serialize a struct CtdlMessage into the format used on disk and network.
1049 * This function loads up a "struct ser_ret" (defined in server.h) which
1050 * contains the length of the serialized message and a pointer to the
1051 * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER.
1053 void serialize_message(struct ser_ret *ret, /* return values */
1054 struct CtdlMessage *msg) /* unserialized msg */
1058 static char *forder = FORDER;
1060 lprintf(9, "serialize_message() called\n");
1062 if (is_valid_message(msg) == 0) return; /* self check */
1064 lprintf(9, "magic number check OK.\n");
1067 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1068 ret->len = ret->len +
1069 strlen(msg->cm_fields[(int)forder[i]]) + 2;
1071 lprintf(9, "message is %d bytes\n", ret->len);
1073 lprintf(9, "calling malloc\n");
1074 ret->ser = mallok(ret->len);
1075 if (ret->ser == NULL) {
1081 ret->ser[1] = msg->cm_anon_type;
1082 ret->ser[2] = msg->cm_format_type;
1085 lprintf(9, "stuff\n");
1086 for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1087 ret->ser[wlen++] = (char)forder[i];
1088 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1089 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1091 if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1093 lprintf(9, "done serializing\n");
1101 * Save a message to disk
1103 void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
1104 char *rec, /* Recipient (mail) */
1105 char *force, /* force a particular room? */
1106 int mailtype, /* local or remote type */
1107 int generate_id) /* 1 = generate 'I' field */
1110 char hold_rm[ROOMNAMELEN];
1111 char actual_rm[ROOMNAMELEN];
1112 char force_room[ROOMNAMELEN];
1113 char content_type[256]; /* We have to learn this */
1114 char recipient[256];
1117 struct usersupp userbuf;
1119 int successful_local_recipients = 0;
1120 struct quickroom qtemp;
1121 struct SuppMsgInfo smi;
1122 FILE *network_fp = NULL;
1123 static int seqnum = 1;
1125 lprintf(9, "CtdlSaveMsg() called\n");
1126 if (is_valid_message(msg) == 0) return; /* self check */
1128 /* If this message has no timestamp, we take the liberty of
1129 * giving it one, right now.
1131 if (msg->cm_fields['T'] == NULL) {
1132 sprintf(aaa, "%ld", time(NULL));
1133 msg->cm_fields['T'] = strdoop(aaa);
1136 /* If this message has no path, we generate one.
1138 if (msg->cm_fields['P'] == NULL) {
1139 msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1140 for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1141 if (isspace(msg->cm_fields['P'][a])) {
1142 msg->cm_fields['P'][a] = ' ';
1147 strcpy(force_room, force);
1149 /* Strip non-printable characters out of the recipient name */
1150 strcpy(recipient, rec);
1151 for (a = 0; a < strlen(recipient); ++a)
1152 if (!isprint(recipient[a]))
1153 strcpy(&recipient[a], &recipient[a + 1]);
1155 /* Learn about what's inside, because it's what's inside that counts */
1157 switch (msg->cm_format_type) {
1159 strcpy(content_type, "text/x-citadel-variformat");
1162 strcpy(content_type, "text/plain");
1165 strcpy(content_type, "text/plain");
1166 /* advance past header fields */
1167 mptr = msg->cm_fields['M'];
1170 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1171 safestrncpy(content_type, mptr,
1172 sizeof(content_type));
1173 strcpy(content_type, &content_type[14]);
1174 for (a = 0; a < strlen(content_type); ++a)
1175 if ((content_type[a] == ';')
1176 || (content_type[a] == ' ')
1177 || (content_type[a] == 13)
1178 || (content_type[a] == 10))
1179 content_type[a] = 0;
1186 /* Perform "before save" hooks (aborting if any return nonzero) */
1187 if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
1189 /* Network mail - send a copy to the network program. */
1190 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1191 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1192 (long) getpid(), CC->cs_pid, ++seqnum);
1193 lprintf(9, "Saving a copy to %s\n", aaa);
1194 network_fp = fopen(aaa, "ab+");
1195 if (network_fp == NULL)
1196 lprintf(2, "ERROR: %s\n", strerror(errno));
1199 /* Save it to disk */
1200 newmsgid = send_message(msg, generate_id, network_fp);
1201 if (network_fp != NULL) {
1203 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1208 strcpy(actual_rm, CC->quickroom.QRname);
1209 strcpy(hold_rm, "");
1211 /* If this is being done by the networker delivering a private
1212 * message, we want to BYPASS saving the sender's copy (because there
1213 * is no local sender; it would otherwise go to the Trashcan).
1215 if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1216 /* If the user is a twit, move to the twit room for posting */
1218 if (CC->usersupp.axlevel == 2) {
1219 strcpy(hold_rm, actual_rm);
1220 strcpy(actual_rm, config.c_twitroom);
1222 /* ...or if this message is destined for Aide> then go there. */
1223 if (strlen(force_room) > 0) {
1224 strcpy(hold_rm, actual_rm);
1225 strcpy(actual_rm, force_room);
1227 /* This call to usergoto() changes rooms if necessary. It also
1228 * causes the latest message list to be read into memory.
1230 usergoto(actual_rm, 0);
1232 /* read in the quickroom record, obtaining a lock... */
1233 lgetroom(&CC->quickroom, actual_rm);
1235 /* Fix an obscure bug */
1236 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1237 CC->quickroom.QRflags =
1238 CC->quickroom.QRflags & ~QR_MAILBOX;
1240 /* Add the message pointer to the room */
1241 CC->quickroom.QRhighest =
1242 AddMessageToRoom(&CC->quickroom, newmsgid);
1244 /* update quickroom */
1245 lputroom(&CC->quickroom);
1246 ++successful_local_recipients;
1249 /* Bump this user's messages posted counter. */
1250 lgetuser(&CC->usersupp, CC->curr_user);
1251 CC->usersupp.posted = CC->usersupp.posted + 1;
1252 lputuser(&CC->usersupp);
1254 /* If this is private, local mail, make a copy in the
1255 * recipient's mailbox and bump the reference count.
1257 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1258 if (getuser(&userbuf, recipient) == 0) {
1259 MailboxName(actual_rm, &userbuf, MAILROOM);
1260 if (lgetroom(&qtemp, actual_rm) == 0) {
1262 AddMessageToRoom(&qtemp, newmsgid);
1264 ++successful_local_recipients;
1268 /* If we've posted in a room other than the current room, then we
1269 * have to now go back to the current room...
1271 if (strlen(hold_rm) > 0) {
1272 usergoto(hold_rm, 0);
1275 /* Write a supplemental message info record. This doesn't have to
1276 * be a critical section because nobody else knows about this message
1279 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1280 smi.smi_msgnum = newmsgid;
1281 smi.smi_refcount = successful_local_recipients;
1282 safestrncpy(smi.smi_content_type, content_type, 64);
1283 PutSuppMsgInfo(&smi);
1285 /* Perform "after save" hooks */
1286 PerformMessageHooks(msg, EVT_AFTERSAVE);
1292 * Convenience function for generating small administrative messages.
1294 void quickie_message(char *from, char *to, char *room, char *text)
1296 struct CtdlMessage *msg;
1298 msg = mallok(sizeof(struct CtdlMessage));
1299 memset(msg, 0, sizeof(struct CtdlMessage));
1300 msg->cm_magic = CTDLMESSAGE_MAGIC;
1301 msg->cm_anon_type = MES_NORMAL;
1302 msg->cm_format_type = 0;
1303 msg->cm_fields['A'] = strdoop(from);
1304 msg->cm_fields['O'] = strdoop(room);
1305 msg->cm_fields['N'] = strdoop(NODENAME);
1307 msg->cm_fields['R'] = strdoop(to);
1308 msg->cm_fields['M'] = strdoop(text);
1310 CtdlSaveMsg(msg, "", room, MES_LOCAL, 1);
1311 CtdlFreeMessage(msg);
1312 syslog(LOG_NOTICE, text);
1317 * Build a binary message to be saved on disk.
1320 struct CtdlMessage *make_message(
1321 struct usersupp *author, /* author's usersupp structure */
1322 char *recipient, /* NULL if it's not mail */
1323 char *room, /* room where it's going */
1324 int type, /* see MES_ types in header file */
1325 int net_type, /* see MES_ types in header file */
1326 int format_type, /* local or remote (see citadel.h) */
1327 char *fake_name) /* who we're masquerading as */
1333 size_t message_len = 0;
1334 size_t buffer_len = 0;
1336 struct CtdlMessage *msg;
1338 msg = mallok(sizeof(struct CtdlMessage));
1339 memset(msg, 0, sizeof(struct CtdlMessage));
1340 msg->cm_magic = CTDLMESSAGE_MAGIC;
1341 msg->cm_anon_type = type;
1342 msg->cm_format_type = format_type;
1344 /* Don't confuse the poor folks if it's not routed mail. */
1345 strcpy(dest_node, "");
1347 /* If net_type is MES_BINARY, split out the destination node. */
1348 if (net_type == MES_BINARY) {
1349 strcpy(dest_node, NODENAME);
1350 for (a = 0; a < strlen(recipient); ++a) {
1351 if (recipient[a] == '@') {
1353 strcpy(dest_node, &recipient[a + 1]);
1358 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1359 if (net_type == MES_INTERNET) {
1360 strcpy(dest_node, "internet");
1363 while (isspace(recipient[strlen(recipient) - 1]))
1364 recipient[strlen(recipient) - 1] = 0;
1366 sprintf(buf, "cit%ld", author->usernum); /* Path */
1367 msg->cm_fields['P'] = strdoop(buf);
1369 sprintf(buf, "%ld", time(NULL)); /* timestamp */
1370 msg->cm_fields['T'] = strdoop(buf);
1372 if (fake_name[0]) /* author */
1373 msg->cm_fields['A'] = strdoop(fake_name);
1375 msg->cm_fields['A'] = strdoop(author->fullname);
1377 if (CC->quickroom.QRflags & QR_MAILBOX) /* room */
1378 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
1380 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1382 msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */
1383 msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */
1385 if (recipient[0] != 0)
1386 msg->cm_fields['R'] = strdoop(recipient);
1387 if (dest_node[0] != 0)
1388 msg->cm_fields['D'] = strdoop(dest_node);
1390 msg->cm_fields['M'] = mallok(4096);
1391 if (msg->cm_fields['M'] == NULL) {
1392 while (client_gets(buf), strcmp(buf, "000")) ;; /* flush */
1396 msg->cm_fields['M'][0] = 0;
1400 /* read in the lines of message text one by one */
1401 while (client_gets(buf), strcmp(buf, "000")) {
1403 /* augment the buffer if we have to */
1404 if ((message_len + strlen(buf) + 2) > buffer_len) {
1405 ptr = reallok(msg->cm_fields['M'], (buffer_len * 2) );
1406 if (ptr == NULL) { /* flush if can't allocate */
1407 while (client_gets(buf), strcmp(buf, "000")) ;;
1410 buffer_len = (buffer_len * 2);
1411 msg->cm_fields['M'] = ptr;
1415 strcat(msg->cm_fields['M'], buf);
1416 strcat(msg->cm_fields['M'], "\n");
1418 /* if we've hit the max msg length, flush the rest */
1419 if (message_len >= config.c_maxmsglen) {
1420 while (client_gets(buf), strcmp(buf, "000")) ;;
1433 * message entry - mode 0 (normal)
1435 void cmd_ent0(char *entargs)
1438 char recipient[256];
1440 int format_type = 0;
1441 char newusername[256];
1442 struct CtdlMessage *msg;
1446 struct usersupp tempUS;
1449 post = extract_int(entargs, 0);
1450 extract(recipient, entargs, 1);
1451 anon_flag = extract_int(entargs, 2);
1452 format_type = extract_int(entargs, 3);
1454 /* first check to make sure the request is valid. */
1456 if (!(CC->logged_in)) {
1457 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1460 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1461 cprintf("%d Need to be validated to enter ",
1462 ERROR + HIGHER_ACCESS_REQUIRED);
1463 cprintf("(except in %s> to sysop)\n", MAILROOM);
1466 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1467 cprintf("%d Need net privileges to enter here.\n",
1468 ERROR + HIGHER_ACCESS_REQUIRED);
1471 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1472 cprintf("%d Sorry, this is a read-only room.\n",
1473 ERROR + HIGHER_ACCESS_REQUIRED);
1480 if (CC->usersupp.axlevel < 6) {
1481 cprintf("%d You don't have permission to masquerade.\n",
1482 ERROR + HIGHER_ACCESS_REQUIRED);
1485 extract(newusername, entargs, 4);
1486 memset(CC->fake_postname, 0, 32);
1487 strcpy(CC->fake_postname, newusername);
1488 cprintf("%d Ok\n", OK);
1491 CC->cs_flags |= CS_POSTING;
1494 if (CC->quickroom.QRflags & QR_MAILBOX) {
1495 if (CC->usersupp.axlevel >= 2) {
1496 strcpy(buf, recipient);
1498 strcpy(buf, "sysop");
1499 e = alias(buf); /* alias and mail type */
1500 if ((buf[0] == 0) || (e == MES_ERROR)) {
1501 cprintf("%d Unknown address - cannot send message.\n",
1502 ERROR + NO_SUCH_USER);
1505 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1506 cprintf("%d Net privileges required for network mail.\n",
1507 ERROR + HIGHER_ACCESS_REQUIRED);
1510 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1511 && ((CC->usersupp.flags & US_INTERNET) == 0)
1512 && (!CC->internal_pgm)) {
1513 cprintf("%d You don't have access to Internet mail.\n",
1514 ERROR + HIGHER_ACCESS_REQUIRED);
1517 if (!strcasecmp(buf, "sysop")) {
1522 goto SKFALL; /* don't search local file */
1523 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1524 cprintf("%d Can't send mail to yourself!\n",
1525 ERROR + NO_SUCH_USER);
1528 /* Check to make sure the user exists; also get the correct
1529 * upper/lower casing of the name.
1531 a = getuser(&tempUS, buf);
1533 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1536 strcpy(buf, tempUS.fullname);
1539 SKFALL: b = MES_NORMAL;
1540 if (CC->quickroom.QRflags & QR_ANONONLY)
1542 if (CC->quickroom.QRflags & QR_ANONOPT) {
1546 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1549 /* If we're only checking the validity of the request, return
1550 * success without creating the message.
1553 cprintf("%d %s\n", OK, buf);
1557 cprintf("%d send message\n", SEND_LISTING);
1559 /* Read in the message from the client. */
1560 if (CC->fake_postname[0])
1561 msg = make_message(&CC->usersupp, buf,
1562 CC->quickroom.QRname, b, e, format_type,
1564 else if (CC->fake_username[0])
1565 msg = make_message(&CC->usersupp, buf,
1566 CC->quickroom.QRname, b, e, format_type,
1569 msg = make_message(&CC->usersupp, buf,
1570 CC->quickroom.QRname, b, e, format_type, "");
1573 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1574 CtdlFreeMessage(msg);
1575 CC->fake_postname[0] = '\0';
1582 * message entry - mode 3 (raw)
1584 void cmd_ent3(char *entargs)
1589 unsigned char ch, which_field;
1590 struct usersupp tempUS;
1592 struct CtdlMessage *msg;
1595 if (CC->internal_pgm == 0) {
1596 cprintf("%d This command is for internal programs only.\n",
1601 /* See if there's a recipient, but make sure it's a real one */
1602 extract(recp, entargs, 1);
1603 for (a = 0; a < strlen(recp); ++a)
1604 if (!isprint(recp[a]))
1605 strcpy(&recp[a], &recp[a + 1]);
1606 while (isspace(recp[0]))
1607 strcpy(recp, &recp[1]);
1608 while (isspace(recp[strlen(recp) - 1]))
1609 recp[strlen(recp) - 1] = 0;
1611 /* If we're in Mail, check the recipient */
1612 if (strlen(recp) > 0) {
1613 e = alias(recp); /* alias and mail type */
1614 if ((recp[0] == 0) || (e == MES_ERROR)) {
1615 cprintf("%d Unknown address - cannot send message.\n",
1616 ERROR + NO_SUCH_USER);
1619 if (e == MES_LOCAL) {
1620 a = getuser(&tempUS, recp);
1622 cprintf("%d No such user.\n",
1623 ERROR + NO_SUCH_USER);
1629 /* At this point, message has been approved. */
1630 if (extract_int(entargs, 0) == 0) {
1631 cprintf("%d OK to send\n", OK);
1635 msglen = extract_long(entargs, 2);
1636 msg = mallok(sizeof(struct CtdlMessage));
1638 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1642 memset(msg, 0, sizeof(struct CtdlMessage));
1643 tempbuf = mallok(msglen);
1644 if (tempbuf == NULL) {
1645 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
1650 cprintf("%d %ld\n", SEND_BINARY, msglen);
1652 client_read(&ch, 1); /* 0xFF magic number */
1653 msg->cm_magic = CTDLMESSAGE_MAGIC;
1654 client_read(&ch, 1); /* anon type */
1655 msg->cm_anon_type = ch;
1656 client_read(&ch, 1); /* format type */
1657 msg->cm_format_type = ch;
1658 msglen = msglen - 3;
1660 while (msglen > 0) {
1661 client_read(&which_field, 1);
1665 client_read(&ch, 1);
1667 a = strlen(tempbuf);
1670 } while ( (ch != 0) && (msglen > 0) );
1671 msg->cm_fields[which_field] = strdoop(tempbuf);
1674 CtdlSaveMsg(msg, recp, "", e, 0);
1675 CtdlFreeMessage(msg);
1681 * API function to delete messages which match a set of criteria
1682 * (returns the actual number of messages deleted)
1684 int CtdlDeleteMessages(char *room_name, /* which room */
1685 long dmsgnum, /* or "0" for any */
1686 char *content_type /* or NULL for any */
1690 struct quickroom qrbuf;
1691 struct cdbdata *cdbfr;
1692 long *msglist = NULL;
1695 int num_deleted = 0;
1697 struct SuppMsgInfo smi;
1699 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1700 room_name, dmsgnum, content_type);
1702 /* get room record, obtaining a lock... */
1703 if (lgetroom(&qrbuf, room_name) != 0) {
1704 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1706 return (0); /* room not found */
1708 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1710 if (cdbfr != NULL) {
1711 msglist = mallok(cdbfr->len);
1712 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1713 num_msgs = cdbfr->len / sizeof(long);
1717 for (i = 0; i < num_msgs; ++i) {
1720 /* Set/clear a bit for each criterion */
1722 if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
1723 delete_this |= 0x01;
1725 if (content_type == NULL) {
1726 delete_this |= 0x02;
1728 GetSuppMsgInfo(&smi, msglist[i]);
1729 if (!strcasecmp(smi.smi_content_type,
1731 delete_this |= 0x02;
1735 /* Delete message only if all bits are set */
1736 if (delete_this == 0x03) {
1737 AdjRefCount(msglist[i], -1);
1743 num_msgs = sort_msglist(msglist, num_msgs);
1744 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1745 msglist, (num_msgs * sizeof(long)));
1747 qrbuf.QRhighest = msglist[num_msgs - 1];
1751 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1752 return (num_deleted);
1758 * Delete message from current room
1760 void cmd_dele(char *delstr)
1765 getuser(&CC->usersupp, CC->curr_user);
1766 if ((CC->usersupp.axlevel < 6)
1767 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1768 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1769 cprintf("%d Higher access required.\n",
1770 ERROR + HIGHER_ACCESS_REQUIRED);
1773 delnum = extract_long(delstr, 0);
1775 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1778 cprintf("%d %d message%s deleted.\n", OK,
1779 num_deleted, ((num_deleted != 1) ? "s" : ""));
1781 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1787 * move a message to another room
1789 void cmd_move(char *args)
1793 struct quickroom qtemp;
1796 num = extract_long(args, 0);
1797 extract(targ, args, 1);
1799 getuser(&CC->usersupp, CC->curr_user);
1800 if ((CC->usersupp.axlevel < 6)
1801 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1802 cprintf("%d Higher access required.\n",
1803 ERROR + HIGHER_ACCESS_REQUIRED);
1806 if (getroom(&qtemp, targ) != 0) {
1807 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1810 /* Bump the reference count, otherwise the message will be deleted
1811 * from disk when we remove it from the source room.
1813 AdjRefCount(num, 1);
1815 /* yank the message out of the current room... */
1816 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
1819 /* put the message into the target room */
1820 lgetroom(&qtemp, targ);
1821 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1823 cprintf("%d Message moved.\n", OK);
1825 AdjRefCount(num, (-1)); /* oops */
1826 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1833 * GetSuppMsgInfo() - Get the supplementary record for a message
1835 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1838 struct cdbdata *cdbsmi;
1841 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1842 smibuf->smi_msgnum = msgnum;
1843 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1845 /* Use the negative of the message number for its supp record index */
1846 TheIndex = (0L - msgnum);
1848 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1849 if (cdbsmi == NULL) {
1850 return; /* record not found; go with defaults */
1852 memcpy(smibuf, cdbsmi->ptr,
1853 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1854 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1861 * PutSuppMsgInfo() - (re)write supplementary record for a message
1863 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1867 /* Use the negative of the message number for its supp record index */
1868 TheIndex = (0L - smibuf->smi_msgnum);
1870 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1871 smibuf->smi_msgnum, smibuf->smi_refcount);
1873 cdb_store(CDB_MSGMAIN,
1874 &TheIndex, sizeof(long),
1875 smibuf, sizeof(struct SuppMsgInfo));
1880 * AdjRefCount - change the reference count for a message;
1881 * delete the message if it reaches zero
1883 void AdjRefCount(long msgnum, int incr)
1886 struct SuppMsgInfo smi;
1889 /* This is a *tight* critical section; please keep it that way, as
1890 * it may get called while nested in other critical sections.
1891 * Complicating this any further will surely cause deadlock!
1893 begin_critical_section(S_SUPPMSGMAIN);
1894 GetSuppMsgInfo(&smi, msgnum);
1895 smi.smi_refcount += incr;
1896 PutSuppMsgInfo(&smi);
1897 end_critical_section(S_SUPPMSGMAIN);
1899 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1900 msgnum, smi.smi_refcount);
1902 /* If the reference count is now zero, delete the message
1903 * (and its supplementary record as well).
1905 if (smi.smi_refcount == 0) {
1906 lprintf(9, "Deleting message <%ld>\n", msgnum);
1908 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1909 delnum = (0L - msgnum);
1910 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1915 * Write a generic object to this room
1917 * Note: this could be much more efficient. Right now we use two temporary
1918 * files, and still pull the message into memory as with all others.
1920 void CtdlWriteObject(char *req_room, /* Room to stuff it in */
1921 char *content_type, /* MIME type of this object */
1922 char *tempfilename, /* Where to fetch it from */
1923 struct usersupp *is_mailbox, /* Mailbox room? */
1924 int is_binary, /* Is encoding necessary? */
1925 int is_unique /* Del others of this type? */
1930 char filename[PATH_MAX];
1933 struct quickroom qrbuf;
1934 char roomname[ROOMNAMELEN];
1935 struct CtdlMessage *msg;
1938 if (is_mailbox != NULL)
1939 MailboxName(roomname, is_mailbox, req_room);
1941 safestrncpy(roomname, req_room, sizeof(roomname));
1942 lprintf(9, "CtdlWriteObject() to <%s>\n", roomname);
1944 strcpy(filename, tmpnam(NULL));
1945 fp = fopen(filename, "w");
1949 tempfp = fopen(tempfilename, "r");
1950 if (tempfp == NULL) {
1956 fprintf(fp, "Content-type: %s\n", content_type);
1957 lprintf(9, "Content-type: %s\n", content_type);
1959 if (is_binary == 0) {
1960 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
1961 while (ch = getc(tempfp), ch > 0)
1967 fprintf(fp, "Content-transfer-encoding: base64\n\n");
1970 sprintf(cmdbuf, "./base64 -e <%s >>%s",
1971 tempfilename, filename);
1975 lprintf(9, "Allocating\n");
1976 msg = mallok(sizeof(struct CtdlMessage));
1977 memset(msg, 0, sizeof(struct CtdlMessage));
1978 msg->cm_magic = CTDLMESSAGE_MAGIC;
1979 msg->cm_anon_type = MES_NORMAL;
1980 msg->cm_format_type = 4;
1981 msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
1982 msg->cm_fields['O'] = strdoop(req_room);
1983 msg->cm_fields['N'] = strdoop(config.c_nodename);
1984 msg->cm_fields['H'] = strdoop(config.c_humannode);
1986 lprintf(9, "Loading\n");
1987 fp = fopen(filename, "rb");
1988 fseek(fp, 0L, SEEK_END);
1991 msg->cm_fields['M'] = mallok(len);
1992 fread(msg->cm_fields['M'], len, 1, fp);
1996 /* Create the requested room if we have to. */
1997 if (getroom(&qrbuf, roomname) != 0) {
1998 create_room(roomname, 4, "", 0);
2000 /* If the caller specified this object as unique, delete all
2001 * other objects of this type that are currently in the room.
2004 lprintf(9, "Deleted %d other msgs of this type\n",
2005 CtdlDeleteMessages(roomname, 0L, content_type));
2007 /* Now write the data */
2008 CtdlSaveMsg(msg, "", roomname, MES_LOCAL, 1);
2009 CtdlFreeMessage(msg);