1 // Message journaling functions.
3 // Copyright (c) 1987-2023 by the citadel.org team
5 // This program is open source software. Use, duplication, or disclosure
6 // is subject to the terms of the GNU General Public License, version 3.
9 #include <libcitadel.h>
10 #include "ctdl_module.h"
11 #include "citserver.h"
14 #include "serv_vcard.h" // Needed for vcard_getuser and extract_inet_email_addrs
15 #include "internet_addressing.h"
16 #include "journaling.h"
18 struct jnlq *jnlq = NULL; // journal queue
21 * Hand off a copy of a message to be journalized.
23 void JournalBackgroundSubmit(struct CtdlMessage *msg,
24 StrBuf *saved_rfc822_version,
25 struct recptypes *recps) {
27 struct jnlq *jptr = NULL;
29 /* Avoid double journaling! */
30 if (!CM_IsEmpty(msg, eJournal)) {
31 FreeStrBuf(&saved_rfc822_version);
35 jptr = (struct jnlq *)malloc(sizeof(struct jnlq));
37 FreeStrBuf(&saved_rfc822_version);
40 memset(jptr, 0, sizeof(struct jnlq));
41 if (recps != NULL) memcpy(&jptr->recps, recps, sizeof(struct recptypes));
42 if (!CM_IsEmpty(msg, eAuthor)) jptr->from = strdup(msg->cm_fields[eAuthor]);
43 if (!CM_IsEmpty(msg, erFc822Addr)) jptr->rfca = strdup(msg->cm_fields[erFc822Addr]);
44 if (!CM_IsEmpty(msg, eMsgSubject)) jptr->subj = strdup(msg->cm_fields[eMsgSubject]);
45 if (!CM_IsEmpty(msg, emessageId)) jptr->msgn = strdup(msg->cm_fields[emessageId]);
46 jptr->rfc822 = SmashStrBuf(&saved_rfc822_version);
48 /* Add to the queue */
49 begin_critical_section(S_JOURNAL_QUEUE);
52 end_critical_section(S_JOURNAL_QUEUE);
57 * Convert a local user name to an internet email address for the journal
58 * FIXME - grab the user's Internet email address from the user record, not from vCard !!!!
60 void local_to_inetemail(char *inetemail, char *localuser, size_t inetemail_len) {
64 strcpy(inetemail, "");
65 if (CtdlGetUser(&us, localuser) != 0) {
69 v = vcard_get_user(&us);
74 extract_inet_email_addrs(inetemail, inetemail_len, NULL, 0, v, 1);
80 * Called by JournalRunQueue() to send an individual message.
82 void JournalRunQueueMsg(struct jnlq *jmsg) {
84 struct CtdlMessage *journal_msg = NULL;
85 struct recptypes *journal_recps = NULL;
86 StrBuf *message_text = NULL;
87 char mime_boundary[256];
97 journal_recps = validate_recipients(CtdlGetConfigStr("c_journal_dest"), NULL, 0);
98 if (journal_recps != NULL) {
100 if ( (journal_recps->num_local > 0)
101 || (journal_recps->num_internet > 0)
102 || (journal_recps->num_room > 0)
106 * Construct journal message.
107 * Note that we are transferring ownership of some of the memory here.
109 journal_msg = malloc(sizeof(struct CtdlMessage));
110 memset(journal_msg, 0, sizeof(struct CtdlMessage));
111 journal_msg->cm_magic = CTDLMESSAGE_MAGIC;
112 journal_msg->cm_anon_type = MES_NORMAL;
113 journal_msg->cm_format_type = FMT_RFC822;
114 CM_SetField(journal_msg, eJournal, "is journal");
116 if (!IsEmptyStr(jmsg->from)) {
117 CM_SetField(journal_msg, eAuthor, jmsg->from);
120 if (!IsEmptyStr(jmsg->rfca)) {
121 CM_SetField(journal_msg, erFc822Addr, jmsg->rfca);
124 if (!IsEmptyStr(jmsg->subj)) {
125 CM_SetField(journal_msg, eMsgSubject, jmsg->subj);
128 mblen = snprintf(mime_boundary, sizeof(mime_boundary),
129 "--Citadel-Journal-%08lx-%04x--", time(NULL), ++seq);
131 if (!IsEmptyStr(jmsg->rfc822)) {
132 rfc822len = strlen(jmsg->rfc822);
138 message_text = NewStrBufPlain(NULL, rfc822len + sizeof(struct recptypes) + 1024);
141 * Here is where we begin to compose the journalized message.
142 * (The "ExJournalReport" header is consumed by some email retention services which assume the journaling agent is Exchange.)
144 StrBufAppendBufPlain(
146 HKEY("Content-type: multipart/mixed; boundary=\""),
150 StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
152 StrBufAppendBufPlain(
155 "Content-Identifier: ExJournalReport\r\n"
156 "MIME-Version: 1.0\r\n"
162 StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
164 StrBufAppendBufPlain(
167 "Content-type: text/plain\r\n"
171 if (CM_IsEmpty(journal_msg, eAuthor))
172 StrBufAppendBufPlain(
174 journal_msg->cm_fields[eAuthor], -1, 0);
176 StrBufAppendBufPlain(
180 if (!CM_IsEmpty(journal_msg, erFc822Addr)) {
181 StrBufAppendPrintf(message_text, " <%s>",
182 journal_msg->cm_fields[erFc822Addr]);
185 StrBufAppendBufPlain(message_text, HKEY("\r\nMessage-ID: <"), 0);
186 StrBufAppendBufPlain(message_text, jmsg->msgn, -1, 0);
187 StrBufAppendBufPlain(message_text, HKEY(">\r\nRecipients:\r\n"), 0);
189 if (jmsg->recps.num_local > 0) {
190 for (i=0; i<jmsg->recps.num_local; ++i) {
191 extract_token(recipient, jmsg->recps.recp_local, i, '|', sizeof recipient);
192 local_to_inetemail(inetemail, recipient, sizeof inetemail);
193 StrBufAppendPrintf(message_text, " %s <%s>\r\n", recipient, inetemail);
197 if (jmsg->recps.num_internet > 0) {
198 for (i=0; i<jmsg->recps.num_internet; ++i) {
199 extract_token(recipient, jmsg->recps.recp_internet, i, '|', sizeof recipient);
200 StrBufAppendPrintf(message_text, " %s\r\n", recipient);
204 StrBufAppendBufPlain(message_text, HKEY("\r\n" "--"), 0);
205 StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
206 StrBufAppendBufPlain(message_text, HKEY("\r\nContent-type: message/rfc822\r\n\r\n"), 0);
207 StrBufAppendBufPlain(message_text, jmsg->rfc822, rfc822len, 0);
208 StrBufAppendBufPlain(message_text, HKEY("--"), 0);
209 StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
210 StrBufAppendBufPlain(message_text, HKEY("--\r\n"), 0);
212 CM_SetAsFieldSB(journal_msg, eMesageText, &message_text);
218 /* Submit journal message */
219 CtdlSubmitMsg(journal_msg, journal_recps, "");
220 CM_Free(journal_msg);
223 free_recipients(journal_recps);
226 /* We are responsible for freeing this memory. */
234 void JournalRunQueue(void) {
235 struct jnlq *jptr = NULL;
237 while (jnlq != NULL) {
238 begin_critical_section(S_JOURNAL_QUEUE);
243 end_critical_section(S_JOURNAL_QUEUE);
244 JournalRunQueueMsg(jptr);