2 * Copyright (C) 2000-2002,2004 Thomas Roessler <roessler@does-not-exist.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * A simple URL parser.
36 static const struct mapping_t UrlMap[] =
43 { "mailto", U_MAILTO },
49 static int url_pct_decode (char *s)
61 isxdigit ((unsigned char) s[1]) &&
62 isxdigit ((unsigned char) s[2]) &&
63 hexval (s[1]) >= 0 && hexval (s[2]) >= 0)
65 *d++ = (hexval (s[1]) << 4) | (hexval (s[2]));
78 url_scheme_t url_check_scheme (const char *s)
84 if (!s || !(t = strchr (s, ':')))
86 if ((size_t)(t - s) >= sizeof (sbuf) - 1)
89 strfcpy (sbuf, s, t - s + 1);
90 for (t = sbuf; *t; t++)
91 *t = ascii_tolower (*t);
93 if ((i = mutt_getvaluebyname (sbuf, UrlMap)) == -1)
96 return (url_scheme_t) i;
99 int url_parse_file (char *d, const char *src, size_t dl)
101 if (ascii_strncasecmp (src, "file:", 5))
103 else if (!ascii_strncasecmp (src, "file://", 7)) /* we don't support remote files */
106 strfcpy (d, src + 5, dl);
108 return url_pct_decode (d);
111 /* ciss_parse_userhost: fill in components of ciss with info from src. Note
112 * these are pointers into src, which is altered with '\0's. Port of 0
113 * means no port given. */
114 static int ciss_parse_userhost (ciss_url_t *ciss, char *src)
123 if (strncmp (src, "//", 2) != 0)
126 return url_pct_decode (ciss->path);
131 if ((ciss->path = strchr (src, '/')))
132 *ciss->path++ = '\0';
134 if ((t = strrchr (src, '@')))
137 if ((p = strchr (src, ':')))
141 if (url_pct_decode (ciss->pass) < 0)
145 if (url_pct_decode (ciss->user) < 0)
150 /* IPv6 literal address. It may contain colons, so set t to start
151 * the port scan after it.
153 if ((*src == '[') && (t = strchr (src, ']')))
161 if ((p = strchr (t, ':')))
165 if (mutt_atoi (p, &t) < 0 || t < 0 || t > 0xffff)
167 ciss->port = (unsigned short)t;
173 return url_pct_decode (ciss->host) >= 0 &&
174 (!ciss->path || url_pct_decode (ciss->path) >= 0) ? 0 : -1;
177 /* url_parse_ciss: Fill in ciss_url_t. char* elements are pointers into src,
178 * which is modified by this call (duplicate it first if you need to). */
179 int url_parse_ciss (ciss_url_t *ciss, char *src)
183 if ((ciss->scheme = url_check_scheme (src)) == U_UNKNOWN)
186 tmp = strchr (src, ':') + 1;
188 return ciss_parse_userhost (ciss, tmp);
191 static void url_pct_encode (char *dst, size_t l, const char *src)
193 static const char *alph = "0123456789ABCDEF";
197 while (src && *src && l)
199 if (strchr ("/:%", *src))
205 *dst++ = alph[(*src >> 4) & 0xf];
206 *dst++ = alph[*src & 0xf];
217 int url_ciss_tostring (ciss_url_t* ciss, char* dest, size_t len, int flags)
222 dest_buf = mutt_buffer_pool_get ();
224 retval = url_ciss_tobuffer (ciss, dest_buf, flags);
226 strfcpy (dest, mutt_b2s (dest_buf), len);
228 mutt_buffer_pool_release (&dest_buf);
233 /* url_ciss_tobuffer: output the URL string for a given CISS object. */
234 int url_ciss_tobuffer (ciss_url_t* ciss, BUFFER* dest, int flags)
236 if (ciss->scheme == U_UNKNOWN)
239 mutt_buffer_printf (dest, "%s:", mutt_getnamebyvalue (ciss->scheme, UrlMap));
243 if (!(flags & U_PATH))
244 mutt_buffer_addstr (dest, "//");
249 url_pct_encode (u, sizeof (u), ciss->user);
251 if (flags & U_DECODE_PASSWD && ciss->pass)
254 url_pct_encode (p, sizeof (p), ciss->pass);
255 mutt_buffer_add_printf (dest, "%s:%s@", u, p);
258 mutt_buffer_add_printf (dest, "%s@", u);
261 if (strchr (ciss->host, ':'))
262 mutt_buffer_add_printf (dest, "[%s]", ciss->host);
264 mutt_buffer_add_printf (dest, "%s", ciss->host);
267 mutt_buffer_add_printf (dest, ":%hu/", ciss->port);
269 mutt_buffer_addstr (dest, "/");
273 mutt_buffer_addstr (dest, ciss->path);
278 int url_parse_mailto (ENVELOPE *e, char **body, const char *src)
289 if (!(t = strchr (src, ':')))
292 /* copy string for safe use of strtok() */
293 if ((tmp = safe_strdup (t + 1)) == NULL)
296 if ((headers = strchr (tmp, '?')))
299 if (url_pct_decode (tmp) < 0)
302 e->to = rfc822_parse_adrlist (e->to, tmp);
304 tag = headers ? strtok_r (headers, "&", &p) : NULL;
306 for (; tag; tag = strtok_r (NULL, "&", &p))
308 if ((value = strchr (tag, '=')))
310 if (!value || !*value)
313 if (url_pct_decode (tag) < 0)
315 if (url_pct_decode (value) < 0)
318 /* Determine if this header field is on the allowed list. Since Mutt
319 * interprets some header fields specially (such as
320 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
321 * only safe fields are allowed.
323 * RFC2368, "4. Unsafe headers"
324 * The user agent interpreting a mailto URL SHOULD choose not to create
325 * a message if any of the headers are considered dangerous; it may also
326 * choose to create a message with only a subset of the headers given in
329 if (mutt_matches_ignore(tag, MailtoAllow))
331 if (!ascii_strcasecmp (tag, "body"))
334 mutt_str_replace (body, value);
339 size_t taglen = mutt_strlen (tag);
341 safe_asprintf (&scratch, "%s: %s", tag, value);
342 scratch[taglen] = 0; /* overwrite the colon as mutt_parse_rfc822_line expects */
343 value = skip_email_wsp(&scratch[taglen + 1]);
344 mutt_parse_rfc822_line (e, NULL, scratch, value, 1, 0, 1, &last);
350 /* RFC2047 decode after the RFC822 parsing */
351 rfc2047_decode_envelope (e);