~ubuntu-dev/ubuntu/lucid/dovecot/lucid-201002101901

« back to all changes in this revision

Viewing changes to libsieve/src/lib-sieve/plugins/enotify/ntfy-mailto.c

  • Committer: Chuck Short
  • Date: 2010-01-21 20:21:25 UTC
  • mfrom: (4.1.11 squeeze)
  • Revision ID: zulcss@ubuntu.com-20100121202125-pme73o491kfwj5nc
* Merge from debian testing, remaining changes:
  + Add new binary pkg dovecot-postfix that integrates postfix and dovecot
    automatically: (LP: #164837)
  + debian/control:
    - add new binary with short description
    - set Architecture all for dovecot-postfix (LP: #329878)
  + debian/dovecot-postfix.postinst:
    - create initial certificate symlinks to snakeoil.
    - set up postfix with postconf to:
      - use Maildir/ as the default mailbox.
      - use dovecot as the sasl authentication server.
      - use dovecot LDA (deliver).
      - use tls for smtp{d} services.
    - fix certificates paths in postfix' main.cf
    - add reject_unauth_destination to postfix' recipient restrictions
    - add reject_unknown_sender_domain to postfix' sender restriction
    - rename configuration name on remove, delete on purge
    - restart dovecot after linking certificates
    - handle use case when postfix is unconfigurated
  + debian/dovecot-postfix.dirs: create backup directory for postfix's config
    configuration
  + restart postfix and dovecot.
  + debian/dovecot-postfix.postrm:
    - remove all dovecot related configuration from postfix.
    - restart postfix and dovecot.
  + debian/dovecot-common.init:
    - check if /etc/dovecot/dovecot-postfix.conf exists and use it
      as the configuration file if so.
  + debian/patches/warning-ubuntu-postfix.dpatch
    - add warning about dovecot-postfix.conf in dovecot default
      configuration file
  + debian/patches/dovecot-postfix.conf.diff:
    - Ubuntu server custom changes to the default dovecot configuration for
      better interfation with postfix.
    - enable sieve plugin.
    - Ubuntu server custom changes to the default dovecot configuration for
      better integration with postfix:
      - enable imap, pop3, imaps, pop3s and managesieve by default.
      - enable dovecot LDA (deliver).
      - enable SASL auth socket in postfix private directory
   + debian/rules:
     - copy, patch and install dovecot-postfix.conf in /etc/dovecot/.
     - build architecure independent packages too
   + Use Snakeoil SSL certificates by default.
     - debian/control: Depend on ssl-cert.
     - debian/patches/ssl-cert-snakeoil.dpatch: Change default SSL cert
       paths to snakeoil.
     - debian/dovecot-common.postinst: Relax grep for SSL_* a bit.
   + Add autopkgtest to debian/tests/*.
   + Fast TearDown: Update the lsb init header to not stop in level 6.
   + Add ufw integration:
     - Created debian/dovecot-common.ufw.profile.
     - debian/rules: install profile.
     - debian/control: suggest ufw.
   + debian/{control,rules}: enable PIE hardening.
   + dovecot-imapd, dovecot-pop3: Replaces dovecot-common (<< 1:1.1). (LP: #254721)
   + debian/control: Update Vcs-* headers.
   + Add SMTP-AUTH support for Outlook (login auth mechanism)
* New upstream release.
* debian/patches/gold-fix.patch: Removed. Fixed upstream.
* Moved libexec to lib corrections in dovecot-managesieve.patch and
  dovecot-managesieve-dist.patch to dovecot-example.patch
* debian/patches/dovecot-mboxlocking.patch: Regenerated to avoid FTBFS
  when quilt isn't installed.
* debian/patches/quota-mountpoint.patch: Removed. Not needed anymore.
* debian/patches/dovecot-quota.patch: Removed. Quotas aren't properly
  enabled unless mail_plugins = quota imap_quota.
* debian/patches/gold-fix.patch: Fixed configure script to build even
  with binutils-gold or --no-add-needed linker flag (Closes: #554306)
* debian/dovecot-common.init: fixed LSB headers. Thanks to Pascal Volk.
  (Closes: #558040)
* debian/changelog: added CVE references to previous changelog entry.
* debian/rules: checked up the build system. It's not fragile anymore.
  (Closes: 493803)
* debian/dovecot-common.postinst: Now invoking dpkg-reconfigure
  on dovecot-common is enough to generate new certificates
  if the previous ones were removed. (Closes: #545582)
* debian/rules: No longer install convert-tool in /usr/bin.
  It isn't an user utility and it should stay in /usr/lib/dovecot
  like all other similar tool.
* New upstream release. (Closes: #557601)
* [SECURITY] Fixes local information disclosure and denial of service.
  (see: http://www.dovecot.org/list/dovecot-news/2009-November/000143.html
  and CVE-2009-3897)
* Added myself to uploaders.
* Switched to the new source format "3.0 (quilt)":
  - removed dpatch from build-depends
  - removed debian/README.source because now we use only standard
    dpkg features
  - regenerated all patches
* Prepared to switch to multi-origin source:
  - recreated dovecot-libsieve.patch and dovecot-managesieve-dist.patch
    starting from the upstream tarball
  - removed all autotools related build-depends and build-conflict
  - renamed dovecot-libsieve and dovecot-managesieve directories
    to libsieve and managesieve.
* debian/rules: Moved the configuration of libsieve and managesieve from
  the build phase to the configuration phase
* Added dovecot-dbg package  with debugging symbols.  Thanks Stephan Bosch.
  (Closes: #554710)
* Fixed some stray libexec'isms in the default configuration.
* New upstream release.
* debian/dovecot-common.init:
  - use $CONF when starting the daemon. (Closes: #549944)
  - always output start/stop messages. (Closes: #523810)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file 
 
2
 */
 
3
 
 
4
/* Notify method mailto
 
5
 * --------------------
 
6
 *
 
7
 * Authors: Stephan Bosch
 
8
 * Specification: RFC 5436
 
9
 * Implementation: full
 
10
 * Status: testing
 
11
 * 
 
12
 */
 
13
 
 
14
/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and
 
15
 *   draft-duerst-mailto-bis-05.txt. Should fully migrate to new specification
 
16
 *   when it matures. This requires modifications to the address parser (no
 
17
 *   whitespace allowed within the address itself) and UTF-8 support will be
 
18
 *   required in the URL.
 
19
 */
 
20
 
 
21
#include "lib.h"
 
22
#include "array.h"
 
23
#include "str.h"
 
24
#include "ioloop.h"
 
25
#include "str-sanitize.h"
 
26
#include "message-date.h"
 
27
#include "mail-storage.h"
 
28
 
 
29
#include "rfc2822.h"
 
30
 
 
31
#include "sieve-ext-enotify.h"
 
32
#include "sieve-address.h"
 
33
#include "sieve-message.h"
 
34
 
 
35
/*
 
36
 * Configuration
 
37
 */
 
38
 
 
39
#define NTFY_MAILTO_MAX_RECIPIENTS  8
 
40
#define NTFY_MAILTO_MAX_HEADERS     16
 
41
#define NTFY_MAILTO_MAX_SUBJECT     256
 
42
 
 
43
/* 
 
44
 * Types 
 
45
 */
 
46
 
 
47
struct ntfy_mailto_header_field {
 
48
        const char *name;
 
49
        const char *body;
 
50
};
 
51
 
 
52
struct ntfy_mailto_recipient {
 
53
        const char *full;
 
54
        const char *normalized;
 
55
        bool carbon_copy;
 
56
};
 
57
 
 
58
ARRAY_DEFINE_TYPE(recipients, struct ntfy_mailto_recipient);
 
59
ARRAY_DEFINE_TYPE(headers, struct ntfy_mailto_header_field);
 
60
 
 
61
/* 
 
62
 * Mailto notification method
 
63
 */
 
64
 
 
65
static bool ntfy_mailto_compile_check_uri
 
66
        (const struct sieve_enotify_log *nlog, const char *uri, const char *uri_body);
 
67
static bool ntfy_mailto_compile_check_from
 
68
        (const struct sieve_enotify_log *nlog, string_t *from);
 
69
 
 
70
static const char *ntfy_mailto_runtime_get_notify_capability
 
71
        (const struct sieve_enotify_log *nlog, const char *uri, const char *uri_body, 
 
72
                const char *capability);
 
73
static bool ntfy_mailto_runtime_check_uri
 
74
        (const struct sieve_enotify_log *nlog, const char *uri, const char *uri_body);
 
75
static bool ntfy_mailto_runtime_check_operands
 
76
        (const struct sieve_enotify_log *nlog, const char *uri,const char *uri_body, 
 
77
                string_t *message, string_t *from, pool_t context_pool, 
 
78
                void **method_context);
 
79
 
 
80
static int ntfy_mailto_action_check_duplicates
 
81
        (const struct sieve_enotify_log *nlog, void *method_ctx1, void *method_ctx2,
 
82
                const char *dupl_location);
 
83
 
 
84
static void ntfy_mailto_action_print
 
85
        (const struct sieve_enotify_print_env *penv, 
 
86
                const struct sieve_enotify_action *act);        
 
87
 
 
88
static bool ntfy_mailto_action_execute
 
89
        (const struct sieve_enotify_exec_env *nenv, 
 
90
                const struct sieve_enotify_action *act);
 
91
 
 
92
const struct sieve_enotify_method mailto_notify = {
 
93
        "mailto",
 
94
        ntfy_mailto_compile_check_uri,
 
95
        NULL,
 
96
        ntfy_mailto_compile_check_from,
 
97
        NULL,
 
98
        ntfy_mailto_runtime_check_uri,
 
99
        ntfy_mailto_runtime_get_notify_capability,
 
100
        ntfy_mailto_runtime_check_operands,
 
101
        NULL,
 
102
        ntfy_mailto_action_check_duplicates,
 
103
        ntfy_mailto_action_print,
 
104
        ntfy_mailto_action_execute
 
105
};
 
106
 
 
107
/*
 
108
 * Method context data
 
109
 */
 
110
 
 
111
struct ntfy_mailto_context {
 
112
        ARRAY_TYPE(recipients) recipients;
 
113
        ARRAY_TYPE(headers) headers;
 
114
        const char *subject;
 
115
        const char *body;
 
116
        const char *from_normalized;
 
117
};
 
118
 
 
119
/*
 
120
 * Reserved headers
 
121
 */
 
122
 
 
123
static const char *_reserved_headers[] = {
 
124
        "auto-submitted",
 
125
        "received",
 
126
        "message-id",
 
127
        "data",
 
128
        "bcc",
 
129
        "in-reply-to",
 
130
        "references",
 
131
        "resent-date",
 
132
        "resent-from",
 
133
        "resent-sender",
 
134
        "resent-to",
 
135
        "resent-cc",
 
136
        "resent-bcc",
 
137
        "resent-msg-id",
 
138
        "from",
 
139
        "sender",
 
140
        NULL
 
141
};
 
142
 
 
143
static const char *_unique_headers[] = {
 
144
        "reply-to",
 
145
        NULL
 
146
};
 
147
 
 
148
static inline bool _ntfy_mailto_header_allowed(const char *field_name)
 
149
{
 
150
        const char **rhdr = _reserved_headers;
 
151
 
 
152
        /* Check whether it is reserved */
 
153
        while ( *rhdr != NULL ) {
 
154
                if ( strcasecmp(field_name, *rhdr) == 0 )
 
155
                        return FALSE;
 
156
                rhdr++;
 
157
        }
 
158
 
 
159
        return TRUE;
 
160
}
 
161
 
 
162
static inline bool _ntfy_mailto_header_unique(const char *field_name)
 
163
{
 
164
        const char **rhdr = _unique_headers;
 
165
 
 
166
        /* Check whether it is supposed to be unique */
 
167
        while ( *rhdr != NULL ) {
 
168
                if ( strcasecmp(field_name, *rhdr) == 0 )
 
169
                        return TRUE;
 
170
                rhdr++;
 
171
        }
 
172
 
 
173
        return FALSE;
 
174
}
 
175
 
 
176
/*
 
177
 * Mailto URI parsing
 
178
 */
 
179
 
 
180
/* Util functions */
 
181
 
 
182
#define _uri_parse_error(LOG, ...) \
 
183
        sieve_enotify_error(LOG, "invalid mailto URI: " __VA_ARGS__ )
 
184
        
 
185
#define _uri_parse_warning(LOG, ...) \
 
186
        sieve_enotify_warning(LOG, "mailto URI: " __VA_ARGS__ )
 
187
 
 
188
/* FIXME: much of this implementation will be common to other URI schemes. This
 
189
 *        should be merged into a common implementation.
 
190
 */
 
191
 
 
192
static const char _qchar_lookup[256] = {
 
193
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 00
 
194
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 10
 
195
        0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,  // 20
 
196
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,  // 30
 
197
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 40
 
198
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,  // 50
 
199
        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 60
 
200
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,  // 70
 
201
 
 
202
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 80
 
203
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 90
 
204
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A0
 
205
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B0
 
206
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C0
 
207
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D0
 
208
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E0
 
209
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // F0
 
210
};
 
211
 
 
212
static inline bool _is_qchar(unsigned char c)
 
213
{
 
214
        return _qchar_lookup[c];
 
215
}
 
216
  
 
217
static inline int _decode_hex_digit(char digit)
 
218
{
 
219
        switch ( digit ) {
 
220
        case '0': case '1': case '2': case '3': case '4': 
 
221
        case '5': case '6': case '7': case '8': case '9': 
 
222
                return (int) digit - '0';
 
223
 
 
224
        case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
 
225
                return (int) digit - 'a' + 0x0a;
 
226
                
 
227
        case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
 
228
                return (int) digit - 'A' + 0x0A;
 
229
        }
 
230
        
 
231
        return -1;
 
232
}
 
233
 
 
234
static bool _parse_hex_value(const char **in, char *out)
 
235
{
 
236
        char value;
 
237
                
 
238
        if ( **in == '\0' || (value=_decode_hex_digit(**in)) < 0 )
 
239
                return FALSE;
 
240
        
 
241
        *out = value << 4;
 
242
        (*in)++;
 
243
        
 
244
        if ( **in == '\0' || (value=_decode_hex_digit(**in)) < 0 )
 
245
                return FALSE;   
 
246
 
 
247
        *out |= value;
 
248
        (*in)++;
 
249
        return (*out != '\0');  
 
250
}
 
251
 
 
252
static bool _uri_add_valid_recipient
 
253
(const struct sieve_enotify_log *nlog, ARRAY_TYPE(recipients) *recipients, 
 
254
        string_t *recipient, bool cc)
 
255
{
 
256
        const char *error;
 
257
        const char *normalized;
 
258
         
 
259
        /* Verify recipient */
 
260
        if ( (normalized=sieve_address_normalize(recipient, &error)) == NULL ) {
 
261
                _uri_parse_error(nlog, "invalid recipient '%s': %s",
 
262
                        str_sanitize(str_c(recipient), 80), error);
 
263
                return FALSE;
 
264
        }
 
265
                                        
 
266
        /* Add recipient to the list */
 
267
        if ( recipients != NULL ) {
 
268
                struct ntfy_mailto_recipient *new_recipient;
 
269
                struct ntfy_mailto_recipient *rcpts;
 
270
                unsigned int count, i;
 
271
                pool_t pool;
 
272
 
 
273
                rcpts = array_get_modifiable(recipients, &count);
 
274
                
 
275
                /* Enforce limits */
 
276
                if ( count >= NTFY_MAILTO_MAX_RECIPIENTS ) {
 
277
                        if ( count == NTFY_MAILTO_MAX_RECIPIENTS ) {
 
278
                                _uri_parse_warning(nlog, 
 
279
                                        "more than the maximum %u recipients specified; "
 
280
                                        "rest is discarded", NTFY_MAILTO_MAX_RECIPIENTS);
 
281
                        }
 
282
                        return TRUE;    
 
283
                }
 
284
                
 
285
                /* Check for duplicate first */
 
286
                for ( i = 0; i < count; i++ ) {
 
287
                        if ( sieve_address_compare(rcpts[i].normalized, normalized, TRUE) == 0 ) 
 
288
                                {
 
289
                                /* Upgrade existing Cc: recipient to a To: recipient if possible */
 
290
                                rcpts[i].carbon_copy = ( rcpts[i].carbon_copy && cc );
 
291
                                
 
292
                                _uri_parse_warning(nlog, "ignored duplicate recipient '%s'",
 
293
                                        str_sanitize(str_c(recipient), 80));
 
294
                                return TRUE;
 
295
                        } 
 
296
                }                       
 
297
 
 
298
                /* Add */
 
299
                pool = array_get_pool(recipients);
 
300
                new_recipient = array_append_space(recipients);
 
301
                new_recipient->carbon_copy = cc;
 
302
                new_recipient->full = p_strdup(pool, str_c(recipient));
 
303
                new_recipient->normalized = p_strdup(pool, normalized);
 
304
        }
 
305
 
 
306
        return TRUE;
 
307
}
 
308
 
 
309
static bool _uri_parse_recipients
 
310
(const struct sieve_enotify_log *nlog, const char **uri_p, 
 
311
        ARRAY_TYPE(recipients) *recipients_r)
 
312
{
 
313
        string_t *to = t_str_new(128);
 
314
        const char *p = *uri_p;
 
315
        
 
316
        if ( *p == '\0' || *p == '?' )
 
317
                return TRUE;
 
318
                
 
319
        while ( *p != '\0' && *p != '?' ) {
 
320
                if ( *p == '%' ) {
 
321
                        /* % encoded character */
 
322
                        char ch;
 
323
                        
 
324
                        p++;
 
325
                        
 
326
                        /* Parse 2-digit hex value */
 
327
                        if ( !_parse_hex_value(&p, &ch) ) {
 
328
                                _uri_parse_error(nlog, "invalid %% encoding");
 
329
                                return FALSE;
 
330
                        }
 
331
 
 
332
                        /* Check for delimiter */
 
333
                        if ( ch == ',' ) {
 
334
                                /* Verify and add recipient */
 
335
                                if ( !_uri_add_valid_recipient(nlog, recipients_r, to, FALSE) )
 
336
                                        return FALSE;
 
337
                        
 
338
                                /* Reset for next recipient */
 
339
                                str_truncate(to, 0);
 
340
                        }       else {
 
341
                                /* Content character */
 
342
                                str_append_c(to, ch);
 
343
                        }
 
344
                } else {
 
345
                        if ( *p == ':' || *p == ';' || *p == ',' || !_is_qchar(*p) ) {
 
346
                                _uri_parse_error
 
347
                                        (nlog, "invalid character '%c' in 'to' part", *p);
 
348
                                return FALSE;
 
349
                        }
 
350
 
 
351
                        /* Content character */
 
352
                        str_append_c(to, *p);
 
353
                        p++;
 
354
                }
 
355
        }       
 
356
        
 
357
        /* Skip '?' */
 
358
        if ( *p != '\0' ) p++;
 
359
        
 
360
        /* Verify and add recipient */
 
361
        if ( !_uri_add_valid_recipient(nlog, recipients_r, to, FALSE) )
 
362
                return FALSE;
 
363
 
 
364
        *uri_p = p;
 
365
        return TRUE;
 
366
}
 
367
 
 
368
static bool _uri_parse_header_recipients
 
369
(const struct sieve_enotify_log *nlog, string_t *rcpt_header, 
 
370
        ARRAY_TYPE(recipients) *recipients_r, bool cc)
 
371
{
 
372
        string_t *to = t_str_new(128);
 
373
        const char *p = (const char *) str_data(rcpt_header);
 
374
        const char *pend = p + str_len(rcpt_header);
 
375
                
 
376
        while ( p < pend ) {
 
377
                if ( *p == ',' ) {
 
378
                        /* Verify and add recipient */
 
379
                        if ( !_uri_add_valid_recipient(nlog, recipients_r, to, cc) )
 
380
                                return FALSE;
 
381
                        
 
382
                        /* Reset for next recipient */
 
383
                        str_truncate(to, 0);
 
384
                } else {
 
385
                        /* Content character */
 
386
                        str_append_c(to, *p);
 
387
                }
 
388
                p++;
 
389
        }       
 
390
        
 
391
        /* Verify and add recipient */
 
392
        if ( !_uri_add_valid_recipient(nlog, recipients_r, to, cc) )
 
393
                return FALSE;
 
394
 
 
395
        return TRUE;    
 
396
}
 
397
 
 
398
static bool _uri_header_is_duplicate
 
399
(ARRAY_TYPE(headers) *headers, const char *field_name)
 
400
{       
 
401
        if ( _ntfy_mailto_header_unique(field_name) ) {
 
402
                const struct ntfy_mailto_header_field *hdrs;
 
403
                unsigned int count, i;
 
404
 
 
405
                hdrs = array_get(headers, &count);      
 
406
                for ( i = 0; i < count; i++ ) {
 
407
                        if ( strcasecmp(hdrs[i].name, field_name) == 0 ) 
 
408
                                return TRUE;
 
409
                }
 
410
        }
 
411
        
 
412
        return FALSE;
 
413
}
 
414
 
 
415
static bool _uri_parse_headers
 
416
(const struct sieve_enotify_log *nlog, const char **uri_p, 
 
417
        ARRAY_TYPE(headers) *headers_r, ARRAY_TYPE(recipients) *recipients_r,
 
418
        const char **body, const char **subject)
 
419
{
 
420
        unsigned int header_count = 0;
 
421
        string_t *field = t_str_new(128);
 
422
        const char *p = *uri_p;
 
423
        pool_t pool = NULL;
 
424
 
 
425
        if ( body != NULL )
 
426
                *body = NULL;
 
427
                
 
428
        if ( subject != NULL )
 
429
                *subject = NULL;
 
430
                        
 
431
        if ( headers_r != NULL )
 
432
                pool = array_get_pool(headers_r);
 
433
                
 
434
        while ( *p != '\0' ) {
 
435
                enum {
 
436
                        _HNAME_IGNORED, 
 
437
                        _HNAME_GENERIC,
 
438
                        _HNAME_TO,
 
439
                        _HNAME_CC,
 
440
                        _HNAME_SUBJECT, 
 
441
                        _HNAME_BODY 
 
442
                } hname_type = _HNAME_GENERIC;
 
443
                struct ntfy_mailto_header_field *hdrf = NULL;
 
444
                const char *field_name;
 
445
                
 
446
                /* Parse field name */
 
447
                while ( *p != '\0' && *p != '=' ) {
 
448
                        char ch = *p;
 
449
                        p++;
 
450
                        
 
451
                        if ( ch == '%' ) {
 
452
                                /* Encoded, parse 2-digit hex value */
 
453
                                if ( !_parse_hex_value(&p, &ch) ) {
 
454
                                        _uri_parse_error(nlog, "invalid %% encoding");
 
455
                                        return FALSE;
 
456
                                }
 
457
                        } else if ( ch != '=' && !_is_qchar(ch) ) {
 
458
                                _uri_parse_error
 
459
                                        (nlog, "invalid character '%c' in header field name part", ch);
 
460
                                return FALSE;
 
461
                        }
 
462
 
 
463
                        str_append_c(field, ch);
 
464
                }
 
465
                if ( *p != '\0' ) p++;
 
466
 
 
467
                /* Verify field name */
 
468
                if ( !rfc2822_header_field_name_verify(str_c(field), str_len(field)) ) {
 
469
                        _uri_parse_error(nlog, "invalid header field name");
 
470
                        return FALSE;
 
471
                }
 
472
 
 
473
                if ( header_count >= NTFY_MAILTO_MAX_HEADERS ) {
 
474
                        /* Refuse to accept more headers than allowed by policy */
 
475
                        if ( header_count == NTFY_MAILTO_MAX_HEADERS ) {
 
476
                                _uri_parse_warning(nlog, "more than the maximum %u headers specified; "
 
477
                                        "rest is discarded", NTFY_MAILTO_MAX_HEADERS);
 
478
                        }
 
479
                        
 
480
                        hname_type = _HNAME_IGNORED;
 
481
                } else {
 
482
                        /* Add new header field to array and assign its name */
 
483
                        
 
484
                        field_name = str_c(field);
 
485
                        if ( strcasecmp(field_name, "to") == 0 )
 
486
                                hname_type = _HNAME_TO;
 
487
                        else if ( strcasecmp(field_name, "cc") == 0 )
 
488
                                hname_type = _HNAME_CC;
 
489
                        else if ( strcasecmp(field_name, "subject") == 0 )
 
490
                                hname_type = _HNAME_SUBJECT;
 
491
                        else if ( strcasecmp(field_name, "body") == 0 )
 
492
                                hname_type = _HNAME_BODY;
 
493
                        else if ( _ntfy_mailto_header_allowed(field_name) ) {
 
494
                                if ( headers_r != NULL ) {
 
495
                                        if ( !_uri_header_is_duplicate(headers_r, field_name) ) {
 
496
                                                hdrf = array_append_space(headers_r);
 
497
                                                hdrf->name = p_strdup(pool, field_name);
 
498
                                        } else {
 
499
                                                _uri_parse_warning(nlog, 
 
500
                                                        "ignored duplicate for unique header field '%s'",
 
501
                                                        str_sanitize(field_name, 32));
 
502
                                                hname_type = _HNAME_IGNORED;
 
503
                                        }
 
504
                                } else {
 
505
                                        hname_type = _HNAME_IGNORED;
 
506
                                }
 
507
                        } else {
 
508
                                _uri_parse_warning(nlog, "ignored reserved header field '%s'",
 
509
                                        str_sanitize(field_name, 32));
 
510
                                hname_type = _HNAME_IGNORED;
 
511
                        }
 
512
                }
 
513
                
 
514
                header_count++;
 
515
                        
 
516
                /* Reset for field body */
 
517
                str_truncate(field, 0);
 
518
                
 
519
                /* Parse field body */          
 
520
                while ( *p != '\0' && *p != '&' ) {
 
521
                        char ch = *p;
 
522
                        p++;
 
523
                        
 
524
                        if ( ch == '%' ) {
 
525
                                /* Encoded, parse 2-digit hex value */
 
526
                                if ( !_parse_hex_value(&p, &ch) ) {
 
527
                                        _uri_parse_error(nlog, "invalid %% encoding");
 
528
                                        return FALSE;
 
529
                                }
 
530
                        } else if ( ch != '=' && !_is_qchar(ch) ) {
 
531
                                _uri_parse_error
 
532
                                        (nlog, "invalid character '%c' in header field value part", ch);
 
533
                                return FALSE;
 
534
                        }
 
535
                        str_append_c(field, ch);
 
536
                }
 
537
                if ( *p != '\0' ) p++;
 
538
                
 
539
                /* Verify field body */
 
540
                if ( hname_type == _HNAME_BODY ) {
 
541
                        // FIXME: verify body ... 
 
542
                } else {
 
543
                        if ( !rfc2822_header_field_body_verify(str_c(field), str_len(field)) ) {
 
544
                                _uri_parse_error
 
545
                                        (nlog, "invalid header field body");
 
546
                                return FALSE;
 
547
                        }
 
548
                }
 
549
                
 
550
                /* Assign field body */
 
551
 
 
552
                switch ( hname_type ) {
 
553
                case _HNAME_IGNORED:
 
554
                        break;
 
555
                case _HNAME_TO:
 
556
                        /* Gracefully allow duplicate To fields */
 
557
                        if ( !_uri_parse_header_recipients(nlog, field, recipients_r, FALSE) )
 
558
                                return FALSE;
 
559
                        break;
 
560
                case _HNAME_CC:
 
561
                        /* Gracefully allow duplicate Cc fields */
 
562
                        if ( !_uri_parse_header_recipients(nlog, field, recipients_r, TRUE) )
 
563
                                return FALSE;
 
564
                        break;
 
565
                case _HNAME_SUBJECT:
 
566
                        if ( subject != NULL ) {
 
567
                                /* Igore duplicate subject field */
 
568
                                if ( *subject == NULL )
 
569
                                        *subject = p_strdup(pool, str_c(field));
 
570
                                else
 
571
                                        _uri_parse_warning(nlog, "ignored duplicate subject field");
 
572
                        }
 
573
                        break;
 
574
                case _HNAME_BODY:
 
575
                        if ( body != NULL ) {
 
576
                                /* Igore duplicate body field */
 
577
                                if ( *body == NULL )
 
578
                                        *body = p_strdup(pool, str_c(field));
 
579
                                else 
 
580
                                        _uri_parse_warning(nlog, "ignored duplicate body field");
 
581
                        }                               
 
582
                        break;
 
583
                case _HNAME_GENERIC:
 
584
                        if ( hdrf != NULL ) 
 
585
                                hdrf->body = p_strdup(pool, str_c(field));
 
586
                        break;
 
587
                }
 
588
                        
 
589
                /* Reset for next name */
 
590
                str_truncate(field, 0);
 
591
        }       
 
592
        
 
593
        /* Skip '&' */
 
594
        if ( *p != '\0' ) p++;
 
595
 
 
596
        *uri_p = p;
 
597
        return TRUE;
 
598
}
 
599
 
 
600
static bool ntfy_mailto_parse_uri
 
601
(const struct sieve_enotify_log *nlog, const char *uri_body, 
 
602
        ARRAY_TYPE(recipients) *recipients_r, ARRAY_TYPE(headers) *headers_r,
 
603
        const char **body, const char **subject)
 
604
{
 
605
        const char *p = uri_body;
 
606
        
 
607
        /* 
 
608
         * mailtoURI   = "mailto:" [ to ] [ hfields ]
 
609
         * to          = [ addr-spec *("%2C" addr-spec ) ]
 
610
         * hfields     = "?" hfield *( "&" hfield )
 
611
         * hfield      = hfname "=" hfvalue
 
612
         * hfname      = *qchar
 
613
         * hfvalue     = *qchar
 
614
         * addr-spec   = local-part "@" domain
 
615
         * local-part  = dot-atom / quoted-string
 
616
         * qchar       = unreserved / pct-encoded / some-delims
 
617
         * some-delims = "!" / "$" / "'" / "(" / ")" / "*"
 
618
         *               / "+" / "," / ";" / ":" / "@"
 
619
         *
 
620
         * to         ~= *tqchar
 
621
         * tqchar     ~= <qchar> without ";" and ":" 
 
622
         * 
 
623
         * Scheme 'mailto:' already parsed, starting parse after colon
 
624
         */
 
625
 
 
626
        /* First extract to-part by searching for '?' and decoding % items
 
627
         */
 
628
 
 
629
        if ( !_uri_parse_recipients(nlog, &p, recipients_r) )
 
630
                return FALSE;   
 
631
 
 
632
        /* Extract hfield items */      
 
633
        
 
634
        while ( *p != '\0' ) {          
 
635
                /* Extract hfield item by searching for '&' and decoding '%' items */
 
636
                if ( !_uri_parse_headers(nlog, &p, headers_r, recipients_r, body, subject) )
 
637
                        return FALSE;           
 
638
        }
 
639
        
 
640
        return TRUE;
 
641
}
 
642
 
 
643
/*
 
644
 * Validation
 
645
 */
 
646
 
 
647
static bool ntfy_mailto_compile_check_uri
 
648
(const struct sieve_enotify_log *nlog, const char *uri ATTR_UNUSED,
 
649
        const char *uri_body)
 
650
{       
 
651
        ARRAY_TYPE(recipients) recipients;
 
652
        ARRAY_TYPE(headers) headers;
 
653
        const char *body = NULL, *subject = NULL;
 
654
 
 
655
        t_array_init(&recipients, NTFY_MAILTO_MAX_RECIPIENTS);
 
656
        t_array_init(&headers, NTFY_MAILTO_MAX_HEADERS);
 
657
        
 
658
        if ( !ntfy_mailto_parse_uri
 
659
                (nlog, uri_body, &recipients, &headers, &body, &subject) )
 
660
                return FALSE;
 
661
                
 
662
        if ( array_count(&recipients) == 0 )
 
663
                sieve_enotify_warning(nlog, "notification URI specifies no recipients");
 
664
        
 
665
        return TRUE;
 
666
}
 
667
 
 
668
static bool ntfy_mailto_compile_check_from
 
669
(const struct sieve_enotify_log *nlog, string_t *from)
 
670
{
 
671
        const char *error;
 
672
        bool result = FALSE;
 
673
 
 
674
        T_BEGIN {
 
675
                result = sieve_address_validate(from, &error);
 
676
 
 
677
                if ( !result ) {
 
678
                        sieve_enotify_error(nlog,
 
679
                                "specified :from address '%s' is invalid for "
 
680
                                "the mailto method: %s",
 
681
                                str_sanitize(str_c(from), 128), error);
 
682
                }
 
683
        } T_END;
 
684
 
 
685
        return result;
 
686
}
 
687
 
 
688
/*
 
689
 * Runtime
 
690
 */
 
691
 
 
692
static const char *ntfy_mailto_runtime_get_notify_capability
 
693
(const struct sieve_enotify_log *nlog ATTR_UNUSED, const char *uri ATTR_UNUSED, 
 
694
        const char *uri_body, const char *capability)
 
695
{
 
696
        if ( !ntfy_mailto_parse_uri(NULL, uri_body, NULL, NULL, NULL, NULL) ) {
 
697
                return NULL;
 
698
        }
 
699
        
 
700
        if ( strcasecmp(capability, "online") == 0 ) 
 
701
                return "maybe";
 
702
        
 
703
        return NULL;
 
704
}
 
705
 
 
706
static bool ntfy_mailto_runtime_check_uri
 
707
(const struct sieve_enotify_log *nlog ATTR_UNUSED, const char *uri ATTR_UNUSED,
 
708
        const char *uri_body)
 
709
{
 
710
        return ntfy_mailto_parse_uri(NULL, uri_body, NULL, NULL, NULL, NULL);
 
711
}
 
712
 
 
713
static bool ntfy_mailto_runtime_check_operands
 
714
(const struct sieve_enotify_log *nlog, const char *uri ATTR_UNUSED,
 
715
        const char *uri_body, string_t *message ATTR_UNUSED, string_t *from, 
 
716
        pool_t context_pool, void **method_context)
 
717
{
 
718
        struct ntfy_mailto_context *mtctx;
 
719
        const char *error, *normalized;
 
720
 
 
721
        /* Need to create context before validation to have arrays present */
 
722
        mtctx = p_new(context_pool, struct ntfy_mailto_context, 1);
 
723
 
 
724
        /* Validate :from */
 
725
        if ( from != NULL ) {
 
726
                T_BEGIN {
 
727
                        normalized = sieve_address_normalize(from, &error);
 
728
 
 
729
                        if ( normalized == NULL ) {
 
730
                                sieve_enotify_error(nlog,
 
731
                                        "specified :from address '%s' is invalid for "
 
732
                                        "the mailto method: %s",
 
733
                                        str_sanitize(str_c(from), 128), error);
 
734
                        } else 
 
735
                                mtctx->from_normalized = p_strdup(context_pool, normalized);
 
736
                } T_END;
 
737
 
 
738
                if ( !normalized ) return FALSE;
 
739
        }
 
740
 
 
741
        p_array_init(&mtctx->recipients, context_pool, NTFY_MAILTO_MAX_RECIPIENTS);
 
742
        p_array_init(&mtctx->headers, context_pool, NTFY_MAILTO_MAX_HEADERS);
 
743
 
 
744
        if ( !ntfy_mailto_parse_uri
 
745
                (nlog, uri_body, &mtctx->recipients, &mtctx->headers, &mtctx->body, 
 
746
                        &mtctx->subject) ) {
 
747
                return FALSE;
 
748
        }
 
749
 
 
750
        *method_context = (void *) mtctx;
 
751
        return TRUE;    
 
752
}
 
753
 
 
754
/*
 
755
 * Action duplicates
 
756
 */
 
757
 
 
758
static int ntfy_mailto_action_check_duplicates
 
759
(const struct sieve_enotify_log *nlog ATTR_UNUSED, 
 
760
        void *method_ctx1, void *method_ctx2,
 
761
        const char *dupl_location ATTR_UNUSED)
 
762
{
 
763
        struct ntfy_mailto_context *mt_new = 
 
764
                (struct ntfy_mailto_context *) method_ctx1;
 
765
        struct ntfy_mailto_context *mt_old = 
 
766
                (struct ntfy_mailto_context *) method_ctx2;
 
767
        const struct ntfy_mailto_recipient *new_rcpts, *old_rcpts;
 
768
        unsigned int new_count, old_count, i, j;
 
769
        unsigned int del_start = 0, del_len = 0;
 
770
 
 
771
        new_rcpts = array_get(&mt_new->recipients, &new_count);
 
772
        old_rcpts = array_get(&mt_old->recipients, &old_count);
 
773
 
 
774
        for ( i = 0; i < new_count; i++ ) {
 
775
                for ( j = 0; j < old_count; j++ ) {
 
776
                        if ( sieve_address_compare
 
777
                                (new_rcpts[i].normalized, old_rcpts[j].normalized, TRUE) == 0 )
 
778
                                break;                          
 
779
                }
 
780
 
 
781
                if ( j == old_count ) {
 
782
                        /* Not duplicate */
 
783
                        if ( del_len > 0 ) {
 
784
                                /* Perform pending deletion */
 
785
                                array_delete(&mt_new->recipients, del_start, del_len);
 
786
 
 
787
                                /* Make sure the loop integrity is maintained */
 
788
                                i -= del_len;
 
789
                                new_rcpts = array_get(&mt_new->recipients, &new_count);
 
790
                        }
 
791
                        del_len = 0;            
 
792
                } else {
 
793
                        /* Mark deletion */
 
794
                        if ( del_len == 0 )
 
795
                                del_start = i;
 
796
                        del_len++;
 
797
                }
 
798
        }
 
799
 
 
800
        /* Perform pending deletion */
 
801
        if ( del_len > 0 ) {
 
802
                array_delete(&mt_new->recipients, del_start, del_len);                  
 
803
        }
 
804
 
 
805
        return ( array_count(&mt_new->recipients) > 0 ? 0 : 1 );
 
806
}
 
807
 
 
808
/*
 
809
 * Action printing
 
810
 */
 
811
 
 
812
static void ntfy_mailto_action_print
 
813
(const struct sieve_enotify_print_env *penv, 
 
814
        const struct sieve_enotify_action *act)
 
815
{
 
816
        unsigned int count, i;
 
817
        const struct ntfy_mailto_recipient *recipients;
 
818
        const struct ntfy_mailto_header_field *headers;
 
819
        struct ntfy_mailto_context *mtctx = 
 
820
                (struct ntfy_mailto_context *) act->method_context;
 
821
        
 
822
        /* Print main method parameters */
 
823
 
 
824
        sieve_enotify_method_printf
 
825
                (penv,   "    => importance   : %d\n", act->importance);
 
826
 
 
827
        if ( act->message != NULL )
 
828
                sieve_enotify_method_printf
 
829
                        (penv, "    => subject      : %s\n", act->message);
 
830
        else if ( mtctx->subject != NULL )
 
831
                sieve_enotify_method_printf
 
832
                        (penv, "    => subject      : %s\n", mtctx->subject);
 
833
 
 
834
        if ( act->from != NULL )
 
835
                sieve_enotify_method_printf
 
836
                        (penv, "    => from         : %s\n", act->from);
 
837
 
 
838
        /* Print mailto: recipients */
 
839
 
 
840
        sieve_enotify_method_printf(penv,   "    => recipients   :\n" );
 
841
 
 
842
        recipients = array_get(&mtctx->recipients, &count);
 
843
        if ( count == 0 ) {
 
844
                sieve_enotify_method_printf(penv,   "       NONE, action has no effect\n");
 
845
        } else {
 
846
                for ( i = 0; i < count; i++ ) {
 
847
                        if ( recipients[i].carbon_copy )
 
848
                                sieve_enotify_method_printf
 
849
                                        (penv,   "       + Cc: %s\n", recipients[i].full);
 
850
                        else
 
851
                                sieve_enotify_method_printf
 
852
                                        (penv,   "       + To: %s\n", recipients[i].full);
 
853
                }
 
854
        }
 
855
 
 
856
        /* Print accepted headers for notification message */
 
857
        
 
858
        headers = array_get(&mtctx->headers, &count);
 
859
        if ( count > 0 ) {
 
860
                sieve_enotify_method_printf(penv,   "    => headers      :\n" );        
 
861
                for ( i = 0; i < count; i++ ) {
 
862
                        sieve_enotify_method_printf(penv,   "       + %s: %s\n", 
 
863
                                headers[i].name, headers[i].body);
 
864
                }
 
865
        }
 
866
 
 
867
        /* Print body for notification message */
 
868
        
 
869
        if ( mtctx->body != NULL )
 
870
                sieve_enotify_method_printf
 
871
                        (penv, "    => body         : \n--\n%s\n--\n", mtctx->body);
 
872
 
 
873
        /* Finish output with an empty line */
 
874
 
 
875
        sieve_enotify_method_printf(penv,   "\n");
 
876
}
 
877
 
 
878
/*
 
879
 * Action execution
 
880
 */
 
881
 
 
882
static bool _contains_8bit(const char *msg)
 
883
{
 
884
        const unsigned char *s = (const unsigned char *)msg;
 
885
 
 
886
        for (; *s != '\0'; s++) {
 
887
                if ((*s & 0x80) != 0)
 
888
                        return TRUE;
 
889
        }
 
890
        
 
891
        return FALSE;
 
892
}
 
893
 
 
894
static bool ntfy_mailto_send
 
895
(const struct sieve_enotify_exec_env *nenv, 
 
896
        const struct sieve_enotify_action *act, const char *recipient)
 
897
 
898
        const struct sieve_enotify_log *nlog = nenv->notify_log;
 
899
        const struct sieve_message_data *msgdata = nenv->msgdata;
 
900
        const struct sieve_script_env *senv = nenv->scriptenv;
 
901
        struct ntfy_mailto_context *mtctx = 
 
902
                (struct ntfy_mailto_context *) act->method_context;     
 
903
        const char *from = NULL, *from_smtp = NULL; 
 
904
        const char *subject = mtctx->subject;
 
905
        const char *body = mtctx->body;
 
906
        string_t *to, *cc;
 
907
        const struct ntfy_mailto_recipient *recipients;
 
908
        void *smtp_handle;
 
909
        unsigned int count, i;
 
910
        FILE *f;
 
911
        const char *outmsgid;
 
912
 
 
913
        /* Get recipients */
 
914
        recipients = array_get(&mtctx->recipients, &count);
 
915
        if ( count == 0  ) {
 
916
                sieve_enotify_warning(nlog, 
 
917
                        "notify mailto uri specifies no recipients; action has no effect");
 
918
                return TRUE;
 
919
        }
 
920
 
 
921
        /* Just to be sure */
 
922
        if ( senv->smtp_open == NULL || senv->smtp_close == NULL ) {
 
923
                sieve_enotify_warning(nlog, 
 
924
                        "notify mailto method has no means to send mail");
 
925
                return TRUE;
 
926
        }
 
927
        
 
928
        /* Determine message from address */
 
929
        if ( act->from == NULL ) {
 
930
                from = t_strdup_printf("Postmaster <%s>", senv->postmaster_address);
 
931
        } else {
 
932
                from = act->from;
 
933
        }
 
934
 
 
935
        /* Determine SMTP from address */
 
936
        if ( sieve_message_get_sender(nenv->msgctx) != NULL ) {
 
937
                if ( mtctx->from_normalized == NULL ) {
 
938
                        from_smtp = senv->postmaster_address;
 
939
                } else {
 
940
                        from_smtp = mtctx->from_normalized;
 
941
                }
 
942
        }
 
943
        
 
944
        /* Determine subject */
 
945
        if ( act->message != NULL ) {
 
946
                /* FIXME: handle UTF-8 */
 
947
                subject = str_sanitize(act->message, NTFY_MAILTO_MAX_SUBJECT);
 
948
        } else if ( subject == NULL ) {
 
949
                const char *const *hsubject;
 
950
                
 
951
                /* Fetch subject from original message */
 
952
                if ( mail_get_headers_utf8
 
953
                        (msgdata->mail, "subject", &hsubject) >= 0 )
 
954
                        subject = str_sanitize(t_strdup_printf("Notification: %s", hsubject[0]), 
 
955
                                NTFY_MAILTO_MAX_SUBJECT);
 
956
                else
 
957
                        subject = "Notification: (no subject)";
 
958
        }
 
959
 
 
960
        /* Compose To and Cc headers */
 
961
        to = NULL;
 
962
        cc = NULL;
 
963
        for ( i = 0; i < count; i++ ) {
 
964
                if ( recipients[i].carbon_copy ) {
 
965
                        if ( cc == NULL ) {
 
966
                                cc = t_str_new(256);
 
967
                                str_append(cc, recipients[i].full);
 
968
                        } else {
 
969
                                str_append(cc, ", ");
 
970
                                str_append(cc, recipients[i].full);
 
971
                        }
 
972
                } else {
 
973
                        if ( to == NULL ) {
 
974
                                to = t_str_new(256);
 
975
                                str_append(to, recipients[i].full);
 
976
                        } else {
 
977
                                str_append(to, ", ");
 
978
                                str_append(to, recipients[i].full);
 
979
                        }
 
980
                }
 
981
        }
 
982
 
 
983
        /* Send message to all recipients */
 
984
        for ( i = 0; i < count; i++ ) {
 
985
                const struct ntfy_mailto_header_field *headers;
 
986
                unsigned int h, hcount;
 
987
 
 
988
                smtp_handle = senv->smtp_open(recipients[i].normalized, from_smtp, &f);
 
989
                outmsgid = sieve_message_get_new_id(senv);
 
990
        
 
991
                rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION);
 
992
                rfc2822_header_field_write(f, "Message-ID", outmsgid);
 
993
                rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time));
 
994
                rfc2822_header_field_write(f, "Subject", subject);
 
995
 
 
996
                rfc2822_header_field_printf(f, "From", "%s", from);
 
997
 
 
998
                if ( to != NULL )
 
999
                        rfc2822_header_field_printf(f, "To", "%s", str_c(to));
 
1000
                
 
1001
                if ( cc != NULL )
 
1002
                        rfc2822_header_field_printf(f, "Cc", "%s", str_c(cc));
 
1003
                        
 
1004
                rfc2822_header_field_printf(f, "Auto-Submitted", 
 
1005
                        "auto-notified; owner-email=\"%s\"", recipient);
 
1006
                rfc2822_header_field_write(f, "Precedence", "bulk");
 
1007
 
 
1008
                /* Set importance */
 
1009
                switch ( act->importance ) {
 
1010
                case 1:
 
1011
                        rfc2822_header_field_write(f, "X-Priority", "1 (Highest)");
 
1012
                        rfc2822_header_field_write(f, "Importance", "High");
 
1013
                        break;
 
1014
                case 3:
 
1015
                        rfc2822_header_field_write(f, "X-Priority", "5 (Lowest)");
 
1016
                        rfc2822_header_field_write(f, "Importance", "Low");
 
1017
                        break;
 
1018
                case 2:
 
1019
                default:
 
1020
                        rfc2822_header_field_write(f, "X-Priority", "3 (Normal)");
 
1021
                        rfc2822_header_field_write(f, "Importance", "Normal");
 
1022
                        break;
 
1023
                }
 
1024
                
 
1025
                /* Add custom headers */
 
1026
                
 
1027
                headers = array_get(&mtctx->headers, &hcount);
 
1028
                for ( h = 0; h < hcount; h++ ) {
 
1029
                        const char *name = rfc2822_header_field_name_sanitize(headers[h].name);
 
1030
                
 
1031
                        rfc2822_header_field_write(f, name, headers[h].body);
 
1032
                }
 
1033
                        
 
1034
                /* Generate message body */
 
1035
                if ( body != NULL ) {
 
1036
                        if (_contains_8bit(body)) {
 
1037
                                rfc2822_header_field_write(f, "MIME-Version", "1.0");
 
1038
                                rfc2822_header_field_write
 
1039
                                        (f, "Content-Type", "text/plain; charset=UTF-8");
 
1040
                                rfc2822_header_field_write(f, "Content-Transfer-Encoding", "8bit");
 
1041
                        }
 
1042
                        
 
1043
                        fprintf(f, "\r\n");
 
1044
                        fprintf(f, "%s\r\n", body);
 
1045
                        
 
1046
                } else {
 
1047
                        fprintf(f, "\r\n");
 
1048
                        fprintf(f, "Notification of new message.\r\n");
 
1049
                }
 
1050
        
 
1051
                if ( senv->smtp_close(smtp_handle) ) {
 
1052
                        sieve_enotify_log(nlog, 
 
1053
                                "sent mail notification to <%s>", 
 
1054
                                str_sanitize(recipients[i].normalized, 80));
 
1055
                } else {
 
1056
                        sieve_enotify_error(nlog,
 
1057
                                "failed to send mail notification to <%s> "
 
1058
                                "(refer to system log for more information)", 
 
1059
                                str_sanitize(recipients[i].normalized, 80));
 
1060
                }
 
1061
        }
 
1062
 
 
1063
        return TRUE;
 
1064
}
 
1065
 
 
1066
static bool ntfy_mailto_action_execute
 
1067
(const struct sieve_enotify_exec_env *nenv, 
 
1068
        const struct sieve_enotify_action *act)
 
1069
{
 
1070
        const char *const *headers;
 
1071
        const char *sender = sieve_message_get_sender(nenv->msgctx);
 
1072
        const char *recipient = sieve_message_get_recipient(nenv->msgctx);
 
1073
 
 
1074
        /* Is the recipient unset? 
 
1075
         */
 
1076
        if ( recipient == NULL ) {
 
1077
                sieve_enotify_warning(nenv->notify_log, 
 
1078
                        "notify mailto action aborted: envelope recipient is <>");
 
1079
                return TRUE;
 
1080
        }
 
1081
        
 
1082
        /* Is the message an automatic reply ? */
 
1083
        if ( mail_get_headers
 
1084
                (nenv->msgdata->mail, "auto-submitted", &headers) >= 0 ) {
 
1085
                const char *const *hdsp = headers;
 
1086
 
 
1087
                /* Theoretically multiple headers could exist, so lets make sure */
 
1088
                while ( *hdsp != NULL ) {
 
1089
                        if ( strcasecmp(*hdsp, "no") != 0 ) {
 
1090
                                sieve_enotify_log(nenv->notify_log, 
 
1091
                                        "not sending notification for auto-submitted message from <%s>", 
 
1092
                                        str_sanitize(sender, 128));     
 
1093
                                        return TRUE;                             
 
1094
                        }
 
1095
                        hdsp++;
 
1096
                }
 
1097
        }
 
1098
 
 
1099
        return ntfy_mailto_send(nenv, act, recipient);
 
1100
}
 
1101
 
 
1102
 
 
1103
 
 
1104