~ubuntu-branches/ubuntu/maverick/evolution-data-server/maverick-proposed

« back to all changes in this revision

Viewing changes to servers/exchange/lib/e2k-rule-xml.c

  • Committer: Bazaar Package Importer
  • Author(s): Didier Roche
  • Date: 2010-05-17 17:02:06 UTC
  • mfrom: (1.1.79 upstream) (1.6.12 experimental)
  • Revision ID: james.westby@ubuntu.com-20100517170206-4ufr52vwrhh26yh0
Tags: 2.30.1-1ubuntu1
* Merge from debian experimental. Remaining change:
  (LP: #42199, #229669, #173703, #360344, #508494)
  + debian/control:
    - add Vcs-Bzr tag
    - don't use libgnome
    - Use Breaks instead of Conflicts against evolution 2.25 and earlier.
  + debian/evolution-data-server.install,
    debian/patches/45_libcamel_providers_version.patch:
    - use the upstream versioning, not a Debian-specific one 
  + debian/libedata-book1.2-dev.install, debian/libebackend-1.2-dev.install,
    debian/libcamel1.2-dev.install, debian/libedataserverui1.2-dev.install:
    - install html documentation
  + debian/rules:
    - don't build documentation it's shipped with the tarball

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
 
 
3
 
/* Copyright (C) 2002-2004 Novell, Inc.
4
 
 *
5
 
 * This program is free software; you can redistribute it and/or
6
 
 * modify it under the terms of version 2 of the GNU Lesser General Public
7
 
 * License as published by the Free Software Foundation.
8
 
 *
9
 
 * This program is distributed in the hope that it will be useful,
10
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 
 * General Public License for more details.
13
 
 *
14
 
 * You should have received a copy of the GNU Lesser General Public
15
 
 * License along with this program; if not, write to the
16
 
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
 
 * Boston, MA 02110-1301, USA.
18
 
 */
19
 
 
20
 
#ifdef HAVE_CONFIG_H
21
 
#include "config.h"
22
 
#endif
23
 
 
24
 
#include <string.h>
25
 
 
26
 
#include "e2k-rule-xml.h"
27
 
#include "e2k-action.h"
28
 
#include "e2k-properties.h"
29
 
#include "e2k-propnames.h"
30
 
#include "e2k-proptags.h"
31
 
#include "e2k-utils.h"
32
 
#include "mapi.h"
33
 
 
34
 
static const gchar *contains_types[] = { NULL, "contains", NULL, NULL, NULL, "not contains", NULL, NULL };
35
 
static const gchar *subject_types[] = { "is", "contains", "starts with", NULL, "is not", "not contains", "not starts with", NULL };
36
 
#define E2K_FL_NEGATE 4
37
 
#define E2K_FL_MAX 8
38
 
 
39
 
#if 0
40
 
static gboolean
41
 
fuzzy_level_from_name (const gchar *name, const gchar *map[],
42
 
                       gint *fuzzy_level, gboolean *negated)
43
 
{
44
 
        gint i;
45
 
 
46
 
        for (i = 0; i < E2K_FL_MAX; i++) {
47
 
                if (map[i] && !strcmp (name, map[i])) {
48
 
                        *fuzzy_level = i & ~E2K_FL_NEGATE;
49
 
                        *negated = (*fuzzy_level != i);
50
 
                        return TRUE;
51
 
                }
52
 
        }
53
 
 
54
 
        return FALSE;
55
 
}
56
 
#endif
57
 
 
58
 
static inline const gchar *
59
 
fuzzy_level_to_name (gint fuzzy_level, gboolean negated, const gchar *map[])
60
 
{
61
 
        fuzzy_level = E2K_FL_MATCH_TYPE (fuzzy_level);
62
 
        if (negated)
63
 
                fuzzy_level |= E2K_FL_NEGATE;
64
 
 
65
 
        return map[fuzzy_level];
66
 
}
67
 
 
68
 
static const gchar *is_types[] = { NULL, NULL, NULL, NULL, "is", "is not" };
69
 
static const gchar *date_types[] = { "before", "before", "after", "after", NULL, NULL };
70
 
static const gchar *gsizeypes[] = { "less than", "less than", "greater than", "greater than", NULL, NULL };
71
 
 
72
 
#if 0
73
 
static gboolean
74
 
relop_from_name (const gchar *name, const gchar *map[],
75
 
                 E2kRestrictionRelop *relop)
76
 
{
77
 
        gint i;
78
 
 
79
 
        for (i = 0; i < E2K_RELOP_RE; i++) {
80
 
                if (map[i] && !strcmp (name, map[i])) {
81
 
                        *relop = i;
82
 
                        return TRUE;
83
 
                }
84
 
        }
85
 
 
86
 
        return FALSE;
87
 
}
88
 
#endif
89
 
 
90
 
static inline const gchar *
91
 
relop_to_name (E2kRestrictionRelop relop, gboolean negated, const gchar *map[])
92
 
{
93
 
        static const gint negate_map[] = {
94
 
                E2K_RELOP_GE, E2K_RELOP_GT, E2K_RELOP_LE, E2K_RELOP_LT,
95
 
                E2K_RELOP_NE, E2K_RELOP_EQ
96
 
        };
97
 
 
98
 
        if (negated)
99
 
                relop = negate_map[relop];
100
 
 
101
 
        return map[relop];
102
 
}
103
 
 
104
 
/* Check if @rn encodes Outlook's "Message was sent only to me" rule */
105
 
static gboolean
106
 
restriction_is_only_to_me (E2kRestriction *rn)
107
 
{
108
 
        E2kRestriction *sub;
109
 
 
110
 
        if (rn->type != E2K_RESTRICTION_AND || rn->res.and.nrns != 3)
111
 
                return FALSE;
112
 
 
113
 
        sub = rn->res.and.rns[0];
114
 
        if (sub->type != E2K_RESTRICTION_PROPERTY ||
115
 
            sub->res.property.relop != E2K_RELOP_EQ ||
116
 
            sub->res.property.pv.prop.proptag != E2K_PROPTAG_PR_MESSAGE_TO_ME ||
117
 
            sub->res.property.pv.value == NULL)
118
 
                return FALSE;
119
 
 
120
 
        sub = rn->res.and.rns[1];
121
 
        if (sub->type != E2K_RESTRICTION_NOT)
122
 
                return FALSE;
123
 
        sub = sub->res.not.rn;
124
 
        if (sub->type != E2K_RESTRICTION_CONTENT ||
125
 
            !(sub->res.content.fuzzy_level & E2K_FL_SUBSTRING) ||
126
 
            sub->res.content.pv.prop.proptag != E2K_PROPTAG_PR_DISPLAY_TO ||
127
 
            strcmp (sub->res.content.pv.value, ";") != 0)
128
 
                return FALSE;
129
 
 
130
 
        sub = rn->res.and.rns[2];
131
 
        if (sub->type != E2K_RESTRICTION_PROPERTY ||
132
 
            sub->res.content.pv.prop.proptag != E2K_PROPTAG_PR_DISPLAY_CC ||
133
 
            strcmp (sub->res.content.pv.value, "") != 0)
134
 
                return FALSE;
135
 
 
136
 
        return TRUE;
137
 
}
138
 
 
139
 
/* Check if @rn encodes Outlook's "is a delegatable meeting request" rule */
140
 
static gboolean
141
 
restriction_is_delegation (E2kRestriction *rn)
142
 
{
143
 
        E2kRestriction *sub;
144
 
 
145
 
        if (rn->type != E2K_RESTRICTION_AND || rn->res.and.nrns != 3)
146
 
                return FALSE;
147
 
 
148
 
        sub = rn->res.and.rns[0];
149
 
        if (sub->type != E2K_RESTRICTION_CONTENT ||
150
 
            E2K_FL_MATCH_TYPE (sub->res.content.fuzzy_level) != E2K_FL_PREFIX ||
151
 
            sub->res.content.pv.prop.proptag != E2K_PROPTAG_PR_MESSAGE_CLASS ||
152
 
            strcmp (sub->res.content.pv.value, "IPM.Schedule.Meeting") != 0)
153
 
                return FALSE;
154
 
 
155
 
        sub = rn->res.and.rns[1];
156
 
        if (sub->type != E2K_RESTRICTION_NOT ||
157
 
            sub->res.not.rn->type != E2K_RESTRICTION_EXIST ||
158
 
            sub->res.not.rn->res.exist.prop.proptag != E2K_PROPTAG_PR_DELEGATED_BY_RULE)
159
 
                return FALSE;
160
 
 
161
 
        sub = rn->res.and.rns[2];
162
 
        if (sub->type != E2K_RESTRICTION_OR || sub->res.or.nrns != 2)
163
 
                return FALSE;
164
 
 
165
 
        sub = rn->res.and.rns[2]->res.or.rns[0];
166
 
        if (sub->type != E2K_RESTRICTION_NOT ||
167
 
            sub->res.not.rn->type != E2K_RESTRICTION_EXIST ||
168
 
            sub->res.not.rn->res.exist.prop.proptag != E2K_PROPTAG_PR_SENSITIVITY)
169
 
                return FALSE;
170
 
 
171
 
        sub = rn->res.and.rns[2]->res.or.rns[1];
172
 
        if (sub->type != E2K_RESTRICTION_PROPERTY ||
173
 
            sub->res.property.relop != E2K_RELOP_NE ||
174
 
            sub->res.property.pv.prop.proptag != E2K_PROPTAG_PR_SENSITIVITY ||
175
 
            GPOINTER_TO_INT (sub->res.property.pv.value) != MAPI_SENSITIVITY_PRIVATE)
176
 
                return FALSE;
177
 
 
178
 
        return TRUE;
179
 
}
180
 
 
181
 
static xmlNode *
182
 
new_value (xmlNode *part,
183
 
           const xmlChar *name,
184
 
           const xmlChar *type,
185
 
           const xmlChar *value)
186
 
{
187
 
        xmlNode *node;
188
 
 
189
 
        node = xmlNewChild (part, NULL, (xmlChar *) "value", NULL);
190
 
        xmlSetProp (node, (xmlChar *) "name", name);
191
 
        xmlSetProp (node, (xmlChar *) "type", type);
192
 
        if (value)
193
 
                xmlSetProp (node, (xmlChar *) "value", value);
194
 
 
195
 
        return node;
196
 
}
197
 
 
198
 
static xmlNode *
199
 
new_value_int (xmlNode *part,
200
 
               const xmlChar *name,
201
 
               const xmlChar *type,
202
 
               const xmlChar *value_name,
203
 
               glong value)
204
 
{
205
 
        xmlNode *node;
206
 
        gchar *str;
207
 
 
208
 
        node = xmlNewChild (part, NULL, (xmlChar *) "value", NULL);
209
 
        xmlSetProp (node, (xmlChar *) "name", name);
210
 
        xmlSetProp (node, (xmlChar *) "type", type);
211
 
 
212
 
        str = g_strdup_printf ("%ld", value);
213
 
        xmlSetProp (node, (xmlChar *) value_name, (xmlChar *) str);
214
 
        g_free (str);
215
 
 
216
 
        return node;
217
 
}
218
 
 
219
 
static xmlNode *
220
 
new_part (const xmlChar *part_name)
221
 
{
222
 
        xmlNode *part;
223
 
 
224
 
        part = xmlNewNode (NULL, (xmlChar *) "part");
225
 
        xmlSetProp (part, (xmlChar *) "name", part_name);
226
 
        return part;
227
 
}
228
 
 
229
 
static xmlNode *
230
 
match (const xmlChar *part_name,
231
 
       const xmlChar *value_name,
232
 
       const xmlChar *value_value,
233
 
       const xmlChar *string_name,
234
 
       const xmlChar *string_value)
235
 
{
236
 
        xmlNode *part, *value;
237
 
 
238
 
        part = new_part (part_name);
239
 
        value = new_value (
240
 
                part, value_name, (xmlChar *) "option", value_value);
241
 
        value = new_value (part, string_name, (xmlChar *) "string", NULL);
242
 
        xmlNewTextChild (value, NULL, (xmlChar *) "string", string_value);
243
 
 
244
 
        return part;
245
 
}
246
 
 
247
 
static xmlNode *
248
 
message_is (const xmlChar *name,
249
 
            const xmlChar *type_name,
250
 
            const xmlChar *kind,
251
 
            gboolean negated)
252
 
{
253
 
        xmlNode *part;
254
 
 
255
 
        part = new_part (name);
256
 
        new_value (
257
 
                part, type_name, (xmlChar *) "option",
258
 
                negated ? (xmlChar *) "is not" : (xmlChar *) "is");
259
 
        new_value (part, (xmlChar *) "kind", (xmlChar *) "option", kind);
260
 
 
261
 
        return part;
262
 
}
263
 
 
264
 
static xmlNode *
265
 
address_is (E2kRestriction *comment_rn, gboolean recipients, gboolean negated)
266
 
{
267
 
        xmlNode *part;
268
 
        E2kRestriction *rn;
269
 
        E2kPropValue *pv;
270
 
        const gchar *relation, *display_name, *p;
271
 
        gchar *addr, *full_addr;
272
 
        GByteArray *ba;
273
 
        gint i;
274
 
 
275
 
        rn = comment_rn->res.comment.rn;
276
 
        if (rn->type != E2K_RESTRICTION_PROPERTY ||
277
 
            rn->res.property.relop != E2K_RELOP_EQ)
278
 
                return NULL;
279
 
        pv = &rn->res.property.pv;
280
 
 
281
 
        if ((recipients && pv->prop.proptag != E2K_PROPTAG_PR_SEARCH_KEY) ||
282
 
            (!recipients && pv->prop.proptag != E2K_PROPTAG_PR_SENDER_SEARCH_KEY))
283
 
                return NULL;
284
 
 
285
 
        relation = relop_to_name (rn->res.property.relop, negated, is_types);
286
 
        if (!relation)
287
 
                return NULL;
288
 
 
289
 
        /* Extract the address part */
290
 
        ba = pv->value;
291
 
        p = strchr ((gchar *)ba->data, ':');
292
 
        if (p)
293
 
                addr = g_ascii_strdown (p + 1, -1);
294
 
        else
295
 
                addr = g_ascii_strdown ((gchar *)ba->data, -1);
296
 
 
297
 
        /* Find the display name in the comment */
298
 
        display_name = NULL;
299
 
        for (i = 0; i < comment_rn->res.comment.nprops; i++) {
300
 
                pv = &comment_rn->res.comment.props[i];
301
 
                if (E2K_PROPTAG_TYPE (pv->prop.proptag) == E2K_PT_UNICODE) {
302
 
                        display_name = pv->value;
303
 
                        break;
304
 
                }
305
 
        }
306
 
 
307
 
        if (display_name)
308
 
                full_addr = g_strdup_printf ("%s <%s>", display_name, addr);
309
 
        else
310
 
                full_addr = g_strdup_printf ("<%s>", addr);
311
 
 
312
 
        if (recipients) {
313
 
                part = match (
314
 
                        (xmlChar *) "recipient",
315
 
                        (xmlChar *) "recipient-type",
316
 
                        (xmlChar *) relation,
317
 
                        (xmlChar *) "recipient",
318
 
                        (xmlChar *) full_addr);
319
 
        } else {
320
 
                part = match (
321
 
                        (xmlChar *) "sender",
322
 
                        (xmlChar *) "sender-type",
323
 
                        (xmlChar *) relation,
324
 
                        (xmlChar *) "sender",
325
 
                        (xmlChar *) full_addr);
326
 
        }
327
 
 
328
 
        g_free (full_addr);
329
 
        g_free (addr);
330
 
        return part;
331
 
}
332
 
 
333
 
static gboolean
334
 
restriction_to_xml (E2kRestriction *rn, xmlNode *partset,
335
 
                    E2kRestrictionType wrap_type, gboolean negated)
336
 
{
337
 
        xmlNode *part, *value, *node;
338
 
        E2kPropValue *pv;
339
 
        const gchar *match_type;
340
 
        gint i;
341
 
 
342
 
        switch (rn->type) {
343
 
        case E2K_RESTRICTION_AND:
344
 
        case E2K_RESTRICTION_OR:
345
 
                /* Check for special rules */
346
 
                if (restriction_is_only_to_me (rn)) {
347
 
                        part = message_is (
348
 
                                (xmlChar *) "message-to-me",
349
 
                                (xmlChar *) "message-to-me-type",
350
 
                                (xmlChar *) "only", negated);
351
 
                        break;
352
 
                } else if (restriction_is_delegation (rn)) {
353
 
                        part = message_is (
354
 
                                (xmlChar *) "special-message",
355
 
                                (xmlChar *) "special-message-type",
356
 
                                (xmlChar *) "delegated-meeting-request",
357
 
                                negated);
358
 
                        break;
359
 
                }
360
 
 
361
 
                /* If we are inside an "and" and hit another "and",
362
 
                 * we can just remove the extra level:
363
 
                 *    (and foo (and bar baz) quux) =>
364
 
                 *    (and foo bar baz quux)
365
 
                 * Likewise for "or"s.
366
 
                 *
367
 
                 * If we are inside an "and" and hit a "(not (or" (or
368
 
                 * vice versa), we can use DeMorgan's Law and then
369
 
                 * apply the above rule:
370
 
                 *    (and foo (not (or bar baz)) quux) =>
371
 
                 *    (and foo (and (not bar) (not baz)) quux) =>
372
 
                 *    (and foo (not bar) (not baz) quux)
373
 
                 *
374
 
                 * This handles both cases.
375
 
                 */
376
 
                if ((rn->type == wrap_type && !negated) ||
377
 
                    (rn->type != wrap_type && negated)) {
378
 
                        for (i = 0; i < rn->res.and.nrns; i++) {
379
 
                                if (!restriction_to_xml (rn->res.and.rns[i],
380
 
                                                         partset, wrap_type,
381
 
                                                         negated))
382
 
                                        return FALSE;
383
 
                        }
384
 
                        return TRUE;
385
 
                }
386
 
 
387
 
                /* Otherwise, we have a rule that can't be expressed
388
 
                 * as "match all" or "match any".
389
 
                 */
390
 
                return FALSE;
391
 
 
392
 
        case E2K_RESTRICTION_NOT:
393
 
                return restriction_to_xml (rn->res.not.rn, partset,
394
 
                                           wrap_type, !negated);
395
 
 
396
 
        case E2K_RESTRICTION_CONTENT:
397
 
        {
398
 
                gint fuzzy_level = E2K_FL_MATCH_TYPE (rn->res.content.fuzzy_level);
399
 
 
400
 
                pv = &rn->res.content.pv;
401
 
 
402
 
                switch (pv->prop.proptag) {
403
 
                case E2K_PROPTAG_PR_BODY:
404
 
                        match_type = fuzzy_level_to_name (fuzzy_level, negated,
405
 
                                                          contains_types);
406
 
                        if (!match_type)
407
 
                                return FALSE;
408
 
 
409
 
                        part = match (
410
 
                                (xmlChar *) "body",
411
 
                                (xmlChar *) "body-type",
412
 
                                (xmlChar *) match_type,
413
 
                                (xmlChar *) "word",
414
 
                                (xmlChar *) pv->value);
415
 
                        break;
416
 
 
417
 
                case E2K_PROPTAG_PR_SUBJECT:
418
 
                        match_type = fuzzy_level_to_name (fuzzy_level, negated,
419
 
                                                          subject_types);
420
 
                        if (!match_type)
421
 
                                return FALSE;
422
 
 
423
 
                        part = match (
424
 
                                (xmlChar *) "subject",
425
 
                                (xmlChar *) "subject-type",
426
 
                                (xmlChar *) match_type,
427
 
                                (xmlChar *) "subject",
428
 
                                (xmlChar *) pv->value);
429
 
                        break;
430
 
 
431
 
                case E2K_PROPTAG_PR_TRANSPORT_MESSAGE_HEADERS:
432
 
                        match_type = fuzzy_level_to_name (fuzzy_level, negated,
433
 
                                                          contains_types);
434
 
                        if (!match_type)
435
 
                                return FALSE;
436
 
 
437
 
                        part = match (
438
 
                                (xmlChar *) "full-headers",
439
 
                                (xmlChar *) "full-headers-type",
440
 
                                (xmlChar *) match_type,
441
 
                                (xmlChar *) "word",
442
 
                                (xmlChar *) pv->value);
443
 
                        break;
444
 
 
445
 
                case E2K_PROPTAG_PR_MESSAGE_CLASS:
446
 
                        if ((fuzzy_level == E2K_FL_FULLSTRING) &&
447
 
                            !strcmp (pv->value, "IPM.Note.Rules.OofTemplate.Microsoft")) {
448
 
                                part = message_is (
449
 
                                        (xmlChar *) "special-message",
450
 
                                        (xmlChar *) "special-message-type",
451
 
                                        (xmlChar *) "oof", negated);
452
 
                        } else if ((fuzzy_level == E2K_FL_PREFIX) &&
453
 
                                   !strcmp (pv->value, "IPM.Schedule.Meeting")) {
454
 
                                part = message_is (
455
 
                                        (xmlChar *) "special-message",
456
 
                                        (xmlChar *) "special-message-type",
457
 
                                        (xmlChar *) "meeting-request", negated);
458
 
                        } else
459
 
                                return FALSE;
460
 
 
461
 
                        break;
462
 
 
463
 
                default:
464
 
                        return FALSE;
465
 
                }
466
 
                break;
467
 
        }
468
 
 
469
 
        case E2K_RESTRICTION_PROPERTY:
470
 
        {
471
 
                E2kRestrictionRelop relop;
472
 
                const gchar *relation;
473
 
 
474
 
                relop = rn->res.property.relop;
475
 
                if (relop >= E2K_RELOP_RE)
476
 
                        return FALSE;
477
 
 
478
 
                pv = &rn->res.property.pv;
479
 
 
480
 
                switch (pv->prop.proptag) {
481
 
                case E2K_PROPTAG_PR_MESSAGE_TO_ME:
482
 
                        if ((relop == E2K_RELOP_EQ && !pv->value) ||
483
 
                            (relop == E2K_RELOP_NE && pv->value))
484
 
                                negated = !negated;
485
 
 
486
 
                        part = message_is (
487
 
                                (xmlChar *) "message-to-me",
488
 
                                (xmlChar *) "message-to-me-type",
489
 
                                (xmlChar *) "to", negated);
490
 
                        break;
491
 
 
492
 
                case E2K_PROPTAG_PR_MESSAGE_CC_ME:
493
 
                        if ((relop == E2K_RELOP_EQ && !pv->value) ||
494
 
                            (relop == E2K_RELOP_NE && pv->value))
495
 
                                negated = !negated;
496
 
 
497
 
                        part = message_is (
498
 
                                (xmlChar *) "message-to-me",
499
 
                                (xmlChar *) "message-to-me-type",
500
 
                                (xmlChar *) "cc", negated);
501
 
                        break;
502
 
 
503
 
                case E2K_PROPTAG_PR_MESSAGE_DELIVERY_TIME:
504
 
                case E2K_PROPTAG_PR_CLIENT_SUBMIT_TIME:
505
 
                {
506
 
                        gchar *timestamp;
507
 
 
508
 
                        relation = relop_to_name (relop, negated, date_types);
509
 
                        if (!relation)
510
 
                                return FALSE;
511
 
 
512
 
                        if (pv->prop.proptag == E2K_PROPTAG_PR_MESSAGE_DELIVERY_TIME)
513
 
                                part = new_part ((xmlChar *) "received-date");
514
 
                        else
515
 
                                part = new_part ((xmlChar *) "sent-date");
516
 
 
517
 
                        value = new_value (
518
 
                                part,
519
 
                                (xmlChar *) "date-spec-type",
520
 
                                (xmlChar *) "option",
521
 
                                (xmlChar *) relation);
522
 
                        value = new_value (
523
 
                                part,
524
 
                                (xmlChar *) "versus",
525
 
                                (xmlChar *) "datespec", NULL);
526
 
 
527
 
                        node = xmlNewChild (
528
 
                                value, NULL, (xmlChar *) "datespec", NULL);
529
 
                        xmlSetProp (
530
 
                                node,
531
 
                                (xmlChar *) "type",
532
 
                                (xmlChar *) "1");
533
 
 
534
 
                        timestamp = g_strdup_printf ("%lu", (gulong)e2k_parse_timestamp (pv->value));
535
 
                        xmlSetProp (
536
 
                                node,
537
 
                                (xmlChar *) "value",
538
 
                                (xmlChar *) timestamp);
539
 
                        g_free (timestamp);
540
 
                        break;
541
 
                }
542
 
 
543
 
                case E2K_PROPTAG_PR_MESSAGE_SIZE:
544
 
                        relation = relop_to_name (relop, negated, gsizeypes);
545
 
                        if (!relation)
546
 
                                return FALSE;
547
 
 
548
 
                        part = new_part ((xmlChar *) "size");
549
 
                        new_value (
550
 
                                part,
551
 
                                (xmlChar *) "size-type",
552
 
                                (xmlChar *) "option",
553
 
                                (xmlChar *) relation);
554
 
                        new_value_int (
555
 
                                part,
556
 
                                (xmlChar *) "versus",
557
 
                                (xmlChar *) "integer",
558
 
                                (xmlChar *) "integer",
559
 
                                GPOINTER_TO_INT (pv->value) / 1024);
560
 
                        break;
561
 
 
562
 
                case E2K_PROPTAG_PR_IMPORTANCE:
563
 
                        relation = relop_to_name (relop, negated, is_types);
564
 
                        if (!relation)
565
 
                                return FALSE;
566
 
 
567
 
                        part = new_part ((xmlChar *) "importance");
568
 
                        new_value (
569
 
                                part,
570
 
                                (xmlChar *) "importance-type",
571
 
                                (xmlChar *) "option",
572
 
                                (xmlChar *) relation);
573
 
                        new_value_int (
574
 
                                part,
575
 
                                (xmlChar *) "importance",
576
 
                                (xmlChar *) "option",
577
 
                                (xmlChar *) "value",
578
 
                                GPOINTER_TO_INT (pv->value));
579
 
                        break;
580
 
 
581
 
                case E2K_PROPTAG_PR_SENSITIVITY:
582
 
                        relation = relop_to_name (relop, negated, is_types);
583
 
                        if (!relation)
584
 
                                return FALSE;
585
 
 
586
 
                        part = new_part ((xmlChar *) "sensitivity");
587
 
                        xmlSetProp (
588
 
                                part,
589
 
                                (xmlChar *) "name",
590
 
                                (xmlChar *) "sensitivity");
591
 
                        new_value (
592
 
                                part,
593
 
                                (xmlChar *) "sensitivity-type",
594
 
                                (xmlChar *) "option",
595
 
                                (xmlChar *) relation);
596
 
                        new_value_int (
597
 
                                part,
598
 
                                (xmlChar *) "sensitivity",
599
 
                                (xmlChar *) "option",
600
 
                                (xmlChar *) "value",
601
 
                                GPOINTER_TO_INT (pv->value));
602
 
                        break;
603
 
 
604
 
                default:
605
 
                        return FALSE;
606
 
                }
607
 
                break;
608
 
        }
609
 
 
610
 
        case E2K_RESTRICTION_COMMENT:
611
 
                part = address_is (rn, FALSE, negated);
612
 
                if (!part)
613
 
                        return FALSE;
614
 
                break;
615
 
 
616
 
        case E2K_RESTRICTION_BITMASK:
617
 
                if (rn->res.bitmask.prop.proptag != E2K_PROPTAG_PR_MESSAGE_FLAGS ||
618
 
                    rn->res.bitmask.mask != MAPI_MSGFLAG_HASATTACH)
619
 
                        return FALSE;
620
 
 
621
 
                part = new_part ((xmlChar *) "attachments");
622
 
                if (rn->res.bitmask.bitop == E2K_BMR_NEZ) {
623
 
                        new_value (
624
 
                                part,
625
 
                                (xmlChar *) "match-type",
626
 
                                (xmlChar *) "option",
627
 
                                negated ?
628
 
                                (xmlChar *) "not exist" :
629
 
                                (xmlChar *) "exist");
630
 
                } else {
631
 
                        new_value (
632
 
                                part,
633
 
                                (xmlChar *) "match-type",
634
 
                                (xmlChar *) "option",
635
 
                                negated ?
636
 
                                (xmlChar *) "exist" :
637
 
                                (xmlChar *) "not exist");
638
 
                }
639
 
                break;
640
 
 
641
 
        case E2K_RESTRICTION_SUBRESTRICTION:
642
 
                if (rn->res.sub.subtable.proptag != E2K_PROPTAG_PR_MESSAGE_RECIPIENTS)
643
 
                        return FALSE;
644
 
                if (rn->res.sub.rn->type != E2K_RESTRICTION_COMMENT)
645
 
                        return FALSE;
646
 
 
647
 
                part = address_is (rn->res.sub.rn, TRUE, negated);
648
 
                if (!part)
649
 
                        return FALSE;
650
 
                break;
651
 
 
652
 
        default:
653
 
                return FALSE;
654
 
        }
655
 
 
656
 
        xmlAddChild (partset, part);
657
 
        return TRUE;
658
 
}
659
 
 
660
 
static gchar *
661
 
stringify_entryid (guint8 *data, gint len)
662
 
{
663
 
        GString *string;
664
 
        gchar *ret;
665
 
        gint i;
666
 
 
667
 
        string = g_string_new (NULL);
668
 
 
669
 
        for (i = 0; i < len && i < 22; i++)
670
 
                g_string_append_printf (string, "%02x", data[i]);
671
 
        if (i < len && data[i]) {
672
 
                for (; i < len; i++)
673
 
                        g_string_append_printf (string, "%02x", data[i]);
674
 
        }
675
 
 
676
 
        ret = string->str;
677
 
        g_string_free (string, FALSE);
678
 
        return ret;
679
 
}
680
 
 
681
 
static gboolean
682
 
action_to_xml (E2kAction *act, xmlNode *actionset)
683
 
{
684
 
        xmlNode *part, *value;
685
 
        gchar *entryid;
686
 
 
687
 
        switch (act->type) {
688
 
        case E2K_ACTION_MOVE:
689
 
        case E2K_ACTION_COPY:
690
 
                part = new_part (
691
 
                        act->type == E2K_ACTION_MOVE ?
692
 
                        (xmlChar *) "move-to-folder" :
693
 
                        (xmlChar *) "copy-to-folder");
694
 
                value = new_value (
695
 
                        part,
696
 
                        (xmlChar *) "folder",
697
 
                        (xmlChar *) "folder-source-key", NULL);
698
 
                entryid = stringify_entryid (
699
 
                        act->act.xfer.folder_source_key->data + 1,
700
 
                        act->act.xfer.folder_source_key->len - 1);
701
 
                xmlNewTextChild (
702
 
                        value, NULL,
703
 
                        (xmlChar *) "entryid",
704
 
                        (xmlChar *) entryid);
705
 
                g_free (entryid);
706
 
                break;
707
 
 
708
 
        case E2K_ACTION_REPLY:
709
 
        case E2K_ACTION_OOF_REPLY:
710
 
                part = new_part (
711
 
                        act->type == E2K_ACTION_REPLY ?
712
 
                        (xmlChar *) "reply" :
713
 
                        (xmlChar *) "oof-reply");
714
 
                value = new_value (
715
 
                        part,
716
 
                        (xmlChar *) "template",
717
 
                        (xmlChar *) "message-entryid", NULL);
718
 
                entryid = stringify_entryid (
719
 
                        act->act.reply.entryid->data,
720
 
                        act->act.reply.entryid->len);
721
 
                xmlNewTextChild (
722
 
                        value, NULL,
723
 
                        (xmlChar *) "entryid",
724
 
                        (xmlChar *) entryid);
725
 
                g_free (entryid);
726
 
                break;
727
 
 
728
 
        case E2K_ACTION_DEFER:
729
 
                part = new_part ((xmlChar *) "defer");
730
 
                break;
731
 
 
732
 
        case E2K_ACTION_BOUNCE:
733
 
                part = new_part ((xmlChar *) "bounce");
734
 
                switch (act->act.bounce_code) {
735
 
                case E2K_ACTION_BOUNCE_CODE_TOO_LARGE:
736
 
                        new_value (
737
 
                                part,
738
 
                                (xmlChar *) "bounce_code",
739
 
                                (xmlChar *) "option",
740
 
                                (xmlChar *) "size");
741
 
                        break;
742
 
                case E2K_ACTION_BOUNCE_CODE_FORM_MISMATCH:
743
 
                        new_value (
744
 
                                part,
745
 
                                (xmlChar *) "bounce_code",
746
 
                                (xmlChar *) "option",
747
 
                                (xmlChar *) "form-mismatch");
748
 
                        break;
749
 
                case E2K_ACTION_BOUNCE_CODE_ACCESS_DENIED:
750
 
                        new_value (
751
 
                                part,
752
 
                                (xmlChar *) "bounce_code",
753
 
                                (xmlChar *) "option",
754
 
                                (xmlChar *) "permission");
755
 
                        break;
756
 
                }
757
 
                break;
758
 
 
759
 
        case E2K_ACTION_FORWARD:
760
 
        case E2K_ACTION_DELEGATE:
761
 
        {
762
 
                gint i, j;
763
 
                E2kAddrList *list;
764
 
                E2kAddrEntry *entry;
765
 
                E2kPropValue *pv;
766
 
                const gchar *display_name, *email;
767
 
                gchar *full_addr;
768
 
 
769
 
                list = act->act.addr_list;
770
 
                for (i = 0; i < list->nentries; i++) {
771
 
                        entry = &list->entry[i];
772
 
                        display_name = email = NULL;
773
 
                        for (j = 0; j < entry->nvalues; j++) {
774
 
                                pv = &entry->propval[j];
775
 
                                if (pv->prop.proptag == E2K_PROPTAG_PR_TRANSMITTABLE_DISPLAY_NAME)
776
 
                                        display_name = pv->value;
777
 
                                else if (pv->prop.proptag == E2K_PROPTAG_PR_EMAIL_ADDRESS)
778
 
                                        email = pv->value;
779
 
                        }
780
 
 
781
 
                        if (!email)
782
 
                                continue;
783
 
                        if (display_name)
784
 
                                full_addr = g_strdup_printf ("%s <%s>", display_name, email);
785
 
                        else
786
 
                                full_addr = g_strdup_printf ("<%s>", email);
787
 
 
788
 
                        part = new_part (
789
 
                                act->type == E2K_ACTION_FORWARD ?
790
 
                                (xmlChar *) "forward" :
791
 
                                (xmlChar *) "delegate");
792
 
                        value = new_value (
793
 
                                part,
794
 
                                (xmlChar *) "recipient",
795
 
                                (xmlChar *) "recipient", NULL);
796
 
                        xmlNewTextChild (
797
 
                                value, NULL,
798
 
                                (xmlChar *) "recipient",
799
 
                                (xmlChar *) full_addr);
800
 
                        g_free (full_addr);
801
 
 
802
 
                        xmlAddChild (actionset, part);
803
 
                }
804
 
                return TRUE;
805
 
        }
806
 
 
807
 
        case E2K_ACTION_TAG:
808
 
                if (act->act.proptag.prop.proptag != E2K_PROPTAG_PR_IMPORTANCE)
809
 
                        return FALSE;
810
 
 
811
 
                part = new_part ((xmlChar *) "set-importance");
812
 
                new_value_int (
813
 
                        part,
814
 
                        (xmlChar *) "importance",
815
 
                        (xmlChar *) "option",
816
 
                        (xmlChar *) "value",
817
 
                        GPOINTER_TO_INT (act->act.proptag.value));
818
 
                break;
819
 
 
820
 
        case E2K_ACTION_DELETE:
821
 
                part = new_part ((xmlChar *) "delete");
822
 
                break;
823
 
 
824
 
        case E2K_ACTION_MARK_AS_READ:
825
 
                part = new_part ((xmlChar *) "mark-read");
826
 
                break;
827
 
 
828
 
        default:
829
 
                return FALSE;
830
 
        }
831
 
 
832
 
        xmlAddChild (actionset, part);
833
 
        return TRUE;
834
 
}
835
 
 
836
 
static gboolean
837
 
rule_to_xml (E2kRule *rule, xmlNode *ruleset)
838
 
{
839
 
        xmlNode *top, *set;
840
 
        E2kRestriction *rn;
841
 
        gint i;
842
 
 
843
 
        top = xmlNewChild (ruleset, NULL, (xmlChar *) "rule", NULL);
844
 
 
845
 
        xmlSetProp (
846
 
                top,
847
 
                (xmlChar *) "source",
848
 
                (rule->state & E2K_RULE_STATE_ONLY_WHEN_OOF) ?
849
 
                (xmlChar *) "oof" : (xmlChar *) "incoming");
850
 
        xmlSetProp (
851
 
                top,
852
 
                (xmlChar *) "enabled",
853
 
                (rule->state & E2K_RULE_STATE_ENABLED) ?
854
 
                (xmlChar *) "1" : (xmlChar *) "0");
855
 
 
856
 
        if (rule->name)
857
 
                xmlNewTextChild (
858
 
                        top, NULL,
859
 
                        (xmlChar *) "title",
860
 
                        (xmlChar *) rule->name);
861
 
 
862
 
        set = xmlNewChild (top, NULL, (xmlChar *) "partset", NULL);
863
 
        rn = rule->condition;
864
 
        if (rn) {
865
 
                E2kRestrictionType wrap_type;
866
 
 
867
 
                if (rn->type == E2K_RESTRICTION_OR) {
868
 
                        xmlSetProp (
869
 
                                top,
870
 
                                (xmlChar *) "grouping",
871
 
                                (xmlChar *) "any");
872
 
                        wrap_type = E2K_RESTRICTION_OR;
873
 
                } else {
874
 
                        xmlSetProp (
875
 
                                top,
876
 
                                (xmlChar *) "grouping",
877
 
                                (xmlChar *) "all");
878
 
                        wrap_type = E2K_RESTRICTION_AND;
879
 
                }
880
 
 
881
 
                if (!restriction_to_xml (rn, set, wrap_type, FALSE)) {
882
 
                        g_warning ("could not express restriction as xml");
883
 
                        xmlUnlinkNode (top);
884
 
                        xmlFreeNode (top);
885
 
                        return FALSE;
886
 
                }
887
 
        } else
888
 
                xmlSetProp (top, (xmlChar *) "grouping", (xmlChar *) "all");
889
 
 
890
 
        set = xmlNewChild (top, NULL, (xmlChar *) "actionset", NULL);
891
 
        for (i = 0; i < rule->actions->len; i++) {
892
 
                if (!action_to_xml (rule->actions->pdata[i], set)) {
893
 
                        g_warning ("could not express action as xml");
894
 
                        xmlUnlinkNode (top);
895
 
                        xmlFreeNode (top);
896
 
                        return FALSE;
897
 
                }
898
 
        }
899
 
 
900
 
        if (rule->state & E2K_RULE_STATE_EXIT_LEVEL)
901
 
                xmlAddChild (set, new_part ((xmlChar *) "stop"));
902
 
 
903
 
        return TRUE;
904
 
}
905
 
 
906
 
/**
907
 
 * e2k_rules_to_xml:
908
 
 * @rules: an #E2kRules
909
 
 *
910
 
 * Encodes @rules into an XML format like that used by the evolution
911
 
 * filter code.
912
 
 *
913
 
 * Return value: the XML rules
914
 
 **/
915
 
xmlDoc *
916
 
e2k_rules_to_xml (E2kRules *rules)
917
 
{
918
 
        xmlDoc *doc;
919
 
        xmlNode *top, *ruleset;
920
 
        gint i;
921
 
 
922
 
        doc = xmlNewDoc (NULL);
923
 
        top = xmlNewNode (NULL, (xmlChar *) "filteroptions");
924
 
        xmlDocSetRootElement (doc, top);
925
 
 
926
 
        ruleset = xmlNewChild (top, NULL, (xmlChar *) "ruleset", NULL);
927
 
 
928
 
        for (i = 0; i < rules->rules->len; i++)
929
 
                rule_to_xml (rules->rules->pdata[i], ruleset);
930
 
 
931
 
        return doc;
932
 
}