b1de39e7dee3a687fa04f5019799d4bcb094a128
[users/heiko/exim.git] / src / src / spool_mbox.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
6  * License: GPL
7  * Copyright (c) The Exim Maintainers 2016
8  */
9
10 /* Code for setting up a MBOX style spool file inside a /scan/<msgid>
11 sub directory of exim's spool directory. */
12
13 #include "exim.h"
14 #ifdef WITH_CONTENT_SCAN
15
16 extern int malware_ok;
17 extern int spam_ok;
18
19 int spool_mbox_ok = 0;
20 uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
21
22 /* returns a pointer to the FILE, and puts the size in bytes into mbox_file_size
23  * normally, source_file_override is NULL */
24
25 FILE *
26 spool_mbox(unsigned long *mbox_file_size, const uschar *source_file_override)
27 {
28 uschar message_subdir[2];
29 uschar buffer[16384];
30 uschar *temp_string;
31 uschar *mbox_path;
32 FILE *mbox_file = NULL;
33 FILE *data_file = NULL;
34 FILE *yield = NULL;
35 header_line *my_headerlist;
36 struct stat statbuf;
37 int i, j;
38 void *reset_point = store_get(0);
39
40 mbox_path = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id,
41   message_id);
42
43 /* Skip creation if already spooled out as mbox file */
44 if (!spool_mbox_ok)
45   {
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))
49     {
50     log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
51       "scan directory %s/scan/%s", spool_directory, temp_string));
52     goto OUT;
53     }
54
55   /* open [message_id].eml file for writing */
56   mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE);
57   if (mbox_file == NULL)
58     {
59     log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
60       "scan file %s", mbox_path));
61     goto OUT;
62     }
63
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. */
68
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}}");
73
74   if (temp_string != NULL)
75     {
76     i = fwrite(temp_string, Ustrlen(temp_string), 1, mbox_file);
77     if (i != 1)
78       {
79       log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
80           mailbox headers to %s", mbox_path);
81       goto OUT;
82       }
83     }
84
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)
89     {
90     /* skip deleted headers */
91     if (my_headerlist->type == '*') continue;
92
93     i = fwrite(my_headerlist->text, my_headerlist->slen, 1, mbox_file);
94     if (i != 1)
95       {
96       log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
97           message headers to %s", mbox_path);
98       goto OUT;
99       }
100     }
101
102   /* End headers */
103   if (fwrite("\n", 1, 1, mbox_file) != 1)
104     {
105     log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
106       message headers to %s", mbox_path);
107     goto OUT;
108     }
109
110   /* copy body file */
111   if (source_file_override == NULL)
112     {
113     message_subdir[1] = '\0';
114     for (i = 0; i < 2; i++)
115       {
116       message_subdir[0] = split_spool_directory == (i == 0) ? message_id[5] : 0;
117       temp_string = spool_fname(US"input", message_subdir, message_id, US"-D");
118       if ((data_file = Ufopen(temp_string, "rb"))) break;
119       }
120     }
121   else
122     data_file = Ufopen(source_file_override, "rb");
123
124   if (!data_file)
125     {
126     log_write(0, LOG_MAIN|LOG_PANIC, "Could not open datafile for message %s",
127       message_id);
128     goto OUT;
129     }
130
131   /* The code used to use this line, but it doesn't work in Cygwin.
132
133       (void)fread(data_buffer, 1, 18, data_file);
134     
135      What's happening is that spool_mbox used to use an fread to jump over the
136      file header. That fails under Cygwin because the header is locked, but
137      doing an fseek succeeds. We have to output the leading newline
138      explicitly, because the one in the file is parted of the locked area.  */
139
140   if (!source_file_override)
141     (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
142
143   do
144     {
145     j = fread(buffer, 1, sizeof(buffer), data_file);
146
147     if (j > 0)
148       {
149       i = fwrite(buffer, j, 1, mbox_file);
150       if (i != 1)
151         {
152         log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \
153             message body to %s", mbox_path);
154         goto OUT;
155         }
156       }
157     } while (j > 0);
158
159   (void)fclose(mbox_file);
160   mbox_file = NULL;
161
162   Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
163   spooled_message_id[sizeof(spooled_message_id)-1] = '\0';
164   spool_mbox_ok = 1;
165   }
166
167 /* get the size of the mbox message and open [message_id].eml file for reading*/
168
169 if (  !(yield = Ufopen(mbox_path,"rb"))
170    || fstat(fileno(yield), &statbuf) != 0
171    )
172   log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno,
173     "scan file %s", mbox_path));
174 else
175   *mbox_file_size = statbuf.st_size;
176
177 OUT:
178 if (data_file) (void)fclose(data_file);
179 if (mbox_file) (void)fclose(mbox_file);
180 store_reset(reset_point);
181 return yield;
182 }
183
184
185
186
187
188 /* remove mbox spool file and temp directory */
189 void
190 unspool_mbox(void)
191 {
192 spam_ok = 0;
193 malware_ok = 0;
194
195 if (spool_mbox_ok && !no_mbox_unspool)
196   {
197   uschar *mbox_path;
198   uschar *file_path;
199   struct dirent *entry;
200   DIR *tempdir;
201
202   mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
203
204   tempdir = opendir(CS mbox_path);
205   if (!tempdir)
206     {
207     debug_printf("Unable to opendir(%s): %s\n", mbox_path, strerror(errno));
208     /* Just in case we still can: */
209     rmdir(CS mbox_path);
210     return;
211     }
212   /* loop thru dir & delete entries */
213   while((entry = readdir(tempdir)) != NULL)
214     {
215     uschar *name = US entry->d_name;
216     int dummy;
217     if (Ustrcmp(name, US".") == 0 || Ustrcmp(name, US"..") == 0) continue;
218
219     file_path = string_sprintf("%s/%s", mbox_path, name);
220     debug_printf("unspool_mbox(): unlinking '%s'\n", file_path);
221     dummy = unlink(CS file_path);
222     }
223
224   closedir(tempdir);
225
226   /* remove directory */
227   rmdir(CS mbox_path);
228   store_reset(mbox_path);
229   }
230 spool_mbox_ok = 0;
231 }
232
233 #endif