1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
7 * Copyright (c) The Exim Maintainers 2016
10 /* Code for setting up a MBOX style spool file inside a /scan/<msgid>
11 sub directory of exim's spool directory. */
14 #ifdef WITH_CONTENT_SCAN
16 /* externals, we must reset them on unspooling */
17 #ifdef WITH_OLD_DEMIME
19 extern struct file_extension *file_extensions;
22 extern int malware_ok;
25 int spool_mbox_ok = 0;
26 uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
28 /* returns a pointer to the FILE, and puts the size in bytes into mbox_file_size
29 * normally, source_file_override is NULL */
32 spool_mbox(unsigned long *mbox_file_size, const uschar *source_file_override)
34 uschar message_subdir[2];
38 FILE *mbox_file = NULL;
39 FILE *data_file = NULL;
41 header_line *my_headerlist;
44 void *reset_point = store_get(0);
46 mbox_path = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id,
49 /* Skip creation if already spooled out as mbox file */
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))
56 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
57 "scan directory %s/scan/%s", spool_directory, temp_string));
61 /* open [message_id].eml file for writing */
62 mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE);
63 if (mbox_file == NULL)
65 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
66 "scan file %s", mbox_path));
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. */
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}}");
80 if (temp_string != NULL)
82 i = fwrite(temp_string, Ustrlen(temp_string), 1, mbox_file);
85 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
86 mailbox headers to %s", mbox_path);
91 /* write all header lines to mbox file */
92 my_headerlist = header_list;
93 for (my_headerlist = header_list; my_headerlist != NULL;
94 my_headerlist = my_headerlist->next)
96 /* skip deleted headers */
97 if (my_headerlist->type == '*') continue;
99 i = fwrite(my_headerlist->text, my_headerlist->slen, 1, mbox_file);
102 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
103 message headers to %s", mbox_path);
109 if (fwrite("\n", 1, 1, mbox_file) != 1)
111 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
112 message headers to %s", mbox_path);
117 if (source_file_override == NULL)
119 message_subdir[1] = '\0';
120 for (i = 0; i < 2; i++)
122 message_subdir[0] = (split_spool_directory == (i == 0))? message_id[5] : 0;
123 temp_string = string_sprintf("%s/input/%s/%s-D", spool_directory,
124 message_subdir, message_id);
125 data_file = Ufopen(temp_string, "rb");
126 if (data_file != NULL) break;
130 data_file = Ufopen(source_file_override, "rb");
132 if (data_file == NULL)
134 log_write(0, LOG_MAIN|LOG_PANIC, "Could not open datafile for message %s",
139 /* The code used to use this line, but it doesn't work in Cygwin.
141 (void)fread(data_buffer, 1, 18, data_file);
143 What's happening is that spool_mbox used to use an fread to jump over the
144 file header. That fails under Cygwin because the header is locked, but
145 doing an fseek succeeds. We have to output the leading newline
146 explicitly, because the one in the file is parted of the locked area. */
148 if (!source_file_override)
149 (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
153 j = fread(buffer, 1, sizeof(buffer), data_file);
157 i = fwrite(buffer, j, 1, mbox_file);
160 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
161 message body to %s", mbox_path);
167 (void)fclose(mbox_file);
170 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
171 spooled_message_id[sizeof(spooled_message_id)-1] = '\0';
175 /* get the size of the mbox message and open [message_id].eml file for reading*/
176 if (Ustat(mbox_path, &statbuf) != 0 ||
177 (yield = Ufopen(mbox_path,"rb")) == NULL)
179 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
180 "scan file %s", mbox_path));
184 *mbox_file_size = statbuf.st_size;
187 if (data_file) (void)fclose(data_file);
188 if (mbox_file) (void)fclose(mbox_file);
189 store_reset(reset_point);
196 /* remove mbox spool file, demimed files and temp directory */
202 /* reset all exiscan state variables */
203 #ifdef WITH_OLD_DEMIME
205 demime_errorlevel = 0;
206 demime_reason = NULL;
207 file_extensions = NULL;
213 if (spool_mbox_ok && !no_mbox_unspool)
218 struct dirent *entry;
221 mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
223 tempdir = opendir(CS mbox_path);
226 debug_printf("Unable to opendir(%s): %s\n", mbox_path, strerror(errno));
227 /* Just in case we still can: */
231 /* loop thru dir & delete entries */
232 while((entry = readdir(tempdir)) != NULL)
234 uschar *name = US entry->d_name;
235 if (Ustrcmp(name, US".") == 0 || Ustrcmp(name, US"..") == 0) continue;
237 file_path = string_sprintf("%s/%s", mbox_path, name);
238 debug_printf("unspool_mbox(): unlinking '%s'\n", file_path);
239 n = unlink(CS file_path);
244 /* remove directory */
246 store_reset(mbox_path);