testcase
[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
7 * Copyright (c) The Exim Maintainers 2016
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
8544e77a
PP
22/* returns a pointer to the FILE, and puts the size in bytes into mbox_file_size
23 * normally, source_file_override is NULL */
8523533c 24
0f0c8159
JH
25FILE *
26spool_mbox(unsigned long *mbox_file_size, const uschar *source_file_override)
27{
3c51463e
JH
28uschar message_subdir[2];
29uschar buffer[16384];
30uschar *temp_string;
31uschar *mbox_path;
32FILE *mbox_file = NULL;
33FILE *data_file = NULL;
34FILE *yield = NULL;
35header_line *my_headerlist;
36struct stat statbuf;
37int i, j;
38void *reset_point = store_get(0);
39
40mbox_path = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id,
41 message_id);
42
43/* Skip creation if already spooled out as mbox file */
44if (!spool_mbox_ok)
45 {
46 /* create temp directory inside scan dir, directory_make works recursively */
47 temp_string = string_sprintf("scan/%s", message_id);
48 if (!directory_make(spool_directory, temp_string, 0750, FALSE))
49 {
50 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
51 "scan directory %s/scan/%s", spool_directory, temp_string));
52 goto OUT;
53 }
8e669ac1 54
3c51463e
JH
55 /* open [message_id].eml file for writing */
56 mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE);
57 if (mbox_file == NULL)
58 {
59 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
60 "scan file %s", mbox_path));
61 goto OUT;
62 }
63
64 /* Generate mailbox headers. The $received_for variable is (up to at least
65 Exim 4.64) never set here, because it is only set when expanding the
66 contents of the Received: header line. However, the code below will use it
67 if it should become available in future. */
68
69 temp_string = expand_string(
70 US"From ${if def:return_path{$return_path}{MAILER-DAEMON}} ${tod_bsdinbox}\n"
71 "${if def:sender_address{X-Envelope-From: <${sender_address}>\n}}"
72 "${if def:recipients{X-Envelope-To: ${recipients}\n}}");
73
74 if (temp_string != NULL)
f951fd57 75 {
3c51463e
JH
76 i = fwrite(temp_string, Ustrlen(temp_string), 1, mbox_file);
77 if (i != 1)
78 {
1ac6b2e7 79 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
3c51463e 80 mailbox headers to %s", mbox_path);
1ac6b2e7 81 goto OUT;
3c51463e 82 }
1ac6b2e7 83 }
f951fd57 84
3c51463e
JH
85 /* write all header lines to mbox file */
86 my_headerlist = header_list;
87 for (my_headerlist = header_list; my_headerlist != NULL;
88 my_headerlist = my_headerlist->next)
89 {
90 /* skip deleted headers */
91 if (my_headerlist->type == '*') continue;
92
93 i = fwrite(my_headerlist->text, my_headerlist->slen, 1, mbox_file);
94 if (i != 1)
95 {
96 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
97 message headers to %s", mbox_path);
f951fd57 98 goto OUT;
3c51463e
JH
99 }
100 }
8e669ac1 101
3c51463e
JH
102 /* End headers */
103 if (fwrite("\n", 1, 1, mbox_file) != 1)
104 {
105 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
106 message headers to %s", mbox_path);
107 goto OUT;
108 }
21a04aa3 109
3c51463e
JH
110 /* copy body file */
111 if (source_file_override == NULL)
112 {
113 message_subdir[1] = '\0';
114 for (i = 0; i < 2; i++)
115 {
a2da3176
JH
116 message_subdir[0] = split_spool_directory == (i == 0) ? message_id[5] : 0;
117 temp_string = string_sprintf("%s/input/%s/%s/%s-D",
118 spool_directory, queue_name, message_subdir, message_id);
119 if ((data_file = Ufopen(temp_string, "rb"))) break;
3c51463e
JH
120 }
121 }
122 else
123 data_file = Ufopen(source_file_override, "rb");
8523533c 124
a2da3176 125 if (!data_file)
3c51463e
JH
126 {
127 log_write(0, LOG_MAIN|LOG_PANIC, "Could not open datafile for message %s",
128 message_id);
f951fd57 129 goto OUT;
3c51463e
JH
130 }
131
132 /* The code used to use this line, but it doesn't work in Cygwin.
8523533c 133
3c51463e
JH
134 (void)fread(data_buffer, 1, 18, data_file);
135
136 What's happening is that spool_mbox used to use an fread to jump over the
137 file header. That fails under Cygwin because the header is locked, but
138 doing an fseek succeeds. We have to output the leading newline
139 explicitly, because the one in the file is parted of the locked area. */
8523533c 140
3c51463e
JH
141 if (!source_file_override)
142 (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
143
144 do
145 {
146 j = fread(buffer, 1, sizeof(buffer), data_file);
147
148 if (j > 0)
149 {
150 i = fwrite(buffer, j, 1, mbox_file);
151 if (i != 1)
152 {
153 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
154 message body to %s", mbox_path);
155 goto OUT;
156 }
157 }
158 } while (j > 0);
159
160 (void)fclose(mbox_file);
161 mbox_file = NULL;
162
8d468c4c
JH
163 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
164 spooled_message_id[sizeof(spooled_message_id)-1] = '\0';
3c51463e
JH
165 spool_mbox_ok = 1;
166 }
167
168/* get the size of the mbox message and open [message_id].eml file for reading*/
169if (Ustat(mbox_path, &statbuf) != 0 ||
170 (yield = Ufopen(mbox_path,"rb")) == NULL)
171 {
172 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
173 "scan file %s", mbox_path));
174 goto OUT;
175 }
176
177*mbox_file_size = statbuf.st_size;
178
179OUT:
180if (data_file) (void)fclose(data_file);
181if (mbox_file) (void)fclose(mbox_file);
182store_reset(reset_point);
183return yield;
8523533c
TK
184}
185
3c51463e
JH
186
187
188
8e669ac1 189
03d5892b 190/* remove mbox spool file and temp directory */
3c51463e
JH
191void
192unspool_mbox(void)
193{
3c51463e
JH
194spam_ok = 0;
195malware_ok = 0;
8e669ac1 196
3c51463e
JH
197if (spool_mbox_ok && !no_mbox_unspool)
198 {
199 uschar *mbox_path;
200 uschar *file_path;
201 int n;
202 struct dirent *entry;
203 DIR *tempdir;
204
205 mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
206
207 tempdir = opendir(CS mbox_path);
208 if (!tempdir)
209 {
210 debug_printf("Unable to opendir(%s): %s\n", mbox_path, strerror(errno));
211 /* Just in case we still can: */
f951fd57 212 rmdir(CS mbox_path);
3c51463e
JH
213 return;
214 }
215 /* loop thru dir & delete entries */
216 while((entry = readdir(tempdir)) != NULL)
217 {
218 uschar *name = US entry->d_name;
219 if (Ustrcmp(name, US".") == 0 || Ustrcmp(name, US"..") == 0) continue;
220
221 file_path = string_sprintf("%s/%s", mbox_path, name);
222 debug_printf("unspool_mbox(): unlinking '%s'\n", file_path);
223 n = unlink(CS file_path);
224 }
225
226 closedir(tempdir);
227
228 /* remove directory */
229 rmdir(CS mbox_path);
230 store_reset(mbox_path);
231 }
232spool_mbox_ok = 0;
8523533c
TK
233}
234
235#endif