mutt stable branch with some hacks
at jcs 358 lines 8.0 kB view raw
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 } 71 else 72 *d++ = *s; 73 } 74 *d ='\0'; 75 return 0; 76} 77 78url_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 99int 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. */ 114static 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). */ 179int 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 191static 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 217int 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. */ 234int 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 278int 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 355out: 356 FREE (&tmp); 357 return rc; 358}