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);