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