Merge branch 'debian' into hcoop_489
[hcoop/debian/exim4.git] / src / spool_mbox.c
CommitLineData
420a0d19
CE
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
2813c06e
CE
5/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
6 * License: GPL
7 * Copyright (c) The Exim Maintainers 2016
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;
2813c06e 20uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
420a0d19
CE
21
22/* returns a pointer to the FILE, and puts the size in bytes into mbox_file_size
23 * normally, source_file_override is NULL */
24
2813c06e
CE
25FILE *
26spool_mbox(unsigned long *mbox_file_size, const uschar *source_file_override)
27{
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 }
420a0d19 54
2813c06e
CE
55 /* open [message_id].eml file for writing */
56 mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE);
57 if (mbox_file == NULL)
420a0d19 58 {
2813c06e
CE
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)
75 {
76 i = fwrite(temp_string, Ustrlen(temp_string), 1, mbox_file);
77 if (i != 1)
78 {
420a0d19 79 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
2813c06e 80 mailbox headers to %s", mbox_path);
420a0d19 81 goto OUT;
2813c06e 82 }
420a0d19
CE
83 }
84
2813c06e
CE
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);
420a0d19 98 goto OUT;
2813c06e
CE
99 }
100 }
420a0d19 101
2813c06e
CE
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 }
420a0d19 109
2813c06e
CE
110 /* copy body file */
111 if (source_file_override == NULL)
112 {
113 message_subdir[1] = '\0';
114 for (i = 0; i < 2; i++)
115 {
116 message_subdir[0] = split_spool_directory == (i == 0) ? message_id[5] : 0;
117 temp_string = spool_fname(US"input", message_subdir, message_id, US"-D");
118 if ((data_file = Ufopen(temp_string, "rb"))) break;
119 }
120 }
121 else
122 data_file = Ufopen(source_file_override, "rb");
420a0d19 123
2813c06e
CE
124 if (!data_file)
125 {
126 log_write(0, LOG_MAIN|LOG_PANIC, "Could not open datafile for message %s",
127 message_id);
420a0d19 128 goto OUT;
2813c06e
CE
129 }
130
131 /* The code used to use this line, but it doesn't work in Cygwin.
132
133 (void)fread(data_buffer, 1, 18, data_file);
134
135 What's happening is that spool_mbox used to use an fread to jump over the
136 file header. That fails under Cygwin because the header is locked, but
137 doing an fseek succeeds. We have to output the leading newline
138 explicitly, because the one in the file is parted of the locked area. */
139
140 if (!source_file_override)
141 (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
142
143 do
144 {
145 j = fread(buffer, 1, sizeof(buffer), data_file);
146
147 if (j > 0)
148 {
149 i = fwrite(buffer, j, 1, mbox_file);
150 if (i != 1)
151 {
152 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
153 message body to %s", mbox_path);
154 goto OUT;
155 }
156 }
157 } while (j > 0);
158
159 (void)fclose(mbox_file);
160 mbox_file = NULL;
161
162 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
163 spooled_message_id[sizeof(spooled_message_id)-1] = '\0';
164 spool_mbox_ok = 1;
165 }
166
167/* get the size of the mbox message and open [message_id].eml file for reading*/
420a0d19 168
2813c06e
CE
169if ( !(yield = Ufopen(mbox_path,"rb"))
170 || fstat(fileno(yield), &statbuf) != 0
171 )
172 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
173 "scan file %s", mbox_path));
174else
420a0d19
CE
175 *mbox_file_size = statbuf.st_size;
176
2813c06e
CE
177OUT:
178if (data_file) (void)fclose(data_file);
179if (mbox_file) (void)fclose(mbox_file);
180store_reset(reset_point);
181return yield;
420a0d19
CE
182}
183
420a0d19 184
420a0d19 185
420a0d19 186
2813c06e
CE
187
188/* remove mbox spool file and temp directory */
189void
190unspool_mbox(void)
191{
192spam_ok = 0;
193malware_ok = 0;
194
195if (spool_mbox_ok && !no_mbox_unspool)
196 {
197 uschar *mbox_path;
198 uschar *file_path;
199 struct dirent *entry;
200 DIR *tempdir;
201
202 mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
203
204 tempdir = opendir(CS mbox_path);
205 if (!tempdir)
206 {
207 debug_printf("Unable to opendir(%s): %s\n", mbox_path, strerror(errno));
208 /* Just in case we still can: */
420a0d19 209 rmdir(CS mbox_path);
2813c06e
CE
210 return;
211 }
212 /* loop thru dir & delete entries */
213 while((entry = readdir(tempdir)) != NULL)
214 {
215 uschar *name = US entry->d_name;
216 int dummy;
217 if (Ustrcmp(name, US".") == 0 || Ustrcmp(name, US"..") == 0) continue;
218
219 file_path = string_sprintf("%s/%s", mbox_path, name);
220 debug_printf("unspool_mbox(): unlinking '%s'\n", file_path);
221 dummy = unlink(CS file_path);
222 }
223
224 closedir(tempdir);
225
226 /* remove directory */
227 rmdir(CS mbox_path);
228 store_reset(mbox_path);
229 }
230spool_mbox_ok = 0;
420a0d19
CE
231}
232
233#endif