1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015 */
8 /* Code for setting up a MBOX style spool file inside a /scan/<msgid>
9 sub directory of exim's spool directory. */
12 #ifdef WITH_CONTENT_SCAN
14 /* externals, we must reset them on unspooling */
15 #ifdef WITH_OLD_DEMIME
17 extern struct file_extension *file_extensions;
20 extern int malware_ok;
23 int spool_mbox_ok = 0;
24 uschar spooled_message_id[17];
26 /* returns a pointer to the FILE, and puts the size in bytes into mbox_file_size
27 * normally, source_file_override is NULL */
30 spool_mbox(unsigned long *mbox_file_size, const uschar *source_file_override)
32 uschar message_subdir[2];
36 FILE *mbox_file = NULL;
37 FILE *data_file = NULL;
39 header_line *my_headerlist;
42 void *reset_point = store_get(0);
44 mbox_path = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id,
47 /* Skip creation if already spooled out as mbox file */
50 /* create temp directory inside scan dir, directory_make works recursively */
51 temp_string = string_sprintf("scan/%s", message_id);
52 if (!directory_make(spool_directory, temp_string, 0750, FALSE))
54 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
55 "scan directory %s/scan/%s", spool_directory, temp_string));
59 /* open [message_id].eml file for writing */
60 mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE);
61 if (mbox_file == NULL)
63 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
64 "scan file %s", mbox_path));
68 /* Generate mailbox headers. The $received_for variable is (up to at least
69 Exim 4.64) never set here, because it is only set when expanding the
70 contents of the Received: header line. However, the code below will use it
71 if it should become available in future. */
73 temp_string = expand_string(
74 US"From ${if def:return_path{$return_path}{MAILER-DAEMON}} ${tod_bsdinbox}\n"
75 "${if def:sender_address{X-Envelope-From: <${sender_address}>\n}}"
76 "${if def:recipients{X-Envelope-To: ${recipients}\n}}");
78 if (temp_string != NULL)
80 i = fwrite(temp_string, Ustrlen(temp_string), 1, mbox_file);
83 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
84 mailbox headers to %s", mbox_path);
89 /* write all header lines to mbox file */
90 my_headerlist = header_list;
91 for (my_headerlist = header_list; my_headerlist != NULL;
92 my_headerlist = my_headerlist->next)
94 /* skip deleted headers */
95 if (my_headerlist->type == '*') continue;
97 i = fwrite(my_headerlist->text, my_headerlist->slen, 1, mbox_file);
100 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
101 message headers to %s", mbox_path);
107 if (fwrite("\n", 1, 1, mbox_file) != 1)
109 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
110 message headers to %s", mbox_path);
115 if (source_file_override == NULL)
117 message_subdir[1] = '\0';
118 for (i = 0; i < 2; i++)
120 message_subdir[0] = (split_spool_directory == (i == 0))? message_id[5] : 0;
121 temp_string = string_sprintf("%s/input/%s/%s-D", spool_directory,
122 message_subdir, message_id);
123 data_file = Ufopen(temp_string, "rb");
124 if (data_file != NULL) break;
128 data_file = Ufopen(source_file_override, "rb");
130 if (data_file == NULL)
132 log_write(0, LOG_MAIN|LOG_PANIC, "Could not open datafile for message %s",
137 /* The code used to use this line, but it doesn't work in Cygwin.
139 (void)fread(data_buffer, 1, 18, data_file);
141 What's happening is that spool_mbox used to use an fread to jump over the
142 file header. That fails under Cygwin because the header is locked, but
143 doing an fseek succeeds. We have to output the leading newline
144 explicitly, because the one in the file is parted of the locked area. */
146 if (!source_file_override)
147 (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
151 j = fread(buffer, 1, sizeof(buffer), data_file);
155 i = fwrite(buffer, j, 1, mbox_file);
158 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
159 message body to %s", mbox_path);
165 (void)fclose(mbox_file);
168 Ustrcpy(spooled_message_id, message_id);
172 /* get the size of the mbox message and open [message_id].eml file for reading*/
173 if (Ustat(mbox_path, &statbuf) != 0 ||
174 (yield = Ufopen(mbox_path,"rb")) == NULL)
176 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
177 "scan file %s", mbox_path));
181 *mbox_file_size = statbuf.st_size;
184 if (data_file) (void)fclose(data_file);
185 if (mbox_file) (void)fclose(mbox_file);
186 store_reset(reset_point);
193 /* remove mbox spool file, demimed files and temp directory */
199 /* reset all exiscan state variables */
200 #ifdef WITH_OLD_DEMIME
202 demime_errorlevel = 0;
203 demime_reason = NULL;
204 file_extensions = NULL;
210 if (spool_mbox_ok && !no_mbox_unspool)
215 struct dirent *entry;
218 mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
220 tempdir = opendir(CS mbox_path);
223 debug_printf("Unable to opendir(%s): %s\n", mbox_path, strerror(errno));
224 /* Just in case we still can: */
228 /* loop thru dir & delete entries */
229 while((entry = readdir(tempdir)) != NULL)
231 uschar *name = US entry->d_name;
232 if (Ustrcmp(name, US".") == 0 || Ustrcmp(name, US"..") == 0) continue;
234 file_path = string_sprintf("%s/%s", mbox_path, name);
235 debug_printf("unspool_mbox(): unlinking '%s'\n", file_path);
236 n = unlink(CS file_path);
241 /* remove directory */
243 store_reset(mbox_path);