mutt stable branch with some hacks
at master 353 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 } 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