Annotation of win32/gnome/gmime-x.x.x/gmime-utils.c, revision 1.1
1.1 ! paf 1: /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
! 2: /*
! 3: * Authors: Michael Zucchi <notzed@helixcode.com>
! 4: * Jeffrey Stedfast <fejj@helixcode.com>
! 5: *
! 6: * Copyright 2000 Helix Code, Inc. (www.helixcode.com)
! 7: *
! 8: * This program is free software; you can redistribute it and/or modify
! 9: * it under the terms of the GNU General Public License as published by
! 10: * the Free Software Foundation; either version 2 of the License, or
! 11: * (at your option) any later version.
! 12: *
! 13: * This program is distributed in the hope that it will be useful,
! 14: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 15: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 16: * GNU General Public License for more details.
! 17: *
! 18: * You should have received a copy of the GNU General Public License
! 19: * along with this program; if not, write to the Free Software
! 20: * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
! 21: *
! 22: */
! 23:
! 24:
! 25: #ifdef HAVE_CONFIG_H
! 26: #include <config.h>
! 27: #endif
! 28:
! 29: #include <stdio.h>
! 30: #include <stdlib.h>
! 31: #include <string.h>
! 32: #include <ctype.h>
! 33: #include <errno.h>
! 34:
! 35: #ifdef HAVE_ALLOCA_H
! 36: #include <alloca.h>
! 37: #endif
! 38:
! 39: #include "gmime-utils.h"
! 40: #include "gmime-table-private.h"
! 41: #include "gmime-part.h"
! 42: #include "gmime-charset.h"
! 43: #include "gmime-iconv.h"
! 44: #include "gmime-iconv-utils.h"
! 45: #include "unicode.h"
! 46:
! 47: #define d(x)
! 48: #define w(x) x
! 49:
! 50: #ifndef HAVE_ISBLANK
! 51: #define isblank(c) (c == ' ' || c == '\t')
! 52: #endif
! 53:
! 54: #define GMIME_UUENCODE_CHAR(c) ((c) ? (c) + ' ' : '`')
! 55: #define GMIME_UUDECODE_CHAR(c) (((c) - ' ') & 077)
! 56:
! 57: #define GMIME_FOLD_PREENCODED (GMIME_FOLD_LEN / 2)
! 58:
! 59: /* date parser macros */
! 60: #define NUMERIC_CHARS "1234567890"
! 61: #define WEEKDAY_CHARS "SundayMondayTuesdayWednesdayThursdayFridaySaturday"
! 62: #define MONTH_CHARS "JanuaryFebruaryMarchAprilMayJuneJulyAugustSeptemberOctoberNovemberDecember"
! 63: #define TIMEZONE_ALPHA_CHARS "UTCGMTESTEDTCSTCDTMSTPSTPDTZAMNY()"
! 64: #define TIMEZONE_NUMERIC_CHARS "-+1234567890"
! 65: #define TIME_CHARS "1234567890:"
! 66:
! 67: #define DATE_TOKEN_NON_NUMERIC (1 << 0)
! 68: #define DATE_TOKEN_NON_WEEKDAY (1 << 1)
! 69: #define DATE_TOKEN_NON_MONTH (1 << 2)
! 70: #define DATE_TOKEN_NON_TIME (1 << 3)
! 71: #define DATE_TOKEN_HAS_COLON (1 << 4)
! 72: #define DATE_TOKEN_NON_TIMEZONE_ALPHA (1 << 5)
! 73: #define DATE_TOKEN_NON_TIMEZONE_NUMERIC (1 << 6)
! 74: #define DATE_TOKEN_HAS_SIGN (1 << 7)
! 75:
! 76: /* from gmime.c */
! 77: extern int gmime_interfaces_utf8;
! 78:
! 79: static char *base64_alphabet =
! 80: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
! 81:
! 82: static unsigned char tohex[16] = {
! 83: '0', '1', '2', '3', '4', '5', '6', '7',
! 84: '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
! 85: };
! 86:
! 87: static unsigned char gmime_base64_rank[256] = {
! 88: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
! 89: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
! 90: 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
! 91: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
! 92: 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
! 93: 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
! 94: 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
! 95: 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
! 96: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
! 97: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
! 98: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
! 99: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
! 100: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
! 101: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
! 102: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
! 103: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
! 104: };
! 105:
! 106: static unsigned char gmime_uu_rank[256] = {
! 107: 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
! 108: 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
! 109: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
! 110: 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
! 111: 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
! 112: 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
! 113: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
! 114: 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
! 115: 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
! 116: 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
! 117: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
! 118: 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
! 119: 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
! 120: 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
! 121: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
! 122: 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
! 123: };
! 124:
! 125: static unsigned char gmime_datetok_table[256] = {
! 126: 128,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
! 127: 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
! 128: 111,111,111,111,111,111,111,111, 79, 79,111,175,111,175,111,111,
! 129: 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,119,111,111,111,111,111,
! 130: 111, 75,111, 79, 75, 79,105, 79,111,111,107,111,111, 73, 75,107,
! 131: 79,111,111, 73, 77, 79,111,109,111, 79, 79,111,111,111,111,111,
! 132: 111,105,107,107,109,105,111,107,105,105,111,111,107,107,105,105,
! 133: 107,111,105,105,105,105,107,111,111,105,111,111,111,111,111,111,
! 134: 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
! 135: 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
! 136: 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
! 137: 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
! 138: 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
! 139: 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
! 140: 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
! 141: 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
! 142: };
! 143:
! 144: /* hrm, is there a library for this shit? */
! 145: static struct {
! 146: char *name;
! 147: int offset;
! 148: } tz_offsets [] = {
! 149: { "UT", 0 },
! 150: { "GMT", 0 },
! 151: { "EST", -500 }, /* these are all US timezones. bloody yanks */
! 152: { "EDT", -400 },
! 153: { "CST", -600 },
! 154: { "CDT", -500 },
! 155: { "MST", -700 },
! 156: { "MDT", -600 },
! 157: { "PST", -800 },
! 158: { "PDT", -700 },
! 159: { "Z", 0 },
! 160: { "A", -100 },
! 161: { "M", -1200 },
! 162: { "N", 100 },
! 163: { "Y", 1200 },
! 164: };
! 165:
! 166: static char *tm_months[] = {
! 167: "Jan", "Feb", "Mar", "Apr", "May", "Jun",
! 168: "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
! 169: };
! 170:
! 171: static char *tm_days[] = {
! 172: "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
! 173: };
! 174:
! 175:
! 176: /**
! 177: * g_mime_utils_header_format_date:
! 178: * @time: time_t date representation
! 179: * @offset: Timezone offset
! 180: *
! 181: * Allocates a string buffer containing the rfc822 formatted date
! 182: * string represented by @time and @offset.
! 183: *
! 184: * Returns a valid string representation of the date.
! 185: **/
! 186: char *
! 187: g_mime_utils_header_format_date (time_t time, int offset)
! 188: {
! 189: struct tm tm;
! 190:
! 191: time += ((offset / 100) * (60 * 60)) + (offset % 100) * 60;
! 192:
! 193: memcpy (&tm, gmtime (&time), sizeof (tm));
! 194:
! 195: return g_strdup_printf ("%s, %02d %s %04d %02d:%02d:%02d %+05d",
! 196: tm_days[tm.tm_wday], tm.tm_mday,
! 197: tm_months[tm.tm_mon],
! 198: tm.tm_year + 1900,
! 199: tm.tm_hour, tm.tm_min, tm.tm_sec,
! 200: offset);
! 201: }
! 202:
! 203: /* This is where it gets ugly... */
! 204:
! 205: struct _date_token {
! 206: struct _date_token *next;
! 207: const unsigned char *start;
! 208: unsigned int len;
! 209: unsigned int mask;
! 210: };
! 211:
! 212: static struct _date_token *
! 213: datetok (const char *date)
! 214: {
! 215: struct _date_token *tokens = NULL, *token, *tail = (struct _date_token *) &tokens;
! 216: const unsigned char *start, *end;
! 217: unsigned int mask;
! 218:
! 219: start = date;
! 220: while (*start) {
! 221: /* kill leading whitespace */
! 222: for ( ; *start && isspace ((int) *start); start++);
! 223:
! 224: mask = 0;
! 225:
! 226: /* find the end of this token */
! 227: for (end = start; *end && !strchr ("-/,\t\r\n ", *end); end++) {
! 228: mask |= gmime_datetok_table[*end];
! 229: }
! 230:
! 231: if (end != start) {
! 232: token = g_malloc (sizeof (struct _date_token));
! 233: token->next = NULL;
! 234: token->start = start;
! 235: token->len = end - start;
! 236: token->mask = mask;
! 237:
! 238: tail->next = token;
! 239: tail = token;
! 240: }
! 241:
! 242: if (*end)
! 243: start = end + 1;
! 244: else
! 245: break;
! 246: }
! 247:
! 248: return tokens;
! 249: }
! 250:
! 251: static int
! 252: decode_int (const unsigned char *in, unsigned int inlen)
! 253: {
! 254: register const unsigned char *inptr;
! 255: const unsigned char *inend;
! 256: int sign = 1, val = 0;
! 257:
! 258: inptr = in;
! 259: inend = in + inlen;
! 260:
! 261: if (*inptr == '-') {
! 262: sign = -1;
! 263: inptr++;
! 264: } else if (*inptr == '+')
! 265: inptr++;
! 266:
! 267: for ( ; inptr < inend; inptr++) {
! 268: if (!isdigit ((int) *inptr))
! 269: return -1;
! 270: else
! 271: val = (val * 10) + (*inptr - '0');
! 272: }
! 273:
! 274: val *= sign;
! 275:
! 276: return val;
! 277: }
! 278:
! 279: #if 0
! 280: static int
! 281: get_days_in_month (int month, int year)
! 282: {
! 283: switch (month) {
! 284: case 1:
! 285: case 3:
! 286: case 5:
! 287: case 7:
! 288: case 8:
! 289: case 10:
! 290: case 12:
! 291: return 31;
! 292: case 4:
! 293: case 6:
! 294: case 9:
! 295: case 11:
! 296: return 30;
! 297: case 2:
! 298: if (g_date_is_leap_year (year))
! 299: return 29;
! 300: else
! 301: return 28;
! 302: default:
! 303: return 0;
! 304: }
! 305: }
! 306: #endif
! 307:
! 308: static int
! 309: get_wday (const unsigned char *in, unsigned int inlen)
! 310: {
! 311: int wday;
! 312:
! 313: g_return_val_if_fail (in != NULL, -1);
! 314:
! 315: if (inlen < 3)
! 316: return -1;
! 317:
! 318: for (wday = 0; wday < 7; wday++)
! 319: if (!strncasecmp (in, tm_days[wday], 3))
! 320: return wday;
! 321:
! 322: return -1; /* unknown week day */
! 323: }
! 324:
! 325: static int
! 326: get_mday (const unsigned char *in, unsigned int inlen)
! 327: {
! 328: int mday;
! 329:
! 330: g_return_val_if_fail (in != NULL, -1);
! 331:
! 332: mday = decode_int (in, inlen);
! 333:
! 334: if (mday < 0 || mday > 31)
! 335: mday = -1;
! 336:
! 337: return mday;
! 338: }
! 339:
! 340: static int
! 341: get_month (const unsigned char *in, unsigned int inlen)
! 342: {
! 343: int i;
! 344:
! 345: g_return_val_if_fail (in != NULL, -1);
! 346:
! 347: if (inlen < 3)
! 348: return -1;
! 349:
! 350: for (i = 0; i < 12; i++)
! 351: if (!strncasecmp (in, tm_months[i], 3))
! 352: return i;
! 353:
! 354: return -1; /* unknown month */
! 355: }
! 356:
! 357: static int
! 358: get_year (const unsigned char *in, unsigned int inlen)
! 359: {
! 360: int year;
! 361:
! 362: g_return_val_if_fail (in != NULL, -1);
! 363:
! 364: year = decode_int (in, inlen);
! 365: if (year == -1)
! 366: return -1;
! 367:
! 368: if (year < 100)
! 369: year += (year < 70) ? 2000 : 1900;
! 370:
! 371: if (year < 1969)
! 372: return -1;
! 373:
! 374: return year;
! 375: }
! 376:
! 377: static gboolean
! 378: get_time (const unsigned char *in, unsigned int inlen, int *hour, int *min, int *sec)
! 379: {
! 380: register const unsigned char *inptr;
! 381: const unsigned char *inend;
! 382: int *val, colons = 0;
! 383:
! 384: *hour = *min = *sec = 0;
! 385:
! 386: inend = in + inlen;
! 387: val = hour;
! 388: for (inptr = in; inptr < inend; inptr++) {
! 389: if (*inptr == ':') {
! 390: colons++;
! 391: switch (colons) {
! 392: case 1:
! 393: val = min;
! 394: break;
! 395: case 2:
! 396: val = sec;
! 397: break;
! 398: default:
! 399: return FALSE;
! 400: }
! 401: } else if (!isdigit ((int) *inptr))
! 402: return FALSE;
! 403: else
! 404: *val = (*val * 10) + (*inptr - '0');
! 405: }
! 406:
! 407: return TRUE;
! 408: }
! 409:
! 410: static int
! 411: get_tzone (struct _date_token **token)
! 412: {
! 413: int i;
! 414:
! 415: for (i = 0; *token && i < 2; *token = (*token)->next, i++) {
! 416: const unsigned char *inptr = (*token)->start;
! 417: unsigned int inlen = (*token)->len;
! 418:
! 419: if (*inptr == '+' || *inptr == '-') {
! 420: return decode_int (inptr, inlen);
! 421: } else {
! 422: int t;
! 423:
! 424: if (*inptr == '(')
! 425: inptr++;
! 426:
! 427: for (t = 0; t < 15; t++) {
! 428: unsigned int len = MIN (strlen (tz_offsets[t].name), inlen - 1);
! 429:
! 430: if (!strncmp (inptr, tz_offsets[t].name, len))
! 431: return tz_offsets[t].offset;
! 432: }
! 433: }
! 434: }
! 435:
! 436: return -1;
! 437: }
! 438:
! 439: static time_t
! 440: parse_rfc822_date (struct _date_token *tokens, int *tzone)
! 441: {
! 442: int hour, min, sec, offset, n;
! 443: struct _date_token *token;
! 444: struct tm tm;
! 445: time_t t;
! 446:
! 447: g_return_val_if_fail (tokens != NULL, (time_t) 0);
! 448:
! 449: token = tokens;
! 450:
! 451: memset ((void *) &tm, 0, sizeof (struct tm));
! 452:
! 453: if ((n = get_wday (token->start, token->len)) != -1) {
! 454: /* not all dates may have this... */
! 455: tm.tm_wday = n;
! 456: token = token->next;
! 457: }
! 458:
! 459: /* get the mday */
! 460: if (!token || (n = get_mday (token->start, token->len)) == -1)
! 461: return (time_t) 0;
! 462:
! 463: tm.tm_mday = n;
! 464: token = token->next;
! 465:
! 466: /* get the month */
! 467: if (!token || (n = get_month (token->start, token->len)) == -1)
! 468: return (time_t) 0;
! 469:
! 470: tm.tm_mon = n;
! 471: token = token->next;
! 472:
! 473: /* get the year */
! 474: if (!token || (n = get_year (token->start, token->len)) == -1)
! 475: return (time_t) 0;
! 476:
! 477: tm.tm_year = n - 1900;
! 478: token = token->next;
! 479:
! 480: /* get the hour/min/sec */
! 481: if (!token || !get_time (token->start, token->len, &hour, &min, &sec))
! 482: return (time_t) 0;
! 483:
! 484: tm.tm_hour = hour;
! 485: tm.tm_min = min;
! 486: tm.tm_sec = sec;
! 487: token = token->next;
! 488:
! 489: /* get the timezone */
! 490: if (!token || (n = get_tzone (&token)) == -1) {
! 491: /* I guess we assume tz is GMT? */
! 492: offset = 0;
! 493: } else {
! 494: offset = n;
! 495: }
! 496:
! 497: t = mktime (&tm);
! 498: #if defined (HAVE_TIMEZONE)
! 499: t -= timezone;
! 500: #elif defined (HAVE_TM_GMTOFF)
! 501: t += tm.tm_gmtoff;
! 502: #else
! 503: #error Neither HAVE_TIMEZONE nor HAVE_TM_GMTOFF defined. Rerun autoheader, autoconf, etc.
! 504: #endif
! 505:
! 506: /* t is now GMT of the time we want, but not offset by the timezone ... */
! 507:
! 508: /* this should convert the time to the GMT equiv time */
! 509: t -= ((offset / 100) * 60 * 60) + (offset % 100) * 60;
! 510:
! 511: if (tzone)
! 512: *tzone = offset;
! 513:
! 514: return t;
! 515: }
! 516:
! 517:
! 518: #define date_token_mask(t) (((struct _date_token *) t)->mask)
! 519: #define is_numeric(t) ((date_token_mask (t) & DATE_TOKEN_NON_NUMERIC) == 0)
! 520: #define is_weekday(t) ((date_token_mask (t) & DATE_TOKEN_NON_WEEKDAY) == 0)
! 521: #define is_month(t) ((date_token_mask (t) & DATE_TOKEN_NON_MONTH) == 0)
! 522: #define is_time(t) (((date_token_mask (t) & DATE_TOKEN_NON_TIME) == 0) && (date_token_mask (t) & DATE_TOKEN_HAS_COLON))
! 523: #define is_tzone_alpha(t) ((date_token_mask (t) & DATE_TOKEN_NON_TIMEZONE_ALPHA) == 0)
! 524: #define is_tzone_numeric(t) (((date_token_mask (t) & DATE_TOKEN_NON_TIMEZONE_NUMERIC) == 0) && (date_token_mask (t) & DATE_TOKEN_HAS_SIGN))
! 525: #define is_tzone(t) (is_tzone_alpha (t) || is_tzone_numeric (t))
! 526:
! 527: static time_t
! 528: parse_broken_date (struct _date_token *tokens, int *tzone)
! 529: {
! 530: gboolean got_wday, got_month, got_tzone;
! 531: int hour, min, sec, offset, n;
! 532: struct _date_token *token;
! 533: struct tm tm;
! 534: time_t t;
! 535:
! 536: memset ((void *) &tm, 0, sizeof (struct tm));
! 537: got_wday = got_month = got_tzone = FALSE;
! 538: offset = 0;
! 539:
! 540: token = tokens;
! 541: while (token) {
! 542: if (is_weekday (token) && !got_wday) {
! 543: if ((n = get_wday (token->start, token->len)) != -1) {
! 544: d(printf ("weekday; "));
! 545: got_wday = TRUE;
! 546: tm.tm_wday = n;
! 547: goto next_token;
! 548: }
! 549: }
! 550:
! 551: if (is_month (token) && !got_month) {
! 552: if ((n = get_month (token->start, token->len)) != -1) {
! 553: d(printf ("month; "));
! 554: got_month = TRUE;
! 555: tm.tm_mon = n;
! 556: goto next_token;
! 557: }
! 558: }
! 559:
! 560: if (is_time (token) && !tm.tm_hour && !tm.tm_min && !tm.tm_sec) {
! 561: if (get_time (token->start, token->len, &hour, &min, &sec)) {
! 562: d(printf ("time; "));
! 563: tm.tm_hour = hour;
! 564: tm.tm_min = min;
! 565: tm.tm_sec = sec;
! 566: goto next_token;
! 567: }
! 568: }
! 569:
! 570: if (is_tzone (token) && !got_tzone) {
! 571: struct _date_token *t = token;
! 572:
! 573: if ((n = get_tzone (&t)) != -1) {
! 574: d(printf ("tzone; "));
! 575: got_tzone = TRUE;
! 576: offset = n;
! 577: goto next_token;
! 578: }
! 579: }
! 580:
! 581: if (is_numeric (token)) {
! 582: if (token->len == 4 && !tm.tm_year) {
! 583: if ((n = get_year (token->start, token->len)) != -1) {
! 584: d(printf ("year; "));
! 585: tm.tm_year = n - 1900;
! 586: goto next_token;
! 587: }
! 588: } else {
! 589: if (!got_month && !got_wday && token->next && is_numeric (token->next)) {
! 590: d(printf ("mon; "));
! 591: n = decode_int (token->start, token->len);
! 592: got_month = TRUE;
! 593: tm.tm_mon = n - 1;
! 594: goto next_token;
! 595: } else if (!tm.tm_mday && (n = get_mday (token->start, token->len)) != -1) {
! 596: d(printf ("mday; "));
! 597: tm.tm_mday = n;
! 598: goto next_token;
! 599: } else if (!tm.tm_year) {
! 600: d(printf ("2-digit year; "));
! 601: n = get_year (token->start, token->len);
! 602: tm.tm_year = n - 1900;
! 603: goto next_token;
! 604: }
! 605: }
! 606: }
! 607:
! 608: d(printf ("???; "));
! 609:
! 610: next_token:
! 611:
! 612: token = token->next;
! 613: }
! 614:
! 615: d(printf ("\n"));
! 616:
! 617: t = mktime (&tm);
! 618: #if defined (HAVE_TIMEZONE)
! 619: t -= timezone;
! 620: #elif defined (HAVE_TM_GMTOFF)
! 621: t += tm.tm_gmtoff;
! 622: #else
! 623: #error Neither HAVE_TIMEZONE nor HAVE_TM_GMTOFF defined. Rerun autoheader, autoconf, etc.
! 624: #endif
! 625:
! 626: /* t is now GMT of the time we want, but not offset by the timezone ... */
! 627:
! 628: /* this should convert the time to the GMT equiv time */
! 629: t -= ((offset / 100) * 60 * 60) + (offset % 100) * 60;
! 630:
! 631: if (tzone)
! 632: *tzone = offset;
! 633:
! 634: return t;
! 635: }
! 636:
! 637: #if 0
! 638: static void
! 639: gmime_datetok_table_init ()
! 640: {
! 641: int i;
! 642:
! 643: memset (gmime_datetok_table, 0, sizeof (gmime_datetok_table));
! 644:
! 645: for (i = 0; i < 256; i++) {
! 646: if (!strchr (NUMERIC_CHARS, i))
! 647: gmime_datetok_table[i] |= DATE_TOKEN_NON_NUMERIC;
! 648:
! 649: if (!strchr (WEEKDAY_CHARS, i))
! 650: gmime_datetok_table[i] |= DATE_TOKEN_NON_WEEKDAY;
! 651:
! 652: if (!strchr (MONTH_CHARS, i))
! 653: gmime_datetok_table[i] |= DATE_TOKEN_NON_MONTH;
! 654:
! 655: if (!strchr (TIME_CHARS, i))
! 656: gmime_datetok_table[i] |= DATE_TOKEN_NON_TIME;
! 657:
! 658: if (!strchr (TIMEZONE_ALPHA_CHARS, i))
! 659: gmime_datetok_table[i] |= DATE_TOKEN_NON_TIMEZONE_ALPHA;
! 660:
! 661: if (!strchr (TIMEZONE_NUMERIC_CHARS, i))
! 662: gmime_datetok_table[i] |= DATE_TOKEN_NON_TIMEZONE_NUMERIC;
! 663:
! 664: if (((char) i) == ':')
! 665: gmime_datetok_table[i] |= DATE_TOKEN_HAS_COLON;
! 666:
! 667: if (strchr ("+-", i))
! 668: gmime_datetok_table[i] |= DATE_TOKEN_HAS_SIGN;
! 669: }
! 670:
! 671: printf ("static unsigned char gmime_datetok_table[256] = {");
! 672: for (i = 0; i < 256; i++) {
! 673: if (i % 16 == 0)
! 674: printf ("\n\t");
! 675: printf ("%3d,", gmime_datetok_table[i]);
! 676: }
! 677: printf ("\n};\n");
! 678: }
! 679: #endif
! 680:
! 681:
! 682: /**
! 683: * g_mime_utils_header_decode_date:
! 684: * @in: input date string
! 685: * @saveoffset:
! 686: *
! 687: * Decodes the rfc822 date string and saves the GMT offset into
! 688: * @saveoffset if non-NULL.
! 689: *
! 690: * Returns the time_t representation of the date string specified by
! 691: * @in. If 'saveoffset' is non-NULL, the value of the timezone offset
! 692: * will be stored.
! 693: **/
! 694: time_t
! 695: g_mime_utils_header_decode_date (const char *in, int *saveoffset)
! 696: {
! 697: struct _date_token *token, *tokens;
! 698: time_t date;
! 699:
! 700: tokens = datetok (in);
! 701:
! 702: date = parse_rfc822_date (tokens, saveoffset);
! 703: if (!date)
! 704: date = parse_broken_date (tokens, saveoffset);
! 705:
! 706: /* cleanup */
! 707: while (tokens) {
! 708: token = tokens;
! 709: tokens = tokens->next;
! 710: g_free (token);
! 711: }
! 712:
! 713: return date;
! 714: }
! 715:
! 716: /*
! 717: static void
! 718: g_string_append_len (GString *out, const char *in, size_t len)
! 719: {
! 720: char *buf;
! 721:
! 722: buf = alloca (len + 1);
! 723: strlcpy (buf, in, len);
! 724:
! 725: g_string_append (out, buf);
! 726: }
! 727: */
! 728: /**
! 729: * g_mime_utils_header_fold:
! 730: * @in: input header string
! 731: *
! 732: * Folds a header according to the rules in rfc822.
! 733: *
! 734: * Returns an allocated string containing the folded header.
! 735: **/
! 736: char *
! 737: g_mime_utils_header_fold (const char *in)
! 738: {
! 739: gboolean last_was_lwsp = FALSE;
! 740: register const char *inptr;
! 741: size_t len, outlen, i;
! 742: GString *out;
! 743: char *ret;
! 744:
! 745: inptr = in;
! 746: len = strlen (in);
! 747: if (len <= GMIME_FOLD_LEN)
! 748: return g_strdup (in);
! 749:
! 750: out = g_string_new ("");
! 751: outlen = 0;
! 752: while (*inptr) {
! 753: len = strcspn (inptr, " \t");
! 754:
! 755: if (outlen + len > GMIME_FOLD_LEN) {
! 756: if (last_was_lwsp)
! 757: g_string_truncate (out, out->len - 1);
! 758:
! 759: g_string_append (out, "\n\t");
! 760: outlen = 1;
! 761:
! 762: /* check for very long words, just cut them up */
! 763: while (outlen + len > GMIME_FOLD_LEN) {
! 764: for (i = 0; i < GMIME_FOLD_LEN - outlen; i++)
! 765: g_string_append_c (out, inptr[i]);
! 766: inptr += GMIME_FOLD_LEN - outlen;
! 767: len -= GMIME_FOLD_LEN - outlen;
! 768: g_string_append (out, "\n\t");
! 769: outlen = 1;
! 770: }
! 771: last_was_lwsp = FALSE;
! 772: } else if (len > 0) {
! 773: outlen += len;
! 774: g_string_append_len (out, inptr, len);
! 775: inptr += len;
! 776: last_was_lwsp = FALSE;
! 777: } else {
! 778: if (*inptr == '\t') {
! 779: /* tabs are a good place to fold, odds
! 780: are that this is where the previous
! 781: mailer folded it */
! 782: g_string_append (out, "\n\t");
! 783: outlen = 1;
! 784: inptr++;
! 785: last_was_lwsp = FALSE;
! 786: } else {
! 787: g_string_append_c (out, *inptr++);
! 788: outlen++;
! 789: last_was_lwsp = TRUE;
! 790: }
! 791: }
! 792: }
! 793:
! 794: ret = out->str;
! 795: g_string_free (out, FALSE);
! 796:
! 797: return ret;
! 798: }
! 799:
! 800:
! 801: /**
! 802: * g_mime_utils_header_printf:
! 803: * @format: string format
! 804: * @Varargs: arguments
! 805: *
! 806: * Allocates a buffer containing a formatted header specified by the
! 807: * @Varargs.
! 808: *
! 809: * Returns an allocated string containing the folded header specified
! 810: * by @format and the following arguments.
! 811: **/
! 812: char *
! 813: g_mime_utils_header_printf (const char *format, ...)
! 814: {
! 815: char *buf, *ret;
! 816: va_list ap;
! 817:
! 818: va_start (ap, format);
! 819: buf = g_strdup_vprintf (format, ap);
! 820: va_end (ap);
! 821:
! 822: ret = g_mime_utils_header_fold (buf);
! 823: g_free (buf);
! 824:
! 825: return ret;
! 826: }
! 827:
! 828: static gboolean
! 829: need_quotes (const char *string)
! 830: {
! 831: gboolean quoted = FALSE;
! 832: const char *inptr;
! 833:
! 834: inptr = string;
! 835:
! 836: while (*inptr) {
! 837: if (*inptr == '\\')
! 838: inptr++;
! 839: else if (*inptr == '"')
! 840: quoted = !quoted;
! 841: else if (!quoted && (is_tspecial (*inptr) || *inptr == '.'))
! 842: return TRUE;
! 843:
! 844: if (*inptr)
! 845: inptr++;
! 846: }
! 847:
! 848: return FALSE;
! 849: }
! 850:
! 851: /**
! 852: * g_mime_utils_quote_string:
! 853: * @string: input string
! 854: *
! 855: * Quotes @string as needed according to the rules in rfc2045.
! 856: *
! 857: * Returns an allocated string containing the escaped and quoted (if
! 858: * needed to be) input string. The decision to quote the string is
! 859: * based on whether or not the input string contains any 'tspecials'
! 860: * as defined by rfc2045.
! 861: **/
! 862: char *
! 863: g_mime_utils_quote_string (const char *string)
! 864: {
! 865: gboolean quote;
! 866: const char *c;
! 867: char *qstring;
! 868: GString *out;
! 869:
! 870: out = g_string_new ("");
! 871: quote = need_quotes (string);
! 872:
! 873: for (c = string; *c; c++) {
! 874: if ((*c == '"' && quote) || *c == '\\')
! 875: g_string_append_c (out, '\\');
! 876:
! 877: g_string_append_c (out, *c);
! 878: }
! 879:
! 880: if (quote) {
! 881: g_string_prepend_c (out, '"');
! 882: g_string_append_c (out, '"');
! 883: }
! 884:
! 885: qstring = out->str;
! 886: g_string_free (out, FALSE);
! 887:
! 888: return qstring;
! 889: }
! 890:
! 891:
! 892: /**
! 893: * g_mime_utils_unquote_string: Unquote a string.
! 894: * @string: string
! 895: *
! 896: * Unquotes and unescapes a string.
! 897: **/
! 898: void
! 899: g_mime_utils_unquote_string (char *string)
! 900: {
! 901: /* if the string is quoted, unquote it */
! 902: char *inptr, *inend;
! 903:
! 904: if (!string)
! 905: return;
! 906:
! 907: inptr = string;
! 908: inend = string + strlen (string);
! 909:
! 910: /* get rid of the wrapping quotes */
! 911: if (*inptr == '"' && *(inend - 1) == '"') {
! 912: inend--;
! 913: *inend = '\0';
! 914: if (*inptr)
! 915: memmove (inptr, inptr + 1, inend - inptr);
! 916: }
! 917:
! 918: /* un-escape the string */
! 919: inend--;
! 920: while (inptr < inend) {
! 921: if (*inptr == '\\') {
! 922: memmove (inptr, inptr + 1, inend - inptr);
! 923: inend--;
! 924: }
! 925:
! 926: inptr++;
! 927: }
! 928: }
! 929:
! 930:
! 931: /**
! 932: * g_mime_utils_text_is_8bit:
! 933: * @text: text to check for 8bit chars
! 934: * @len: text length
! 935: *
! 936: * Determines if @text contains 8bit characters within the first @len
! 937: * bytes.
! 938: *
! 939: * Returns TRUE if the text contains 8bit characters or FALSE
! 940: * otherwise.
! 941: **/
! 942: gboolean
! 943: g_mime_utils_text_is_8bit (const unsigned char *text, size_t len)
! 944: {
! 945: const unsigned char *c, *inend;
! 946:
! 947: g_return_val_if_fail (text != NULL, FALSE);
! 948:
! 949: inend = text + len;
! 950: for (c = text; c < inend; c++)
! 951: if (*c > (unsigned char) 127)
! 952: return TRUE;
! 953:
! 954: return FALSE;
! 955: }
! 956:
! 957:
! 958: /**
! 959: * g_mime_utils_best_encoding:
! 960: * @text: text to encode
! 961: * @len: text length
! 962: *
! 963: * Determines the best content encoding for the first @len bytes of
! 964: * @text.
! 965: *
! 966: * Returns a GMimePartEncodingType that is determined to be the best
! 967: * encoding type for the specified block of text. ("best" in this
! 968: * particular case means best compression)
! 969: **/
! 970: GMimePartEncodingType
! 971: g_mime_utils_best_encoding (const unsigned char *text, size_t len)
! 972: {
! 973: const unsigned char *ch, *inend;
! 974: size_t count = 0;
! 975:
! 976: inend = text + len;
! 977: for (ch = text; ch < inend; ch++)
! 978: if (*ch > (unsigned char) 127)
! 979: count++;
! 980:
! 981: if ((float) count <= len * 0.17)
! 982: return GMIME_PART_ENCODING_QUOTEDPRINTABLE;
! 983: else
! 984: return GMIME_PART_ENCODING_BASE64;
! 985: }
! 986:
! 987: /* this decodes rfc2047's version of quoted-printable */
! 988: static ssize_t
! 989: quoted_decode (const unsigned char *in, size_t len, unsigned char *out)
! 990: {
! 991: register const unsigned char *inptr;
! 992: register unsigned char *outptr;
! 993: const unsigned char *inend;
! 994: unsigned char c, c1;
! 995:
! 996: inend = in + len;
! 997: outptr = out;
! 998:
! 999: inptr = in;
! 1000: while (inptr < inend) {
! 1001: c = *inptr++;
! 1002: if (c == '=') {
! 1003: if (inend - inptr >= 2) {
! 1004: c = toupper (*inptr++);
! 1005: c1 = toupper (*inptr++);
! 1006: *outptr++ = (((c >= 'A' ? c - 'A' + 10 : c - '0') & 0x0f) << 4)
! 1007: | ((c1 >= 'A' ? c1 - 'A' + 10 : c1 - '0') & 0x0f);
! 1008: } else {
! 1009: /* data was truncated */
! 1010: return -1;
! 1011: }
! 1012: } else if (c == '_') {
! 1013: /* _'s are an rfc2047 shortcut for encoding spaces */
! 1014: *outptr++ = ' ';
! 1015: } else {
! 1016: *outptr++ = c;
! 1017: }
! 1018: }
! 1019:
! 1020: return (outptr - out);
! 1021: }
! 1022:
! 1023: #define is_rfc2047_encoded_word(atom, len) (len >= 7 && !strncmp (atom, "=?", 2) && !strncmp (atom + len - 2, "?=", 2))
! 1024:
! 1025: static unsigned char *
! 1026: rfc2047_decode_word (const unsigned char *in, size_t inlen)
! 1027: {
! 1028: const register unsigned char *inptr;
! 1029: const unsigned char *inend;
! 1030:
! 1031: inptr = in + 2;
! 1032: inend = in + inlen - 2;
! 1033:
! 1034: inptr = memchr (inptr, '?', inend - inptr);
! 1035: if (inptr && inptr[2] == '?') {
! 1036: unsigned char *decoded;
! 1037: ssize_t declen;
! 1038: int state = 0;
! 1039: int save = 0;
! 1040:
! 1041: inptr++;
! 1042:
! 1043: switch (*inptr) {
! 1044: case 'B':
! 1045: case 'b':
! 1046: inptr += 2;
! 1047: decoded = alloca (inend - inptr);
! 1048: declen = g_mime_utils_base64_decode_step (inptr, inend - inptr, decoded, &state, &save);
! 1049: break;
! 1050: case 'Q':
! 1051: case 'q':
! 1052: inptr += 2;
! 1053: decoded = alloca (inend - inptr);
! 1054: declen = quoted_decode (inptr, inend - inptr, decoded);
! 1055:
! 1056: if (declen == -1) {
! 1057: d(fprintf (stderr, "encountered broken 'Q' encoding\n"));
! 1058: return NULL;
! 1059: }
! 1060: break;
! 1061: default:
! 1062: d(fprintf (stderr, "unknown encoding\n"));
! 1063: return NULL;
! 1064: }
! 1065:
! 1066: if (gmime_interfaces_utf8) {
! 1067: const char *charset;
! 1068: unsigned char *buf;
! 1069: char *charenc, *p;
! 1070: size_t len;
! 1071: iconv_t cd;
! 1072:
! 1073: len = (inptr - 3) - (in + 2);
! 1074: charenc = alloca (len + 1);
! 1075: memcpy (charenc, in + 2, len);
! 1076: charenc[len] = '\0';
! 1077: charset = charenc;
! 1078:
! 1079: /* rfc2231 updates rfc2047 encoded words...
! 1080: * The ABNF given in RFC 2047 for encoded-words is:
! 1081: * encoded-word := "=?" charset "?" encoding "?" encoded-text "?="
! 1082: * This specification changes this ABNF to:
! 1083: * encoded-word := "=?" charset ["*" language] "?" encoding "?" encoded-text "?="
! 1084: */
! 1085:
! 1086: /* trim off the 'language' part if it's there... */
! 1087: p = strchr (charset, '*');
! 1088: if (p)
! 1089: *p = '\0';
! 1090:
! 1091: /* slight optimization */
! 1092: if (!strcasecmp (charset, "UTF-8"))
! 1093: return g_strndup (decoded, declen);
! 1094:
! 1095: cd = g_mime_iconv_open ("UTF-8", charset);
! 1096: if (cd == (iconv_t) -1) {
! 1097: w(g_warning ("Cannot convert from %s to UTF-8, header display may "
! 1098: "be corrupt: %s", charset, g_strerror (errno)));
! 1099: charset = g_mime_charset_locale_name ();
! 1100: cd = g_mime_iconv_open ("UTF-8", charset);
! 1101: if (cd == (iconv_t) -1)
! 1102: return NULL;
! 1103: }
! 1104:
! 1105: buf = g_mime_iconv_strndup (cd, decoded, declen);
! 1106: g_mime_iconv_close (cd);
! 1107:
! 1108: if (!buf) {
! 1109: w(g_warning ("Failed to convert \"%.*s\" to UTF-8, display may be "
! 1110: "corrupt: %s", declen, decoded, g_strerror (errno)));
! 1111: }
! 1112:
! 1113: return buf;
! 1114: } else {
! 1115: return g_strndup (decoded, declen);
! 1116: }
! 1117: }
! 1118:
! 1119: return NULL;
! 1120: }
! 1121:
! 1122:
! 1123: /**
! 1124: * g_mime_utils_8bit_header_decode:
! 1125: * @in: header to decode
! 1126: *
! 1127: * Decodes and rfc2047 encoded header.
! 1128: *
! 1129: * Returns the mime encoded header as 8bit text.
! 1130: **/
! 1131: char *
! 1132: g_mime_utils_8bit_header_decode (const unsigned char *in)
! 1133: {
! 1134: GString *out, *lwsp, *atom;
! 1135: const unsigned char *inptr;
! 1136: unsigned char *decoded;
! 1137: gboolean last_was_encoded = FALSE;
! 1138: gboolean last_was_space = FALSE;
! 1139:
! 1140: out = g_string_sized_new (256);
! 1141: lwsp = g_string_sized_new (256);
! 1142: atom = g_string_sized_new (256);
! 1143: inptr = in;
! 1144:
! 1145: while (inptr && *inptr) {
! 1146: unsigned char c = *inptr++;
! 1147:
! 1148: if (!is_atom (c) && !last_was_space) {
! 1149: /* we reached the end of an atom */
! 1150: unsigned char *dword = NULL;
! 1151: const unsigned char *word;
! 1152: gboolean was_encoded;
! 1153:
! 1154: if ((was_encoded = is_rfc2047_encoded_word (atom->str, atom->len)))
! 1155: word = dword = rfc2047_decode_word (atom->str, atom->len);
! 1156: else
! 1157: word = atom->str;
! 1158:
! 1159: if (word) {
! 1160: if (!(last_was_encoded && was_encoded)) {
! 1161: /* rfc2047 states that you
! 1162: must ignore all whitespace
! 1163: between encoded words */
! 1164: g_string_append (out, lwsp->str);
! 1165: }
! 1166:
! 1167: g_string_append (out, word);
! 1168: g_free (dword);
! 1169: } else {
! 1170: was_encoded = FALSE;
! 1171: g_string_append (out, lwsp->str);
! 1172: g_string_append (out, atom->str);
! 1173: }
! 1174:
! 1175: last_was_encoded = was_encoded;
! 1176:
! 1177: g_string_truncate (lwsp, 0);
! 1178: g_string_truncate (atom, 0);
! 1179:
! 1180: if (is_lwsp (c)) {
! 1181: g_string_append_c (lwsp, c);
! 1182: last_was_space = TRUE;
! 1183: } else {
! 1184: /* This is mostly here for interoperability with broken
! 1185: mailers that might do something stupid like:
! 1186: =?iso-8859-1?Q?blah?=:\t=?iso-8859-1?Q?I_am_broken?= */
! 1187: g_string_append_c (out, c);
! 1188: last_was_encoded = FALSE;
! 1189: last_was_space = FALSE;
! 1190: }
! 1191:
! 1192: continue;
! 1193: }
! 1194:
! 1195: if (is_atom (c)) {
! 1196: g_string_append_c (atom, c);
! 1197: last_was_space = FALSE;
! 1198: } else {
! 1199: g_string_append_c (lwsp, c);
! 1200: last_was_space = TRUE;
! 1201: }
! 1202: }
! 1203:
! 1204: if (atom->len || lwsp->len) {
! 1205: unsigned char *dword = NULL;
! 1206: const unsigned char *word;
! 1207: gboolean was_encoded;
! 1208:
! 1209: if ((was_encoded = is_rfc2047_encoded_word (atom->str, atom->len)))
! 1210: word = dword = rfc2047_decode_word (atom->str, atom->len);
! 1211: else
! 1212: word = atom->str;
! 1213:
! 1214: if (word) {
! 1215: if (!(last_was_encoded && was_encoded)) {
! 1216: /* rfc2047 states that you
! 1217: must ignore all whitespace
! 1218: between encoded words */
! 1219: g_string_append (out, lwsp->str);
! 1220: }
! 1221:
! 1222: g_string_append (out, word);
! 1223: g_free (dword);
! 1224: } else {
! 1225: g_string_append (out, lwsp->str);
! 1226: g_string_append (out, atom->str);
! 1227: }
! 1228: }
! 1229:
! 1230: g_string_free (lwsp, TRUE);
! 1231: g_string_free (atom, TRUE);
! 1232:
! 1233: decoded = out->str;
! 1234: g_string_free (out, FALSE);
! 1235:
! 1236: return (char *) decoded;
! 1237: }
! 1238:
! 1239: /* rfc2047 version of quoted-printable */
! 1240: static size_t
! 1241: quoted_encode (const unsigned char *in, size_t len, unsigned char *out, gushort safemask)
! 1242: {
! 1243: register const unsigned char *inptr;
! 1244: register unsigned char *outptr;
! 1245: const unsigned char *inend;
! 1246: unsigned char c;
! 1247:
! 1248: inptr = in;
! 1249: inend = in + len;
! 1250: outptr = out;
! 1251:
! 1252: while (inptr < inend) {
! 1253: c = *inptr++;
! 1254: if (c == ' ') {
! 1255: *outptr++ = '_';
! 1256: } else if (gmime_special_table[c] & safemask) {
! 1257: *outptr++ = c;
! 1258: } else {
! 1259: *outptr++ = '=';
! 1260: *outptr++ = tohex[(c >> 4) & 0xf];
! 1261: *outptr++ = tohex[c & 0xf];
! 1262: }
! 1263: }
! 1264:
! 1265: return (outptr - out);
! 1266: }
! 1267:
! 1268: static void
! 1269: rfc2047_encode_word (GString *string, const unsigned char *word, size_t len,
! 1270: const char *charset, gushort safemask)
! 1271: {
! 1272: unsigned char *encoded, *ptr;
! 1273: unsigned char *uword = NULL;
! 1274: iconv_t cd = (iconv_t) -1;
! 1275: size_t enclen, pos;
! 1276: int state = 0;
! 1277: int save = 0;
! 1278: char encoding;
! 1279:
! 1280: if (gmime_interfaces_utf8) {
! 1281: if (strcasecmp (charset, "UTF-8") != 0)
! 1282: cd = g_mime_iconv_open (charset, "UTF-8");
! 1283:
! 1284: if (cd != (iconv_t) -1) {
! 1285: uword = g_mime_iconv_strndup (cd, word, len);
! 1286: g_mime_iconv_close (cd);
! 1287: }
! 1288:
! 1289: if (uword) {
! 1290: len = strlen (uword);
! 1291: word = uword;
! 1292: } else {
! 1293: charset = "UTF-8";
! 1294: }
! 1295: }
! 1296:
! 1297: switch (g_mime_utils_best_encoding (word, len)) {
! 1298: case GMIME_PART_ENCODING_BASE64:
! 1299: enclen = BASE64_ENCODE_LEN (len);
! 1300: encoded = alloca (enclen);
! 1301:
! 1302: encoding = 'b';
! 1303:
! 1304: pos = g_mime_utils_base64_encode_close (word, len, encoded, &state, &save);
! 1305: encoded[pos] = '\0';
! 1306:
! 1307: /* remove \n chars as headers need to be wrapped differently */
! 1308: ptr = encoded;
! 1309: while ((ptr = memchr (ptr, '\n', strlen (ptr))))
! 1310: memmove (ptr, ptr + 1, strlen (ptr));
! 1311:
! 1312: break;
! 1313: case GMIME_PART_ENCODING_QUOTEDPRINTABLE:
! 1314: enclen = QP_ENCODE_LEN (len);
! 1315: encoded = alloca (enclen);
! 1316:
! 1317: encoding = 'q';
! 1318:
! 1319: pos = quoted_encode (word, len, encoded, safemask);
! 1320: encoded[pos] = '\0';
! 1321:
! 1322: break;
! 1323: default:
! 1324: g_assert_not_reached ();
! 1325: }
! 1326:
! 1327: g_free (uword);
! 1328:
! 1329: g_string_sprintfa (string, "=?%s?%c?%s?=", charset, encoding, encoded);
! 1330: }
! 1331:
! 1332:
! 1333: /**
! 1334: * g_mime_utils_8bit_header_encode_phrase:
! 1335: * @in: header to encode
! 1336: *
! 1337: * Encodes a header phrase according to the rules in rfc2047.
! 1338: *
! 1339: * Returns the header phrase as 1 encoded atom. Useful for encoding
! 1340: * internet addresses.
! 1341: **/
! 1342: char *
! 1343: g_mime_utils_8bit_header_encode_phrase (const unsigned char *in)
! 1344: {
! 1345: const char *charset;
! 1346: GString *string;
! 1347: size_t len;
! 1348: char *str;
! 1349:
! 1350: if (in == NULL)
! 1351: return NULL;
! 1352:
! 1353: len = strlen (in);
! 1354:
! 1355: if (gmime_interfaces_utf8) {
! 1356: charset = g_mime_charset_best (in, len);
! 1357: charset = charset ? charset : "iso-8859-1";
! 1358: } else {
! 1359: charset = g_mime_charset_locale_name ();
! 1360: }
! 1361:
! 1362: string = g_string_new ("");
! 1363:
! 1364: rfc2047_encode_word (string, in, strlen (in), charset, IS_ESAFE);
! 1365:
! 1366: str = string->str;
! 1367: g_string_free (string, FALSE);
! 1368:
! 1369: return str;
! 1370: }
! 1371:
! 1372:
! 1373: enum _phrase_word_t {
! 1374: WORD_ATOM,
! 1375: WORD_2047
! 1376: };
! 1377:
! 1378: struct _phrase_word {
! 1379: struct _phrase_word *next;
! 1380: const unsigned char *start, *end;
! 1381: enum _phrase_word_t type;
! 1382: int encoding;
! 1383: };
! 1384:
! 1385: static gboolean
! 1386: word_types_compatable (enum _phrase_word_t type1, enum _phrase_word_t type2)
! 1387: {
! 1388: switch (type1) {
! 1389: case WORD_ATOM:
! 1390: return FALSE;
! 1391: case WORD_2047:
! 1392: return type2 == WORD_2047;
! 1393: default:
! 1394: return FALSE;
! 1395: }
! 1396: }
! 1397:
! 1398: static struct _phrase_word *
! 1399: rfc2047_encode_phrase_get_words (const unsigned char *in)
! 1400: {
! 1401: const unsigned char *inptr, *start, *last;
! 1402: struct _phrase_word *words, *tail, *word;
! 1403: enum _phrase_word_t type = WORD_ATOM;
! 1404: int count = 0, encoding = 0;
! 1405:
! 1406: words = NULL;
! 1407: tail = (struct _phrase_word *) &words;
! 1408:
! 1409: last = start = inptr = in;
! 1410: while (inptr && *inptr) {
! 1411: gboolean is_space;
! 1412: unichar c;
! 1413:
! 1414: if (gmime_interfaces_utf8) {
! 1415: const char *newinptr;
! 1416:
! 1417: newinptr = unicode_next_char (inptr);
! 1418: c = unicode_get_char (inptr);
! 1419: if (newinptr == NULL || !unichar_validate (c)) {
! 1420: w(g_warning ("Invalid UTF-8 sequence encountered"));
! 1421: inptr++;
! 1422: continue;
! 1423: }
! 1424:
! 1425: inptr = newinptr;
! 1426:
! 1427: is_space = unichar_isspace (c);
! 1428: } else {
! 1429: is_space = isspace ((int) *inptr);
! 1430: c = *inptr++;
! 1431: }
! 1432:
! 1433: if (is_space) {
! 1434: if (count > 0) {
! 1435: word = g_new (struct _phrase_word, 1);
! 1436: word->next = NULL;
! 1437: word->start = start;
! 1438: word->end = last;
! 1439: word->type = type;
! 1440: word->encoding = encoding;
! 1441:
! 1442: tail->next = word;
! 1443: tail = word;
! 1444: count = 0;
! 1445: }
! 1446:
! 1447: start = inptr;
! 1448: type = WORD_ATOM;
! 1449: encoding = 0;
! 1450: } else {
! 1451: count++;
! 1452: if (c > 127 && c < 256) {
! 1453: type = WORD_2047;
! 1454: encoding = MAX (encoding, 2);
! 1455: } else if (c >= 256) {
! 1456: type = WORD_2047;
! 1457: encoding = 2;
! 1458: }
! 1459: }
! 1460:
! 1461: last = inptr;
! 1462: }
! 1463:
! 1464: if (count > 0) {
! 1465: word = g_new (struct _phrase_word, 1);
! 1466: word->next = NULL;
! 1467: word->start = start;
! 1468: word->end = last;
! 1469: word->type = type;
! 1470: word->encoding = encoding;
! 1471:
! 1472: tail->next = word;
! 1473: tail = word;
! 1474: }
! 1475:
! 1476: return words;
! 1477: }
! 1478:
! 1479: static gboolean
! 1480: rfc2047_encode_phrase_merge_words (struct _phrase_word **wordsp)
! 1481: {
! 1482: struct _phrase_word *word, *next, *words = *wordsp;
! 1483: gboolean merged = FALSE;
! 1484:
! 1485: /* scan the list, checking for words of similar types that can be merged */
! 1486: word = words;
! 1487: while (word) {
! 1488: next = word->next;
! 1489:
! 1490: while (next) {
! 1491: /* merge nodes of the same type AND we are not creating too long a string */
! 1492: if (word_types_compatable (word->type, next->type)) {
! 1493: if (next->end - word->start < GMIME_FOLD_PREENCODED) {
! 1494: /* the resulting word type is the MAX of the 2 types */
! 1495: word->type = MAX (word->type, next->type);
! 1496:
! 1497: word->end = next->end;
! 1498: word->next = next->next;
! 1499:
! 1500: g_free (next);
! 1501:
! 1502: next = word->next;
! 1503:
! 1504: merged = TRUE;
! 1505: } else {
! 1506: /* if it is going to be too long, make sure we include the
! 1507: separating whitespace */
! 1508: word->end = next->start;
! 1509: break;
! 1510: }
! 1511: } else {
! 1512: break;
! 1513: }
! 1514: }
! 1515:
! 1516: word = word->next;
! 1517: }
! 1518:
! 1519: *wordsp = words;
! 1520:
! 1521: return merged;
! 1522: }
! 1523:
! 1524: static char *
! 1525: rfc2047_encode_phrase (const unsigned char *in)
! 1526: {
! 1527: struct _phrase_word *words, *word, *prev = NULL;
! 1528: GString *out;
! 1529: char *outstr;
! 1530:
! 1531: if (in == NULL)
! 1532: return NULL;
! 1533:
! 1534: words = rfc2047_encode_phrase_get_words (in);
! 1535: if (!words)
! 1536: return NULL;
! 1537:
! 1538: while (rfc2047_encode_phrase_merge_words (&words))
! 1539: ;
! 1540:
! 1541: out = g_string_new ("");
! 1542:
! 1543: /* output words now with spaces between them */
! 1544: word = words;
! 1545: while (word) {
! 1546: const char *start;
! 1547: size_t len;
! 1548:
! 1549: /* append correct number of spaces between words */
! 1550: if (prev && !(prev->type == WORD_2047 && word->type == WORD_2047)) {
! 1551: /* one or both of the words are not encoded so we write the spaces out untouched */
! 1552: len = word->start - prev->end;
! 1553: g_string_append_len (out, prev->end, len);
! 1554: }
! 1555:
! 1556: switch (word->type) {
! 1557: case WORD_ATOM:
! 1558: g_string_append_len (out, word->start, word->end - word->start);
! 1559: break;
! 1560: case WORD_2047:
! 1561: if (prev && prev->type == WORD_2047) {
! 1562: /* include the whitespace chars between these 2 words in the
! 1563: resulting rfc2047 encoded word. */
! 1564: len = word->end - prev->end;
! 1565: start = prev->end;
! 1566:
! 1567: /* encoded words need to be separated by linear whitespace */
! 1568: g_string_append_c (out, ' ');
! 1569: } else {
! 1570: len = word->end - word->start;
! 1571: start = word->start;
! 1572: }
! 1573:
! 1574: if (word->encoding == 1 || !gmime_interfaces_utf8)
! 1575: rfc2047_encode_word (out, start, len, "iso-8859-1", IS_PSAFE);
! 1576: else
! 1577: rfc2047_encode_word (out, start, len,
! 1578: g_mime_charset_best (start, len), IS_PSAFE);
! 1579: break;
! 1580: }
! 1581:
! 1582: g_free (prev);
! 1583: prev = word;
! 1584: word = word->next;
! 1585: }
! 1586:
! 1587: g_free (prev);
! 1588:
! 1589: outstr = out->str;
! 1590: g_string_free (out, FALSE);
! 1591:
! 1592: return outstr;
! 1593: }
! 1594:
! 1595:
! 1596: /**
! 1597: * g_mime_utils_8bit_header_encode:
! 1598: * @in: header to encode
! 1599: *
! 1600: * Encodes a header according to the rules in rfc2047.
! 1601: *
! 1602: * Returns the header as several encoded atoms. Useful for encoding
! 1603: * headers like "Subject".
! 1604: **/
! 1605: char *
! 1606: g_mime_utils_8bit_header_encode (const unsigned char *in)
! 1607: {
! 1608: return rfc2047_encode_phrase (in);
! 1609: }
! 1610:
! 1611:
! 1612: /**
! 1613: * g_mime_utils_base64_encode_close:
! 1614: * @in: input stream
! 1615: * @inlen: length of the input
! 1616: * @out: output string
! 1617: * @state: holds the number of bits that are stored in @save
! 1618: * @save: leftover bits that have not yet been encoded
! 1619: *
! 1620: * Base64 encodes the input stream to the output stream. Call this
! 1621: * when finished encoding data with g_mime_utils_base64_encode_step to
! 1622: * flush off the last little bit.
! 1623: *
! 1624: * Returns the number of bytes encoded.
! 1625: **/
! 1626: size_t
! 1627: g_mime_utils_base64_encode_close (const unsigned char *in, size_t inlen, unsigned char *out, int *state, guint32 *save)
! 1628: {
! 1629: unsigned char *outptr = out;
! 1630: int c1, c2;
! 1631:
! 1632: if (inlen > 0)
! 1633: outptr += g_mime_utils_base64_encode_step (in, inlen, outptr, state, save);
! 1634:
! 1635: c1 = ((unsigned char *)save)[1];
! 1636: c2 = ((unsigned char *)save)[2];
! 1637:
! 1638: switch (((unsigned char *)save)[0]) {
! 1639: case 2:
! 1640: outptr[2] = base64_alphabet [(c2 & 0x0f) << 2];
! 1641: goto skip;
! 1642: case 1:
! 1643: outptr[2] = '=';
! 1644: skip:
! 1645: outptr[0] = base64_alphabet [c1 >> 2];
! 1646: outptr[1] = base64_alphabet [c2 >> 4 | ((c1 & 0x3) << 4)];
! 1647: outptr[3] = '=';
! 1648: outptr += 4;
! 1649: break;
! 1650: }
! 1651:
! 1652: *outptr++ = '\n';
! 1653:
! 1654: *save = 0;
! 1655: *state = 0;
! 1656:
! 1657: return (outptr - out);
! 1658: }
! 1659:
! 1660:
! 1661: /**
! 1662: * g_mime_utils_base64_encode_step:
! 1663: * @in: input stream
! 1664: * @inlen: length of the input
! 1665: * @out: output string
! 1666: * @state: holds the number of bits that are stored in @save
! 1667: * @save: leftover bits that have not yet been encoded
! 1668: *
! 1669: * Base64 encodes a chunk of data. Performs an 'encode step', only
! 1670: * encodes blocks of 3 characters to the output at a time, saves
! 1671: * left-over state in state and save (initialise to 0 on first
! 1672: * invocation).
! 1673: *
! 1674: * Returns the number of bytes encoded.
! 1675: **/
! 1676: size_t
! 1677: g_mime_utils_base64_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, guint32 *save)
! 1678: {
! 1679: const register unsigned char *inptr;
! 1680: register unsigned char *outptr;
! 1681:
! 1682: if (inlen <= 0)
! 1683: return 0;
! 1684:
! 1685: inptr = in;
! 1686: outptr = out;
! 1687:
! 1688: if (inlen + ((unsigned char *)save)[0] > 2) {
! 1689: const unsigned char *inend = in + inlen - 2;
! 1690: register int c1 = 0, c2 = 0, c3 = 0;
! 1691: register int already;
! 1692:
! 1693: already = *state;
! 1694:
! 1695: switch (((char *)save)[0]) {
! 1696: case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
! 1697: case 2: c1 = ((unsigned char *)save)[1];
! 1698: c2 = ((unsigned char *)save)[2]; goto skip2;
! 1699: }
! 1700:
! 1701: /* yes, we jump into the loop, no i'm not going to change it, its beautiful! */
! 1702: while (inptr < inend) {
! 1703: c1 = *inptr++;
! 1704: skip1:
! 1705: c2 = *inptr++;
! 1706: skip2:
! 1707: c3 = *inptr++;
! 1708: *outptr++ = base64_alphabet [c1 >> 2];
! 1709: *outptr++ = base64_alphabet [(c2 >> 4) | ((c1 & 0x3) << 4)];
! 1710: *outptr++ = base64_alphabet [((c2 & 0x0f) << 2) | (c3 >> 6)];
! 1711: *outptr++ = base64_alphabet [c3 & 0x3f];
! 1712: /* this is a bit ugly ... */
! 1713: if ((++already) >= 19) {
! 1714: *outptr++ = '\n';
! 1715: already = 0;
! 1716: }
! 1717: }
! 1718:
! 1719: ((unsigned char *)save)[0] = 0;
! 1720: inlen = 2 - (inptr - inend);
! 1721: *state = already;
! 1722: }
! 1723:
! 1724: d(printf ("state = %d, inlen = %d\n", (int)((char *)save)[0], inlen));
! 1725:
! 1726: if (inlen > 0) {
! 1727: register char *saveout;
! 1728:
! 1729: /* points to the slot for the next char to save */
! 1730: saveout = & (((char *)save)[1]) + ((char *)save)[0];
! 1731:
! 1732: /* inlen can only be 0 1 or 2 */
! 1733: switch (inlen) {
! 1734: case 2: *saveout++ = *inptr++;
! 1735: case 1: *saveout++ = *inptr++;
! 1736: }
! 1737: ((char *)save)[0] += inlen;
! 1738: }
! 1739:
! 1740: d(printf ("mode = %d\nc1 = %c\nc2 = %c\n",
! 1741: (int)((char *)save)[0],
! 1742: (int)((char *)save)[1],
! 1743: (int)((char *)save)[2]));
! 1744:
! 1745: return (outptr - out);
! 1746: }
! 1747:
! 1748: /**
! 1749: * g_mime_utils_base64_decode_step:
! 1750: * @in: input stream
! 1751: * @inlen: max length of data to decode
! 1752: * @out: output stream
! 1753: * @state: holds the number of bits that are stored in @save
! 1754: * @save: leftover bits that have not yet been decoded
! 1755: *
! 1756: * Decodes a chunk of base64 encoded data.
! 1757: *
! 1758: * Returns the number of bytes decoded (which have been dumped in @out).
! 1759: **/
! 1760: size_t
! 1761: g_mime_utils_base64_decode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, guint32 *save)
! 1762: {
! 1763: const register unsigned char *inptr;
! 1764: register unsigned char *outptr;
! 1765: const unsigned char *inend;
! 1766: register guint32 saved;
! 1767: unsigned char c;
! 1768: int i;
! 1769:
! 1770: inend = in + inlen;
! 1771: outptr = out;
! 1772:
! 1773: /* convert 4 base64 bytes to 3 normal bytes */
! 1774: saved = *save;
! 1775: i = *state;
! 1776: inptr = in;
! 1777: while (inptr < inend) {
! 1778: c = gmime_base64_rank[*inptr++];
! 1779: if (c != 0xff) {
! 1780: saved = (saved << 6) | c;
! 1781: i++;
! 1782: if (i == 4) {
! 1783: *outptr++ = saved >> 16;
! 1784: *outptr++ = saved >> 8;
! 1785: *outptr++ = saved;
! 1786: i = 0;
! 1787: }
! 1788: }
! 1789: }
! 1790:
! 1791: *save = saved;
! 1792: *state = i;
! 1793:
! 1794: /* quick scan back for '=' on the end somewhere */
! 1795: /* fortunately we can drop 1 output char for each trailing = (upto 2) */
! 1796: i = 2;
! 1797: while (inptr > in && i) {
! 1798: inptr--;
! 1799: if (gmime_base64_rank[*inptr] != 0xff) {
! 1800: if (*inptr == '=' && outptr > out)
! 1801: outptr--;
! 1802: i--;
! 1803: }
! 1804: }
! 1805:
! 1806: /* if i != 0 then there is a truncation error! */
! 1807: return (outptr - out);
! 1808: }
! 1809:
! 1810:
! 1811: /**
! 1812: * g_mime_utils_uuencode_close:
! 1813: * @in: input stream
! 1814: * @inlen: input stream length
! 1815: * @out: output stream
! 1816: * @uubuf: temporary buffer of 60 bytes
! 1817: * @state: holds the number of bits that are stored in @save
! 1818: * @save: leftover bits that have not yet been encoded
! 1819: *
! 1820: * Uuencodes a chunk of data. Call this when finished encoding data
! 1821: * with g_mime_utils_uuencode_step to flush off the last little bit.
! 1822: *
! 1823: * Returns the number of bytes encoded.
! 1824: **/
! 1825: size_t
! 1826: g_mime_utils_uuencode_close (const unsigned char *in, size_t inlen, unsigned char *out, unsigned char *uubuf, int *state, guint32 *save)
! 1827: {
! 1828: register unsigned char *outptr, *bufptr;
! 1829: register guint32 saved;
! 1830: int uulen, uufill, i;
! 1831:
! 1832: outptr = out;
! 1833:
! 1834: if (inlen > 0)
! 1835: outptr += g_mime_utils_uuencode_step (in, inlen, out, uubuf, state, save);
! 1836:
! 1837: uufill = 0;
! 1838:
! 1839: saved = *save;
! 1840: i = *state & 0xff;
! 1841: uulen = (*state >> 8) & 0xff;
! 1842:
! 1843: bufptr = uubuf + ((uulen / 3) * 4);
! 1844:
! 1845: if (i > 0) {
! 1846: while (i < 3) {
! 1847: saved <<= 8 | 0;
! 1848: uufill++;
! 1849: i++;
! 1850: }
! 1851:
! 1852: if (i == 3) {
! 1853: /* convert 3 normal bytes into 4 uuencoded bytes */
! 1854: unsigned char b0, b1, b2;
! 1855:
! 1856: b0 = saved >> 16;
! 1857: b1 = saved >> 8 & 0xff;
! 1858: b2 = saved & 0xff;
! 1859:
! 1860: *bufptr++ = GMIME_UUENCODE_CHAR ((b0 >> 2) & 0x3f);
! 1861: *bufptr++ = GMIME_UUENCODE_CHAR (((b0 << 4) | ((b1 >> 4) & 0xf)) & 0x3f);
! 1862: *bufptr++ = GMIME_UUENCODE_CHAR (((b1 << 2) | ((b2 >> 6) & 0x3)) & 0x3f);
! 1863: *bufptr++ = GMIME_UUENCODE_CHAR (b2 & 0x3f);
! 1864:
! 1865: i = 0;
! 1866: saved = 0;
! 1867: uulen += 3;
! 1868: }
! 1869: }
! 1870:
! 1871: if (uulen > 0) {
! 1872: int cplen = ((uulen / 3) * 4);
! 1873:
! 1874: *outptr++ = GMIME_UUENCODE_CHAR ((uulen - uufill) & 0xff);
! 1875: memcpy (outptr, uubuf, cplen);
! 1876: outptr += cplen;
! 1877: *outptr++ = '\n';
! 1878: uulen = 0;
! 1879: }
! 1880:
! 1881: *outptr++ = GMIME_UUENCODE_CHAR (uulen & 0xff);
! 1882: *outptr++ = '\n';
! 1883:
! 1884: *save = 0;
! 1885: *state = 0;
! 1886:
! 1887: return (outptr - out);
! 1888: }
! 1889:
! 1890:
! 1891: /**
! 1892: * g_mime_utils_uuencode_step:
! 1893: * @in: input stream
! 1894: * @inlen: input stream length
! 1895: * @out: output stream
! 1896: * @uubuf: temporary buffer of 60 bytes
! 1897: * @state: holds the number of bits that are stored in @save
! 1898: * @save: leftover bits that have not yet been encoded
! 1899: *
! 1900: * Uuencodes a chunk of data. Performs an 'encode step', only encodes
! 1901: * blocks of 45 characters to the output at a time, saves left-over
! 1902: * state in @uubuf, @state and @save (initialize to 0 on first
! 1903: * invocation).
! 1904: *
! 1905: * Returns the number of bytes encoded.
! 1906: **/
! 1907: size_t
! 1908: g_mime_utils_uuencode_step (const unsigned char *in, size_t inlen, unsigned char *out, unsigned char *uubuf, int *state, guint32 *save)
! 1909: {
! 1910: register unsigned char *outptr, *bufptr;
! 1911: const register unsigned char *inptr;
! 1912: const unsigned char *inend;
! 1913: register guint32 saved;
! 1914: int uulen, i;
! 1915:
! 1916: saved = *save;
! 1917: i = *state & 0xff;
! 1918: uulen = (*state >> 8) & 0xff;
! 1919:
! 1920: inptr = in;
! 1921: inend = in + inlen;
! 1922:
! 1923: outptr = out;
! 1924:
! 1925: bufptr = uubuf + ((uulen / 3) * 4);
! 1926:
! 1927: while (inptr < inend) {
! 1928: while (uulen < 45 && inptr < inend) {
! 1929: while (i < 3 && inptr < inend) {
! 1930: saved = (saved << 8) | *inptr++;
! 1931: i++;
! 1932: }
! 1933:
! 1934: if (i == 3) {
! 1935: /* convert 3 normal bytes into 4 uuencoded bytes */
! 1936: unsigned char b0, b1, b2;
! 1937:
! 1938: b0 = saved >> 16;
! 1939: b1 = saved >> 8 & 0xff;
! 1940: b2 = saved & 0xff;
! 1941:
! 1942: *bufptr++ = GMIME_UUENCODE_CHAR ((b0 >> 2) & 0x3f);
! 1943: *bufptr++ = GMIME_UUENCODE_CHAR (((b0 << 4) | ((b1 >> 4) & 0xf)) & 0x3f);
! 1944: *bufptr++ = GMIME_UUENCODE_CHAR (((b1 << 2) | ((b2 >> 6) & 0x3)) & 0x3f);
! 1945: *bufptr++ = GMIME_UUENCODE_CHAR (b2 & 0x3f);
! 1946:
! 1947: i = 0;
! 1948: saved = 0;
! 1949: uulen += 3;
! 1950: }
! 1951: }
! 1952:
! 1953: if (uulen >= 45) {
! 1954: *outptr++ = GMIME_UUENCODE_CHAR (uulen & 0xff);
! 1955: memcpy (outptr, uubuf, ((uulen / 3) * 4));
! 1956: outptr += ((uulen / 3) * 4);
! 1957: *outptr++ = '\n';
! 1958: uulen = 0;
! 1959: bufptr = uubuf;
! 1960: }
! 1961: }
! 1962:
! 1963: *save = saved;
! 1964: *state = ((uulen & 0xff) << 8) | (i & 0xff);
! 1965:
! 1966: return (outptr - out);
! 1967: }
! 1968:
! 1969:
! 1970: /**
! 1971: * g_mime_utils_uudecode_step:
! 1972: * @in: input stream
! 1973: * @inlen: max length of data to decode (normally strlen (in) ??)
! 1974: * @out: output stream
! 1975: * @state: holds the number of bits that are stored in @save
! 1976: * @save: leftover bits that have not yet been decoded
! 1977: *
! 1978: * Uudecodes a chunk of data. Performs a 'decode step' on a chunk of
! 1979: * uuencoded data. Assumes the "begin <mode> <file name>" line has
! 1980: * been stripped off.
! 1981: *
! 1982: * Returns the number of bytes decoded.
! 1983: **/
! 1984: size_t
! 1985: g_mime_utils_uudecode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, guint32 *save)
! 1986: {
! 1987: const register unsigned char *inptr;
! 1988: register unsigned char *outptr;
! 1989: const unsigned char *inend;
! 1990: unsigned char ch;
! 1991: register guint32 saved;
! 1992: gboolean last_was_eoln;
! 1993: int uulen, i;
! 1994:
! 1995: if (*state & GMIME_UUDECODE_STATE_END)
! 1996: return 0;
! 1997:
! 1998: saved = *save;
! 1999: i = *state & 0xff;
! 2000: uulen = (*state >> 8) & 0xff;
! 2001: if (uulen == 0)
! 2002: last_was_eoln = TRUE;
! 2003: else
! 2004: last_was_eoln = FALSE;
! 2005:
! 2006: inend = in + inlen;
! 2007: outptr = out;
! 2008:
! 2009: inptr = in;
! 2010: while (inptr < inend) {
! 2011: if (*inptr == '\n') {
! 2012: last_was_eoln = TRUE;
! 2013:
! 2014: inptr++;
! 2015: continue;
! 2016: } else if (!uulen || last_was_eoln) {
! 2017: /* first octet on a line is the uulen octet */
! 2018: uulen = gmime_uu_rank[*inptr];
! 2019: last_was_eoln = FALSE;
! 2020: if (uulen == 0) {
! 2021: *state |= GMIME_UUDECODE_STATE_END;
! 2022: break;
! 2023: }
! 2024:
! 2025: inptr++;
! 2026: continue;
! 2027: }
! 2028:
! 2029: ch = *inptr++;
! 2030:
! 2031: if (uulen > 0) {
! 2032: /* save the byte */
! 2033: saved = (saved << 8) | ch;
! 2034: i++;
! 2035: if (i == 4) {
! 2036: /* convert 4 uuencoded bytes to 3 normal bytes */
! 2037: unsigned char b0, b1, b2, b3;
! 2038:
! 2039: b0 = saved >> 24;
! 2040: b1 = saved >> 16 & 0xff;
! 2041: b2 = saved >> 8 & 0xff;
! 2042: b3 = saved & 0xff;
! 2043:
! 2044: if (uulen >= 3) {
! 2045: *outptr++ = gmime_uu_rank[b0] << 2 | gmime_uu_rank[b1] >> 4;
! 2046: *outptr++ = gmime_uu_rank[b1] << 4 | gmime_uu_rank[b2] >> 2;
! 2047: *outptr++ = gmime_uu_rank[b2] << 6 | gmime_uu_rank[b3];
! 2048: } else {
! 2049: if (uulen >= 1) {
! 2050: *outptr++ = gmime_uu_rank[b0] << 2 | gmime_uu_rank[b1] >> 4;
! 2051: }
! 2052: if (uulen >= 2) {
! 2053: *outptr++ = gmime_uu_rank[b1] << 4 | gmime_uu_rank[b2] >> 2;
! 2054: }
! 2055: }
! 2056:
! 2057: i = 0;
! 2058: saved = 0;
! 2059: uulen -= 3;
! 2060: }
! 2061: } else {
! 2062: break;
! 2063: }
! 2064: }
! 2065:
! 2066: *save = saved;
! 2067: *state = (*state & GMIME_UUDECODE_STATE_MASK) | ((uulen & 0xff) << 8) | (i & 0xff);
! 2068:
! 2069: return (outptr - out);
! 2070: }
! 2071:
! 2072:
! 2073: /**
! 2074: * g_mime_utils_quoted_encode_close:
! 2075: * @in: input stream
! 2076: * @inlen: length of the input
! 2077: * @out: output string
! 2078: * @state: holds the number of bits that are stored in @save
! 2079: * @save: leftover bits that have not yet been encoded
! 2080: *
! 2081: * Quoted-printable encodes a block of text. Call this when finished
! 2082: * encoding data with g_mime_utils_quoted_encode_step to flush off the
! 2083: * last little bit.
! 2084: *
! 2085: * Returns the number of bytes encoded.
! 2086: **/
! 2087: size_t
! 2088: g_mime_utils_quoted_encode_close (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
! 2089: {
! 2090: register unsigned char *outptr = out;
! 2091: int last;
! 2092:
! 2093: if (inlen > 0)
! 2094: outptr += g_mime_utils_quoted_encode_step (in, inlen, outptr, state, save);
! 2095:
! 2096: last = *state;
! 2097: if (last != -1) {
! 2098: /* space/tab must be encoded if its the last character on
! 2099: the line */
! 2100: if (is_qpsafe (last) && !isblank (last)) {
! 2101: *outptr++ = last;
! 2102: } else {
! 2103: *outptr++ = '=';
! 2104: *outptr++ = tohex[(last >> 4) & 0xf];
! 2105: *outptr++ = tohex[last & 0xf];
! 2106: }
! 2107: }
! 2108:
! 2109: *outptr++ = '\n';
! 2110:
! 2111: *save = 0;
! 2112: *state = -1;
! 2113:
! 2114: return (outptr - out);
! 2115: }
! 2116:
! 2117:
! 2118: /**
! 2119: * g_mime_utils_quoted_encode_step:
! 2120: * @in: input stream
! 2121: * @inlen: length of the input
! 2122: * @out: output string
! 2123: * @state: holds the number of bits that are stored in @save
! 2124: * @save: leftover bits that have not yet been encoded
! 2125: *
! 2126: * Quoted-printable encodes a block of text. Performs an 'encode
! 2127: * step', saves left-over state in state and save (initialise to -1 on
! 2128: * first invocation).
! 2129: *
! 2130: * Returns the number of bytes encoded.
! 2131: **/
! 2132: size_t
! 2133: g_mime_utils_quoted_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
! 2134: {
! 2135: const register unsigned char *inptr, *inend;
! 2136: register unsigned char *outptr;
! 2137: unsigned char c;
! 2138: register int sofar = *save; /* keeps track of how many chars on a line */
! 2139: register int last = *state; /* keeps track if last char to end was a space cr etc */
! 2140:
! 2141: inptr = in;
! 2142: inend = in + inlen;
! 2143: outptr = out;
! 2144: while (inptr < inend) {
! 2145: c = *inptr++;
! 2146: if (c == '\r') {
! 2147: if (last != -1) {
! 2148: *outptr++ = '=';
! 2149: *outptr++ = tohex[(last >> 4) & 0xf];
! 2150: *outptr++ = tohex[last & 0xf];
! 2151: sofar += 3;
! 2152: }
! 2153: last = c;
! 2154: } else if (c == '\n') {
! 2155: if (last != -1 && last != '\r') {
! 2156: *outptr++ = '=';
! 2157: *outptr++ = tohex[(last >> 4) & 0xf];
! 2158: *outptr++ = tohex[last & 0xf];
! 2159: }
! 2160: *outptr++ = '\n';
! 2161: sofar = 0;
! 2162: last = -1;
! 2163: } else {
! 2164: if (last != -1) {
! 2165: if (is_qpsafe (last)) {
! 2166: *outptr++ = last;
! 2167: sofar++;
! 2168: } else {
! 2169: *outptr++ = '=';
! 2170: *outptr++ = tohex[(last >> 4) & 0xf];
! 2171: *outptr++ = tohex[last & 0xf];
! 2172: sofar += 3;
! 2173: }
! 2174: }
! 2175:
! 2176: if (is_qpsafe (c)) {
! 2177: if (sofar > 74) {
! 2178: *outptr++ = '=';
! 2179: *outptr++ = '\n';
! 2180: sofar = 0;
! 2181: }
! 2182:
! 2183: /* delay output of space char */
! 2184: if (isblank (c)) {
! 2185: last = c;
! 2186: } else {
! 2187: *outptr++ = c;
! 2188: sofar++;
! 2189: last = -1;
! 2190: }
! 2191: } else {
! 2192: if (sofar > 72) {
! 2193: *outptr++ = '=';
! 2194: *outptr++ = '\n';
! 2195: sofar = 3;
! 2196: } else
! 2197: sofar += 3;
! 2198:
! 2199: *outptr++ = '=';
! 2200: *outptr++ = tohex[(c >> 4) & 0xf];
! 2201: *outptr++ = tohex[c & 0xf];
! 2202: last = -1;
! 2203: }
! 2204: }
! 2205: }
! 2206: *save = sofar;
! 2207: *state = last;
! 2208:
! 2209: return (outptr - out);
! 2210: }
! 2211:
! 2212:
! 2213: /**
! 2214: * g_mime_utils_quoted_decode_step: decode a chunk of QP encoded data
! 2215: * @in: input stream
! 2216: * @inlen: max length of data to decode
! 2217: * @out: output stream
! 2218: * @savestate: holds the number of bits that are stored in @save
! 2219: * @saved: leftover bits that have not yet been decoded
! 2220: *
! 2221: * Decodes a block of quoted-printable encoded data. Performs a
! 2222: * 'decode step' on a chunk of QP encoded data.
! 2223: *
! 2224: * Returns the number of bytes decoded.
! 2225: **/
! 2226: size_t
! 2227: g_mime_utils_quoted_decode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *savestate, int *saved)
! 2228: {
! 2229: /* FIXME: this does not strip trailing spaces from lines (as
! 2230: * it should, rfc 2045, section 6.7) Should it also
! 2231: * canonicalise the end of line to CR LF??
! 2232: *
! 2233: * Note: Trailing rubbish (at the end of input), like = or =x
! 2234: * or =\r will be lost.
! 2235: */
! 2236: const register unsigned char *inptr;
! 2237: register unsigned char *outptr;
! 2238: const unsigned char *inend;
! 2239: unsigned char c;
! 2240: int state, save;
! 2241:
! 2242: inend = in + inlen;
! 2243: outptr = out;
! 2244:
! 2245: d(printf ("quoted-printable, decoding text '%.*s'\n", inlen, in));
! 2246:
! 2247: state = *savestate;
! 2248: save = *saved;
! 2249: inptr = in;
! 2250: while (inptr < inend) {
! 2251: switch (state) {
! 2252: case 0:
! 2253: while (inptr < inend) {
! 2254: c = *inptr++;
! 2255: /* FIXME: use a specials table to avoid 3 comparisons for the common case */
! 2256: if (c == '=') {
! 2257: state = 1;
! 2258: break;
! 2259: }
! 2260: #ifdef CANONICALISE_EOL
! 2261: /*else if (c=='\r') {
! 2262: state = 3;
! 2263: } else if (c=='\n') {
! 2264: *outptr++ = '\r';
! 2265: *outptr++ = c;
! 2266: } */
! 2267: #endif
! 2268: else {
! 2269: *outptr++ = c;
! 2270: }
! 2271: }
! 2272: break;
! 2273: case 1:
! 2274: c = *inptr++;
! 2275: if (c == '\n') {
! 2276: /* soft break ... unix end of line */
! 2277: state = 0;
! 2278: } else {
! 2279: save = c;
! 2280: state = 2;
! 2281: }
! 2282: break;
! 2283: case 2:
! 2284: c = *inptr++;
! 2285: if (isxdigit (c) && isxdigit (save)) {
! 2286: c = toupper (c);
! 2287: save = toupper (save);
! 2288: *outptr++ = (((save >= 'A' ? save - 'A' + 10 : save - '0') & 0x0f) << 4)
! 2289: | ((c >= 'A' ? c - 'A' + 10 : c - '0') & 0x0f);
! 2290: } else if (c == '\n' && save == '\r') {
! 2291: /* soft break ... canonical end of line */
! 2292: } else {
! 2293: /* just output the data */
! 2294: *outptr++ = '=';
! 2295: *outptr++ = save;
! 2296: *outptr++ = c;
! 2297: }
! 2298: state = 0;
! 2299: break;
! 2300: #ifdef CANONICALISE_EOL
! 2301: case 3:
! 2302: /* convert \n -> to \r\n, leaves \r\n alone */
! 2303: c = *inptr++;
! 2304: if (c == '\n') {
! 2305: *outptr++ = '\r';
! 2306: *outptr++ = c;
! 2307: } else {
! 2308: *outptr++ = '\r';
! 2309: *outptr++ = '\n';
! 2310: *outptr++ = c;
! 2311: }
! 2312: state = 0;
! 2313: break;
! 2314: #endif
! 2315: }
! 2316: }
! 2317:
! 2318: *savestate = state;
! 2319: *saved = save;
! 2320:
! 2321: return (outptr - out);
! 2322: }
E-mail: