14 using namespace mimetic;
16 engine::engine(const command_line& cl)
17 : m_cl(cl), m_pcre(false),
18 m_match_mode(match_type_none)
21 if(cl.is_set(p_match_shell) || cl.is_set(p_match_regex))
23 cl.is_set(p_match_shell) ?
28 cl.is_set(p_case_insensitive) ? match_flag_case_insensitive : 0;
31 cl.is_set(p_perl_regex) ? match_flag_perl_mode: 0;
33 if((m_match_mode & match_type_mask) == match_type_none)
34 m_match_mode |= match_type_regex; // default
37 int engine::posix_regex_match(const string& text, const string& pattern, int match_mode)
41 r = regcomp(&rex, pattern.c_str(),
42 ( match_mode & match_flag_case_insensitive ? REG_ICASE: 0));
46 regerror(r, &rex, buf, 255);
49 r = regexec(&rex, text.c_str(), 0, 0, 0);
54 int engine::perl_regex_match(const string& text, const string& pattern, int match_mode )
63 int engine::pattern_match(const string& text, const string& pattern, int match_mode)
65 switch(match_mode & match_type_mask)
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);
81 int engine::shell_match(const string& text, const string& pattern, int match_mode)
87 int engine::regex_match(const string& text, const string& pattern, int match_mode)
90 return engine::perl_regex_match(text, pattern, match_mode);
92 return engine::posix_regex_match(text, pattern, match_mode);
95 void engine::action_attach(MimeEntity& me, parts_hierarchy* ph, const string& fqn)
97 bool isMultipart = me.header().contentType().isMultipart();
98 bool isTopLevel = !ph->size();
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
109 Attachment* pA = new Attachment(fqn);
115 me.body().parts().push_back(pA);
118 mm = new MultipartMixed;
119 mm->body().parts().push_back(&me);
120 mm->body().parts().push_back(pA);
124 MimeEntity *parent = *ph->begin();
125 replace(parent->body().parts().begin(),
126 parent->body().parts().end(),
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)
138 transform(name.begin(), name.end(),
139 name.begin(), ::tolower);
140 if(name.find("content-") == 0 || name == "mime-version")
142 mm->header().insert(pos, *bit);
148 void engine::action(MimeEntity& me, parts_hierarchy* ph)
150 MimeEntity* parent = (ph->size() ? *ph->begin() : &me);
151 if(m_cl.is_set(p_add_header))
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)
158 Field f(bit->second);
159 parent->header().push_back(f);
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)
168 Field f(bit->second);
169 me.header().push_back(f);
172 if(m_cl.is_set(p_attach))
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);
181 if(m_cl.is_set(p_print_msg))
183 else if(m_cl.is_set(p_print_part))
188 int engine::exact_match(const string& text, const string& pattern, int match_mode)
190 if(match_mode & match_flag_case_insensitive)
193 return is == pattern;
195 return text == pattern;
200 * expr: pat1 [=|~] pat2
201 * pat1 is the pattern that represents the field name
202 * pat2 is the pattern that represents the field value
204 int engine::pattern_field_match(const MimeEntity& me, const string& expr,
207 int has_value = 0; // left part of the expr
208 char prev = 0; // previous char
209 string field_pat, value_pat;
211 for(size_t i = 0; i < expr.length(); ++i)
213 if( (expr[i] == '=' || expr[i] == '~') && prev != '\\')
215 has_value++; // right part
220 field_pat.append(1, expr[i]);
222 value_pat.append(1, expr[i]);
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)
232 if(pattern_match(bit->name(), field_pat, match_mode))
233 { // we've found a matching field, let's check the value
237 if(pattern_match(bit->value(), value_pat, match_mode))
244 string engine::remove_external_blanks(const string& str) const
246 // a dirty way to trim ext.blanks
248 for(int i = s.length() - 1; i >= 0; --i)
253 while(s.length() && s[0] == ' ')
258 int engine::fixed_field_match(const MimeEntity& me, const string& name, const string& value, int match_mode)
260 if(!me.header().hasField(name))
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) ;
268 int engine::has_binary_attach(const MimeEntity& me, const command_line_switch& cls)
270 const Header& h = me.header();
271 const ContentType& ct = h.contentType();
272 if(ct.type() == "text" || ct.type() == "multipart" || ct.type() == "message")
274 const ContentTransferEncoding& cte = h.contentTransferEncoding();
275 if(cte.mechanism() == "base64")
280 int engine::field_match(const MimeEntity& me, const command_line_switch& cls)
282 const string& name = cls.first, value = cls.second;
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);
289 return fixed_field_match(me, name, value,
293 int engine::has_field(const MimeEntity& me, const command_line_switch& cls)
295 return me.header().hasField(cls.second);
298 int engine::match_filename(const string& filename, const string& pattern)
300 // convert shell pattern string to regex
303 for(size_t i = 0; i < pattern.length(); ++i)
309 re_pattern.append(".");
312 re_pattern.append(".*");
324 re_pattern.append(1, '\\');
325 re_pattern.append(1, c);
328 re_pattern.append(1, c);
331 return regex_match(filename, re_pattern, 0);
334 int engine::attach_filename(const MimeEntity& me,const command_line_switch& cls)
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;
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))
355 MimeEntity* engine::match(MimeEntity& me, int level, parts_hierarchy* ph)
357 int matched = 1, child_match = 0, free = 0;
361 ph = new parts_hierarchy;
364 if(m_cl.is_set(p_recursive))
366 MimeEntityList& parts = me.body().parts();
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());
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",
385 command_line::const_iterator bit = m_cl.begin(), eit = m_cl.end();
386 for(; bit != eit; ++bit)
388 const string& name = bit->first, value = bit->second;
389 if(name == "attach-filename") {
390 if(!attach_filename(me, *bit))
395 } else if(name == "has-field") {
396 if(!has_field(me, *bit))
401 } else if(name == "has-binary-attach") {
402 if(!has_binary_attach(me, *bit))
407 } else if(name == "field") {
408 if(!field_match(me, *bit))
413 } else if(name == "ifield") {
414 if(!field_match(me, *bit))
421 char **std_name = std_fields;
422 for( int i = 0 ; std_name[i] ; ++i)
424 if(name == std_name[i])
425 if(!field_match(me, *bit))
440 return ( matched || child_match ? &me : 0);