1
/* Copyright (c) 2002-2012 Pigeonhole authors, see the included COPYING file
1
/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file
4
4
/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and
5
5
* draft-duerst-mailto-bis-05.txt. Should fully migrate to new specification
6
6
* when it matures. This requires modifications to the address parser (no
7
7
* whitespace allowed within the address itself) and UTF-8 support will be
8
8
* required in the URL.
27
27
#define uri_mailto_error(PARSER, ...) \
28
28
sieve_error((PARSER)->ehandler, NULL, "invalid mailto URI: " __VA_ARGS__ )
30
30
#define uri_mailto_warning(PARSER, ...) \
31
31
sieve_warning((PARSER)->ehandler, NULL, "mailto URI: " __VA_ARGS__ )
49
* Reserved and unique headers
49
* Reserved and unique headers
52
52
static inline bool uri_mailto_header_is_reserved
53
53
(struct uri_mailto_parser *parser, const char *field_name)
115
115
return _qchar_lookup[c];
118
118
static inline int _decode_hex_digit(unsigned char digit)
120
120
switch ( digit ) {
121
case '0': case '1': case '2': case '3': case '4':
122
case '5': case '6': case '7': case '8': case '9':
121
case '0': case '1': case '2': case '3': case '4':
122
case '5': case '6': case '7': case '8': case '9':
123
123
return (int) digit - '0';
125
125
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
126
126
return (int) digit - 'a' + 0x0a;
128
128
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
129
129
return (int) digit - 'A' + 0x0A;
135
135
static bool _parse_hex_value(const char **in, char *out)
137
137
int value, digit;
139
139
if ( (digit=_decode_hex_digit((unsigned char) **in)) < 0 )
142
142
value = digit << 4;
145
145
if ( (digit=_decode_hex_digit((unsigned char) **in)) < 0 )
168
168
unsigned int count, i;
169
169
const char *error;
170
170
const char *normalized;
172
172
/* Verify recipient */
173
173
if ( (normalized=sieve_address_normalize(recipient, &error)) == NULL ) {
174
174
uri_mailto_error(parser, "invalid recipient '%s': %s",
175
175
str_sanitize(str_c(recipient), 80), error);
179
179
/* Add recipient to the uri */
181
181
/* Get current recipients */
182
182
rcpts = array_get_modifiable(&uri->recipients, &count);
184
184
/* Enforce limits */
185
185
if ( parser->max_recipients > 0 && (int)count >= parser->max_recipients ) {
186
186
if ( (int)count == parser->max_recipients) {
187
uri_mailto_warning(parser,
187
uri_mailto_warning(parser,
188
188
"more than the maximum %u recipients specified; "
189
189
"rest is discarded", parser->max_recipients);
194
194
/* Check for duplicate first */
195
195
for ( i = 0; i < count; i++ ) {
196
if ( sieve_address_compare(rcpts[i].normalized, normalized, TRUE) == 0 )
196
if ( sieve_address_compare(rcpts[i].normalized, normalized, TRUE) == 0 )
198
198
/* Upgrade existing Cc: recipient to a To: recipient if possible */
199
199
rcpts[i].carbon_copy = ( rcpts[i].carbon_copy && cc );
201
201
uri_mailto_warning(parser, "ignored duplicate recipient '%s'",
202
202
str_sanitize(str_c(recipient), 80));
208
208
new_recipient = array_append_space(&uri->recipients);
220
220
string_t *to = t_str_new(128);
221
221
const char *p = *uri_p;
223
223
if ( *p == '\0' || *p == '?' )
226
226
while ( *p != '\0' && *p != '?' ) {
227
227
if ( *p == '%' ) {
228
228
/* % encoded character */
233
233
/* Parse 2-digit hex value */
234
234
if ( !_parse_hex_value(&p, &ch) ) {
235
235
uri_mailto_error(parser, "invalid %% encoding");
278
278
string_t *to = t_str_new(128);
279
279
const char *p = (const char *) str_data(rcpt_header);
280
280
const char *pend = p + str_len(rcpt_header);
282
282
while ( p < pend ) {
283
283
if ( *p == ',' ) {
284
284
/* Verify and add recipient */
285
285
if ( !uri_mailto_add_valid_recipient(parser, to, cc) )
288
288
/* Reset for next recipient */
289
289
str_truncate(to, 0);
292
292
str_append_c(to, *p);
297
297
/* Verify and add recipient */
298
298
if ( !uri_mailto_add_valid_recipient(parser, to, cc) )
304
304
/* URI header parsing */
306
306
static bool uri_mailto_header_is_duplicate
307
307
(struct uri_mailto_parser *parser, const char *field_name)
309
309
struct uri_mailto *uri = parser->uri;
311
311
if ( uri == NULL ) return FALSE;
314
314
const struct uri_mailto_header_field *hdrs;
315
315
unsigned int count, i;
317
hdrs = array_get(&uri->headers, &count);
317
hdrs = array_get(&uri->headers, &count);
318
318
for ( i = 0; i < count; i++ ) {
319
if ( strcasecmp(hdrs[i].name, field_name) == 0 )
319
if ( strcasecmp(hdrs[i].name, field_name) == 0 )
331
331
unsigned int header_count = 0;
332
332
string_t *field = t_str_new(128);
333
333
const char *p = *uri_p;
335
335
while ( *p != '\0' ) {
343
343
} hname_type = _HNAME_GENERIC;
344
344
struct uri_mailto_header_field *hdrf = NULL;
345
345
const char *field_name;
347
347
/* Parse field name */
348
348
while ( *p != '\0' && *p != '=' ) {
352
352
if ( ch == '%' ) {
353
353
/* Encoded, parse 2-digit hex value */
354
354
if ( !_parse_hex_value(&p, &ch) ) {
374
if ( parser->max_headers > -1 &&
374
if ( parser->max_headers > -1 &&
375
375
(int)header_count >= parser->max_headers ) {
376
376
/* Refuse to accept more headers than allowed by policy */
377
377
if ( (int)header_count == parser->max_headers ) {
378
378
uri_mailto_warning(parser, "more than the maximum %u headers specified; "
379
379
"rest is discarded", parser->max_headers);
382
382
hname_type = _HNAME_IGNORED;
384
384
/* Add new header field to array and assign its name */
386
386
field_name = str_c(field);
387
387
if ( strcasecmp(field_name, "to") == 0 )
388
388
hname_type = _HNAME_TO;
398
398
hdrf = array_append_space(&uri->headers);
399
399
hdrf->name = p_strdup(parser->pool, field_name);
401
uri_mailto_warning(parser,
401
uri_mailto_warning(parser,
402
402
"ignored duplicate for unique header field '%s'",
403
403
str_sanitize(field_name, 32));
404
404
hname_type = _HNAME_IGNORED;
412
412
hname_type = _HNAME_IGNORED;
418
418
/* Reset for field body */
419
419
str_truncate(field, 0);
421
/* Parse field body */
421
/* Parse field body */
422
422
while ( *p != '\0' && *p != '&' ) {
426
426
if ( ch == '%' ) {
427
427
/* Encoded, parse 2-digit hex value */
428
428
if ( !_parse_hex_value(&p, &ch) ) {
437
437
str_append_c(field, ch);
439
439
if ( *p != '\0' ) p++;
441
441
/* Verify field body */
442
442
if ( hname_type == _HNAME_BODY ) {
443
// FIXME: verify body ...
443
// FIXME: verify body ...
445
445
if ( !rfc2822_header_field_body_verify
446
446
(str_c(field), str_len(field), FALSE, FALSE) ) {
478
478
if ( uri != NULL ) {
479
479
if ( uri->body == NULL )
480
480
uri->body = p_strdup(parser->pool, str_c(field));
482
482
uri_mailto_warning(parser, "ignored duplicate body field");
485
485
case _HNAME_GENERIC:
486
if ( uri != NULL && hdrf != NULL )
486
if ( uri != NULL && hdrf != NULL )
487
487
hdrf->body = p_strdup(parser->pool, str_c(field));
491
491
/* Reset for next name */
492
492
str_truncate(field, 0);
496
496
if ( *p != '\0' ) p++;
503
503
(struct uri_mailto_parser *parser, const char *uri_body)
505
505
const char *p = uri_body;
508
508
* mailtoURI = "mailto:" [ to ] [ hfields ]
509
509
* to = [ addr-spec *("%2C" addr-spec ) ]
510
510
* hfields = "?" hfield *( "&" hfield )
518
518
* / "+" / "," / ";" / ":" / "@"
521
* tqchar ~= <qchar> without ";" and ":"
521
* tqchar ~= <qchar> without ";" and ":"
523
523
* Scheme 'mailto:' already parsed, starting parse after colon
529
529
if ( !uri_mailto_parse_recipients(parser, &p) )
532
/* Extract hfield items */
534
while ( *p != '\0' ) {
532
/* Extract hfield items */
534
while ( *p != '\0' ) {
535
535
/* Extract hfield item by searching for '&' and decoding '%' items */
536
536
if ( !uri_mailto_parse_headers(parser, &p) )
547
547
bool uri_mailto_validate
548
(const char *uri_body, const char **reserved_headers,
549
const char **unique_headers, int max_recipients, int max_headers,
548
(const char *uri_body, const char **reserved_headers,
549
const char **unique_headers, int max_recipients, int max_headers,
550
550
struct sieve_error_handler *ehandler)
552
552
struct uri_mailto_parser parser;
557
557
parser.max_headers = max_headers;
558
558
parser.reserved_headers = reserved_headers;
559
559
parser.unique_headers = unique_headers;
561
561
/* If no errors are reported, we don't need to record any data */
562
if ( ehandler != NULL ) {
562
if ( ehandler != NULL ) {
563
563
parser.pool = pool_datastack_create();
565
565
parser.uri = p_new(parser.pool, struct uri_mailto, 1);
566
566
p_array_init(&parser.uri->recipients, parser.pool, max_recipients);
567
567
p_array_init(&parser.uri->headers, parser.pool, max_headers);
570
570
if ( !uri_mailto_parse_uri(&parser, uri_body) )
573
573
if ( ehandler != NULL ) {
574
574
if ( array_count(&parser.uri->recipients) == 0 )
575
575
uri_mailto_warning(&parser, "notification URI specifies no recipients");
585
585
struct uri_mailto *uri_mailto_parse
586
(const char *uri_body, pool_t pool, const char **reserved_headers,
587
const char **unique_headers, int max_recipients, int max_headers,
586
(const char *uri_body, pool_t pool, const char **reserved_headers,
587
const char **unique_headers, int max_recipients, int max_headers,
588
588
struct sieve_error_handler *ehandler)
590
590
struct uri_mailto_parser parser;
592
592
parser.pool = pool;
593
593
parser.ehandler = ehandler;
594
594
parser.max_recipients = max_recipients;
599
599
parser.uri = p_new(pool, struct uri_mailto, 1);
600
600
p_array_init(&parser.uri->recipients, pool, max_recipients);
601
601
p_array_init(&parser.uri->headers, pool, max_headers);
603
603
if ( !uri_mailto_parse_uri(&parser, uri_body) )
606
606
if ( ehandler != NULL ) {
607
607
if ( array_count(&parser.uri->recipients) == 0 )
608
608
uri_mailto_warning(&parser, "notification URI specifies no recipients");