uri-util.c revision 3ec5378aadaa699c38b2e02be30aae1add36eb7c
2e37d45867d081db150ab78dad303b9077aea24fTimo Sirainen/* Copyright (c) 2010-2013 Dovecot authors, see the included COPYING file */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "lib.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "array.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "str.h"
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen#include "net.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "uri-util.h"
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include <ctype.h>
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen/*
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen * Generic URI parsing.
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen *
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen * [URI-GEN] RFC3986 Appendix A:
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen *
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen * host = IP-literal / IPv4address / reg-name
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * port = *DIGIT
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * reg-name = *( unreserved / pct-encoded / sub-delims )
daa7e7459749ae8f82cd3eed9c44522d81c609a3Timo Sirainen * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
bbadd5331f534017cf62d5183003b3d9fdad079eTimo Sirainen * pct-encoded = "%" HEXDIG HEXDIG
373492be949e159fda651807b3acda2c5c077027Timo Sirainen * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
889437fa2b6f44ffe0a8a7bcac94c00b71856767Timo Sirainen * / "*" / "+" / "," / ";" / "="
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * IP-literal = "[" ( IPv6address / IPvFuture ) "]"
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * IPv6address = 6( h16 ":" ) ls32
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * / "::" 5( h16 ":" ) ls32
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * / [ h16 ] "::" 4( h16 ":" ) ls32
373492be949e159fda651807b3acda2c5c077027Timo Sirainen * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
bbadd5331f534017cf62d5183003b3d9fdad079eTimo Sirainen * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
bbadd5331f534017cf62d5183003b3d9fdad079eTimo Sirainen * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * / [ *4( h16 ":" ) h16 ] "::" ls32
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * / [ *5( h16 ":" ) h16 ] "::" h16
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * / [ *6( h16 ":" ) h16 ] "::"
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * h16 = 1*4HEXDIG
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * ls32 = ( h16 ":" h16 ) / IPv4address
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen * dec-octet = DIGIT ; 0-9
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * / %x31-39 DIGIT ; 10-99
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * / "1" 2DIGIT ; 100-199
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * / "2" %x30-34 DIGIT ; 200-249
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * / "25" %x30-35 ; 250-255
635df5b4cbcd7b24c825e01d9dd66d3a4274c4c7Timo Sirainen */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen#define URI_MAX_SCHEME_NAME_LEN 64
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen/* Character lookup table
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen *
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" [bit0]
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * / "*" / "+" / "," / ";" / "=" [bit1]
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" [bit2]
99430beb12dfbc6c9c160f08e2102aeab38a589cTimo Sirainen * pchar = unreserved / sub-delims / ":" / "@" [bit0|bit1|bit3]
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * 'pfchar' = unreserved / sub-delims / ":" / "@" / "/"
93a7d1ee4b518b5c85f9721dc6539e4dab6aae00Timo Sirainen * [bit0|bit1|bit3|bit5]
f7f25f9e1a38678d0e97d2e609beac16285fac6bTimo Sirainen * 'uchar' = unreserved / sub-delims / ":" [bit0|bit1|bit4]
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * 'qchar' = pchar / "/" / "?" [bit0|bit1|bit3|bit5|bit6]
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen *
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
35fcdde46a71ac151c2518d48c841019f1181bb2Timo Sirainen#define CHAR_MASK_UNRESERVED (1<<0)
35fcdde46a71ac151c2518d48c841019f1181bb2Timo Sirainen#define CHAR_MASK_SUB_DELIMS (1<<1)
35fcdde46a71ac151c2518d48c841019f1181bb2Timo Sirainen#define CHAR_MASK_PCHAR ((1<<0)|(1<<1)|(1<<3))
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#define CHAR_MASK_PFCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5))
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#define CHAR_MASK_UCHAR ((1<<0)|(1<<1)|(1<<4))
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#define CHAR_MASK_QCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<6))
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
635df5b4cbcd7b24c825e01d9dd66d3a4274c4c7Timo Sirainenstatic unsigned const char _uri_char_lookup[256] = {
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen 0, 2, 0, 4, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, 36, // 20
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 2, 0, 2, 0, 68, // 30
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 4, 0, 1, // 50
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 70
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen};
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstatic inline int _decode_hex_digit(const unsigned char digit)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen switch (digit) {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen case '0': case '1': case '2': case '3': case '4':
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen case '5': case '6': case '7': case '8': case '9':
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return digit - '0';
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return digit - 'a' + 0x0a;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return digit - 'A' + 0x0A;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen }
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return -1;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen}
ba8498efbf886ca8b69fdb20c0ba2f5dba9416e3Timo Sirainen
93a7d1ee4b518b5c85f9721dc6539e4dab6aae00Timo Sirainenstatic int ATTR_NULL(3)
f7f25f9e1a38678d0e97d2e609beac16285fac6bTimo Sirainenuri_parse_pct_encoded(struct uri_parser *parser, const unsigned char **p,
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen const unsigned char *pend, unsigned char *ch_r)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen int value;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (**p == 0 || *(*p+1) == 0 || (pend != NULL && *p+1 >= pend)) {
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen parser->error = "Unexpected URI boundary after '%'";
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen return -1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if ((value = _decode_hex_digit(**p)) < 0) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen parser->error = t_strdup_printf(
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen "Expecting hex digit after '%%', but found '%c'", **p);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return -1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen *ch_r = (value & 0x0f) << 4;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen *p += 1;
380dbb60ae291cbe39d1f710284562ca9167150bTimo Sirainen
380dbb60ae291cbe39d1f710284562ca9167150bTimo Sirainen if ((value = _decode_hex_digit(**p)) < 0) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen parser->error = t_strdup_printf(
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen "Expecting hex digit after '%%%c', but found '%c'", *((*p)-1), **p);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return -1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
1b823b2b7790a1e1b7974fcf11a4c48a28e70f37Timo Sirainen
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen *ch_r |= (value & 0x0f);
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen *p += 1;
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen if (*ch_r == '\0') {
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen parser->error =
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen "Percent encoding is not allowed to encode NUL character";
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen return -1;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen }
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen return 1;
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen}
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
1b823b2b7790a1e1b7974fcf11a4c48a28e70f37Timo Sirainenstatic int
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenuri_parse_unreserved_char(struct uri_parser *parser, unsigned char *ch_r)
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (*parser->cur == '%') {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen parser->cur++;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (uri_parse_pct_encoded(parser, &parser->cur,
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen parser->end, ch_r) <= 0)
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen return -1;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen return 1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if ((*parser->cur & 0x80) != 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if ((_uri_char_lookup[*parser->cur] & CHAR_MASK_UNRESERVED) != 0) {
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen *ch_r = *parser->cur;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen parser->cur++;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return 1;
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen }
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen return 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenint uri_parse_unreserved(struct uri_parser *parser, string_t *part)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
1b823b2b7790a1e1b7974fcf11a4c48a28e70f37Timo Sirainen int len = 0;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen while (parser->cur < parser->end) {
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen int ret;
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen unsigned char ch = 0;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen if ((ret = uri_parse_unreserved_char(parser, &ch)) < 0)
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen return -1;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
1b823b2b7790a1e1b7974fcf11a4c48a28e70f37Timo Sirainen if (ret == 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen break;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (part != NULL)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen str_append_c(part, ch);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen len++;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen }
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen return len > 0 ? 1 : 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
57d2429fae575e96ca276355af675deb66b76d00Timo Sirainenbool uri_data_decode(struct uri_parser *parser, const char *data,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char *until, const char **decoded_r)
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const unsigned char *p = (const unsigned char *)data;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const unsigned char *pend = (const unsigned char *)until;
57d2429fae575e96ca276355af675deb66b76d00Timo Sirainen string_t *decoded;
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen if (pend == NULL) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* NULL means unlimited; solely rely on '\0' */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen pend = (const unsigned char *)(size_t)-1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (p >= pend || *p == '\0') {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (decoded_r != NULL)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen *decoded_r = "";
61618d4c58080570f689614fec204ae14e90cef2Timo Sirainen return TRUE;
50e20db49f29917fe9adcf1b56b11badf28bd0e4Timo Sirainen }
50e20db49f29917fe9adcf1b56b11badf28bd0e4Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen decoded = uri_parser_get_tmpbuf(parser, 256);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen while (p < pend && *p != '\0') {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen unsigned char ch;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (*p == '%') {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen p++;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (uri_parse_pct_encoded(parser, &p, NULL, &ch) <= 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return FALSE;
50e20db49f29917fe9adcf1b56b11badf28bd0e4Timo Sirainen
a7f5035eebbd138a5436a2eb2ce1fa5fd3d269fbTimo Sirainen str_append_c(decoded, ch);
a7f5035eebbd138a5436a2eb2ce1fa5fd3d269fbTimo Sirainen } else {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen str_append_c(decoded, *p);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen p++;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
c6f894e1522f7b0b6068c228900914073c145175Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (decoded_r != NULL)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen *decoded_r = t_strdup(str_c(decoded));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return TRUE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenint uri_cut_scheme(const char **uri_p, const char **scheme_r)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char *p = *uri_p;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen size_t len = 1;
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen /* RFC 3968:
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen if (!i_isalpha(*p))
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return -1;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen p++;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen while (len < URI_MAX_SCHEME_NAME_LEN && *p != '\0') {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (!i_isalnum(*p) && *p != '+' && *p != '-' && *p != '.')
f2df3069766c747cbf020fea5d3a4261949064b0Timo Sirainen break;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen p++;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen len++;
062ea54b7775d0c92ed67b9b1f4d93fa8ec80c84Timo Sirainen }
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (*p != ':')
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return -1;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen *scheme_r = t_strdup_until(*uri_p, p);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen *uri_p = p + 1;
a7f5035eebbd138a5436a2eb2ce1fa5fd3d269fbTimo Sirainen return 0;
a7f5035eebbd138a5436a2eb2ce1fa5fd3d269fbTimo Sirainen}
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenint uri_parse_scheme(struct uri_parser *parser, const char **scheme_r)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen const char *p;
c6f894e1522f7b0b6068c228900914073c145175Timo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (parser->cur >= parser->end)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return 0;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen p = (const char *)parser->cur;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (uri_cut_scheme(&p, scheme_r) < 0)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return 0;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen parser->cur = (const unsigned char *)p;
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen return 1;
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic int
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainenuri_parse_dec_octet(struct uri_parser *parser, string_t *literal,
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen uint8_t *octet_r)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen uint8_t octet = 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen int count = 0;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen /* RFC 3986:
383d0e8c24451468d6bea17e4b55d74de744abe6Timo Sirainen *
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * dec-octet = DIGIT ; 0-9
383d0e8c24451468d6bea17e4b55d74de744abe6Timo Sirainen * / %x31-39 DIGIT ; 10-99
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen * / "1" 2DIGIT ; 100-199
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen * / "2" %x30-34 DIGIT ; 200-249
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen * / "25" %x30-35 ; 250-255
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen */
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainen while (parser->cur < parser->end && i_isdigit(*parser->cur)) {
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen uint8_t prev = octet;
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen octet = octet * 10 + (parser->cur[0] - '0');
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen if (octet < prev)
15f526e5ac611b4532568d131fcd0abf664abe41Timo Sirainen return -1;
15f526e5ac611b4532568d131fcd0abf664abe41Timo Sirainen
15f526e5ac611b4532568d131fcd0abf664abe41Timo Sirainen if (literal != NULL)
15f526e5ac611b4532568d131fcd0abf664abe41Timo Sirainen str_append_c(literal, *parser->cur);
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen
6bd263caf006edc75205f446fa0283c6f364941bTimo Sirainen parser->cur++;
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainen count++;
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainen }
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainen
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainen if (count > 0) {
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainen *octet_r = octet;
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainen return 1;
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainen }
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainen return 0;
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen}
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainenstatic int
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainenuri_parse_ipv4address(struct uri_parser *parser, string_t *literal,
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen struct in_addr *ip4_r)
0f62889d833767acf9c2ad010c3269806b4cfae3Timo Sirainen{
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen uint8_t octet;
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen uint32_t ip = 0;
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen int ret;
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen int i;
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen /* RFC 3986:
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen *
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen */
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0)
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen return ret;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen ip = octet;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen for (i = 0; i < 3 && parser->cur < parser->end; i++) {
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen if (*parser->cur != '.')
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen return -1;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen if (literal != NULL)
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen str_append_c(literal, '.');
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen parser->cur++;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0)
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen return -1;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen ip = (ip << 8) + octet;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen }
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen if (ip4_r != NULL)
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen ip4_r->s_addr = htonl(ip);
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen return 1;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen}
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainenstatic int uri_parse_reg_name(struct uri_parser *parser, string_t *reg_name)
272aca0a772140d3a45a425a3fd67854ae2ccec2Timo Sirainen{
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen int len = 0;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen /* RFC 3986:
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen *
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen * reg-name = *( unreserved / pct-encoded / sub-delims )
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen */
1d22eaac93de41319918a1fc6de42bb302e25c1aTimo Sirainen
1d22eaac93de41319918a1fc6de42bb302e25c1aTimo Sirainen while (parser->cur < parser->end) {
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen int ret;
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen unsigned char c;
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* unreserved / pct-encoded */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if ((ret = uri_parse_unreserved_char(parser, &c)) < 0)
2024157e8de36edd31f5fd72f5ea7364a0955fa7Timo Sirainen return -1;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (ret > 0) {
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen if (reg_name != NULL)
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen str_append_c(reg_name, c);
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen len++;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen continue;
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen }
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen /* sub-delims */
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen c = *parser->cur;
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen if ((c & 0x80) == 0 && (_uri_char_lookup[c] & CHAR_MASK_SUB_DELIMS) != 0) {
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen if (reg_name != NULL)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen str_append_c(reg_name, *parser->cur);
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen parser->cur++;
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen len++;
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen continue;
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen }
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen break;
5d60e31c7b701b606067a20bc88dcc8a6de7bbd6Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return len > 0 ? 1 : 0;
}
#ifdef HAVE_IPV6
static int
uri_parse_ip_literal(struct uri_parser *parser, string_t *literal,
struct in6_addr *ip6_r)
{
const unsigned char *p;
const char *address;
int ret;
/* IP-literal = "[" ( IPv6address / IPvFuture ) "]"
* IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
* IPv6address = ; Syntax not relevant: parsed using inet_pton()
*/
/* "[" already verified */
/* Scan for end of address */
for (p = parser->cur+1; p < parser->end; p++) {
if (*p == ']')
break;
}
if (p >= parser->end || *p != ']') {
parser->error = "Expecting ']' at end of IP-literal";
return -1;
}
if (literal != NULL)
str_append_n(literal, parser->cur, p-parser->cur+1);
address = t_strdup_until(parser->cur+1, p);
parser->cur = p + 1;
if (*address == '\0') {
parser->error = "Empty IPv6 host address";
return -1;
}
if (*address == 'v') {
parser->error = t_strdup_printf(
"Future IP host address '%s' not supported", address);
return -1;
}
if ((ret = inet_pton(AF_INET6, address, ip6_r)) <= 0) {
parser->error = t_strdup_printf(
"Invalid IPv6 host address '%s'", address);
return -1;
}
return 1;
}
#endif
static int uri_parse_host(struct uri_parser *parser, struct uri_authority *auth)
{
const unsigned char *preserve;
struct in_addr ip4;
struct in6_addr ip6;
string_t *literal = NULL;
int ret;
/* RFC 3986:
*
* host = IP-literal / IPv4address / reg-name
*/
literal = uri_parser_get_tmpbuf(parser, 256);
/* IP-literal / */
if (parser->cur < parser->end && *parser->cur == '[') {
#ifdef HAVE_IPV6
if ((ret=uri_parse_ip_literal(parser, literal, &ip6)) <= 0)
return -1;
if (auth != NULL) {
auth->host_literal = t_strdup(str_c(literal));
auth->host_ip.family = AF_INET6;
auth->host_ip.u.ip6 = ip6;
auth->have_host_ip = TRUE;
}
return 1;
#else
parser->error = "IPv6 host address is not supported";
return -1;
#endif
}
/* IPv4address /
*
* If it fails to parse, we try to parse it as a reg-name
*/
preserve = parser->cur;
if ((ret = uri_parse_ipv4address(parser, literal, &ip4)) > 0) {
if (auth != NULL) {
auth->host_literal = t_strdup(str_c(literal));
auth->host_ip.family = AF_INET;
auth->host_ip.u.ip4 = ip4;
auth->have_host_ip = TRUE;
}
return ret;
}
parser->cur = preserve;
str_truncate(literal, 0);
/* reg-name */
if ((ret = uri_parse_reg_name(parser, literal)) != 0) {
if (ret > 0 && auth != NULL) {
auth->host_literal = t_strdup(str_c(literal));
auth->have_host_ip = FALSE;
}
return ret;
}
return 0;
}
static int uri_parse_port(struct uri_parser *parser, struct uri_authority *auth)
{
in_port_t port = 0;
int count = 0;
/* RFC 3986:
*
* port = *DIGIT
*/
while (parser->cur < parser->end && i_isdigit(*parser->cur)) {
in_port_t prev = port;
port = port * 10 + (in_port_t)(parser->cur[0] - '0');
if (port < prev) {
parser->error = "Port number is too high";
return -1;
}
parser->cur++;
count++;
}
if (count > 0) {
if (auth != NULL) {
auth->port = port;
auth->have_port = TRUE;
}
return 1;
}
return 0;
}
int uri_parse_authority(struct uri_parser *parser,
struct uri_authority *auth)
{
const unsigned char *p;
int ret;
/*
* authority = [ userinfo "@" ] host [ ":" port ]
*/
if (auth != NULL)
memset(auth, 0, sizeof(*auth));
/* Scan ahead to check whether there is a [userinfo "@"] uri component */
for (p = parser->cur; p < parser->end; p++){
/* refuse 8bit characters */
if ((*p & 0x80) != 0)
break;
/* break at first delimiter */
if (*p != '%' && (_uri_char_lookup[*p] & CHAR_MASK_UCHAR) == 0)
break;
}
/* Extract userinfo */
if (p < parser->end && *p == '@') {
if (auth != NULL)
auth->enc_userinfo = t_strdup_until(parser->cur, p);
parser->cur = p+1;
}
/* host */
if ((ret = uri_parse_host(parser, auth)) <= 0) {
if (ret == 0) {
parser->error = "Missing 'host' component";
return -1;
}
return ret;
}
/* [":" ... */
if (parser->cur >= parser->end || *parser->cur != ':')
return 1;
parser->cur++;
/* ... port] */
if ((ret = uri_parse_port(parser, auth)) < 0)
return ret;
return 1;
}
int uri_parse_slashslash_authority(struct uri_parser *parser,
struct uri_authority *auth)
{
/* "//" authority */
if ((parser->end - parser->cur) <= 2 || parser->cur[0] != '/' ||
parser->cur[1] != '/')
return 0;
parser->cur += 2;
return uri_parse_authority(parser, auth);
}
int uri_parse_path_segment(struct uri_parser *parser, const char **segment_r)
{
const unsigned char *p = parser->cur;
while (p < parser->end) {
if (*p == '%') {
p++;
continue;
}
if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_PCHAR) == 0)
break;
p++;
}
if (p == parser->cur)
return 0;
if (segment_r != NULL)
*segment_r = t_strdup_until(parser->cur, p);
parser->cur = p;
return 1;
}
int uri_parse_path(struct uri_parser *parser,
int *relative_r, const char *const **path_r)
{
const unsigned char *pbegin = parser->cur;
ARRAY_TYPE(const_string) segments;
const char *segment = NULL;
unsigned int count;
int relative = 1;
int ret;
t_array_init(&segments, 16);
/* check for a leading '/' and indicate absolute path
when it is present
*/
if (parser->cur < parser->end && *parser->cur == '/') {
parser->cur++;
relative = 0;
}
/* parse first segment */
if ((ret = uri_parse_path_segment(parser, &segment)) < 0)
return -1;
for (;;) {
if (ret > 0) {
/* strip dot segments */
if (segment[0] == '.') {
if (segment[1] == '.') {
if (segment[2] == '\0') {
/* '..' -> skip and... */
segment = NULL;
/* ... pop last segment (if any) */
count = array_count(&segments);
if (count > 0) {
array_delete(&segments, count-1, 1);
} else if ( relative > 0 ) {
relative++;
}
}
} else if (segment[1] == '\0') {
/* '.' -> skip */
segment = NULL;
}
}
} else {
segment = "";
}
if (segment != NULL)
array_append(&segments, &segment, 1);
if (parser->cur >= parser->end || *parser->cur != '/')
break;
parser->cur++;
/* parse next path segment */
if ((ret = uri_parse_path_segment(parser, &segment)) < 0)
return -1;
}
if (parser->cur == pbegin) {
/* path part of URI is missing */
return 0;
}
/* special treatment for a trailing '..' or '.' */
if (segment == NULL) {
segment = "";
array_append(&segments, &segment, 1);
}
array_append_zero(&segments);
*path_r = array_get(&segments, &count);
*relative_r = relative;
return 1;
}
int uri_parse_query(struct uri_parser *parser, const char **query_r)
{
const unsigned char *p = parser->cur;
/* RFC 3986:
*
* URI = { ... } [ "?" query ] { ... }
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
*/
if (p >= parser->end || *p != '?')
return 0;
p++;
while (p < parser->end) {
if (*p == '%') {
p++;
continue;
}
if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_QCHAR) == 0)
break;
p++;
}
if (query_r != NULL)
*query_r = t_strdup_until(parser->cur+1, p);
parser->cur = p;
return 1;
}
int uri_parse_fragment(struct uri_parser *parser, const char **fragment_r)
{
const unsigned char *p = parser->cur;
/* RFC 3986:
*
* URI = { ... } [ "#" fragment ]
* fragment = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
*/
if (p >= parser->end || *p != '#')
return 0;
p++;
while (p < parser->end) {
if (*p == '%') {
p++;
continue;
}
if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_QCHAR) == 0)
break;
p++;
}
if (fragment_r != NULL)
*fragment_r = t_strdup_until(parser->cur+1, p);
parser->cur = p;
return 1;
}
void uri_parser_init(struct uri_parser *parser, pool_t pool, const char *data)
{
parser->pool = pool;
parser->begin = parser->cur = (unsigned char *)data;
parser->end = (unsigned char *)data + strlen(data);
parser->error = NULL;
parser->tmpbuf = NULL;
}
string_t *uri_parser_get_tmpbuf(struct uri_parser *parser, size_t size)
{
if (parser->tmpbuf == NULL)
parser->tmpbuf = t_str_new(size);
else
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, "%%%02x", *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);
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);
}