1 1.9 christos /* $NetBSD: mime_header.c,v 1.9 2013/02/14 18:23:45 christos Exp $ */ 2 1.1 christos 3 1.1 christos /*- 4 1.1 christos * Copyright (c) 2006 The NetBSD Foundation, Inc. 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * This code is derived from software contributed to The NetBSD Foundation 8 1.1 christos * by Anon Ymous. 9 1.1 christos * 10 1.1 christos * Redistribution and use in source and binary forms, with or without 11 1.1 christos * modification, are permitted provided that the following conditions 12 1.1 christos * are met: 13 1.1 christos * 1. Redistributions of source code must retain the above copyright 14 1.1 christos * notice, this list of conditions and the following disclaimer. 15 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 christos * notice, this list of conditions and the following disclaimer in the 17 1.1 christos * documentation and/or other materials provided with the distribution. 18 1.1 christos * 19 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 christos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 christos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 christos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 christos * POSSIBILITY OF SUCH DAMAGE. 30 1.1 christos */ 31 1.1 christos 32 1.1 christos 33 1.1 christos /* 34 1.1 christos * This module contains the core MIME header decoding routines. 35 1.1 christos * Please refer to RFC 2047 and RFC 2822. 36 1.1 christos */ 37 1.1 christos 38 1.1 christos #ifdef MIME_SUPPORT 39 1.1 christos 40 1.1 christos #include <sys/cdefs.h> 41 1.1 christos #ifndef __lint__ 42 1.9 christos __RCSID("$NetBSD: mime_header.c,v 1.9 2013/02/14 18:23:45 christos Exp $"); 43 1.1 christos #endif /* not __lint__ */ 44 1.1 christos 45 1.8 christos #include <assert.h> 46 1.1 christos #include <stdio.h> 47 1.1 christos #include <stdlib.h> 48 1.1 christos #include <string.h> 49 1.1 christos 50 1.1 christos #include "def.h" 51 1.1 christos #include "extern.h" 52 1.1 christos #include "mime.h" 53 1.1 christos #include "mime_header.h" 54 1.1 christos #include "mime_codecs.h" 55 1.1 christos 56 1.1 christos static const char * 57 1.1 christos grab_charset(char *from_cs, size_t from_cs_len, const char *p) 58 1.1 christos { 59 1.1 christos char *q; 60 1.1 christos q = from_cs; 61 1.1 christos for (/*EMPTY*/; *p != '?'; p++) { 62 1.1 christos if (*p == '\0' || q >= from_cs + from_cs_len - 1) 63 1.1 christos return NULL; 64 1.1 christos *q++ = *p; 65 1.1 christos } 66 1.1 christos *q = '\0'; 67 1.1 christos return ++p; /* if here, then we got the '?' */ 68 1.1 christos } 69 1.1 christos 70 1.1 christos /* 71 1.1 christos * An encoded word is a string of at most 75 non-white space 72 1.1 christos * characters of the following form: 73 1.1 christos * 74 1.1 christos * =?charset?X?encoding?= 75 1.1 christos * 76 1.1 christos * where: 77 1.1 christos * 'charset' is the original character set of the unencoded string. 78 1.1 christos * 79 1.1 christos * 'X' is the encoding type 'B' or 'Q' for "base64" or 80 1.1 christos * "quoted-printable", respectively, 81 1.1 christos * 'encoding' is the encoded string. 82 1.1 christos * 83 1.1 christos * Both 'charset' and 'X' are case independent and 'encoding' cannot 84 1.1 christos * contain any whitespace or '?' characters. The 'encoding' must also 85 1.1 christos * be fully contained within the encoded words, i.e., it cannot be 86 1.1 christos * split between encoded words. 87 1.1 christos * 88 1.1 christos * Note: the 'B' encoding is a slightly modified "quoted-printable" 89 1.1 christos * encoding. In particular, spaces (' ') may be encoded as '_' to 90 1.1 christos * improve undecoded readability. 91 1.1 christos */ 92 1.1 christos static int 93 1.1 christos decode_word(const char **ibuf, char **obuf, char *oend, const char *to_cs) 94 1.1 christos { 95 1.1 christos ssize_t declen; 96 1.1 christos size_t enclen, dstlen; 97 1.1 christos char decword[LINESIZE]; 98 1.1 christos char from_cs[LINESIZE]; 99 1.1 christos const char *encword, *iend, *p; 100 1.1 christos char *dstend; 101 1.1 christos char enctype; 102 1.1 christos 103 1.1 christos p = *ibuf; 104 1.1 christos if (p[0] != '=' && p[1] != '?') 105 1.1 christos return -1; 106 1.1 christos if (strlen(p) < 2 + 1 + 3 + 1 + 2) 107 1.1 christos return -1; 108 1.1 christos p = grab_charset(from_cs, sizeof(from_cs), p + 2); 109 1.1 christos if (p == NULL) 110 1.1 christos return -1; 111 1.1 christos enctype = *p++; 112 1.1 christos if (*p++ != '?') 113 1.1 christos return -1; 114 1.1 christos encword = p; 115 1.1 christos p = strchr(p, '?'); 116 1.1 christos if (p == NULL || p[1] != '=') 117 1.1 christos return -1; 118 1.1 christos enclen = p - encword; /* length of encoded substring */ 119 1.1 christos iend = p + 2; 120 1.1 christos /* encoded words are at most 75 characters (RFC 2047, sec 2) */ 121 1.1 christos if (iend > *ibuf + 75) 122 1.1 christos return -1; 123 1.1 christos 124 1.8 christos if (oend < *obuf + 1) { 125 1.8 christos assert(/*CONSTCOND*/ 0); /* We have a coding error! */ 126 1.8 christos return -1; 127 1.8 christos } 128 1.1 christos dstend = to_cs ? decword : *obuf; 129 1.7 lukem dstlen = (to_cs ? sizeof(decword) : (size_t)(oend - *obuf)) - 1; 130 1.1 christos 131 1.9 christos declen = mime_rfc2047_decode(enctype, dstend, dstlen, encword, enclen); 132 1.1 christos if (declen == -1) 133 1.1 christos return -1; 134 1.1 christos 135 1.1 christos dstend += declen; 136 1.1 christos #ifdef CHARSET_SUPPORT 137 1.1 christos if (to_cs != NULL) { 138 1.1 christos iconv_t cd; 139 1.1 christos const char *src; 140 1.1 christos size_t srclen; 141 1.1 christos size_t cnt; 142 1.1 christos 143 1.1 christos cd = iconv_open(to_cs, from_cs); 144 1.1 christos if (cd == (iconv_t)-1) 145 1.1 christos return -1; 146 1.1 christos 147 1.1 christos src = decword; 148 1.1 christos srclen = declen; 149 1.1 christos dstend = *obuf; 150 1.1 christos dstlen = oend - *obuf - 1; 151 1.1 christos cnt = mime_iconv(cd, &src, &srclen, &dstend, &dstlen); 152 1.4 christos 153 1.1 christos (void)iconv_close(cd); 154 1.1 christos if (cnt == (size_t)-1) 155 1.1 christos return -1; 156 1.1 christos } 157 1.1 christos #endif /* CHARSET_SUPPORT */ 158 1.1 christos *dstend = '\0'; 159 1.1 christos *ibuf = iend; 160 1.1 christos *obuf = dstend; 161 1.1 christos return 0; 162 1.1 christos } 163 1.1 christos 164 1.1 christos 165 1.1 christos /* 166 1.1 christos * Folding White Space. See RFC 2822. 167 1.4 christos * 168 1.4 christos * Note: RFC 2822 specifies that '\n' and '\r' only occur as CRLF 169 1.4 christos * pairs (i.e., "\r\n") and never separately. However, by the time 170 1.4 christos * mail(1) sees the messages, all CRLF pairs have been converted to 171 1.4 christos * '\n' characters. 172 1.4 christos * 173 1.4 christos * XXX - pull is_FWS() and skip_FWS() up to def.h? 174 1.1 christos */ 175 1.1 christos static inline int 176 1.1 christos is_FWS(int c) 177 1.1 christos { 178 1.4 christos return c == ' ' || c == '\t' || c == '\n'; 179 1.1 christos } 180 1.1 christos 181 1.1 christos static inline const char * 182 1.1 christos skip_FWS(const char *p) 183 1.1 christos { 184 1.4 christos while (is_FWS(*p)) 185 1.1 christos p++; 186 1.1 christos return p; 187 1.1 christos } 188 1.1 christos 189 1.1 christos static inline void 190 1.1 christos copy_skipped_FWS(char **dst, char *dstend, const char **src, const char *srcend) 191 1.1 christos { 192 1.1 christos const char *p, *pend; 193 1.1 christos char *q, *qend; 194 1.1 christos 195 1.1 christos p = *src; 196 1.1 christos q = *dst; 197 1.1 christos pend = srcend; 198 1.1 christos qend = dstend; 199 1.1 christos 200 1.1 christos if (p) { /* copy any skipped linear-white-space */ 201 1.1 christos while (p < pend && q < qend) 202 1.1 christos *q++ = *p++; 203 1.1 christos *dst = q; 204 1.1 christos *src = NULL; 205 1.1 christos } 206 1.1 christos } 207 1.1 christos 208 1.1 christos /* 209 1.1 christos * Decode an unstructured field. 210 1.1 christos * 211 1.1 christos * See RFC 2822 Sec 2.2.1 and 3.6.5. 212 1.1 christos * Encoded words may occur anywhere in unstructured fields provided 213 1.1 christos * they are separated from any other text or encoded words by at least 214 1.1 christos * one linear-white-space character. (See RFC 2047 sec 5.1.) If two 215 1.1 christos * encoded words occur sequentially (separated by only FWS) then the 216 1.1 christos * separating FWS is removed. 217 1.1 christos * 218 1.1 christos * NOTE: unstructured fields cannot contain 'quoted-pairs' (see 219 1.1 christos * RFC2822 sec 3.2.6 and RFC 2047), but that is no problem as a '\\' 220 1.1 christos * (or any non-whitespace character) immediately before an 221 1.1 christos * encoded-word will prevent it from being decoded. 222 1.1 christos * 223 1.1 christos * hstring should be a NULL terminated string. 224 1.1 christos * outbuf should be sufficiently large to hold the result. 225 1.1 christos */ 226 1.1 christos static void 227 1.1 christos mime_decode_usfield(char *outbuf, size_t outsize, const char *hstring) 228 1.1 christos { 229 1.1 christos const char *p, *p0; 230 1.1 christos char *q, *qend; 231 1.1 christos int lastc; 232 1.1 christos const char *charset; 233 1.1 christos 234 1.1 christos charset = value(ENAME_MIME_CHARSET); 235 1.1 christos qend = outbuf + outsize - 1; /* Make sure there is room for the trailing NULL! */ 236 1.1 christos q = outbuf; 237 1.1 christos p = hstring; 238 1.1 christos p0 = NULL; 239 1.1 christos lastc = (unsigned char)' '; 240 1.1 christos while (*p && q < qend) { 241 1.1 christos const char *p1; 242 1.1 christos char *q1; 243 1.1 christos if (is_FWS(lastc) && p[0] == '=' && p[1] == '?' && 244 1.1 christos decode_word((p1 = p, &p1), (q1 = q, &q1), qend, charset) == 0 && 245 1.4 christos (*p1 == '\0' || is_FWS(*p1))) { 246 1.1 christos p0 = p1; /* pointer to first character after encoded word */ 247 1.1 christos q = q1; 248 1.1 christos p = skip_FWS(p1); 249 1.1 christos lastc = (unsigned char)*p0; 250 1.1 christos } 251 1.1 christos else { 252 1.1 christos copy_skipped_FWS(&q, qend, &p0, p); 253 1.1 christos lastc = (unsigned char)*p; 254 1.1 christos if (q < qend) 255 1.1 christos *q++ = *p++; 256 1.1 christos } 257 1.1 christos } 258 1.1 christos copy_skipped_FWS(&q, qend, &p0, p); 259 1.1 christos *q = '\0'; 260 1.1 christos } 261 1.1 christos 262 1.1 christos /* 263 1.1 christos * Decode a field comment. 264 1.1 christos * 265 1.1 christos * Comments only occur in structured fields, can be nested (rfc 2822, 266 1.1 christos * sec 3.2.3), and can contain 'encoded-words' and 'quoted-pairs'. 267 1.1 christos * Otherwise, they can be regarded as unstructured fields that are 268 1.1 christos * bounded by '(' and ')' characters. 269 1.1 christos */ 270 1.1 christos static int 271 1.1 christos decode_comment(char **obuf, char *oend, const char **ibuf, const char *iend, const char *charset) 272 1.1 christos { 273 1.1 christos const char *p, *pend, *p0; 274 1.1 christos char *q, *qend; 275 1.1 christos int lastc; 276 1.1 christos 277 1.1 christos p = *ibuf; 278 1.1 christos q = *obuf; 279 1.1 christos pend = iend; 280 1.1 christos qend = oend; 281 1.4 christos lastc = ' '; 282 1.1 christos p0 = NULL; 283 1.1 christos while (p < pend && q < qend) { 284 1.1 christos const char *p1; 285 1.1 christos char *q1; 286 1.1 christos 287 1.1 christos if (is_FWS(lastc) && p[0] == '=' && p[1] == '?' && 288 1.1 christos decode_word((p1 = p, &p1), (q1 = q, &q1), qend, charset) == 0 && 289 1.4 christos (*p1 == ')' || is_FWS(*p1))) { 290 1.1 christos lastc = (unsigned char)*p1; 291 1.1 christos p0 = p1; 292 1.1 christos q = q1; 293 1.1 christos p = skip_FWS(p1); 294 1.1 christos /* 295 1.1 christos * XXX - this check should be unnecessary as *pend should 296 1.1 christos * be '\0' which will stop skip_FWS() 297 1.1 christos */ 298 1.1 christos if (p > pend) 299 1.1 christos p = pend; 300 1.1 christos } 301 1.1 christos else { 302 1.1 christos copy_skipped_FWS(&q, qend, &p0, p); 303 1.1 christos if (q >= qend) /* XXX - q > qend cannot happen */ 304 1.1 christos break; 305 1.1 christos 306 1.1 christos if (*p == ')') { 307 1.1 christos *q++ = *p++; /* copy the closing ')' */ 308 1.1 christos break; /* and get out of here! */ 309 1.1 christos } 310 1.1 christos 311 1.1 christos if (*p == '(') { 312 1.1 christos *q++ = *p++; /* copy the opening '(' */ 313 1.1 christos if (decode_comment(&q, qend, &p, pend, charset) == -1) 314 1.1 christos return -1; /* is this right or should we update? */ 315 1.1 christos lastc = ')'; 316 1.1 christos } 317 1.1 christos else if (*p == '\\' && p + 1 < pend) { /* quoted-pair */ 318 1.1 christos if (p[1] == '(' || p[1] == ')' || p[1] == '\\') /* need quoted-pair*/ 319 1.1 christos *q++ = *p; 320 1.1 christos p++; 321 1.1 christos lastc = (unsigned char)*p; 322 1.1 christos if (q < qend) 323 1.1 christos *q++ = *p++; 324 1.1 christos } 325 1.1 christos else { 326 1.1 christos lastc = (unsigned char)*p; 327 1.1 christos *q++ = *p++; 328 1.1 christos } 329 1.1 christos } 330 1.1 christos } 331 1.1 christos *ibuf = p; 332 1.1 christos *obuf = q; 333 1.1 christos return 0; 334 1.1 christos } 335 1.1 christos 336 1.1 christos /* 337 1.1 christos * Decode a quoted-string or no-fold-quote. 338 1.1 christos * 339 1.1 christos * These cannot contain encoded words. They can contain quoted-pairs, 340 1.1 christos * making '\\' special. They have no other structure. See RFC 2822 341 1.1 christos * sec 3.2.5 and 3.6.4. 342 1.1 christos */ 343 1.1 christos static void 344 1.1 christos decode_quoted_string(char **obuf, char *oend, const char **ibuf, const char *iend) 345 1.1 christos { 346 1.1 christos const char *p, *pend; 347 1.1 christos char *q, *qend; 348 1.1 christos 349 1.1 christos qend = oend; 350 1.1 christos pend = iend; 351 1.1 christos p = *ibuf; 352 1.1 christos q = *obuf; 353 1.1 christos while (p < pend && q < qend) { 354 1.1 christos if (*p == '"') { 355 1.1 christos *q++ = *p++; /* copy the closing '"' */ 356 1.1 christos break; 357 1.1 christos } 358 1.1 christos if (*p == '\\' && p + 1 < pend) { /* quoted-pair */ 359 1.1 christos if (p[1] == '"' || p[1] == '\\') { 360 1.1 christos *q++ = *p; 361 1.1 christos if (q >= qend) 362 1.1 christos break; 363 1.1 christos } 364 1.1 christos p++; 365 1.1 christos } 366 1.1 christos *q++ = *p++; 367 1.1 christos } 368 1.1 christos *ibuf = p; 369 1.1 christos *obuf = q; 370 1.1 christos } 371 1.1 christos 372 1.1 christos /* 373 1.1 christos * Decode a domain-literal or no-fold-literal. 374 1.1 christos * 375 1.1 christos * These cannot contain encoded words. They can have quoted pairs and 376 1.1 christos * are delimited by '[' and ']' making '\\', '[', and ']' special. 377 1.1 christos * They have no other structure. See RFC 2822 sec 3.4.1 and 3.6.4. 378 1.1 christos */ 379 1.1 christos static void 380 1.1 christos decode_domain_literal(char **obuf, char *oend, const char **ibuf, const char *iend) 381 1.1 christos { 382 1.1 christos const char *p, *pend; 383 1.1 christos char *q, *qend; 384 1.1 christos 385 1.1 christos qend = oend; 386 1.1 christos pend = iend; 387 1.1 christos p = *ibuf; 388 1.1 christos q = *obuf; 389 1.1 christos while (p < pend && q < qend) { 390 1.1 christos if (*p == ']') { 391 1.1 christos *q++ = *p++; /* copy the closing ']' */ 392 1.1 christos break; 393 1.1 christos } 394 1.1 christos if (*p == '\\' && p + 1 < pend) { /* quoted-pair */ 395 1.1 christos if (p[1] == '[' || p[1] == ']' || p[1] == '\\') { 396 1.1 christos *q++ = *p; 397 1.1 christos if (q >= qend) 398 1.1 christos break; 399 1.1 christos } 400 1.1 christos p++; 401 1.1 christos } 402 1.1 christos *q++ = *p++; 403 1.1 christos } 404 1.1 christos *ibuf = p; 405 1.1 christos *obuf = q; 406 1.1 christos } 407 1.1 christos 408 1.1 christos /* 409 1.1 christos * Specials: see RFC 2822 sec 3.2.1. 410 1.1 christos */ 411 1.1 christos static inline int 412 1.1 christos is_specials(int c) 413 1.1 christos { 414 1.1 christos static const char specialtab[] = { 415 1.1 christos 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416 1.1 christos 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 417 1.1 christos 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 418 1.1 christos 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 419 1.4 christos 420 1.1 christos 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 421 1.1 christos 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 422 1.1 christos 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 423 1.1 christos 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 424 1.1 christos }; 425 1.4 christos return !(c & ~0x7f) ? specialtab[c] : 0; 426 1.1 christos } 427 1.1 christos 428 1.1 christos /* 429 1.1 christos * Decode a structured field. 430 1.1 christos * 431 1.1 christos * At the top level, structured fields can only contain encoded-words 432 1.1 christos * via 'phrases' and 'comments'. See RFC 2047 sec 5. 433 1.1 christos */ 434 1.1 christos static void 435 1.1 christos mime_decode_sfield(char *linebuf, size_t bufsize, const char *hstring) 436 1.1 christos { 437 1.1 christos const char *p, *pend, *p0; 438 1.1 christos char *q, *qend; 439 1.1 christos const char *charset; 440 1.1 christos int lastc; 441 1.1 christos 442 1.1 christos charset = value(ENAME_MIME_CHARSET); 443 1.1 christos 444 1.1 christos p = hstring; 445 1.1 christos q = linebuf; 446 1.1 christos pend = hstring + strlen(hstring); 447 1.1 christos qend = linebuf + bufsize - 1; /* save room for the NULL terminator */ 448 1.1 christos lastc = (unsigned char)' '; 449 1.1 christos p0 = NULL; 450 1.1 christos while (p < pend && q < qend) { 451 1.1 christos const char *p1; 452 1.1 christos char *q1; 453 1.1 christos 454 1.1 christos if (*p != '=') { 455 1.1 christos copy_skipped_FWS(&q, qend, &p0, p); 456 1.1 christos if (q >= qend) 457 1.1 christos break; 458 1.1 christos } 459 1.1 christos 460 1.1 christos switch (*p) { 461 1.1 christos case '(': /* start of comment */ 462 1.1 christos *q++ = *p++; /* copy the opening '(' */ 463 1.1 christos (void)decode_comment(&q, qend, &p, pend, charset); 464 1.1 christos lastc = (unsigned char)p[-1]; 465 1.1 christos break; 466 1.1 christos 467 1.1 christos case '"': /* start of quoted-string or no-fold-quote */ 468 1.1 christos *q++ = *p++; /* copy the opening '"' */ 469 1.1 christos decode_quoted_string(&q, qend, &p, pend); 470 1.1 christos lastc = (unsigned char)p[-1]; 471 1.1 christos break; 472 1.1 christos 473 1.1 christos case '[': /* start of domain-literal or no-fold-literal */ 474 1.1 christos *q++ = *p++; /* copy the opening '[' */ 475 1.1 christos decode_domain_literal(&q, qend, &p, pend); 476 1.1 christos lastc = (unsigned char)p[-1]; 477 1.1 christos break; 478 1.1 christos 479 1.1 christos case '\\': /* start of quoted-pair */ 480 1.1 christos if (p + 1 < pend) { /* quoted pair */ 481 1.1 christos if (is_specials(p[1])) { 482 1.1 christos *q++ = *p; 483 1.1 christos if (q >= qend) 484 1.1 christos break; 485 1.1 christos } 486 1.1 christos p++; /* skip the '\\' */ 487 1.1 christos } 488 1.1 christos goto copy_char; 489 1.4 christos 490 1.1 christos case '=': 491 1.1 christos /* 492 1.1 christos * At this level encoded words can appear via 493 1.1 christos * 'phrases' (possibly delimited by ',' as in 494 1.1 christos * 'keywords'). Thus we handle them as such. 495 1.1 christos * Hopefully this is sufficient. 496 1.1 christos */ 497 1.1 christos if ((lastc == ',' || is_FWS(lastc)) && p[1] == '?' && 498 1.1 christos decode_word((p1 = p, &p1), (q1 = q, &q1), qend, charset) == 0 && 499 1.4 christos (*p1 == '\0' || *p1 == ',' || is_FWS(*p1))) { 500 1.1 christos lastc = (unsigned char)*p1; 501 1.1 christos p0 = p1; 502 1.1 christos q = q1; 503 1.1 christos p = skip_FWS(p1); 504 1.1 christos /* 505 1.1 christos * XXX - this check should be 506 1.1 christos * unnecessary as *pend should be '\0' 507 1.1 christos * which will stop skip_FWS() 508 1.1 christos */ 509 1.1 christos if (p > pend) 510 1.1 christos p = pend; 511 1.1 christos break; 512 1.1 christos } 513 1.1 christos else { 514 1.1 christos copy_skipped_FWS(&q, qend, &p0, p); 515 1.1 christos if (q >= qend) 516 1.1 christos break; 517 1.1 christos goto copy_char; 518 1.1 christos } 519 1.1 christos 520 1.1 christos case '<': /* start of angle-addr, msg-id, or path. */ 521 1.1 christos /* 522 1.1 christos * A msg-id cannot contain encoded-pairs or 523 1.1 christos * encoded-words, but angle-addr and path can. 524 1.1 christos * Distinguishing between them seems to be 525 1.1 christos * unnecessary, so let's be loose and just 526 1.1 christos * decode them as if they were all the same. 527 1.1 christos */ 528 1.1 christos default: 529 1.1 christos copy_char: 530 1.1 christos lastc = (unsigned char)*p; 531 1.1 christos *q++ = *p++; 532 1.1 christos break; 533 1.1 christos } 534 1.1 christos } 535 1.1 christos copy_skipped_FWS(&q, qend, &p0, p); 536 1.1 christos *q = '\0'; /* null terminate the result! */ 537 1.1 christos } 538 1.1 christos 539 1.1 christos /* 540 1.1 christos * Returns the correct hfield decoder, or NULL if none. 541 1.1 christos * Info extracted from RFC 2822. 542 1.5 christos * 543 1.5 christos * name - pointer to field name of header line (with colon). 544 1.1 christos */ 545 1.1 christos PUBLIC hfield_decoder_t 546 1.5 christos mime_hfield_decoder(const char *name) 547 1.1 christos { 548 1.1 christos static const struct field_decoder_tbl_s { 549 1.1 christos const char *field_name; 550 1.5 christos size_t field_len; 551 1.1 christos hfield_decoder_t decoder; 552 1.1 christos } field_decoder_tbl[] = { 553 1.5 christos #define X(s) s, sizeof(s) - 1 554 1.5 christos { X("Received:"), NULL }, 555 1.5 christos 556 1.5 christos { X("Content-Type:"), NULL }, 557 1.5 christos { X("Content-Disposition:"), NULL }, 558 1.5 christos { X("Content-Transfer-Encoding:"), NULL }, 559 1.5 christos { X("Content-Description:"), mime_decode_sfield }, 560 1.5 christos { X("Content-ID:"), mime_decode_sfield }, 561 1.5 christos { X("MIME-Version:"), mime_decode_sfield }, 562 1.5 christos 563 1.5 christos { X("Bcc:"), mime_decode_sfield }, 564 1.5 christos { X("Cc:"), mime_decode_sfield }, 565 1.5 christos { X("Date:"), mime_decode_sfield }, 566 1.5 christos { X("From:"), mime_decode_sfield }, 567 1.5 christos { X("In-Reply-To:"), mime_decode_sfield }, 568 1.5 christos { X("Keywords:"), mime_decode_sfield }, 569 1.5 christos { X("Message-ID:"), mime_decode_sfield }, 570 1.5 christos { X("References:"), mime_decode_sfield }, 571 1.5 christos { X("Reply-To:"), mime_decode_sfield }, 572 1.5 christos { X("Return-Path:"), mime_decode_sfield }, 573 1.5 christos { X("Sender:"), mime_decode_sfield }, 574 1.5 christos { X("To:"), mime_decode_sfield }, 575 1.5 christos { X("Subject:"), mime_decode_usfield }, 576 1.5 christos { X("Comments:"), mime_decode_usfield }, 577 1.5 christos { X("X-"), mime_decode_usfield }, 578 1.5 christos { NULL, 0, mime_decode_usfield }, /* optional-fields */ 579 1.5 christos #undef X 580 1.1 christos }; 581 1.1 christos const struct field_decoder_tbl_s *fp; 582 1.1 christos 583 1.1 christos /* XXX - this begs for a hash table! */ 584 1.1 christos for (fp = field_decoder_tbl; fp->field_name; fp++) 585 1.5 christos if (strncasecmp(name, fp->field_name, fp->field_len) == 0) 586 1.5 christos break; 587 1.1 christos return fp->decoder; 588 1.1 christos } 589 1.1 christos 590 1.1 christos #endif /* MIME_SUPPORT */ 591