~ubuntu-branches/ubuntu/natty/mimetic/natty

« back to all changes in this revision

Viewing changes to examples/engine.cxx

  • Committer: Bazaar Package Importer
  • Author(s): gregor herrmann
  • Date: 2006-06-16 13:16:07 UTC
  • Revision ID: james.westby@ubuntu.com-20060616131607-245mqjypkjuahq6b
Tags: upstream-0.9.1
ImportĀ upstreamĀ versionĀ 0.9.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}