Mercurial > dovecot > core-2.2
changeset 15045:345e96ca65a2
Added support for creating IMAP URLs.
author | Stephan Bosch <stephan@rename-it.nl> |
---|---|
date | Fri, 14 Sep 2012 21:02:23 +0300 |
parents | 460db6251639 |
children | 584681a1845c |
files | src/lib-imap/imap-url.c src/lib-imap/imap-url.h src/lib-imap/test-imap-url.c src/lib/uri-util.c src/lib/uri-util.h |
diffstat | 5 files changed, 325 insertions(+), 13 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-imap/imap-url.c Thu Sep 13 17:12:59 2012 +0300 +++ b/src/lib-imap/imap-url.c Fri Sep 14 21:02:23 2012 +0300 @@ -689,6 +689,8 @@ /* [ipartial] */ if (*segment != NULL && strncasecmp(*segment, ";PARTIAL=", 9) == 0) { + have_partial = TRUE; + /* ";PARTIAL=" partial-range */ value = (*segment) + 9; if ((p = strchr(value,';')) != NULL) { @@ -754,6 +756,7 @@ url->uid = uid; if (section != NULL) url->section = p_strdup(parser->pool, str_c(section)); + url->have_partial = have_partial; url->partial_offset = partial_offset; url->partial_size = partial_size; } @@ -925,6 +928,96 @@ * IMAP URL construction */ +static void +imap_url_append_mailbox(const struct imap_url *url, string_t *urlstr) +{ + uri_append_path_data(urlstr, ";", url->mailbox); + if (url->uidvalidity != 0) + str_printfa(urlstr, ";UIDVALIDITY=%u", url->uidvalidity); + if (url->uid == 0) { + /* message list */ + if (url->search_program != NULL) { + str_append_c(urlstr, '?'); + uri_append_query_data(urlstr, ";", url->search_program); + } + } else { + /* message part */ + str_printfa(urlstr, "/;UID=%u", url->uid); + if (url->section != NULL) { + str_append(urlstr, "/;SECTION="); + uri_append_path_data(urlstr, ";", url->section); + } + if (url->have_partial) { + str_append(urlstr, "/;PARTIAL="); + if (url->partial_size == 0) { + str_printfa(urlstr, "%"PRIuUOFF_T, + url->partial_offset); + } else { + str_printfa(urlstr, "%"PRIuUOFF_T".%"PRIuUOFF_T, + url->partial_offset, + url->partial_size); + } + } + + /* urlauth */ + if (url->uauth_access_application != NULL) { + if (url->uauth_expire != (time_t)-1) { + str_append(urlstr, ";EXPIRE="); + str_append(urlstr, iso8601_date_create(url->uauth_expire)); + } + str_append(urlstr, ";URLAUTH="); + str_append(urlstr, url->uauth_access_application); + if (url->uauth_access_user != NULL) { + str_append_c(urlstr, '+'); + uri_append_user_data(urlstr, ";", + url->uauth_access_user); + } + } + } +} + +const char *imap_url_create(const struct imap_url *url) +{ + string_t *urlstr = t_str_new(512); + + /* scheme */ + uri_append_scheme(urlstr, "imap"); + str_append(urlstr, "//"); + + /* user */ + if (url->userid != NULL || url->auth_type != NULL) { + if (url->userid != NULL) + uri_append_user_data(urlstr, ";", url->userid); + if (url->auth_type != NULL) { + str_append(urlstr, ";AUTH="); + uri_append_user_data(urlstr, ";", url->auth_type); + } + str_append_c(urlstr, '@'); + } + + /* server */ + if (url->host_name != NULL) { + /* assume IPv6 literal if starts with '['; avoid encoding */ + if (*url->host_name == '[') + str_append(urlstr, url->host_name); + else + uri_append_host_name(urlstr, url->host_name); + } else if (url->have_host_ip) { + uri_append_host_ip(urlstr, &url->host_ip); + } else + i_unreached(); + if (url->have_port) + uri_append_port(urlstr, url->port); + + /* Older syntax (RFC 2192) requires this slash at all times */ + str_append_c(urlstr, '/'); + + /* mailbox */ + if (url->mailbox != NULL) + imap_url_append_mailbox(url, urlstr); + return str_c(urlstr); +} + const char * imap_url_add_urlauth(const char *rumpurl, const char *mechanism, const unsigned char *token, size_t token_len)
--- a/src/lib-imap/imap-url.h Thu Sep 13 17:12:59 2012 +0300 +++ b/src/lib-imap/imap-url.h Fri Sep 14 21:02:23 2012 +0300 @@ -19,7 +19,7 @@ uint32_t uid; const char *section; uoff_t partial_offset; - uoff_t partial_size; + uoff_t partial_size; /* 0 if not set */ /* message list (uid == 0) */ const char *search_program; @@ -31,9 +31,9 @@ const char *uauth_mechanism; const unsigned char *uauth_token; size_t uauth_token_size; - time_t uauth_expire; + time_t uauth_expire; /* (time_t)-1 if not set */ - unsigned int have_host_ip:1; + unsigned int have_host_ip:1; /* url uses IP address */ unsigned int have_port:1; unsigned int have_partial:1; }; @@ -64,6 +64,8 @@ * IMAP URL construction */ +const char *imap_url_create(const struct imap_url *url); + const char *imap_url_add_urlauth(const char *rumpurl, const char *mechanism, const unsigned char *token, size_t token_len);
--- a/src/lib-imap/test-imap-url.c Thu Sep 13 17:12:59 2012 +0300 +++ b/src/lib-imap/test-imap-url.c Fri Sep 14 21:02:23 2012 +0300 @@ -53,12 +53,14 @@ .host_name = "127.0.0.1", .userid = "user", .have_host_ip = TRUE } +#ifdef HAVE_IPV6 },{ .url = "imap://user@[::1]", .url_parsed = { .host_name = "[::1]", .userid = "user", .have_host_ip = TRUE } +#endif },{ .url = "imap://user@4example.com:423", .url_parsed = { @@ -738,8 +740,10 @@ .url = "imap://[]/INBOX" },{ .url = "imap://[v08.234:232:234:234:2221]/INBOX" +#ifdef HAVE_IPV6 },{ .url = "imap://[1::34a:34:234::6]/INBOX" +#endif },{ .url = "imap://example%a.com/INBOX" },{ @@ -891,11 +895,74 @@ } +const char *parse_create_url_tests[] = { + "imap://host.example.com/", + "imap://[::1]/", + "imap://10.0.0.1/", + "imap://user@host.example.com/", + "imap://user@host.example.com:993/", + "imap://user;AUTH=PLAIN@host.example.com/", + "imap://user;AUTH=PLAIN@host.example.com/INBOX", + "imap://user;AUTH=PLAIN@host.example.com/INBOX/;UID=5", + "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5", + "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" + "/;SECTION=TEXT", + "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" + "/;SECTION=TEXT/;PARTIAL=1", + "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" + "/;SECTION=TEXT/;PARTIAL=1.14", + "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" + "/;SECTION=TEXT/;PARTIAL=1.14;URLAUTH=anonymous", + "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" + "/;SECTION=TEXT/;PARTIAL=1.14;URLAUTH=user+username", + "imap://user;AUTH=PLAIN@host.example.com/INBOX/?SUBJECT%20%22Frop?%22", + "imap://host.%23example.com/", + "imap://user%3ba@host.example.com/", + "imap://user%40example.com@host.example.com/", + "imap://user%40example.com;AUTH=STR%23ANGE@host.example.com/", + "imap://user;AUTH=PLAIN@host.example.com/INBOX/Important%3bWork", + "imap://user@host.example.com/%23shared/news", + "imap://user@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" + "/;SECTION=HEADER.FIELDS%20(DATE%20FROM)", + "imap://user@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" + "/;SECTION=TEXT/;PARTIAL=1.14;URLAUTH=user+user%3bname", +}; + +unsigned int parse_create_url_test_count = N_ELEMENTS(parse_create_url_tests); + +static void test_imap_url_parse_create(void) +{ + unsigned int i; + + for (i = 0; i < parse_create_url_test_count; i++) T_BEGIN { + const char *url = parse_create_url_tests[i]; + struct imap_url *urlp; + const char *error = NULL; + + test_begin(t_strdup_printf("imap url parse/create [%d]", i)); + + if (imap_url_parse + (url, NULL, IMAP_URL_PARSE_ALLOW_URLAUTH, &urlp, &error) < 0) + urlp = NULL; + test_out_reason(t_strdup_printf("parse %s", url), urlp != NULL, error); + if (urlp != NULL) { + const char *urlnew = imap_url_create(urlp); + test_out(t_strdup_printf + ("create %s", urlnew), strcmp(url, urlnew) == 0); + } + + test_end(); + } T_END; + +} + + int main(void) { static void (*test_functions[])(void) = { test_imap_url_valid, test_imap_url_invalid, + test_imap_url_parse_create, NULL }; return test_run(test_functions);
--- a/src/lib/uri-util.c Thu Sep 13 17:12:59 2012 +0300 +++ b/src/lib/uri-util.c Fri Sep 14 21:02:23 2012 +0300 @@ -50,16 +50,25 @@ * / "*" / "+" / "," / ";" / "=" [bit1] * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" [bit2] * pchar = unreserved / sub-delims / ":" / "@" [bit0|bit1|bit3] + * 'pfchar' = unreserved / sub-delims / ":" / "@" / "/" + * [bit0|bit1|bit3|bit5] * 'uchar' = unreserved / sub-delims / ":" [bit0|bit1|bit4] - * 'fchar' = pchar / "/" / "?" [bit0|bit1|bit3|bit5] + * 'qchar' = pchar / "/" / "?" [bit0|bit1|bit3|bit5|bit6] * */ +#define CHAR_MASK_UNRESERVED (1<<0) +#define CHAR_MASK_SUB_DELIMS (1<<1) +#define CHAR_MASK_PCHAR ((1<<0)|(1<<1)|(1<<3)) +#define CHAR_MASK_PFCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)) +#define CHAR_MASK_UCHAR ((1<<0)|(1<<1)|(1<<4)) +#define CHAR_MASK_QCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<6)) + static unsigned const char _uri_char_lookup[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 0, 2, 0, 4, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, 36, // 20 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 2, 0, 2, 0, 36, // 30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 2, 0, 2, 0, 68, // 30 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 4, 0, 1, // 50 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 @@ -133,7 +142,7 @@ if ((*parser->cur & 0x80) != 0) return 0; - if (_uri_char_lookup[*parser->cur] & 0x01) { + if ((_uri_char_lookup[*parser->cur] & CHAR_MASK_UNRESERVED) != 0) { *ch_r = *parser->cur; parser->cur++; return 1; @@ -344,7 +353,7 @@ /* sub-delims */ c = *parser->cur; - if ((c & 0x80) == 0 && (_uri_char_lookup[c] & 0x02) != 0) { + if ((c & 0x80) == 0 && (_uri_char_lookup[c] & CHAR_MASK_SUB_DELIMS) != 0) { if (reg_name != NULL) str_append_c(reg_name, *parser->cur); parser->cur++; @@ -384,7 +393,7 @@ } if (literal != NULL) - str_append_n(literal, parser->cur, parser->end-parser->cur+1); + str_append_n(literal, parser->cur, p-parser->cur+1); address = t_strdup_until(parser->cur+1, p); parser->cur = p + 1; @@ -526,7 +535,7 @@ break; /* break at first delimiter */ - if (*p != '%' && (_uri_char_lookup[*p] & 0x13) == 0) + if (*p != '%' && (_uri_char_lookup[*p] & CHAR_MASK_UCHAR) == 0) break; } @@ -567,7 +576,7 @@ continue; } - if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & 0x0B) == 0) + if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_PCHAR) == 0) break; p++; @@ -658,7 +667,7 @@ continue; } - if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & 0x2B) == 0) + if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_QCHAR) == 0) break; p++; } @@ -690,7 +699,7 @@ continue; } - if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & 0x2B) == 0) + if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_QCHAR) == 0) break; p++; } @@ -718,3 +727,122 @@ str_truncate(parser->tmpbuf, 0); return parser->tmpbuf; } + +/* + * Generic URI construction + */ + +static void +uri_data_encode(string_t *out, const unsigned char esc_table[256], + unsigned char esc_mask, const char *esc_extra, const char *data) +{ + const unsigned char *p = (const unsigned char *)data; + + while (*p != '\0') { + if ((*p & 0x80) != 0 || (esc_table[*p] & esc_mask) == 0 || + strchr(esc_extra, (char)*p) != NULL) { + str_printfa(out, "%%%2x", *p); + } else { + str_append_c(out, *p); + } + p++; + } +} + +void uri_append_scheme(string_t *out, const char *scheme) +{ + str_append(out, scheme); + str_append_c(out, ':'); +} + +void uri_append_user_data(string_t *out, const char *esc, + const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_UCHAR, esc, data); +} + +void uri_append_userinfo(string_t *out, const char *userinfo) +{ + uri_append_user_data(out, "", userinfo); + str_append_c(out, '@'); +} + +void uri_append_host_name(string_t *out, const char *name) +{ + uri_data_encode(out, _uri_char_lookup, + CHAR_MASK_UNRESERVED | CHAR_MASK_SUB_DELIMS, "", name); +} + +void uri_append_host_ip(string_t *out, const struct ip_addr *host_ip) +{ + const char *addr = net_ip2addr(host_ip); + + i_assert(addr != NULL); + + if (host_ip->family == AF_INET) { + str_append(out, addr); + return; + } + + i_assert(host_ip->family == AF_INET6); + str_append_c(out, '['); + str_append(out, addr); + str_append_c(out, ']'); +} + +void uri_append_port(string_t *out, in_port_t port) +{ + str_printfa(out, ":%u", port); +} + +void uri_append_path_segment_data(string_t *out, const char *esc, + const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PCHAR, esc, data); +} + +void uri_append_path_segment(string_t *out, const char *segment) +{ + str_append_c(out, '/'); + if (*segment != '\0') + uri_append_path_data(out, "", segment); +} + +void uri_append_path_data(string_t *out, const char *esc, + const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PFCHAR, esc, data); +} + +void uri_append_path(string_t *out, const char *path) +{ + str_append_c(out, '/'); + if (*path != '\0') + uri_append_path_data(out, "", path); +} + +void uri_append_query_data(string_t *out, const char *esc, + const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data); +} + +void uri_append_query(string_t *out, const char *query) +{ + str_append_c(out, '?'); + if (*query != '\0') + uri_append_query_data(out, "", query); +} + +void uri_append_fragment_data(string_t *out, const char *esc, + const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data); +} + +void uri_append_fragment(string_t *out, const char *fragment) +{ + str_append_c(out, '#'); + if (*fragment != '\0') + uri_append_fragment_data(out, "", fragment); +}
--- a/src/lib/uri-util.h Thu Sep 13 17:12:59 2012 +0300 +++ b/src/lib/uri-util.h Fri Sep 14 21:02:23 2012 +0300 @@ -45,5 +45,27 @@ void uri_parser_init(struct uri_parser *parser, pool_t pool, const char *data); string_t *uri_parser_get_tmpbuf(struct uri_parser *parser, size_t size); +/* + * Generic URI construction + */ + +void uri_append_scheme(string_t *out, const char *scheme); + +void uri_append_user_data(string_t *out, const char *esc, const char *data); +void uri_append_userinfo(string_t *out, const char *userinfo); +void uri_append_host_name(string_t *out, const char *name); +void uri_append_host_ip(string_t *out, const struct ip_addr *host_ip); +void uri_append_port(string_t *out, in_port_t port); + +void uri_append_path_segment_data(string_t *out, const char *esc, const char *data); +void uri_append_path_segment(string_t *out, const char *segment); +void uri_append_path_data(string_t *out, const char *esc, const char *data); +void uri_append_path(string_t *out, const char *path); + +void uri_append_query_data(string_t *out, const char *esc, const char *data); +void uri_append_query(string_t *out, const char *query); + +void uri_append_fragment_data(string_t *out, const char *esc, const char *data); +void uri_append_fragment(string_t *out, const char *fragment); + #endif -