]> granicus.if.org Git - mutt/blob - url.c
Convert pgp_app_handler to use buffer pool.
[mutt] / url.c
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
36 static 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
49 static 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     }
71     else
72       *d++ = *s;
73   }
74   *d ='\0';
75   return 0;
76 }
77
78 url_scheme_t url_check_scheme (const char *s)
79 {
80   char sbuf[STRING];
81   char *t;
82   int i;
83
84   if (!s || !(t = strchr (s, ':')))
85     return U_UNKNOWN;
86   if ((size_t)(t - s) >= sizeof (sbuf) - 1)
87     return U_UNKNOWN;
88
89   strfcpy (sbuf, s, t - s + 1);
90   for (t = sbuf; *t; t++)
91     *t = ascii_tolower (*t);
92
93   if ((i = mutt_getvaluebyname (sbuf, UrlMap)) == -1)
94     return U_UNKNOWN;
95   else
96     return (url_scheme_t) i;
97 }
98
99 int url_parse_file (char *d, const char *src, size_t dl)
100 {
101   if (ascii_strncasecmp (src, "file:", 5))
102     return -1;
103   else if (!ascii_strncasecmp (src, "file://", 7))      /* we don't support remote files */
104     return -1;
105   else
106     strfcpy (d, src + 5, dl);
107
108   return url_pct_decode (d);
109 }
110
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)
115 {
116   char *t, *p;
117
118   ciss->user = NULL;
119   ciss->pass = NULL;
120   ciss->host = NULL;
121   ciss->port = 0;
122
123   if (strncmp (src, "//", 2) != 0)
124   {
125     ciss->path = src;
126     return url_pct_decode (ciss->path);
127   }
128
129   src += 2;
130
131   if ((ciss->path = strchr (src, '/')))
132     *ciss->path++ = '\0';
133
134   if ((t = strrchr (src, '@')))
135   {
136     *t = '\0';
137     if ((p = strchr (src, ':')))
138     {
139       *p = '\0';
140       ciss->pass = p + 1;
141       if (url_pct_decode (ciss->pass) < 0)
142         return -1;
143     }
144     ciss->user = src;
145     if (url_pct_decode (ciss->user) < 0)
146       return -1;
147     src = t + 1;
148   }
149
150   /* IPv6 literal address.  It may contain colons, so set t to start
151    * the port scan after it.
152    */
153   if ((*src == '[') && (t = strchr (src, ']')))
154   {
155     src++;
156     *t++ = '\0';
157   }
158   else
159     t = src;
160
161   if ((p = strchr (t, ':')))
162   {
163     int t;
164     *p++ = '\0';
165     if (mutt_atoi (p, &t) < 0 || t < 0 || t > 0xffff)
166       return -1;
167     ciss->port = (unsigned short)t;
168   }
169   else
170     ciss->port = 0;
171
172   ciss->host = src;
173   return url_pct_decode (ciss->host) >= 0 &&
174     (!ciss->path || url_pct_decode (ciss->path) >= 0) ? 0 : -1;
175 }
176
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)
180 {
181   char *tmp;
182
183   if ((ciss->scheme = url_check_scheme (src)) == U_UNKNOWN)
184     return -1;
185
186   tmp = strchr (src, ':') + 1;
187
188   return ciss_parse_userhost (ciss, tmp);
189 }
190
191 static void url_pct_encode (char *dst, size_t l, const char *src)
192 {
193   static const char *alph = "0123456789ABCDEF";
194
195   *dst = 0;
196   l--;
197   while (src && *src && l)
198   {
199     if (strchr ("/:%", *src))
200     {
201       if (l < 3)
202         break;
203
204       *dst++ = '%';
205       *dst++ = alph[(*src >> 4) & 0xf];
206       *dst++ = alph[*src & 0xf];
207       src++;
208       l -= 3;
209       continue;
210     }
211     *dst++ = *src++;
212     l--;
213   }
214   *dst = 0;
215 }
216
217 int url_ciss_tostring (ciss_url_t* ciss, char* dest, size_t len, int flags)
218 {
219   BUFFER *dest_buf;
220   int retval;
221
222   dest_buf = mutt_buffer_pool_get ();
223
224   retval = url_ciss_tobuffer (ciss, dest_buf, flags);
225   if (!retval)
226     strfcpy (dest, mutt_b2s (dest_buf), len);
227
228   mutt_buffer_pool_release (&dest_buf);
229
230   return retval;
231 }
232
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)
235 {
236   if (ciss->scheme == U_UNKNOWN)
237     return -1;
238
239   mutt_buffer_printf (dest, "%s:", mutt_getnamebyvalue (ciss->scheme, UrlMap));
240
241   if (ciss->host)
242   {
243     if (!(flags & U_PATH))
244       mutt_buffer_addstr (dest, "//");
245
246     if (ciss->user)
247     {
248       char u[STRING];
249       url_pct_encode (u, sizeof (u), ciss->user);
250
251       if (flags & U_DECODE_PASSWD && ciss->pass)
252       {
253         char p[STRING];
254         url_pct_encode (p, sizeof (p), ciss->pass);
255         mutt_buffer_add_printf (dest, "%s:%s@", u, p);
256       }
257       else
258         mutt_buffer_add_printf (dest, "%s@", u);
259     }
260
261     if (strchr (ciss->host, ':'))
262       mutt_buffer_add_printf (dest, "[%s]", ciss->host);
263     else
264       mutt_buffer_add_printf (dest, "%s", ciss->host);
265
266     if (ciss->port)
267       mutt_buffer_add_printf (dest, ":%hu/", ciss->port);
268     else
269       mutt_buffer_addstr (dest, "/");
270   }
271
272   if (ciss->path)
273     mutt_buffer_addstr (dest, ciss->path);
274
275   return 0;
276 }
277
278 int url_parse_mailto (ENVELOPE *e, char **body, const char *src)
279 {
280   char *t, *p;
281   char *tmp;
282   char *headers;
283   char *tag, *value;
284
285   int rc = -1;
286
287   LIST *last = NULL;
288
289   if (!(t = strchr (src, ':')))
290     return -1;
291
292   /* copy string for safe use of strtok() */
293   if ((tmp = safe_strdup (t + 1)) == NULL)
294     return -1;
295
296   if ((headers = strchr (tmp, '?')))
297     *headers++ = '\0';
298
299   if (url_pct_decode (tmp) < 0)
300     goto out;
301
302   e->to = rfc822_parse_adrlist (e->to, tmp);
303
304   tag = headers ? strtok_r (headers, "&", &p) : NULL;
305
306   for (; tag; tag = strtok_r (NULL, "&", &p))
307   {
308     if ((value = strchr (tag, '=')))
309       *value++ = '\0';
310     if (!value || !*value)
311       continue;
312
313     if (url_pct_decode (tag) < 0)
314       goto out;
315     if (url_pct_decode (value) < 0)
316       goto out;
317
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.
322      *
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
327      * the URL.
328      */
329     if (mutt_matches_ignore(tag, MailtoAllow))
330     {
331       if (!ascii_strcasecmp (tag, "body"))
332       {
333         if (body)
334           mutt_str_replace (body, value);
335       }
336       else
337       {
338         char *scratch;
339         size_t taglen = mutt_strlen (tag);
340
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);
345         FREE (&scratch);
346       }
347     }
348   }
349
350   /* RFC2047 decode after the RFC822 parsing */
351   rfc2047_decode_envelope (e);
352
353   rc = 0;
354
355 out:
356   FREE (&tmp);
357   return rc;
358 }