Merge branch 'debian'
[hcoop/debian/exim4.git] / src / spool_mbox.c
CommitLineData
420a0d19
CE
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
2ea97746
CE
5/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
6 * License: GPL
7 * Copyright (c) The Exim Maintainers 2016 - 2018
8 */
420a0d19
CE
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
420a0d19
CE
16extern int malware_ok;
17extern int spam_ok;
18
19int spool_mbox_ok = 0;
2ea97746
CE
20uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
21
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*/
29
30FILE *
31spool_mbox(unsigned long *mbox_file_size, const uschar *source_file_override,
32 uschar ** mbox_fname)
33{
34uschar message_subdir[2];
35uschar buffer[16384];
36uschar *temp_string;
37uschar *mbox_path;
38FILE *mbox_file = NULL, *l_data_file = NULL, *yield = NULL;
39header_line *my_headerlist;
40struct stat statbuf;
41int i, j;
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;
47
48reset_point = store_get(0);
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 }
420a0d19 61
2ea97746 62 /* open [message_id].eml file for writing */
420a0d19 63
2ea97746
CE
64 if (!(mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE)))
65 {
66 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
67 "scan file %s", mbox_path));
68 goto OUT;
69 }
420a0d19 70
2ea97746
CE
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
81 if (temp_string)
82 if (fwrite(temp_string, Ustrlen(temp_string), 1, mbox_file) != 1)
83 {
84 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
85 mailbox headers to %s", mbox_path);
420a0d19 86 goto OUT;
2ea97746
CE
87 }
88
89 /* write all non-deleted header lines to mbox file */
90
91 for (my_headerlist = header_list; my_headerlist;
420a0d19 92 my_headerlist = my_headerlist->next)
2ea97746
CE
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 }
100
101 /* End headers */
102 if (fwrite("\n", 1, 1, mbox_file) != 1)
420a0d19 103 {
2ea97746
CE
104 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
105 message headers to %s", mbox_path);
106 goto OUT;
420a0d19
CE
107 }
108
2ea97746
CE
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
117 {
118 message_subdir[1] = '\0';
119 for (i = 0; i < 2; i++)
120 {
121 message_subdir[0] = split_spool_directory == (i == 0) ? message_id[5] : 0;
122 temp_string = spool_fname(US"input", message_subdir, message_id, US"-D");
123 if ((l_data_file = Ufopen(temp_string, "rb"))) break;
124 }
125 }
126
127 if (!l_data_file)
128 {
129 log_write(0, LOG_MAIN|LOG_PANIC, "Could not open datafile for message %s",
130 message_id);
131 goto OUT;
132 }
133
134 /* The code used to use this line, but it doesn't work in Cygwin.
135
136 (void)fread(data_buffer, 1, 18, l_data_file);
137
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. */
142
143 if (!source_file_override)
144 (void)fseek(l_data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
145
146 do
147 {
148 uschar * s;
149
150 if (!f.spool_file_wireformat || source_file_override)
151 j = fread(buffer, 1, sizeof(buffer), l_data_file);
152 else /* needs CRLF -> NL */
153 if ((s = US fgets(CS buffer, sizeof(buffer), l_data_file)))
154 {
155 uschar * p = s + Ustrlen(s) - 1;
156
157 if (*p == '\n' && p[-1] == '\r')
158 *--p = '\n';
159 else if (*p == '\r')
160 ungetc(*p--, l_data_file);
161
162 j = p - buffer;
163 }
164 else
165 j = 0;
166
167 if (j > 0)
168 if (fwrite(buffer, j, 1, mbox_file) != 1)
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 }
420a0d19
CE
174 } while (j > 0);
175
2ea97746
CE
176 (void)fclose(mbox_file);
177 mbox_file = NULL;
420a0d19 178
2ea97746
CE
179 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
180 spooled_message_id[sizeof(spooled_message_id)-1] = '\0';
181 spool_mbox_ok = 1;
182 }
420a0d19 183
2ea97746 184/* get the size of the mbox message and open [message_id].eml file for reading*/
420a0d19 185
2ea97746
CE
186if ( !(yield = Ufopen(mbox_path,"rb"))
187 || fstat(fileno(yield), &statbuf) != 0
188 )
189 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
190 "scan file %s", mbox_path));
191else
420a0d19
CE
192 *mbox_file_size = statbuf.st_size;
193
2ea97746
CE
194OUT:
195if (l_data_file && !spool_data_file) (void)fclose(l_data_file);
196if (mbox_file) (void)fclose(mbox_file);
197store_reset(reset_point);
198return yield;
420a0d19
CE
199}
200
420a0d19 201
420a0d19 202
420a0d19 203
2ea97746
CE
204
205/* remove mbox spool file and temp directory */
206void
207unspool_mbox(void)
208{
209spam_ok = 0;
210malware_ok = 0;
211
212if (spool_mbox_ok && !f.no_mbox_unspool)
213 {
214 uschar *mbox_path;
215 uschar *file_path;
216 struct dirent *entry;
217 DIR *tempdir;
218
219 mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
220
221 if (!(tempdir = opendir(CS mbox_path)))
222 {
223 debug_printf("Unable to opendir(%s): %s\n", mbox_path, strerror(errno));
224 /* Just in case we still can: */
420a0d19 225 rmdir(CS mbox_path);
2ea97746
CE
226 return;
227 }
228 /* loop thru dir & delete entries */
229 while((entry = readdir(tempdir)))
230 {
231 uschar *name = US entry->d_name;
232 int dummy;
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);
237 dummy = unlink(CS file_path); dummy = dummy; /* compiler quietening */
238 }
239
240 closedir(tempdir);
241
242 /* remove directory */
243 rmdir(CS mbox_path);
244 store_reset(mbox_path);
245 }
246spool_mbox_ok = 0;
420a0d19
CE
247}
248
249#endif