/[debian]/mimetic/branches/upstream/current/examples/engine.cxx
ViewVC logotype

Contents of /mimetic/branches/upstream/current/examples/engine.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 128 - (show annotations)
Sat Feb 25 16:49:20 2006 UTC (15 years, 6 months ago) by gregoa
File size: 12543 byte(s)
[svn-inject] Installing original source of mimetic
1 #include <algorithm>
2 #include <iostream>
3 #include <string>
4 #include <cctype>
5 #include <sys/types.h>
6 #include <regex.h>
7 #ifdef HAS_PCRE
8 #include <pcreposix.h>
9 #endif
10
11 #include "engine.h"
12
13 using namespace std;
14 using namespace mimetic;
15
16 engine::engine(const command_line& cl)
17 : m_cl(cl), m_pcre(false),
18 m_match_mode(match_type_none)
19 {
20 // set options
21 if(cl.is_set(p_match_shell) || cl.is_set(p_match_regex))
22 m_match_mode =
23 cl.is_set(p_match_shell) ?
24 match_type_shell :
25 match_type_regex;
26
27 m_match_mode |=
28 cl.is_set(p_case_insensitive) ? match_flag_case_insensitive : 0;
29
30 m_match_mode |=
31 cl.is_set(p_perl_regex) ? match_flag_perl_mode: 0;
32
33 if((m_match_mode & match_type_mask) == match_type_none)
34 m_match_mode |= match_type_regex; // default
35 }
36
37 int engine::posix_regex_match(const string& text, const string& pattern, int match_mode)
38 {
39 int r;
40 regex_t rex;
41 r = regcomp(&rex, pattern.c_str(),
42 ( match_mode & match_flag_case_insensitive ? REG_ICASE: 0));
43 if(r != 0)
44 {
45 char buf[256];
46 regerror(r, &rex, buf, 255);
47 die(buf);
48 }
49 r = regexec(&rex, text.c_str(), 0, 0, 0);
50 regfree(&rex);
51 return r == 0;
52 }
53
54 int engine::perl_regex_match(const string& text, const string& pattern, int match_mode )
55 {
56 #ifdef HAS_PCRE
57 #else
58 die("uuh?");
59 #endif
60 return 0;
61 }
62
63 int engine::pattern_match(const string& text, const string& pattern, int match_mode)
64 {
65 switch(match_mode & match_type_mask)
66 {
67 case match_type_none:
68 die("match_type_none");
69 case match_type_exact:
70 return exact_match(text, pattern, match_mode);
71 case match_type_regex:
72 return regex_match(text, pattern, match_mode);
73 case match_type_shell:
74 return shell_match(text, pattern, match_mode);
75 default:
76 die("uuh?");
77 }
78 return 0;
79 }
80
81 int engine::shell_match(const string& text, const string& pattern, int match_mode)
82 {
83 die("not impl");
84 return 0;
85 }
86
87 int engine::regex_match(const string& text, const string& pattern, int match_mode)
88 {
89 if(m_pcre)
90 return engine::perl_regex_match(text, pattern, match_mode);
91 else
92 return engine::posix_regex_match(text, pattern, match_mode);
93 }
94
95 void engine::action_attach(MimeEntity& me, parts_hierarchy* ph, const string& fqn)
96 {
97 bool isMultipart = me.header().contentType().isMultipart();
98 bool isTopLevel = !ph->size();
99
100 /*
101 1) me is multipart:
102 add the attach to me as the last part
103 2) me is not multipart
104 create a multipart/mixed with me and the attach childs
105 and put it in the same level/position of me
106 3) me is not multipart and is the top level entity
107 same as 2) but move all me fields to the new top-level
108 */
109 Attachment* pA = new Attachment(fqn);
110 if(!pA->isValid())
111 die("attach error");
112 if(isMultipart)
113 {
114 DBG( "isMultipart");
115 me.body().parts().push_back(pA);
116 } else {
117 MimeEntity *mm;
118 mm = new MultipartMixed;
119 mm->body().parts().push_back(&me);
120 mm->body().parts().push_back(pA);
121 if(!isTopLevel)
122 {
123 DBG( "!isTopLevel");
124 MimeEntity *parent = *ph->begin();
125 replace(parent->body().parts().begin(),
126 parent->body().parts().end(),
127 &me, mm);
128 } else {
129 DBG( "isTopLevel");
130 // add cp fields here
131 Header::iterator bit, eit, pos;
132 bit = me.header().begin(), me.header().end();
133 string name; // field name
134 pos = mm->header().begin(); // insert before others
135 for(; bit != eit; ++bit)
136 {
137 name = bit->name();
138 transform(name.begin(), name.end(),
139 name.begin(), ::tolower);
140 if(name.find("content-") == 0 || name == "mime-version")
141 continue;
142 mm->header().insert(pos, *bit);
143 }
144 }
145 }
146 }
147
148 void engine::action(MimeEntity& me, parts_hierarchy* ph)
149 {
150 MimeEntity* parent = (ph->size() ? *ph->begin() : &me);
151 if(m_cl.is_set(p_add_header))
152 {
153 static const char* key = "add-header";
154 command_line::iterator bit, eit;
155 bit = m_cl.begin(key), eit = m_cl.end(key);
156 for(; bit != eit; ++bit)
157 {
158 Field f(bit->second);
159 parent->header().push_back(f);
160 }
161 }
162 if(m_cl.is_set(p_add_part_header)) {
163 static const char* key = "add-part-header";
164 command_line::iterator bit, eit;
165 bit = m_cl.begin(key), eit = m_cl.end(key);
166 for(; bit != eit; ++bit)
167 {
168 Field f(bit->second);
169 me.header().push_back(f);
170 }
171 }
172 if(m_cl.is_set(p_attach))
173 {
174 static const char* key = "attach";
175 command_line::iterator bit, eit;
176 bit = m_cl.begin(key), eit = m_cl.end(key);
177 for(; bit != eit; ++bit)
178 action_attach(me, ph, bit->second);
179 }
180
181 if(m_cl.is_set(p_print_msg))
182 cout << *parent;
183 else if(m_cl.is_set(p_print_part))
184 cout << me;
185 }
186
187
188 int engine::exact_match(const string& text, const string& pattern, int match_mode)
189 {
190 if(match_mode & match_flag_case_insensitive)
191 {
192 istring is(text);
193 return is == pattern;
194 } else
195 return text == pattern;
196 }
197
198
199 /*
200 * expr: pat1 [=|~] pat2
201 * pat1 is the pattern that represents the field name
202 * pat2 is the pattern that represents the field value
203 */
204 int engine::pattern_field_match(const MimeEntity& me, const string& expr,
205 int match_mode)
206 {
207 int has_value = 0; // left part of the expr
208 char prev = 0; // previous char
209 string field_pat, value_pat;
210 char op;
211 for(size_t i = 0; i < expr.length(); ++i)
212 {
213 if( (expr[i] == '=' || expr[i] == '~') && prev != '\\')
214 {
215 has_value++; // right part
216 op = expr[i];
217 continue;
218 }
219 if(!has_value)
220 field_pat.append(1, expr[i]);
221 else
222 value_pat.append(1, expr[i]);
223 prev = expr[i];
224 }
225 field_pat = remove_external_blanks(field_pat);
226 value_pat = remove_external_blanks(value_pat);
227 // first try to find a field that match the field_pat pattern
228 const Header& h = me.header();
229 Header::const_iterator bit = h.begin(), eit = h.end();
230 for( ; bit != eit; ++bit)
231 {
232 if(pattern_match(bit->name(), field_pat, match_mode))
233 { // we've found a matching field, let's check the value
234 if(!has_value)
235 return 1;
236 else
237 if(pattern_match(bit->value(), value_pat, match_mode))
238 return 1;
239 }
240 }
241 return 0;
242 }
243
244 string engine::remove_external_blanks(const string& str) const
245 {
246 // a dirty way to trim ext.blanks
247 string s = str;
248 for(int i = s.length() - 1; i >= 0; --i)
249 if(s[i] == ' ')
250 s.erase(i, 1);
251 else
252 break;
253 while(s.length() && s[0] == ' ')
254 s.erase(0, 1);
255 return s;
256 }
257
258 int engine::fixed_field_match(const MimeEntity& me, const string& name, const string& value, int match_mode)
259 {
260 if(!me.header().hasField(name))
261 return 0;
262 if(value.length() == 0)
263 return 1; // it exists
264 const string& field_value = me.header().field(name).value();
265 return pattern_match(field_value, value, match_mode) ;
266 }
267
268 int engine::has_binary_attach(const MimeEntity& me, const command_line_switch& cls)
269 {
270 const Header& h = me.header();
271 const ContentType& ct = h.contentType();
272 if(ct.type() == "text" || ct.type() == "multipart" || ct.type() == "message")
273 return 0;
274 const ContentTransferEncoding& cte = h.contentTransferEncoding();
275 if(cte.mechanism() == "base64")
276 return 1;
277 return 1;
278 }
279
280 int engine::field_match(const MimeEntity& me, const command_line_switch& cls)
281 {
282 const string& name = cls.first, value = cls.second;
283 if(name == "field")
284 return pattern_field_match(me, value, m_match_mode);
285 else if (name == "ifield")
286 return pattern_field_match(me, value,
287 m_match_mode | match_flag_case_insensitive);
288 else
289 return fixed_field_match(me, name, value,
290 m_match_mode);
291 }
292
293 int engine::has_field(const MimeEntity& me, const command_line_switch& cls)
294 {
295 return me.header().hasField(cls.second);
296 }
297
298 int engine::match_filename(const string& filename, const string& pattern)
299 {
300 // convert shell pattern string to regex
301 string re_pattern;
302 char c;
303 for(size_t i = 0; i < pattern.length(); ++i)
304 {
305 c = pattern[i];
306 switch(c)
307 {
308 case '?':
309 re_pattern.append(".");
310 break;
311 case '*':
312 re_pattern.append(".*");
313 break;
314 case '[':
315 case '.':
316 case '=':
317 case '<':
318 case '>':
319 case '+':
320 case '_':
321 case '\\':
322 case '-':
323 case ']':
324 re_pattern.append(1, '\\');
325 re_pattern.append(1, c);
326 break;
327 default:
328 re_pattern.append(1, c);
329 }
330 }
331 return regex_match(filename, re_pattern, 0);
332 }
333
334 int engine::attach_filename(const MimeEntity& me,const command_line_switch& cls)
335 {
336 typedef list<string> filename_list;
337 const Header& h = me.header();
338 const ContentType& ct = h.contentType();
339 const ContentDisposition& cd = h.contentDisposition();
340 string pattern = cls.second;
341 filename_list names;
342 // content-type params
343 names.push_back(ct.param("name"));
344 names.push_back(ct.param("filename")); // should not exists
345 // content-disposition params
346 names.push_back(cd.param("name"));
347 names.push_back(cd.param("filename")); // should not exists
348 filename_list::const_iterator bit = names.begin(), eit = names.end();
349 for( ; bit != eit; ++bit)
350 if(match_filename(*bit, pattern))
351 return 1;
352 return 0;
353 }
354
355 MimeEntity* engine::match(MimeEntity& me, int level, parts_hierarchy* ph)
356 {
357 int matched = 1, child_match = 0, free = 0;
358
359 if(ph == 0)
360 {
361 ph = new parts_hierarchy;
362 free++;
363 }
364 if(m_cl.is_set(p_recursive))
365 {
366 MimeEntityList& parts = me.body().parts();
367 if( parts.size() )
368 {
369 ++level;
370 MimeEntityList::iterator mbit, meit;
371 mbit = parts.begin(), meit = parts.end();
372 ph->insert(ph->begin(), &me);
373 for( ; mbit != meit; ++mbit)
374 child_match += (match(**mbit, level, ph ) ? 1 : 0);
375 ph->erase(ph->begin());
376 }
377 }
378 static char *std_fields[] = {
379 "from", "sender", "to", "sujbect", "cc", "bcc",
380 "user-agent", "date", "content-type",
381 "content-transfer-encoding", "content-disposition",
382 "content-description",
383 0
384 };
385 command_line::const_iterator bit = m_cl.begin(), eit = m_cl.end();
386 for(; bit != eit; ++bit)
387 {
388 const string& name = bit->first, value = bit->second;
389 if(name == "attach-filename") {
390 if(!attach_filename(me, *bit))
391 {
392 matched = 0;
393 break;
394 }
395 } else if(name == "has-field") {
396 if(!has_field(me, *bit))
397 {
398 matched = 0;
399 break;
400 }
401 } else if(name == "has-binary-attach") {
402 if(!has_binary_attach(me, *bit))
403 {
404 matched = 0;
405 break;
406 }
407 } else if(name == "field") {
408 if(!field_match(me, *bit))
409 {
410 matched = 0;
411 break;
412 }
413 } else if(name == "ifield") {
414 if(!field_match(me, *bit))
415 {
416 matched = 0;
417 break;
418 }
419 } else {
420 int break_loop = 0;
421 char **std_name = std_fields;
422 for( int i = 0 ; std_name[i] ; ++i)
423 {
424 if(name == std_name[i])
425 if(!field_match(me, *bit))
426 {
427 matched = 0;
428 break_loop++;
429 break;
430 }
431 }
432 if(break_loop)
433 break;
434 }
435 }
436 if(matched)
437 action(me, ph);
438 if(free)
439 delete ph;
440 return ( matched || child_match ? &me : 0);
441 }

  ViewVC Help
Powered by ViewVC 1.1.26