1 // Message journaling functions.
3 // Copyright (c) 1987-2024 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
20 // Hand off a copy of a message to be journalized.
21 void JournalBackgroundSubmit(struct CtdlMessage *msg, StrBuf *saved_rfc822_version, struct recptypes *recps) {
22 struct jnlq *jptr = NULL;
24 // Avoid double journaling!
25 if (!CM_IsEmpty(msg, eJournal)) {
26 FreeStrBuf(&saved_rfc822_version);
30 jptr = (struct jnlq *)malloc(sizeof(struct jnlq));
32 FreeStrBuf(&saved_rfc822_version);
35 memset(jptr, 0, sizeof(struct jnlq));
36 if (recps != NULL) memcpy(&jptr->recps, recps, sizeof(struct recptypes));
37 if (!CM_IsEmpty(msg, eAuthor)) jptr->from = strdup(msg->cm_fields[eAuthor]);
38 if (!CM_IsEmpty(msg, erFc822Addr)) jptr->rfca = strdup(msg->cm_fields[erFc822Addr]);
39 if (!CM_IsEmpty(msg, eMsgSubject)) jptr->subj = strdup(msg->cm_fields[eMsgSubject]);
40 if (!CM_IsEmpty(msg, emessageId)) jptr->msgn = strdup(msg->cm_fields[emessageId]);
41 jptr->rfc822 = SmashStrBuf(&saved_rfc822_version);
44 begin_critical_section(S_JOURNAL_QUEUE);
47 end_critical_section(S_JOURNAL_QUEUE);
51 // Convert a local user name to an internet email address for the journal
52 // FIXME - grab the user's Internet email address from the user record, not from vCard !!!!
53 void local_to_inetemail(char *inetemail, char *localuser, size_t inetemail_len) {
57 strcpy(inetemail, "");
58 if (CtdlGetUser(&us, localuser) != 0) {
62 v = vcard_get_user(&us);
67 extract_inet_email_addrs(inetemail, inetemail_len, NULL, 0, v, 1);
72 // Called by JournalRunQueue() to send an individual message.
73 void JournalRunQueueMsg(struct jnlq *jmsg) {
75 struct CtdlMessage *journal_msg = NULL;
76 struct recptypes *journal_recps = NULL;
77 StrBuf *message_text = NULL;
78 char mime_boundary[256];
88 journal_recps = validate_recipients(CtdlGetConfigStr("c_journal_dest"), 0);
89 if (journal_recps != NULL) {
91 if ( (journal_recps->num_local > 0)
92 || (journal_recps->num_internet > 0)
93 || (journal_recps->num_room > 0)
95 // Construct journal message.
96 // Note that we are transferring ownership of some of the memory here.
97 journal_msg = malloc(sizeof(struct CtdlMessage));
98 memset(journal_msg, 0, sizeof(struct CtdlMessage));
99 journal_msg->cm_magic = CTDLMESSAGE_MAGIC;
100 journal_msg->cm_anon_type = MES_NORMAL;
101 journal_msg->cm_format_type = FMT_RFC822;
102 CM_SetField(journal_msg, eJournal, "is journal");
104 if (!IsEmptyStr(jmsg->from)) {
105 CM_SetField(journal_msg, eAuthor, jmsg->from);
108 if (!IsEmptyStr(jmsg->rfca)) {
109 CM_SetField(journal_msg, erFc822Addr, jmsg->rfca);
112 if (!IsEmptyStr(jmsg->subj)) {
113 CM_SetField(journal_msg, eMsgSubject, jmsg->subj);
116 mblen = snprintf(mime_boundary, sizeof(mime_boundary),
117 "--Citadel-Journal-%08lx-%04x--", time(NULL), ++seq);
119 if (!IsEmptyStr(jmsg->rfc822)) {
120 rfc822len = strlen(jmsg->rfc822);
126 message_text = NewStrBufPlain(NULL, rfc822len + sizeof(struct recptypes) + 1024);
128 // Here is where we begin to compose the journalized message.
129 // (The "ExJournalReport" header is consumed by some email retention services which assume the journaling agent is Exchange.)
130 StrBufAppendBufPlain(
132 HKEY("Content-type: multipart/mixed; boundary=\""),
136 StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
138 StrBufAppendBufPlain(
141 "Content-Identifier: ExJournalReport\r\n"
142 "MIME-Version: 1.0\r\n"
148 StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
150 StrBufAppendBufPlain(
153 "Content-type: text/plain\r\n"
157 if (CM_IsEmpty(journal_msg, eAuthor))
158 StrBufAppendBufPlain(
160 journal_msg->cm_fields[eAuthor], -1, 0);
162 StrBufAppendBufPlain(
166 if (!CM_IsEmpty(journal_msg, erFc822Addr)) {
167 StrBufAppendPrintf(message_text, " <%s>",
168 journal_msg->cm_fields[erFc822Addr]);
171 StrBufAppendBufPlain(message_text, HKEY("\r\nMessage-ID: <"), 0);
172 StrBufAppendBufPlain(message_text, jmsg->msgn, -1, 0);
173 StrBufAppendBufPlain(message_text, HKEY(">\r\nRecipients:\r\n"), 0);
175 if (jmsg->recps.num_local > 0) {
176 for (i=0; i<jmsg->recps.num_local; ++i) {
177 extract_token(recipient, jmsg->recps.recp_local, i, '|', sizeof recipient);
178 local_to_inetemail(inetemail, recipient, sizeof inetemail);
179 StrBufAppendPrintf(message_text, " %s <%s>\r\n", recipient, inetemail);
183 if (jmsg->recps.num_internet > 0) {
184 for (i=0; i<jmsg->recps.num_internet; ++i) {
185 extract_token(recipient, jmsg->recps.recp_internet, i, '|', sizeof recipient);
186 StrBufAppendPrintf(message_text, " %s\r\n", recipient);
190 StrBufAppendBufPlain(message_text, HKEY("\r\n" "--"), 0);
191 StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
192 StrBufAppendBufPlain(message_text, HKEY("\r\nContent-type: message/rfc822\r\n\r\n"), 0);
193 StrBufAppendBufPlain(message_text, jmsg->rfc822, rfc822len, 0);
194 StrBufAppendBufPlain(message_text, HKEY("--"), 0);
195 StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
196 StrBufAppendBufPlain(message_text, HKEY("--\r\n"), 0);
198 CM_SetAsFieldSB(journal_msg, eMessageText, &message_text);
204 // Submit journal message
205 CtdlSubmitMsg(journal_msg, journal_recps, "");
206 CM_Free(journal_msg);
209 free_recipients(journal_recps);
212 // We are responsible for freeing this memory.
218 void JournalRunQueue(void) {
219 struct jnlq *jptr = NULL;
221 while (jnlq != NULL) {
222 begin_critical_section(S_JOURNAL_QUEUE);
227 end_critical_section(S_JOURNAL_QUEUE);
228 JournalRunQueueMsg(jptr);