Docs: document ancillary info for more event types. Bug 2313
[exim.git] / src / src / spool_mbox.c
CommitLineData
8523533c
TK
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
80fea873
JH
5/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
6 * License: GPL
f9ba5e22 7 * Copyright (c) The Exim Maintainers 2016 - 2018
80fea873 8 */
8523533c
TK
9
10/* Code for setting up a MBOX style spool file inside a /scan/<msgid>
11sub directory of exim's spool directory. */
12
13#include "exim.h"
14#ifdef WITH_CONTENT_SCAN
15
8523533c
TK
16extern int malware_ok;
17extern int spam_ok;
18
19int spool_mbox_ok = 0;
6e3b198d 20uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
8523533c 21
040721f2
JH
22/*
23Create an MBOX-style message file from the spooled files.
24
25Returns a pointer to the FILE, and puts the size in bytes into mbox_file_size.
26If mbox_fname is non-null, fill in a pointer to the name.
27Normally, source_file_override is NULL
28*/
8523533c 29
0f0c8159 30FILE *
040721f2
JH
31spool_mbox(unsigned long *mbox_file_size, const uschar *source_file_override,
32 uschar ** mbox_fname)
0f0c8159 33{
3c51463e
JH
34uschar message_subdir[2];
35uschar buffer[16384];
36uschar *temp_string;
37uschar *mbox_path;
1bd642c2 38FILE *mbox_file = NULL, *l_data_file = NULL, *yield = NULL;
3c51463e
JH
39header_line *my_headerlist;
40struct stat statbuf;
41int i, j;
040721f2
JH
42void *reset_point;
43
44mbox_path = string_sprintf("%s/scan/%s/%s.eml",
45 spool_directory, message_id, message_id);
46if (mbox_fname) *mbox_fname = mbox_path;
3c51463e 47
040721f2 48reset_point = store_get(0);
3c51463e
JH
49
50/* Skip creation if already spooled out as mbox file */
51if (!spool_mbox_ok)
52 {
53 /* create temp directory inside scan dir, directory_make works recursively */
54 temp_string = string_sprintf("scan/%s", message_id);
55 if (!directory_make(spool_directory, temp_string, 0750, FALSE))
56 {
57 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
58 "scan directory %s/scan/%s", spool_directory, temp_string));
59 goto OUT;
60 }
8e669ac1 61
3c51463e 62 /* open [message_id].eml file for writing */
328c5688
JH
63
64 if (!(mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE)))
3c51463e
JH
65 {
66 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
67 "scan file %s", mbox_path));
68 goto OUT;
69 }
70
71 /* Generate mailbox headers. The $received_for variable is (up to at least
72 Exim 4.64) never set here, because it is only set when expanding the
73 contents of the Received: header line. However, the code below will use it
74 if it should become available in future. */
75
76 temp_string = expand_string(
77 US"From ${if def:return_path{$return_path}{MAILER-DAEMON}} ${tod_bsdinbox}\n"
78 "${if def:sender_address{X-Envelope-From: <${sender_address}>\n}}"
79 "${if def:recipients{X-Envelope-To: ${recipients}\n}}");
80
328c5688
JH
81 if (temp_string)
82 if (fwrite(temp_string, Ustrlen(temp_string), 1, mbox_file) != 1)
3c51463e 83 {
1ac6b2e7 84 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
3c51463e 85 mailbox headers to %s", mbox_path);
1ac6b2e7 86 goto OUT;
3c51463e 87 }
f951fd57 88
328c5688 89 /* write all non-deleted header lines to mbox file */
3c51463e 90
328c5688
JH
91 for (my_headerlist = header_list; my_headerlist;
92 my_headerlist = my_headerlist->next)
93 if (my_headerlist->type != '*')
94 if (fwrite(my_headerlist->text, my_headerlist->slen, 1, mbox_file) != 1)
95 {
96 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
97 message headers to %s", mbox_path);
98 goto OUT;
99 }
8e669ac1 100
3c51463e
JH
101 /* End headers */
102 if (fwrite("\n", 1, 1, mbox_file) != 1)
103 {
104 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
105 message headers to %s", mbox_path);
106 goto OUT;
107 }
21a04aa3 108
1bd642c2
JH
109 /* Copy body file. If the main receive still has it open then it is holding
110 a lock, and we must not close it (which releases the lock), so just use the
111 global file handle. */
112 if (source_file_override)
113 l_data_file = Ufopen(source_file_override, "rb");
114 else if (spool_data_file)
115 l_data_file = spool_data_file;
116 else
3c51463e
JH
117 {
118 message_subdir[1] = '\0';
119 for (i = 0; i < 2; i++)
120 {
a2da3176 121 message_subdir[0] = split_spool_directory == (i == 0) ? message_id[5] : 0;
41313d92 122 temp_string = spool_fname(US"input", message_subdir, message_id, US"-D");
1bd642c2 123 if ((l_data_file = Ufopen(temp_string, "rb"))) break;
3c51463e
JH
124 }
125 }
8523533c 126
1bd642c2 127 if (!l_data_file)
3c51463e
JH
128 {
129 log_write(0, LOG_MAIN|LOG_PANIC, "Could not open datafile for message %s",
130 message_id);
f951fd57 131 goto OUT;
3c51463e
JH
132 }
133
134 /* The code used to use this line, but it doesn't work in Cygwin.
8523533c 135
1bd642c2 136 (void)fread(data_buffer, 1, 18, l_data_file);
f9ba5e22 137
3c51463e
JH
138 What's happening is that spool_mbox used to use an fread to jump over the
139 file header. That fails under Cygwin because the header is locked, but
140 doing an fseek succeeds. We have to output the leading newline
141 explicitly, because the one in the file is parted of the locked area. */
8523533c 142
3c51463e 143 if (!source_file_override)
1bd642c2 144 (void)fseek(l_data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
3c51463e
JH
145
146 do
147 {
328c5688
JH
148 uschar * s;
149
8768d548 150 if (!f.spool_file_wireformat || source_file_override)
1bd642c2 151 j = fread(buffer, 1, sizeof(buffer), l_data_file);
328c5688 152 else /* needs CRLF -> NL */
1bd642c2 153 if ((s = US fgets(CS buffer, sizeof(buffer), l_data_file)))
328c5688
JH
154 {
155 uschar * p = s + Ustrlen(s) - 1;
156
157 if (*p == '\n' && p[-1] == '\r')
158 *--p = '\n';
159 else if (*p == '\r')
1bd642c2 160 ungetc(*p--, l_data_file);
328c5688
JH
161
162 j = p - buffer;
163 }
164 else
165 j = 0;
3c51463e
JH
166
167 if (j > 0)
328c5688 168 if (fwrite(buffer, j, 1, mbox_file) != 1)
3c51463e
JH
169 {
170 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
171 message body to %s", mbox_path);
172 goto OUT;
173 }
3c51463e
JH
174 } while (j > 0);
175
176 (void)fclose(mbox_file);
177 mbox_file = NULL;
178
8d468c4c
JH
179 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
180 spooled_message_id[sizeof(spooled_message_id)-1] = '\0';
3c51463e
JH
181 spool_mbox_ok = 1;
182 }
183
184/* get the size of the mbox message and open [message_id].eml file for reading*/
10c50704
JH
185
186if ( !(yield = Ufopen(mbox_path,"rb"))
187 || fstat(fileno(yield), &statbuf) != 0
188 )
3c51463e
JH
189 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
190 "scan file %s", mbox_path));
10c50704
JH
191else
192 *mbox_file_size = statbuf.st_size;
3c51463e
JH
193
194OUT:
1bd642c2 195if (l_data_file && !spool_data_file) (void)fclose(l_data_file);
3c51463e
JH
196if (mbox_file) (void)fclose(mbox_file);
197store_reset(reset_point);
198return yield;
8523533c
TK
199}
200
3c51463e
JH
201
202
203
8e669ac1 204
03d5892b 205/* remove mbox spool file and temp directory */
3c51463e
JH
206void
207unspool_mbox(void)
208{
3c51463e
JH
209spam_ok = 0;
210malware_ok = 0;
8e669ac1 211
8768d548 212if (spool_mbox_ok && !f.no_mbox_unspool)
3c51463e
JH
213 {
214 uschar *mbox_path;
215 uschar *file_path;
3c51463e
JH
216 struct dirent *entry;
217 DIR *tempdir;
218
219 mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
220
cab0c277 221 if (!(tempdir = opendir(CS mbox_path)))
3c51463e
JH
222 {
223 debug_printf("Unable to opendir(%s): %s\n", mbox_path, strerror(errno));
224 /* Just in case we still can: */
f951fd57 225 rmdir(CS mbox_path);
3c51463e
JH
226 return;
227 }
228 /* loop thru dir & delete entries */
cab0c277 229 while((entry = readdir(tempdir)))
3c51463e
JH
230 {
231 uschar *name = US entry->d_name;
4dc2379a 232 int dummy;
3c51463e
JH
233 if (Ustrcmp(name, US".") == 0 || Ustrcmp(name, US"..") == 0) continue;
234
235 file_path = string_sprintf("%s/%s", mbox_path, name);
236 debug_printf("unspool_mbox(): unlinking '%s'\n", file_path);
cab0c277 237 dummy = unlink(CS file_path); dummy = dummy; /* compiler quietening */
3c51463e
JH
238 }
239
240 closedir(tempdir);
241
242 /* remove directory */
243 rmdir(CS mbox_path);
244 store_reset(mbox_path);
245 }
246spool_mbox_ok = 0;
8523533c
TK
247}
248
249#endif