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];
23 Create an MBOX-style message file from the spooled files.
25 Returns a pointer to the FILE, and puts the size in bytes into mbox_file_size.
26 If mbox_fname is non-null, fill in a pointer to the name.
27 Normally, source_file_override is NULL
31 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;
46 mbox_path = string_sprintf("%s/scan/%s/%s.eml",
47 spool_directory, message_id, message_id);
48 if (mbox_fname) *mbox_fname = mbox_path;
50 reset_point = store_get(0);
52 /* Skip creation if already spooled out as mbox file */
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))
59 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
60 "scan directory %s/scan/%s", spool_directory, temp_string));
64 /* open [message_id].eml file for writing */
65 mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE);
66 if (mbox_file == NULL)
68 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
69 "scan file %s", mbox_path));
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. */
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}}");
83 if (temp_string != NULL)
85 i = fwrite(temp_string, Ustrlen(temp_string), 1, mbox_file);
88 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
89 mailbox headers to %s", mbox_path);
94 /* write all header lines to mbox file */
95 my_headerlist = header_list;
96 for (my_headerlist = header_list; my_headerlist != NULL;
97 my_headerlist = my_headerlist->next)
99 /* skip deleted headers */
100 if (my_headerlist->type == '*') continue;
102 i = fwrite(my_headerlist->text, my_headerlist->slen, 1, mbox_file);
105 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
106 message headers to %s", mbox_path);
112 if (fwrite("\n", 1, 1, mbox_file) != 1)
114 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
115 message headers to %s", mbox_path);
120 if (!source_file_override)
122 message_subdir[1] = '\0';
123 for (i = 0; i < 2; i++)
125 message_subdir[0] = split_spool_directory == (i == 0) ? message_id[5] : 0;
126 temp_string = spool_fname(US"input", message_subdir, message_id, US"-D");
127 if ((data_file = Ufopen(temp_string, "rb"))) break;
131 data_file = Ufopen(source_file_override, "rb");
135 log_write(0, LOG_MAIN|LOG_PANIC, "Could not open datafile for message %s",
140 /* The code used to use this line, but it doesn't work in Cygwin.
142 (void)fread(data_buffer, 1, 18, data_file);
144 What's happening is that spool_mbox used to use an fread to jump over the
145 file header. That fails under Cygwin because the header is locked, but
146 doing an fseek succeeds. We have to output the leading newline
147 explicitly, because the one in the file is parted of the locked area. */
149 if (!source_file_override)
150 (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
154 j = fread(buffer, 1, sizeof(buffer), data_file);
158 i = fwrite(buffer, j, 1, mbox_file);
161 log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
162 message body to %s", mbox_path);
168 (void)fclose(mbox_file);
171 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
172 spooled_message_id[sizeof(spooled_message_id)-1] = '\0';
176 /* get the size of the mbox message and open [message_id].eml file for reading*/
178 if ( !(yield = Ufopen(mbox_path,"rb"))
179 || fstat(fileno(yield), &statbuf) != 0
181 log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
182 "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);
197 /* remove mbox spool file and temp directory */
204 if (spool_mbox_ok && !no_mbox_unspool)
208 struct dirent *entry;
211 mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
213 tempdir = opendir(CS mbox_path);
216 debug_printf("Unable to opendir(%s): %s\n", mbox_path, strerror(errno));
217 /* Just in case we still can: */
221 /* loop thru dir & delete entries */
222 while((entry = readdir(tempdir)) != NULL)
224 uschar *name = US entry->d_name;
226 if (Ustrcmp(name, US".") == 0 || Ustrcmp(name, US"..") == 0) continue;
228 file_path = string_sprintf("%s/%s", mbox_path, name);
229 debug_printf("unspool_mbox(): unlinking '%s'\n", file_path);
230 dummy = unlink(CS file_path);
235 /* remove directory */
237 store_reset(mbox_path);