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 extern int malware_ok;
19 int spool_mbox_ok = 0;
20 uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
22 /* returns a pointer to the FILE, and puts the size in bytes into mbox_file_size
23 * normally, source_file_override is NULL */
26 spool_mbox(unsigned long *mbox_file_size, const uschar *source_file_override)
28 uschar message_subdir[2];
32 FILE *mbox_file = NULL;
33 FILE *data_file = NULL;
35 header_line *my_headerlist;
38 void *reset_point = store_get(0);
40 mbox_path = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id,
43 /* Skip creation if already spooled out as mbox file */
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))
50 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
51 "scan directory %s/scan/%s", spool_directory, temp_string));
55 /* open [message_id].eml file for writing */
56 mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE);
57 if (mbox_file == NULL)
59 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
60 "scan file %s", mbox_path));
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. */
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}}");
74 if (temp_string != NULL)
76 i = fwrite(temp_string, Ustrlen(temp_string), 1, mbox_file);
79 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
80 mailbox headers to %s", mbox_path);
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)
90 /* skip deleted headers */
91 if (my_headerlist->type == '*') continue;
93 i = fwrite(my_headerlist->text, my_headerlist->slen, 1, mbox_file);
96 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
97 message headers to %s", mbox_path);
103 if (fwrite("\n", 1, 1, mbox_file) != 1)
105 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
106 message headers to %s", mbox_path);
111 if (source_file_override == NULL)
113 message_subdir[1] = '\0';
114 for (i = 0; i < 2; i++)
116 message_subdir[0] = (split_spool_directory == (i == 0))? message_id[5] : 0;
117 temp_string = string_sprintf("%s/input/%s/%s-D", spool_directory,
118 message_subdir, message_id);
119 data_file = Ufopen(temp_string, "rb");
120 if (data_file != NULL) break;
124 data_file = Ufopen(source_file_override, "rb");
126 if (data_file == NULL)
128 log_write(0, LOG_MAIN|LOG_PANIC, "Could not open datafile for message %s",
133 /* The code used to use this line, but it doesn't work in Cygwin.
135 (void)fread(data_buffer, 1, 18, data_file);
137 What's happening is that spool_mbox used to use an fread to jump over the
138 file header. That fails under Cygwin because the header is locked, but
139 doing an fseek succeeds. We have to output the leading newline
140 explicitly, because the one in the file is parted of the locked area. */
142 if (!source_file_override)
143 (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
147 j = fread(buffer, 1, sizeof(buffer), data_file);
151 i = fwrite(buffer, j, 1, mbox_file);
154 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
155 message body to %s", mbox_path);
161 (void)fclose(mbox_file);
164 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
165 spooled_message_id[sizeof(spooled_message_id)-1] = '\0';
169 /* get the size of the mbox message and open [message_id].eml file for reading*/
170 if (Ustat(mbox_path, &statbuf) != 0 ||
171 (yield = Ufopen(mbox_path,"rb")) == NULL)
173 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
174 "scan file %s", mbox_path));
178 *mbox_file_size = statbuf.st_size;
181 if (data_file) (void)fclose(data_file);
182 if (mbox_file) (void)fclose(mbox_file);
183 store_reset(reset_point);
191 /* remove mbox spool file and temp directory */
198 if (spool_mbox_ok && !no_mbox_unspool)
203 struct dirent *entry;
206 mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
208 tempdir = opendir(CS mbox_path);
211 debug_printf("Unable to opendir(%s): %s\n", mbox_path, strerror(errno));
212 /* Just in case we still can: */
216 /* loop thru dir & delete entries */
217 while((entry = readdir(tempdir)) != NULL)
219 uschar *name = US entry->d_name;
220 if (Ustrcmp(name, US".") == 0 || Ustrcmp(name, US"..") == 0) continue;
222 file_path = string_sprintf("%s/%s", mbox_path, name);
223 debug_printf("unspool_mbox(): unlinking '%s'\n", file_path);
224 n = unlink(CS file_path);
229 /* remove directory */
231 store_reset(mbox_path);