Avoid creating before-buffer pointer. Bug 2145
[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
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;
38FILE *mbox_file = NULL;
39FILE *data_file = NULL;
40FILE *yield = NULL;
41header_line *my_headerlist;
42struct stat statbuf;
43int i, j;
040721f2
JH
44void *reset_point;
45
46mbox_path = string_sprintf("%s/scan/%s/%s.eml",
47 spool_directory, message_id, message_id);
48if (mbox_fname) *mbox_fname = mbox_path;
3c51463e 49
040721f2 50reset_point = store_get(0);
3c51463e
JH
51
52/* Skip creation if already spooled out as mbox file */
53if (!spool_mbox_ok)
54 {
55 /* create temp directory inside scan dir, directory_make works recursively */
56 temp_string = string_sprintf("scan/%s", message_id);
57 if (!directory_make(spool_directory, temp_string, 0750, FALSE))
58 {
59 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
60 "scan directory %s/scan/%s", spool_directory, temp_string));
61 goto OUT;
62 }
8e669ac1 63
3c51463e 64 /* open [message_id].eml file for writing */
328c5688
JH
65
66 if (!(mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE)))
3c51463e
JH
67 {
68 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
69 "scan file %s", mbox_path));
70 goto OUT;
71 }
72
73 /* Generate mailbox headers. The $received_for variable is (up to at least
74 Exim 4.64) never set here, because it is only set when expanding the
75 contents of the Received: header line. However, the code below will use it
76 if it should become available in future. */
77
78 temp_string = expand_string(
79 US"From ${if def:return_path{$return_path}{MAILER-DAEMON}} ${tod_bsdinbox}\n"
80 "${if def:sender_address{X-Envelope-From: <${sender_address}>\n}}"
81 "${if def:recipients{X-Envelope-To: ${recipients}\n}}");
82
328c5688
JH
83 if (temp_string)
84 if (fwrite(temp_string, Ustrlen(temp_string), 1, mbox_file) != 1)
3c51463e 85 {
1ac6b2e7 86 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
3c51463e 87 mailbox headers to %s", mbox_path);
1ac6b2e7 88 goto OUT;
3c51463e 89 }
f951fd57 90
328c5688 91 /* write all non-deleted header lines to mbox file */
3c51463e 92
328c5688
JH
93 for (my_headerlist = header_list; my_headerlist;
94 my_headerlist = my_headerlist->next)
95 if (my_headerlist->type != '*')
96 if (fwrite(my_headerlist->text, my_headerlist->slen, 1, mbox_file) != 1)
97 {
98 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
99 message headers to %s", mbox_path);
100 goto OUT;
101 }
8e669ac1 102
3c51463e
JH
103 /* End headers */
104 if (fwrite("\n", 1, 1, mbox_file) != 1)
105 {
106 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
107 message headers to %s", mbox_path);
108 goto OUT;
109 }
21a04aa3 110
3c51463e 111 /* copy body file */
040721f2 112 if (!source_file_override)
3c51463e
JH
113 {
114 message_subdir[1] = '\0';
115 for (i = 0; i < 2; i++)
116 {
a2da3176 117 message_subdir[0] = split_spool_directory == (i == 0) ? message_id[5] : 0;
41313d92 118 temp_string = spool_fname(US"input", message_subdir, message_id, US"-D");
a2da3176 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 {
328c5688
JH
146 uschar * s;
147
148 if (!spool_file_wireformat || source_file_override)
149 j = fread(buffer, 1, sizeof(buffer), data_file);
150 else /* needs CRLF -> NL */
151 if ((s = fgets(buffer, sizeof(buffer), data_file)))
152 {
153 uschar * p = s + Ustrlen(s) - 1;
154
155 if (*p == '\n' && p[-1] == '\r')
156 *--p = '\n';
157 else if (*p == '\r')
158 ungetc(*p--, data_file);
159
160 j = p - buffer;
161 }
162 else
163 j = 0;
3c51463e
JH
164
165 if (j > 0)
328c5688 166 if (fwrite(buffer, j, 1, mbox_file) != 1)
3c51463e
JH
167 {
168 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
169 message body to %s", mbox_path);
170 goto OUT;
171 }
3c51463e
JH
172 } while (j > 0);
173
174 (void)fclose(mbox_file);
175 mbox_file = NULL;
176
8d468c4c
JH
177 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
178 spooled_message_id[sizeof(spooled_message_id)-1] = '\0';
3c51463e
JH
179 spool_mbox_ok = 1;
180 }
181
182/* get the size of the mbox message and open [message_id].eml file for reading*/
10c50704
JH
183
184if ( !(yield = Ufopen(mbox_path,"rb"))
185 || fstat(fileno(yield), &statbuf) != 0
186 )
3c51463e
JH
187 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
188 "scan file %s", mbox_path));
10c50704
JH
189else
190 *mbox_file_size = statbuf.st_size;
3c51463e
JH
191
192OUT:
193if (data_file) (void)fclose(data_file);
194if (mbox_file) (void)fclose(mbox_file);
195store_reset(reset_point);
196return yield;
8523533c
TK
197}
198
3c51463e
JH
199
200
201
8e669ac1 202
03d5892b 203/* remove mbox spool file and temp directory */
3c51463e
JH
204void
205unspool_mbox(void)
206{
3c51463e
JH
207spam_ok = 0;
208malware_ok = 0;
8e669ac1 209
3c51463e
JH
210if (spool_mbox_ok && !no_mbox_unspool)
211 {
212 uschar *mbox_path;
213 uschar *file_path;
3c51463e
JH
214 struct dirent *entry;
215 DIR *tempdir;
216
217 mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
218
219 tempdir = opendir(CS mbox_path);
220 if (!tempdir)
221 {
222 debug_printf("Unable to opendir(%s): %s\n", mbox_path, strerror(errno));
223 /* Just in case we still can: */
f951fd57 224 rmdir(CS mbox_path);
3c51463e
JH
225 return;
226 }
227 /* loop thru dir & delete entries */
228 while((entry = readdir(tempdir)) != NULL)
229 {
230 uschar *name = US entry->d_name;
4dc2379a 231 int dummy;
3c51463e
JH
232 if (Ustrcmp(name, US".") == 0 || Ustrcmp(name, US"..") == 0) continue;
233
234 file_path = string_sprintf("%s/%s", mbox_path, name);
235 debug_printf("unspool_mbox(): unlinking '%s'\n", file_path);
4dc2379a 236 dummy = unlink(CS file_path);
3c51463e
JH
237 }
238
239 closedir(tempdir);
240
241 /* remove directory */
242 rmdir(CS mbox_path);
243 store_reset(mbox_path);
244 }
245spool_mbox_ok = 0;
8523533c
TK
246}
247
248#endif