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