mutt stable branch with some hacks
1/*
2 * Copyright (C) 2000-2002,2004 Thomas Roessler <roessler@does-not-exist.org>
3 *
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.
8 *
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.
13 *
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.
17 */
18
19/*
20 * A simple URL parser.
21 */
22
23#if HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#include "mutt.h"
28#include "mapping.h"
29#include "url.h"
30
31#include "mime.h"
32#include "rfc2047.h"
33
34#include <ctype.h>
35
36static const struct mapping_t UrlMap[] =
37{
38 { "file", U_FILE },
39 { "imap", U_IMAP },
40 { "imaps", U_IMAPS },
41 { "pop", U_POP },
42 { "pops", U_POPS },
43 { "mailto", U_MAILTO },
44 { "smtp", U_SMTP },
45 { "smtps", U_SMTPS },
46 { NULL, U_UNKNOWN }
47};
48
49static int url_pct_decode (char *s)
50{
51 char *d;
52
53 if (!s)
54 return -1;
55
56 for (d = s; *s; s++)
57 {
58 if (*s == '%')
59 {
60 if (s[1] && s[2] &&
61 isxdigit ((unsigned char) s[1]) &&
62 isxdigit ((unsigned char) s[2]) &&
63 hexval (s[1]) >= 0 && hexval (s[2]) >= 0)
64 {
65 *d++ = (hexval (s[1]) << 4) | (hexval (s[2]));
66 s += 2;
67 }
68 else
69 return -1;
70 } else
71 *d++ = *s;
72 }
73 *d ='\0';
74 return 0;
75}
76
77url_scheme_t url_check_scheme (const char *s)
78{
79 char sbuf[STRING];
80 char *t;
81 int i;
82
83 if (!s || !(t = strchr (s, ':')))
84 return U_UNKNOWN;
85 if ((size_t)(t - s) >= sizeof (sbuf) - 1)
86 return U_UNKNOWN;
87
88 strfcpy (sbuf, s, t - s + 1);
89 for (t = sbuf; *t; t++)
90 *t = ascii_tolower (*t);
91
92 if ((i = mutt_getvaluebyname (sbuf, UrlMap)) == -1)
93 return U_UNKNOWN;
94 else
95 return (url_scheme_t) i;
96}
97
98int url_parse_file (char *d, const char *src, size_t dl)
99{
100 if (ascii_strncasecmp (src, "file:", 5))
101 return -1;
102 else if (!ascii_strncasecmp (src, "file://", 7)) /* we don't support remote files */
103 return -1;
104 else
105 strfcpy (d, src + 5, dl);
106
107 return url_pct_decode (d);
108}
109
110/* ciss_parse_userhost: fill in components of ciss with info from src. Note
111 * these are pointers into src, which is altered with '\0's. Port of 0
112 * means no port given. */
113static int ciss_parse_userhost (ciss_url_t *ciss, char *src)
114{
115 char *t, *p;
116
117 ciss->user = NULL;
118 ciss->pass = NULL;
119 ciss->host = NULL;
120 ciss->port = 0;
121
122 if (strncmp (src, "//", 2) != 0)
123 {
124 ciss->path = src;
125 return url_pct_decode (ciss->path);
126 }
127
128 src += 2;
129
130 if ((ciss->path = strchr (src, '/')))
131 *ciss->path++ = '\0';
132
133 if ((t = strrchr (src, '@')))
134 {
135 *t = '\0';
136 if ((p = strchr (src, ':')))
137 {
138 *p = '\0';
139 ciss->pass = p + 1;
140 if (url_pct_decode (ciss->pass) < 0)
141 return -1;
142 }
143 ciss->user = src;
144 if (url_pct_decode (ciss->user) < 0)
145 return -1;
146 src = t + 1;
147 }
148
149 /* IPv6 literal address. It may contain colons, so set t to start
150 * the port scan after it.
151 */
152 if ((*src == '[') && (t = strchr (src, ']')))
153 {
154 src++;
155 *t++ = '\0';
156 }
157 else
158 t = src;
159
160 if ((p = strchr (t, ':')))
161 {
162 int t;
163 *p++ = '\0';
164 if (mutt_atoi (p, &t) < 0 || t < 0 || t > 0xffff)
165 return -1;
166 ciss->port = (unsigned short)t;
167 }
168 else
169 ciss->port = 0;
170
171 ciss->host = src;
172 return url_pct_decode (ciss->host) >= 0 &&
173 (!ciss->path || url_pct_decode (ciss->path) >= 0) ? 0 : -1;
174}
175
176/* url_parse_ciss: Fill in ciss_url_t. char* elements are pointers into src,
177 * which is modified by this call (duplicate it first if you need to). */
178int url_parse_ciss (ciss_url_t *ciss, char *src)
179{
180 char *tmp;
181
182 if ((ciss->scheme = url_check_scheme (src)) == U_UNKNOWN)
183 return -1;
184
185 tmp = strchr (src, ':') + 1;
186
187 return ciss_parse_userhost (ciss, tmp);
188}
189
190static void url_pct_encode (char *dst, size_t l, const char *src)
191{
192 static const char *alph = "0123456789ABCDEF";
193
194 *dst = 0;
195 l--;
196 while (src && *src && l)
197 {
198 if (strchr ("/:%", *src) && l > 3)
199 {
200 *dst++ = '%';
201 *dst++ = alph[(*src >> 4) & 0xf];
202 *dst++ = alph[*src & 0xf];
203 src++;
204 continue;
205 }
206 *dst++ = *src++;
207 }
208 *dst = 0;
209}
210
211/* url_ciss_tostring: output the URL string for a given CISS object. */
212int url_ciss_tostring (ciss_url_t* ciss, char* dest, size_t len, int flags)
213{
214 long l;
215
216 if (ciss->scheme == U_UNKNOWN)
217 return -1;
218
219 snprintf (dest, len, "%s:", mutt_getnamebyvalue (ciss->scheme, UrlMap));
220
221 if (ciss->host)
222 {
223 if (!(flags & U_PATH))
224 safe_strcat (dest, len, "//");
225 len -= (l = strlen (dest)); dest += l;
226
227 if (ciss->user)
228 {
229 char u[STRING];
230 url_pct_encode (u, sizeof (u), ciss->user);
231
232 if (flags & U_DECODE_PASSWD && ciss->pass)
233 {
234 char p[STRING];
235 url_pct_encode (p, sizeof (p), ciss->pass);
236 snprintf (dest, len, "%s:%s@", u, p);
237 }
238 else
239 snprintf (dest, len, "%s@", u);
240
241 len -= (l = strlen (dest)); dest += l;
242 }
243
244 if (strchr (ciss->host, ':'))
245 snprintf (dest, len, "[%s]", ciss->host);
246 else
247 snprintf (dest, len, "%s", ciss->host);
248
249 len -= (l = strlen (dest)); dest += l;
250
251 if (ciss->port)
252 snprintf (dest, len, ":%hu/", ciss->port);
253 else
254 snprintf (dest, len, "/");
255 }
256
257 if (ciss->path)
258 safe_strcat (dest, len, ciss->path);
259
260 return 0;
261}
262
263int url_parse_mailto (ENVELOPE *e, char **body, const char *src)
264{
265 char *t, *p;
266 char *tmp;
267 char *headers;
268 char *tag, *value;
269
270 int rc = -1;
271
272 LIST *last = NULL;
273
274 if (!(t = strchr (src, ':')))
275 return -1;
276
277 /* copy string for safe use of strtok() */
278 if ((tmp = safe_strdup (t + 1)) == NULL)
279 return -1;
280
281 if ((headers = strchr (tmp, '?')))
282 *headers++ = '\0';
283
284 if (url_pct_decode (tmp) < 0)
285 goto out;
286
287 e->to = rfc822_parse_adrlist (e->to, tmp);
288
289 tag = headers ? strtok_r (headers, "&", &p) : NULL;
290
291 for (; tag; tag = strtok_r (NULL, "&", &p))
292 {
293 if ((value = strchr (tag, '=')))
294 *value++ = '\0';
295 if (!value || !*value)
296 continue;
297
298 if (url_pct_decode (tag) < 0)
299 goto out;
300 if (url_pct_decode (value) < 0)
301 goto out;
302
303 /* Determine if this header field is on the allowed list. Since Mutt
304 * interprets some header fields specially (such as
305 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
306 * only safe fields are allowed.
307 *
308 * RFC2368, "4. Unsafe headers"
309 * The user agent interpreting a mailto URL SHOULD choose not to create
310 * a message if any of the headers are considered dangerous; it may also
311 * choose to create a message with only a subset of the headers given in
312 * the URL.
313 */
314 if (mutt_matches_ignore(tag, MailtoAllow))
315 {
316 if (!ascii_strcasecmp (tag, "body"))
317 {
318 if (body)
319 mutt_str_replace (body, value);
320 }
321 else
322 {
323 char *scratch;
324 size_t taglen = mutt_strlen (tag);
325
326 safe_asprintf (&scratch, "%s: %s", tag, value);
327 scratch[taglen] = 0; /* overwrite the colon as mutt_parse_rfc822_line expects */
328 value = skip_email_wsp(&scratch[taglen + 1]);
329 mutt_parse_rfc822_line (e, NULL, scratch, value, 1, 0, 1, &last);
330 FREE (&scratch);
331 }
332 }
333 }
334
335 /* RFC2047 decode after the RFC822 parsing */
336 rfc2047_decode_adrlist (e->from);
337 rfc2047_decode_adrlist (e->to);
338 rfc2047_decode_adrlist (e->cc);
339 rfc2047_decode_adrlist (e->bcc);
340 rfc2047_decode_adrlist (e->reply_to);
341 rfc2047_decode_adrlist (e->mail_followup_to);
342 rfc2047_decode_adrlist (e->return_path);
343 rfc2047_decode_adrlist (e->sender);
344 rfc2047_decode (&e->x_label);
345 rfc2047_decode (&e->subject);
346
347 rc = 0;
348
349out:
350 FREE (&tmp);
351 return rc;
352}
353