428
+ − 1 /*
+ − 2 Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
+ − 3
+ − 4 Permission to use, copy, modify, and distribute this material
+ − 5 for any purpose and without fee is hereby granted, provided
+ − 6 that the above copyright notice and this permission notice
+ − 7 appear in all copies, and that the name of Bellcore not be
+ − 8 used in advertising or publicity pertaining to this
+ − 9 material without the specific, prior written permission
+ − 10 of an authorized representative of Bellcore. BELLCORE
+ − 11 MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
+ − 12 OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
+ − 13 WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+ − 14 */
+ − 15
+ − 16 #define NEWLINE_CHAR '\n'
+ − 17 #include <stdlib.h>
+ − 18 #include <stdio.h>
+ − 19 #include <ctype.h>
+ − 20 #include <string.h>
430
+ − 21 #include <errno.h>
428
+ − 22
+ − 23 static void
+ − 24 output64chunk(int c1, int c2, int c3, int pads, FILE *outfile);
+ − 25
+ − 26 static signed char basis_64[] =
+ − 27 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ − 28
+ − 29 static signed char index_64[128] = {
+ − 30 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ − 31 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ − 32 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+ − 33 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+ − 34 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+ − 35 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+ − 36 -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+ − 37 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+ − 38 };
+ − 39
+ − 40 #define char64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
+ − 41
+ − 42 /*
+ − 43 char64(c)
+ − 44 char c;
+ − 45 {
+ − 46 char *s = (char *) strchr(basis_64, c);
+ − 47 if (s) return(s-basis_64);
+ − 48 return(-1);
+ − 49 }
+ − 50 */
+ − 51
+ − 52 /* the following gets a character, but fakes it properly into two chars if there's a newline character */
+ − 53 static int InNewline=0;
+ − 54
+ − 55 static int
+ − 56 nextcharin (FILE *infile, int PortableNewlines)
+ − 57 {
+ − 58 int c;
+ − 59
+ − 60 #ifndef NEWLINE_CHAR
+ − 61 return(getc(infile));
+ − 62 #else
+ − 63 if (!PortableNewlines) return(getc(infile));
+ − 64 if (InNewline) {
+ − 65 InNewline = 0;
+ − 66 return(10); /* LF */
+ − 67 }
+ − 68 c = getc(infile);
+ − 69 if (c == NEWLINE_CHAR) {
+ − 70 InNewline = 1;
+ − 71 return(13); /* CR */
+ − 72 }
+ − 73 return(c);
+ − 74 #endif
+ − 75 }
+ − 76
+ − 77 static void
+ − 78 to64(FILE *infile, FILE *outfile, int PortableNewlines)
+ − 79 {
+ − 80 int c1, c2, c3, ct=0;
+ − 81 InNewline = 0; /* always reset it */
+ − 82 while ((c1 = nextcharin(infile, PortableNewlines)) != EOF) {
+ − 83 c2 = nextcharin(infile, PortableNewlines);
+ − 84 if (c2 == EOF) {
+ − 85 output64chunk(c1, 0, 0, 2, outfile);
+ − 86 } else {
+ − 87 c3 = nextcharin(infile, PortableNewlines);
+ − 88 if (c3 == EOF) {
+ − 89 output64chunk(c1, c2, 0, 1, outfile);
+ − 90 } else {
+ − 91 output64chunk(c1, c2, c3, 0, outfile);
+ − 92 }
+ − 93 }
+ − 94 ct += 4;
+ − 95 if (ct > 71) {
+ − 96 putc('\n', outfile);
+ − 97 ct = 0;
+ − 98 }
+ − 99 }
+ − 100 if (ct) putc('\n', outfile);
+ − 101 fflush(outfile);
+ − 102 }
+ − 103
+ − 104 static void
+ − 105 output64chunk(int c1, int c2, int c3, int pads, FILE *outfile)
+ − 106 {
+ − 107 putc(basis_64[c1>>2], outfile);
+ − 108 putc(basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)], outfile);
+ − 109 if (pads == 2) {
+ − 110 putc('=', outfile);
+ − 111 putc('=', outfile);
+ − 112 } else if (pads) {
+ − 113 putc(basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)], outfile);
+ − 114 putc('=', outfile);
+ − 115 } else {
+ − 116 putc(basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)], outfile);
+ − 117 putc(basis_64[c3 & 0x3F], outfile);
+ − 118 }
+ − 119 }
+ − 120
+ − 121 static int
+ − 122 PendingBoundary(char *s, char **Boundaries, int *BoundaryCt)
+ − 123 {
+ − 124 int i, len;
+ − 125
+ − 126 if (s[0] != '-' || s[1] != '-') return(0);
+ − 127
+ − 128
+ − 129 for (i=0; i < *BoundaryCt; ++i) {
+ − 130 len = strlen(Boundaries[i]);
+ − 131 if (!strncmp(s, Boundaries[i], len)) {
+ − 132 if (s[len] == '-' && s[len+1] == '-') *BoundaryCt = i;
+ − 133 return(1);
+ − 134 }
+ − 135 }
+ − 136 return(0);
+ − 137 }
+ − 138
+ − 139 /* If we're in portable newline mode, we have to convert CRLF to the
+ − 140 local newline convention on output */
+ − 141
+ − 142 static int CRpending = 0;
+ − 143
+ − 144 #ifdef NEWLINE_CHAR
+ − 145 static void
+ − 146 almostputc(int c, FILE *outfile, int PortableNewlines)
+ − 147 {
+ − 148 if (CRpending) {
+ − 149 if (c == 10) {
+ − 150 putc(NEWLINE_CHAR, outfile);
+ − 151 CRpending = 0;
+ − 152 } else {
+ − 153 putc(13, outfile);
+ − 154 if (c != 13) {
+ − 155 putc(c, outfile);
+ − 156 CRpending = 0;
+ − 157 }
+ − 158 }
+ − 159 } else {
+ − 160 if (PortableNewlines && c == 13) {
+ − 161 CRpending = 1;
+ − 162 } else {
+ − 163 putc(c, outfile);
+ − 164 }
+ − 165 }
+ − 166 }
+ − 167 #else
+ − 168 static void
+ − 169 almostputc(int c, FILE *outfile, int PortableNewlines)
+ − 170 {
+ − 171 putc(c, outfile);
+ − 172 }
+ − 173 #endif
+ − 174
+ − 175 static void
+ − 176 from64(FILE *infile, FILE *outfile,
+ − 177 char **boundaries, int *boundaryct, int PortableNewlines)
+ − 178 {
+ − 179 int c1, c2, c3, c4;
+ − 180 int newline = 1, DataDone = 0;
+ − 181
+ − 182 /* always reinitialize */
+ − 183 CRpending = 0;
+ − 184 while ((c1 = getc(infile)) != EOF) {
+ − 185 if (isspace(c1)) {
+ − 186 if (c1 == '\n') {
+ − 187 newline = 1;
+ − 188 } else {
+ − 189 newline = 0;
+ − 190 }
+ − 191 continue;
+ − 192 }
+ − 193 if (newline && boundaries && c1 == '-') {
+ − 194 char Buf[200];
+ − 195 /* a dash is NOT base 64, so all bets are off if NOT a boundary */
+ − 196 ungetc(c1, infile);
+ − 197 fgets(Buf, sizeof(Buf), infile);
+ − 198 if (boundaries
+ − 199 && (Buf[0] == '-')
+ − 200 && (Buf[1] == '-')
+ − 201 && PendingBoundary(Buf, boundaries, boundaryct)) {
+ − 202 return;
+ − 203 }
+ − 204 fprintf(stderr, "Ignoring unrecognized boundary line: %s\n", Buf);
+ − 205 continue;
+ − 206 }
+ − 207 if (DataDone) continue;
+ − 208 newline = 0;
+ − 209 do {
+ − 210 c2 = getc(infile);
+ − 211 } while (c2 != EOF && isspace(c2));
+ − 212 do {
+ − 213 c3 = getc(infile);
+ − 214 } while (c3 != EOF && isspace(c3));
+ − 215 do {
+ − 216 c4 = getc(infile);
+ − 217 } while (c4 != EOF && isspace(c4));
+ − 218 if (c2 == EOF || c3 == EOF || c4 == EOF) {
+ − 219 fprintf(stderr, "Warning: base64 decoder saw premature EOF!\n");
+ − 220 return;
+ − 221 }
+ − 222 if (c1 == '=' || c2 == '=') {
+ − 223 DataDone=1;
+ − 224 continue;
+ − 225 }
+ − 226 c1 = char64(c1);
+ − 227 c2 = char64(c2);
+ − 228 almostputc(((c1<<2) | ((c2&0x30)>>4)), outfile, PortableNewlines);
+ − 229 if (c3 == '=') {
+ − 230 DataDone = 1;
+ − 231 } else {
+ − 232 c3 = char64(c3);
+ − 233 almostputc((((c2&0XF) << 4) | ((c3&0x3C) >> 2)), outfile, PortableNewlines);
+ − 234 if (c4 == '=') {
+ − 235 DataDone = 1;
+ − 236 } else {
+ − 237 c4 = char64(c4);
+ − 238 almostputc((((c3&0x03) <<6) | c4), outfile, PortableNewlines);
+ − 239 }
+ − 240 }
+ − 241 }
+ − 242 if (CRpending) putc(13, outfile); /* Don't drop a lone trailing char 13 */
+ − 243 }
+ − 244
+ − 245 static signed char basis_hex[] = "0123456789ABCDEF";
+ − 246 static signed char index_hex[128] = {
+ − 247 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ − 248 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ − 249 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ − 250 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
+ − 251 -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ − 252 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ − 253 -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ − 254 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
+ − 255 };
+ − 256
+ − 257 /* The following version generated complaints on Solaris. */
+ − 258 /* #define hexchar(c) (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)]) */
+ − 259 /* Since we're no longer ever calling it with anything signed, this should work: */
+ − 260 #define hexchar(c) (((c) > 127) ? -1 : index_hex[(c)])
+ − 261
+ − 262 /*
+ − 263 hexchar(c)
+ − 264 char c;
+ − 265 {
+ − 266 char *s;
+ − 267 if (islower(c)) c = toupper(c);
+ − 268 s = (char *) strchr(basis_hex, c);
+ − 269 if (s) return(s-basis_hex);
+ − 270 return(-1);
+ − 271 }
+ − 272 */
+ − 273
+ − 274 static void
+ − 275 toqp(FILE *infile, FILE *outfile)
+ − 276 {
+ − 277 int c, ct=0, prevc=255;
+ − 278 while ((c = getc(infile)) != EOF) {
+ − 279 if ((c < 32 && (c != '\n' && c != '\t'))
+ − 280 || (c == '=')
+ − 281 || (c >= 127)
+ − 282 /* Following line is to avoid single periods alone on lines,
+ − 283 which messes up some dumb smtp implementations, sigh... */
+ − 284 || (ct == 0 && c == '.')) {
+ − 285 putc('=', outfile);
+ − 286 putc(basis_hex[c>>4], outfile);
+ − 287 putc(basis_hex[c&0xF], outfile);
+ − 288 ct += 3;
+ − 289 prevc = 'A'; /* close enough */
+ − 290 } else if (c == '\n') {
+ − 291 if (prevc == ' ' || prevc == '\t') {
+ − 292 putc('=', outfile); /* soft & hard lines */
+ − 293 putc(c, outfile);
+ − 294 }
+ − 295 putc(c, outfile);
+ − 296 ct = 0;
+ − 297 prevc = c;
+ − 298 } else {
+ − 299 if (c == 'F' && prevc == '\n') {
+ − 300 /* HORRIBLE but clever hack suggested by MTR for sendmail-avoidance */
+ − 301 c = getc(infile);
+ − 302 if (c == 'r') {
+ − 303 c = getc(infile);
+ − 304 if (c == 'o') {
+ − 305 c = getc(infile);
+ − 306 if (c == 'm') {
+ − 307 c = getc(infile);
+ − 308 if (c == ' ') {
+ − 309 /* This is the case we are looking for */
+ − 310 fputs("=46rom", outfile);
+ − 311 ct += 6;
+ − 312 } else {
+ − 313 fputs("From", outfile);
+ − 314 ct += 4;
+ − 315 }
+ − 316 } else {
+ − 317 fputs("Fro", outfile);
+ − 318 ct += 3;
+ − 319 }
+ − 320 } else {
+ − 321 fputs("Fr", outfile);
+ − 322 ct += 2;
+ − 323 }
+ − 324 } else {
+ − 325 putc('F', outfile);
+ − 326 ++ct;
+ − 327 }
+ − 328 ungetc(c, infile);
+ − 329 prevc = 'x'; /* close enough -- printable */
+ − 330 } else { /* END horrible hack */
+ − 331 putc(c, outfile);
+ − 332 ++ct;
+ − 333 prevc = c;
+ − 334 }
+ − 335 }
+ − 336 if (ct > 72) {
+ − 337 putc('=', outfile);
+ − 338 putc('\n', outfile);
+ − 339 ct = 0;
+ − 340 prevc = '\n';
+ − 341 }
+ − 342 }
+ − 343 if (ct) {
+ − 344 putc('=', outfile);
+ − 345 putc('\n', outfile);
+ − 346 }
+ − 347 }
+ − 348
+ − 349 static void
+ − 350 fromqp(FILE *infile, FILE *outfile, char **boundaries, int *boundaryct)
+ − 351 {
+ − 352 unsigned int c1, c2;
+ − 353 int sawnewline = 1, neednewline = 0;
+ − 354 /* The neednewline hack is necessary because the newline leading into
+ − 355 a multipart boundary is part of the boundary, not the data */
+ − 356
+ − 357 while ((c1 = getc(infile)) != EOF) {
+ − 358 if (sawnewline && boundaries && (c1 == '-')) {
+ − 359 char Buf[200];
+ − 360 unsigned char *s;
+ − 361
+ − 362 ungetc(c1, infile);
+ − 363 fgets(Buf, sizeof(Buf), infile);
+ − 364 if (boundaries
+ − 365 && (Buf[0] == '-')
+ − 366 && (Buf[1] == '-')
+ − 367 && PendingBoundary(Buf, boundaries, boundaryct)) {
+ − 368 return;
+ − 369 }
+ − 370 /* Not a boundary, now we must treat THIS line as q-p, sigh */
+ − 371 if (neednewline) {
+ − 372 putc('\n', outfile);
+ − 373 neednewline = 0;
+ − 374 }
+ − 375 for (s=(unsigned char *) Buf; *s; ++s) {
+ − 376 if (*s == '=') {
+ − 377 if (!*++s) break;
+ − 378 if (*s == '\n') {
+ − 379 /* ignore it */
+ − 380 sawnewline = 1;
+ − 381 } else {
+ − 382 c1 = hexchar(*s);
+ − 383 if (!*++s) break;
+ − 384 c2 = hexchar(*s);
+ − 385 putc(c1<<4 | c2, outfile);
+ − 386 }
+ − 387 } else {
442
+ − 388 #ifdef WIN32_NATIVE
428
+ − 389 if (*s == '\n')
+ − 390 putc('\r', outfile); /* insert CR for binary-mode write */
+ − 391 #endif
+ − 392 putc(*s, outfile);
+ − 393 }
+ − 394 }
+ − 395 } else {
+ − 396 if (neednewline) {
+ − 397 putc('\n', outfile);
+ − 398 neednewline = 0;
+ − 399 }
+ − 400 if (c1 == '=') {
+ − 401 sawnewline = 0;
+ − 402 c1 = getc(infile);
+ − 403 if (c1 == '\n') {
+ − 404 /* ignore it */
+ − 405 sawnewline = 1;
+ − 406 } else {
+ − 407 c2 = getc(infile);
+ − 408 c1 = hexchar(c1);
+ − 409 c2 = hexchar(c2);
+ − 410 putc(c1<<4 | c2, outfile);
+ − 411 if (c2 == '\n') sawnewline = 1;
+ − 412 }
+ − 413 } else {
+ − 414 if (c1 == '\n') {
+ − 415 sawnewline = 1;
+ − 416 neednewline = 1;
+ − 417 } else {
+ − 418 sawnewline = 0;
+ − 419 putc(c1, outfile);
+ − 420 }
+ − 421 }
+ − 422 }
+ − 423 }
+ − 424 if (neednewline) {
+ − 425 putc('\n', outfile);
+ − 426 neednewline = 0;
+ − 427 }
+ − 428 }
+ − 429
+ − 430
+ − 431 /*
+ − 432 Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
+ − 433
+ − 434 Permission to use, copy, modify, and distribute this material
+ − 435 for any purpose and without fee is hereby granted, provided
+ − 436 that the above copyright notice and this permission notice
+ − 437 appear in all copies, and that the name of Bellcore not be
+ − 438 used in advertising or publicity pertaining to this
+ − 439 material without the specific, prior written permission
+ − 440 of an authorized representative of Bellcore. BELLCORE
+ − 441 MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
+ − 442 OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
+ − 443 WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+ − 444 */
442
+ − 445 #ifdef WIN32_NATIVE
+ − 446 #include <io.h>
428
+ − 447 #include <fcntl.h>
+ − 448 #endif
+ − 449
+ − 450 #define BASE64 1
+ − 451 #define QP 2 /* quoted-printable */
+ − 452
+ − 453 int main(int argc, char *argv[])
+ − 454 {
+ − 455 int encode = 1, which = BASE64, i, portablenewlines = 0;
+ − 456 FILE *fp = stdin;
+ − 457 FILE *fpo = stdout;
+ − 458
+ − 459 for (i=1; i<argc; ++i) {
+ − 460 if (argv[i][0] == '-') {
+ − 461 switch (argv[i][1]) {
+ − 462 case 'o':
+ − 463 if (++i >= argc) {
+ − 464 fprintf(stderr, "mimencode: -o requires a file name.\n");
+ − 465 exit(-1);
+ − 466 }
+ − 467 fpo = fopen(argv[i], "w");
+ − 468 if (!fpo) {
+ − 469 perror(argv[i]);
+ − 470 exit(-1);
+ − 471 }
+ − 472 break;
+ − 473 case 'u':
+ − 474 encode = 0;
+ − 475 break;
+ − 476 case 'q':
+ − 477 which = QP;
+ − 478 break;
+ − 479 case 'p':
+ − 480 portablenewlines = 1;
+ − 481 break;
+ − 482 case 'b':
+ − 483 which = BASE64;
+ − 484 break;
+ − 485 default:
+ − 486 fprintf(stderr,
+ − 487 "Usage: mmencode [-u] [-q] [-b] [-p] [-o outputfile] [file name]\n");
+ − 488 exit(-1);
+ − 489 }
+ − 490 } else {
442
+ − 491 #ifdef WIN32_NATIVE
428
+ − 492 if (encode)
+ − 493 fp = fopen(argv[i], "rb");
+ − 494 else
+ − 495 {
+ − 496 fp = fopen(argv[i], "rt");
+ − 497 setmode(fileno(fpo), O_BINARY);
+ − 498 } /* else */
+ − 499 #else
+ − 500 fp = fopen(argv[i], "r");
442
+ − 501 #endif /* WIN32_NATIVE */
428
+ − 502 if (!fp) {
+ − 503 perror(argv[i]);
+ − 504 exit(-1);
+ − 505 }
+ − 506 }
+ − 507 }
442
+ − 508 #ifdef WIN32_NATIVE
428
+ − 509 if (fp == stdin) setmode(fileno(fp), O_BINARY);
442
+ − 510 #endif /* WIN32_NATIVE */
428
+ − 511 if (which == BASE64) {
+ − 512 if (encode) {
+ − 513 to64(fp, fpo, portablenewlines);
+ − 514 } else {
+ − 515 from64(fp,fpo, (char **) NULL, (int *) 0, portablenewlines);
+ − 516 }
+ − 517 } else {
+ − 518 if (encode) toqp(fp, fpo); else fromqp(fp, fpo, NULL, 0);
+ − 519 }
+ − 520 return(0);
+ − 521 }
+ − 522