Copyright year updates (things touched in 2015)
[exim.git] / src / src / regex.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
8 /* Code for matching regular expressions against headers and body.
9  Called from acl.c. */
10
11 #include "exim.h"
12 #ifdef WITH_CONTENT_SCAN
13 #include <unistd.h>
14 #include <sys/mman.h>
15
16 /* Structure to hold a list of Regular expressions */
17 typedef struct pcre_list {
18   pcre *re;
19   uschar *pcre_text;
20   struct pcre_list *next;
21 } pcre_list;
22
23 uschar regex_match_string_buffer[1024];
24
25 extern FILE *mime_stream;
26 extern uschar *mime_current_boundary;
27
28 int
29 regex(const uschar **listptr)
30 {
31   int sep = 0;
32   const uschar *list = *listptr;
33   uschar *regex_string;
34   uschar regex_string_buffer[1024];
35   unsigned long mbox_size;
36   FILE *mbox_file;
37   pcre *re;
38   pcre_list *re_list_head = NULL;
39   pcre_list *re_list_item;
40   const char *pcre_error;
41   int pcre_erroffset;
42   uschar *linebuffer;
43   long f_pos = 0;
44
45   /* reset expansion variable */
46   regex_match_string = NULL;
47
48   if (mime_stream == NULL) {
49     /* We are in the DATA ACL */
50     mbox_file = spool_mbox(&mbox_size, NULL);
51     if (mbox_file == NULL) {
52       /* error while spooling */
53       log_write(0, LOG_MAIN|LOG_PANIC,
54              "regex acl condition: error while creating mbox spool file");
55       return DEFER;
56     };
57   }
58   else {
59     f_pos = ftell(mime_stream);
60     mbox_file = mime_stream;
61   };
62
63   /* precompile our regexes */
64   while ((regex_string = string_nextinlist(&list, &sep,
65                                            regex_string_buffer,
66                                            sizeof(regex_string_buffer))) != NULL) {
67
68     /* parse option */
69     if ( (strcmpic(regex_string,US"false") == 0) ||
70          (Ustrcmp(regex_string,"0") == 0) ) {
71       /* explicitly no matching */
72       continue;
73     };
74
75     /* compile our regular expression */
76     re = pcre_compile( CS regex_string,
77                        0,
78                        &pcre_error,
79                        &pcre_erroffset,
80                        NULL );
81
82     if (re == NULL) {
83       log_write(0, LOG_MAIN,
84            "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
85       continue;
86     }
87     else {
88       re_list_item = store_get(sizeof(pcre_list));
89       re_list_item->re = re;
90       re_list_item->pcre_text = string_copy(regex_string);
91       re_list_item->next = re_list_head;
92       re_list_head = re_list_item;
93     };
94   };
95
96   /* no regexes -> nothing to do */
97   if (re_list_head == NULL) {
98     return FAIL;
99   };
100
101   /* match each line against all regexes */
102   linebuffer = store_get(32767);
103   while (fgets(CS linebuffer, 32767, mbox_file) != NULL) {
104     if ( (mime_stream != NULL) && (mime_current_boundary != NULL) ) {
105       /* check boundary */
106       if (Ustrncmp(linebuffer,"--",2) == 0) {
107         if (Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
108           /* found boundary */
109           break;
110       };
111     };
112     re_list_item = re_list_head;
113     do {
114       /* try matcher on the line */
115       if (pcre_exec(re_list_item->re, NULL, CS linebuffer,
116       (int)Ustrlen(linebuffer), 0, 0, NULL, 0) >= 0) {
117         Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
118         regex_match_string = regex_match_string_buffer;
119         if (mime_stream == NULL)
120           (void)fclose(mbox_file);
121         else {
122           clearerr(mime_stream);
123           fseek(mime_stream,f_pos,SEEK_SET);
124         };
125         return OK;
126       };
127       re_list_item = re_list_item->next;
128     } while (re_list_item != NULL);
129   };
130
131   if (mime_stream == NULL)
132     (void)fclose(mbox_file);
133   else {
134     clearerr(mime_stream);
135     fseek(mime_stream,f_pos,SEEK_SET);
136   };
137
138   /* no matches ... */
139   return FAIL;
140 }
141
142
143 int
144 mime_regex(const uschar **listptr)
145 {
146   int sep = 0;
147   const uschar *list = *listptr;
148   uschar *regex_string;
149   uschar regex_string_buffer[1024];
150   pcre *re;
151   pcre_list *re_list_head = NULL;
152   pcre_list *re_list_item;
153   const char *pcre_error;
154   int pcre_erroffset;
155   FILE *f;
156   uschar *mime_subject = NULL;
157   int mime_subject_len = 0;
158
159   /* reset expansion variable */
160   regex_match_string = NULL;
161
162   /* precompile our regexes */
163   while ((regex_string = string_nextinlist(&list, &sep,
164                                            regex_string_buffer,
165                                            sizeof(regex_string_buffer))) != NULL) {
166
167     /* parse option */
168     if ( (strcmpic(regex_string,US"false") == 0) ||
169          (Ustrcmp(regex_string,"0") == 0) ) {
170       /* explicitly no matching */
171       continue;
172     };
173
174     /* compile our regular expression */
175     re = pcre_compile( CS regex_string,
176                        0,
177                        &pcre_error,
178                        &pcre_erroffset,
179                        NULL );
180
181     if (re == NULL) {
182       log_write(0, LOG_MAIN,
183            "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
184       continue;
185     }
186     else {
187       re_list_item = store_get(sizeof(pcre_list));
188       re_list_item->re = re;
189       re_list_item->pcre_text = string_copy(regex_string);
190       re_list_item->next = re_list_head;
191       re_list_head = re_list_item;
192     };
193   };
194
195   /* no regexes -> nothing to do */
196   if (re_list_head == NULL) {
197     return FAIL;
198   };
199
200   /* check if the file is already decoded */
201   if (mime_decoded_filename == NULL) {
202     const uschar *empty = US"";
203     /* no, decode it first */
204     mime_decode(&empty);
205     if (mime_decoded_filename == NULL) {
206       /* decoding failed */
207       log_write(0, LOG_MAIN,
208            "mime_regex acl condition warning - could not decode MIME part to file.");
209       return DEFER;
210     };
211   };
212
213
214   /* open file */
215   f = fopen(CS mime_decoded_filename, "rb");
216   if (f == NULL) {
217     /* open failed */
218     log_write(0, LOG_MAIN,
219          "mime_regex acl condition warning - can't open '%s' for reading.", mime_decoded_filename);
220     return DEFER;
221   };
222
223   /* get 32k memory */
224   mime_subject = (uschar *)store_get(32767);
225
226   /* read max 32k chars from file */
227   mime_subject_len = fread(mime_subject, 1, 32766, f);
228
229   re_list_item = re_list_head;
230   do {
231     /* try matcher on the mmapped file */
232     debug_printf("Matching '%s'\n", re_list_item->pcre_text);
233     if (pcre_exec(re_list_item->re, NULL, CS mime_subject,
234                   mime_subject_len, 0, 0, NULL, 0) >= 0) {
235       Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
236       regex_match_string = regex_match_string_buffer;
237       (void)fclose(f);
238       return OK;
239     };
240     re_list_item = re_list_item->next;
241   } while (re_list_item != NULL);
242
243   (void)fclose(f);
244
245   /* no matches ... */
246   return FAIL;
247 }
248
249 #endif /* WITH_CONTENT_SCAN */