760910a74612d61e9efba68994ca66b6570aa2c9
[users/jgh/exim.git] / src / src / regex.c
1 /* $Cambridge: exim/src/src/regex.c,v 1.1.2.1 2004/11/26 09:24:05 tom Exp $ */
2
3 /*************************************************
4 *     Exim - an Internet mail transport agent    *
5 *************************************************/
6
7 /* This file is part of the exiscan-acl content scanner
8 patch. It is NOT part of the standard exim distribution. */
9
10 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
11 /* License: GPL */
12
13 /* Code for matching regular expressions against headers and body.
14  Called from acl.c. */
15
16 #include "exim.h"
17 #include <unistd.h>
18 #include <sys/mman.h>
19
20 /* Structure to hold a list of Regular expressions */
21 typedef struct pcre_list {
22   pcre *re;
23   uschar *pcre_text;
24   struct pcre_list *next;
25 } pcre_list;
26
27 uschar regex_match_string_buffer[1024];
28
29 extern FILE *mime_stream;
30 extern uschar *mime_current_boundary;
31
32 int regex(uschar **listptr) {
33   int sep = 0;
34   uschar *list = *listptr;
35   uschar *regex_string;
36   uschar regex_string_buffer[1024];
37   unsigned long long mbox_size;
38   FILE *mbox_file;
39   pcre *re;
40   pcre_list *re_list_head = NULL;
41   pcre_list *re_list_item;
42   const char *pcre_error;
43   int pcre_erroffset;
44   uschar *linebuffer;
45   long f_pos = 0;
46   
47   /* reset expansion variable */
48   regex_match_string = NULL;
49   
50   if (mime_stream == NULL) {
51     /* We are in the DATA ACL */
52     mbox_file = spool_mbox(&mbox_size);
53     if (mbox_file == NULL) {
54       /* error while spooling */
55       log_write(0, LOG_MAIN|LOG_PANIC,
56              "regex acl condition: error while creating mbox spool file");
57       return DEFER;
58     };
59   }
60   else {
61     f_pos = ftell(mime_stream);
62     mbox_file = mime_stream;
63   };
64   
65   /* precompile our regexes */
66   while ((regex_string = string_nextinlist(&list, &sep,
67                                            regex_string_buffer,
68                                            sizeof(regex_string_buffer))) != NULL) {
69     
70     /* parse option */
71     if ( (strcmpic(regex_string,US"false") == 0) || 
72          (Ustrcmp(regex_string,"0") == 0) ) {
73       /* explicitly no matching */
74       continue;
75     };
76     
77     /* compile our regular expression */
78     re = pcre_compile( CS regex_string,
79                        0,
80                        &pcre_error,
81                        &pcre_erroffset,
82                        NULL );
83
84     if (re == NULL) {
85       log_write(0, LOG_MAIN,
86            "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
87       continue;
88     }
89     else {
90       re_list_item = store_get(sizeof(pcre_list));
91       re_list_item->re = re;
92       re_list_item->pcre_text = string_copy(regex_string);
93       re_list_item->next = re_list_head;
94       re_list_head = re_list_item;
95     };
96   };
97   
98   /* no regexes -> nothing to do */
99   if (re_list_head == NULL) {
100     return FAIL;
101   };
102   
103   /* match each line against all regexes */
104   linebuffer = store_get(32767);
105   while (fgets(CS linebuffer, 32767, mbox_file) != NULL) {  
106     if ( (mime_stream != NULL) && (mime_current_boundary != NULL) ) {
107       /* check boundary */
108       if (Ustrncmp(linebuffer,"--",2) == 0) {
109         if (Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
110           /* found boundary */
111           break;
112       };
113     };
114     re_list_item = re_list_head;
115     do {
116       /* try matcher on the line */
117       if (pcre_exec(re_list_item->re, NULL, CS linebuffer,
118                         (int)Ustrlen(linebuffer), 0, 0, NULL, 0) >= 0) {
119         Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
120         regex_match_string = regex_match_string_buffer;
121         if (mime_stream == NULL)
122           fclose(mbox_file);
123         else {
124           clearerr(mime_stream);
125           fseek(mime_stream,f_pos,SEEK_SET);
126         };
127         return OK;
128       };
129       re_list_item = re_list_item->next;
130     } while (re_list_item != NULL);
131   };
132   
133   if (mime_stream == NULL)
134     fclose(mbox_file);
135   else {
136     clearerr(mime_stream);
137     fseek(mime_stream,f_pos,SEEK_SET);
138   };
139     
140   /* no matches ... */
141   return FAIL;
142 }
143
144
145 int mime_regex(uschar **listptr) {
146   int sep = 0;
147   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     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, "r");
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       fclose(f);
238       return OK;
239     };
240     re_list_item = re_list_item->next;
241   } while (re_list_item != NULL);
242
243   fclose(f);
244   
245   /* no matches ... */
246   return FAIL;
247 }
248