eb9e378e7f76872b95d7291d946598bcf2bb8ad2
[citadel.git] / citadel / journaling.c
1 /*
2  * Message journaling functions.
3  */
4
5 #include <stdio.h>
6 #include <libcitadel.h>
7
8 #include "ctdl_module.h"
9
10 #include "citserver.h"
11 #include "user_ops.h"
12 #include "serv_vcard.h"                 /* Needed for vcard_getuser and extract_inet_email_addrs */
13 #include "internet_addressing.h"
14 #include "journaling.h"
15
16 struct jnlq *jnlq = NULL;       /* journal queue */
17
18 /*
19  * Hand off a copy of a message to be journalized.
20  */
21 void JournalBackgroundSubmit(struct CtdlMessage *msg,
22                         StrBuf *saved_rfc822_version,
23                         recptypes *recps) {
24
25         struct jnlq *jptr = NULL;
26
27         /* Avoid double journaling! */
28         if (!CM_IsEmpty(msg, eJournal)) {
29                 FreeStrBuf(&saved_rfc822_version);
30                 return;
31         }
32
33         jptr = (struct jnlq *)malloc(sizeof(struct jnlq));
34         if (jptr == NULL) {
35                 FreeStrBuf(&saved_rfc822_version);
36                 return;
37         }
38         memset(jptr, 0, sizeof(struct jnlq));
39         if (recps != NULL) memcpy(&jptr->recps, recps, sizeof(recptypes));
40         if (!CM_IsEmpty(msg, eAuthor)) jptr->from = strdup(msg->cm_fields[eAuthor]);
41         if (!CM_IsEmpty(msg, eNodeName)) jptr->node = strdup(msg->cm_fields[eNodeName]);
42         if (!CM_IsEmpty(msg, erFc822Addr)) jptr->rfca = strdup(msg->cm_fields[erFc822Addr]);
43         if (!CM_IsEmpty(msg, eMsgSubject)) jptr->subj = strdup(msg->cm_fields[eMsgSubject]);
44         if (!CM_IsEmpty(msg, emessageId)) jptr->msgn = strdup(msg->cm_fields[emessageId]);
45         jptr->rfc822 = SmashStrBuf(&saved_rfc822_version);
46
47         /* Add to the queue */
48         begin_critical_section(S_JOURNAL_QUEUE);
49         jptr->next = jnlq;
50         jnlq = jptr;
51         end_critical_section(S_JOURNAL_QUEUE);
52 }
53
54
55 /*
56  * Convert a local user name to an internet email address for the journal
57  */
58  
59 /*
60  * TODODRW: change this into a CtdlModuleDo type function that returns alternative address info
61  * for this local user. Something like CtdlModuleGoGetAddr(char *localuser, int type, char *alt_addr, size_t alt_addr_len)
62  * where type can be ADDR_EMAIL, ADDR_FIDO, ADDR_UUCP, ADDR_WEB, ADDR_POSTAL etc etc.
63  * This then begs the question of what should be returned. Is it wise to return a single char* using a comma as a
64  * delimiter? Or would it be better to return a linked list of some kind?
65  */
66 void local_to_inetemail(char *inetemail, char *localuser, size_t inetemail_len) {
67         struct ctdluser us;
68         struct vCard *v;
69
70         strcpy(inetemail, "");
71         if (CtdlGetUser(&us, localuser) != 0) {
72                 return;
73         }
74
75         v = vcard_get_user(&us);
76         if (v == NULL) {
77                 return;
78         }
79
80         extract_inet_email_addrs(inetemail, inetemail_len, NULL, 0, v, 1);
81         vcard_free(v);
82 }
83
84
85 /*
86  * Called by JournalRunQueue() to send an individual message.
87  */
88 void JournalRunQueueMsg(struct jnlq *jmsg) {
89
90         struct CtdlMessage *journal_msg = NULL;
91         recptypes *journal_recps = NULL;
92         StrBuf *message_text = NULL;
93         char mime_boundary[256];
94         long mblen;
95         long rfc822len;
96         char recipient[256];
97         char inetemail[256];
98         static int seq = 0;
99         int i;
100
101         if (jmsg == NULL)
102                 return;
103         journal_recps = validate_recipients(config.c_journal_dest, NULL, 0);
104         if (journal_recps != NULL) {
105
106                 if (  (journal_recps->num_local > 0)
107                    || (journal_recps->num_internet > 0)
108                    || (journal_recps->num_ignet > 0)
109                    || (journal_recps->num_room > 0)
110                 ) {
111
112                         /*
113                          * Construct journal message.
114                          * Note that we are transferring ownership of some of the memory here.
115                          */
116                         journal_msg = malloc(sizeof(struct CtdlMessage));
117                         memset(journal_msg, 0, sizeof(struct CtdlMessage));
118                         journal_msg->cm_magic = CTDLMESSAGE_MAGIC;
119                         journal_msg->cm_anon_type = MES_NORMAL;
120                         journal_msg->cm_format_type = FMT_RFC822;
121                         CM_SetField(journal_msg, eJournal, HKEY("is journal"));
122                         CM_SetField(journal_msg, eAuthor, jmsg->from, strlen(jmsg->from));
123                         CM_SetField(journal_msg, eNodeName, jmsg->node, strlen(jmsg->node));
124                         CM_SetField(journal_msg, erFc822Addr, jmsg->rfca, strlen(jmsg->rfca));
125                         CM_SetField(journal_msg, eMsgSubject, jmsg->subj, strlen(jmsg->subj));
126
127                         mblen = snprintf(mime_boundary, sizeof(mime_boundary),
128                                          "--Citadel-Journal-%08lx-%04x--", time(NULL), ++seq);
129                         rfc822len = strlen(jmsg->rfc822);
130                        
131                         message_text = NewStrBufPlain(NULL, rfc822len + sizeof(recptypes) + 1024);
132
133                         /*
134                          * Here is where we begin to compose the journalized message.
135                          * NOTE: the superfluous "Content-Identifer: ExJournalReport" header was
136                          *       requested by a paying customer, and yes, it is intentionally
137                          *       spelled wrong.  Do NOT remove or change it.
138                          */
139                         StrBufAppendBufPlain(
140                                 message_text, 
141                                 HKEY("Content-type: multipart/mixed; boundary=\""), 0);
142
143                         StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
144
145                         StrBufAppendBufPlain(
146                                 message_text, 
147                                 HKEY("\"\r\n"
148                                      "Content-Identifer: ExJournalReport\r\n"
149                                      "MIME-Version: 1.0\r\n"
150                                      "\n"
151                                      "--"), 0);
152
153                         StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
154
155                         StrBufAppendBufPlain(
156                                 message_text, 
157                                 HKEY("\r\n"
158                                      "Content-type: text/plain\r\n"
159                                      "\r\n"
160                                      "Sender: "), 0);
161
162                         if (CM_IsEmpty(journal_msg, eAuthor))
163                                 StrBufAppendBufPlain(
164                                         message_text, 
165                                         journal_msg->cm_fields[eAuthor], -1, 0);
166                         else
167                                 StrBufAppendBufPlain(
168                                         message_text, 
169                                         HKEY("(null)"), 0);
170
171                         if (!CM_IsEmpty(journal_msg, erFc822Addr)) {
172                                 StrBufAppendPrintf(message_text, " <%s>",
173                                                    journal_msg->cm_fields[erFc822Addr]);
174                         }
175                         else if (!CM_IsEmpty(journal_msg, eNodeName)) {
176                                 StrBufAppendPrintf(message_text, " @ %s",
177                                                    journal_msg->cm_fields[eNodeName]);
178                         }
179                         else
180                                 StrBufAppendBufPlain(
181                                         message_text, 
182                                         HKEY(" "), 0);
183
184                         StrBufAppendBufPlain(
185                                 message_text, 
186                                 HKEY("\r\n"
187                                      "Message-ID: <"), 0);
188
189                         StrBufAppendBufPlain(message_text, jmsg->msgn, -1, 0);
190                         StrBufAppendBufPlain(
191                                 message_text, 
192                                 HKEY(">\r\n"
193                                      "Recipients:\r\n"), 0);
194
195                         if (jmsg->recps.num_local > 0) {
196                                 for (i=0; i<jmsg->recps.num_local; ++i) {
197                                         extract_token(recipient, jmsg->recps.recp_local,
198                                                         i, '|', sizeof recipient);
199                                         local_to_inetemail(inetemail, recipient, sizeof inetemail);
200                                         StrBufAppendPrintf(message_text, 
201                                                            "    %s <%s>\r\n", recipient, inetemail);
202                                 }
203                         }
204
205                         if (jmsg->recps.num_ignet > 0) {
206                                 for (i=0; i<jmsg->recps.num_ignet; ++i) {
207                                         extract_token(recipient, jmsg->recps.recp_ignet,
208                                                         i, '|', sizeof recipient);
209                                         StrBufAppendPrintf(message_text, 
210                                                            "    %s\r\n", recipient);
211                                 }
212                         }
213
214                         if (jmsg->recps.num_internet > 0) {
215                                 for (i=0; i<jmsg->recps.num_internet; ++i) {
216                                         extract_token(recipient, jmsg->recps.recp_internet,
217                                                         i, '|', sizeof recipient);
218                                         StrBufAppendPrintf(message_text, 
219                                                 "       %s\r\n", recipient);
220                                 }
221                         }
222
223                         StrBufAppendBufPlain(
224                                 message_text, 
225                                 HKEY("\r\n"
226                                      "--"), 0);
227
228                         StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
229
230                         StrBufAppendBufPlain(
231                                 message_text, 
232                                 HKEY("\r\n"
233                                      "Content-type: message/rfc822\r\n"
234                                      "\r\n"), 0);
235
236                         StrBufAppendBufPlain(message_text, jmsg->rfc822, rfc822len, 0);
237
238                         StrBufAppendBufPlain(
239                                 message_text, 
240                                 HKEY("--"), 0);
241
242                         StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);
243
244                         StrBufAppendBufPlain(
245                                 message_text, 
246                                 HKEY("--\r\n"), 0);
247
248                         CM_SetAsFieldSB(journal_msg, eMesageText, &message_text);
249                         free(jmsg->rfc822);
250                         free(jmsg->msgn);
251                         jmsg->rfc822 = NULL;
252                         jmsg->msgn = NULL;
253                         
254                         /* Submit journal message */
255                         CtdlSubmitMsg(journal_msg, journal_recps, "", 0);
256                         CM_Free(journal_msg);
257                 }
258
259                 free_recipients(journal_recps);
260         }
261
262         /* We are responsible for freeing this memory. */
263         free(jmsg);
264 }
265
266
267 /*
268  * Run the queue.
269  */
270 void JournalRunQueue(void) {
271         struct jnlq *jptr = NULL;
272
273         while (jnlq != NULL) {
274                 begin_critical_section(S_JOURNAL_QUEUE);
275                 if (jnlq != NULL) {
276                         jptr = jnlq;
277                         jnlq = jnlq->next;
278                 }
279                 end_critical_section(S_JOURNAL_QUEUE);
280                 JournalRunQueueMsg(jptr);
281         }
282 }
283
284