1/* 2 * qpcode.c -- 3 * 4 * Implements and registers conversion from and to quoted-printable representation. 5 * 6 * 7 * Copyright (c) 1999 Marshall Rose (mrose@dbc.mtview.ca.us) 8 * All rights reserved. 9 * 10 * Permission is hereby granted, without written agreement and without 11 * license or royalty fees, to use, copy, modify, and distribute this 12 * software and its documentation for any purpose, provided that the 13 * above copyright notice and the following two paragraphs appear in 14 * all copies of this software. 15 * 16 * IN NO EVENT SHALL I LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 17 * INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS 18 * SOFTWARE AND ITS DOCUMENTATION, EVEN IF I HAVE BEEN ADVISED OF THE 19 * POSSIBILITY OF SUCH DAMAGE. 20 * 21 * I SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND 24 * I HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 25 * ENHANCEMENTS, OR MODIFICATIONS. 26 * 27 * CVS: $Id: qpcode.c,v 1.7 2009/05/07 04:57:27 andreas_kupries Exp $ 28 */ 29 30#include <ctype.h> 31#include "transformInt.h" 32 33/* 34 * Converter description 35 * --------------------- 36 * 37 * Reference: 38 * RFC 2045 39 * 40 * Encoding: 41 * Printable characters (other than "=") are passed through; otherwise a 42 * character is represented by "=" followed by the two-digit hexadecimal 43 * representation of the character's value. Ditto for trailing whitespace 44 * at the end of a line. 45 * 46 * Decoding: 47 * Invert the above. 48 */ 49 50 51/* 52 * Declarations of internal procedures. 53 */ 54 55static Trf_ControlBlock CreateEncoder _ANSI_ARGS_ ((ClientData writeClientData, 56 Trf_WriteProc *fun, 57 Trf_Options optInfo, 58 Tcl_Interp* interp, 59 ClientData clientData)); 60static void DeleteEncoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock, 61 ClientData clientData)); 62static int Encode _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock, 63 unsigned int character, 64 Tcl_Interp* interp, 65 ClientData clientData)); 66static int EncodeBuffer _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock, 67 unsigned char* buffer, int bufLen, 68 Tcl_Interp* interp, 69 ClientData clientData)); 70static int FlushEncoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock, 71 Tcl_Interp* interp, 72 ClientData clientData)); 73static void ClearEncoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock, 74 ClientData clientData)); 75 76static Trf_ControlBlock CreateDecoder _ANSI_ARGS_ ((ClientData writeClientData, 77 Trf_WriteProc *fun, 78 Trf_Options optInfo, 79 Tcl_Interp* interp, 80 ClientData clientData)); 81static void DeleteDecoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock, 82 ClientData clientData)); 83static int Decode _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock, 84 unsigned int character, 85 Tcl_Interp* interp, 86 ClientData clientData)); 87static int DecodeBuffer _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock, 88 unsigned char* buffer, int bufLen, 89 Tcl_Interp* interp, 90 ClientData clientData)); 91static int FlushDecoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock, 92 Tcl_Interp* interp, 93 ClientData clientData)); 94static void ClearDecoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock, 95 ClientData clientData)); 96 97 98/* 99 * Converter definition. 100 */ 101 102static Trf_TypeDefinition convDefinition = 103{ 104 "quoted-printable", 105 NULL, /* clientData not used by conversions. */ 106 NULL, /* set later by TrfInit_QP */ /* THREADING: serialize initialization */ 107 { 108 CreateEncoder, 109 DeleteEncoder, 110 Encode, 111 EncodeBuffer, 112 FlushEncoder, 113 ClearEncoder, 114 NULL /* no MaxRead */ 115 }, { 116 CreateDecoder, 117 DeleteDecoder, 118 Decode, 119 DecodeBuffer, 120 FlushDecoder, 121 ClearDecoder, 122 NULL /* no MaxRead */ 123 }, 124 TRF_UNSEEKABLE 125}; 126 127/* 128 * Definition of the control blocks for en- and decoder. 129 */ 130 131#define CPERLIN 76 /* according to RFC 2045 */ 132 133typedef struct _EncoderControl_ { 134 Trf_WriteProc* write; 135 ClientData writeClientData; 136 137 /* add conversion specific items here (qp encode) */ 138 139 int charCount; 140 unsigned char buf[CPERLIN + 8]; 141 142 /* DNew@Invisible.Net added the +8 or FlushEncoder runs off the 143 end when called from the first call point in Encode with a 144 too-long line. */ 145 146} EncoderControl; 147 148 149typedef struct _DecoderControl_ { 150 Trf_WriteProc* write; 151 ClientData writeClientData; 152 153 /* add conversion specific items here (qp decode) */ 154 155 int quoted; 156 unsigned char mask; 157 158} DecoderControl; 159 160static char hex2nib[0x80] = { 161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 163 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 167 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 168 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 169 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 171 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 173 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 177}; 178 179 180 181 182/* 183 *------------------------------------------------------* 184 * 185 * TrfInit_QP -- 186 * 187 * ------------------------------------------------* 188 * Register the conversion implemented in this file. 189 * ------------------------------------------------* 190 * 191 * Sideeffects: 192 * As of 'Trf_Register'. 193 * 194 * Result: 195 * A standard Tcl error code. 196 * 197 *------------------------------------------------------* 198 */ 199 200int 201TrfInit_QP (interp) 202Tcl_Interp* interp; 203{ 204 TrfLock; /* THREADING: serialize initialization */ 205 convDefinition.options = Trf_ConverterOptions (); 206 TrfUnlock; 207 208 return Trf_Register (interp, &convDefinition); 209} 210 211/* 212 *------------------------------------------------------* 213 * 214 * CreateEncoder -- 215 * 216 * ------------------------------------------------* 217 * Allocate and initialize the control block of a 218 * data encoder. 219 * ------------------------------------------------* 220 * 221 * Sideeffects: 222 * Allocates memory. 223 * 224 * Result: 225 * An opaque reference to the control block. 226 * 227 *------------------------------------------------------* 228 */ 229 230static Trf_ControlBlock 231CreateEncoder (writeClientData, fun, optInfo, interp, clientData) 232ClientData writeClientData; 233Trf_WriteProc *fun; 234Trf_Options optInfo; 235Tcl_Interp* interp; 236ClientData clientData; 237{ 238 EncoderControl* c; 239 240 c = (EncoderControl*) ckalloc (sizeof (EncoderControl)); 241 c->write = fun; 242 c->writeClientData = writeClientData; 243 244 /* initialize conversion specific items here (qp encode) */ 245 246 ClearEncoder ((Trf_ControlBlock) c, clientData); 247 248 return ((ClientData) c); 249} 250 251/* 252 *------------------------------------------------------* 253 * 254 * DeleteEncoder -- 255 * 256 * ------------------------------------------------* 257 * Destroy the control block of an encoder. 258 * ------------------------------------------------* 259 * 260 * Sideeffects: 261 * Releases the memory allocated by 'CreateEncoder' 262 * 263 * Result: 264 * None. 265 * 266 *------------------------------------------------------* 267 */ 268 269static void 270DeleteEncoder (ctrlBlock, clientData) 271Trf_ControlBlock ctrlBlock; 272ClientData clientData; 273{ 274 EncoderControl* c = (EncoderControl*) ctrlBlock; 275 276 /* release conversion specific items here (qp encode) */ 277 278 ckfree ((char*) c); 279} 280 281/* 282 *------------------------------------------------------* 283 * 284 * Encode -- 285 * 286 * ------------------------------------------------* 287 * Encode the given character and write the result. 288 * ------------------------------------------------* 289 * 290 * Sideeffects: 291 * As of the called WriteFun. 292 * 293 * Result: 294 * Generated bytes implicitly via WriteFun. 295 * A standard Tcl error code. 296 * 297 *------------------------------------------------------* 298 */ 299 300static int 301Encode (ctrlBlock, character, interp, clientData) 302Trf_ControlBlock ctrlBlock; 303unsigned int character; 304Tcl_Interp* interp; 305ClientData clientData; 306{ 307 EncoderControl* c = (EncoderControl*) ctrlBlock; 308 309 /* execute conversion specific code here (qp encode) */ 310 311 int i; 312 char x = character & 0xff; 313 314 /*in case we need to indicate trailing whitespace... */ 315 if ((c -> charCount >= CPERLIN - 1) 316 && ((x != '\n') || (c -> buf[c -> charCount -1] != '\r')) 317 && ((i = FlushEncoder (ctrlBlock, interp, clientData)) != TCL_OK)) 318 return i; 319 320 /* avoid common problems in old software... */ 321 switch (c -> charCount) { 322 case 1: 323 if (c -> buf[0] == '.') { 324 (void) sprintf ((char*) c -> buf, "=%02X", '.'); 325 c -> charCount = 3; 326 } 327 break; 328 329 case 5: 330 if (!strcmp ((char*) c -> buf, "From ")) { 331 (void) sprintf ((char*) c -> buf, "=%02Xrom ", 'F'); 332 c -> charCount = 7; 333 } 334 break; 335 } 336 337 switch (x) { 338 case '\n': 339 if ((c -> charCount > 0) && (c -> buf[c -> charCount - 1] == '\r')) 340 c -> charCount--; 341 /* and fall... */ 342 343 case ' ': 344 case '\t': 345 case '\r': 346 c -> buf[c -> charCount++] = character; 347 break; 348 349 default: 350 if (('!' <= x) && (x <= '~')) { 351 c -> buf[c -> charCount++] = character; 352 break; 353 } 354 /* else fall... */ 355 case '=': 356 (void) sprintf ((char*) c -> buf + c -> charCount, 357 "=%02X", (unsigned char) x); 358 c -> charCount += 3; 359 break; 360 } 361 362 if ((x == '\n') 363 && ((i = FlushEncoder (ctrlBlock, interp, clientData)) != TCL_OK)) 364 return i; 365 366 return TCL_OK; 367} 368 369/* 370 *------------------------------------------------------* 371 * 372 * EncodeBuffer -- 373 * 374 * ------------------------------------------------* 375 * Encode the given buffer and write the result. 376 * ------------------------------------------------* 377 * 378 * Sideeffects: 379 * As of the called WriteFun. 380 * 381 * Result: 382 * Generated bytes implicitly via WriteFun. 383 * A standard Tcl error code. 384 * 385 *------------------------------------------------------* 386 */ 387 388static int 389EncodeBuffer (ctrlBlock, buffer, bufLen, interp, clientData) 390Trf_ControlBlock ctrlBlock; 391unsigned char* buffer; 392int bufLen; 393Tcl_Interp* interp; 394ClientData clientData; 395{ 396 /* EncoderControl* c = (EncoderControl*) ctrlBlock; unused */ 397 /* execute conversion specific code here (qp encode) */ 398 399 int i = TCL_OK; 400 401 while (bufLen-- > 0) { 402 if ((i = Encode (ctrlBlock, *buffer++ & 0xff, interp, clientData)) 403 != TCL_OK) 404 break; 405 } 406 407 return i; 408} 409 410/* 411 *------------------------------------------------------* 412 * 413 * FlushEncoder -- 414 * 415 * ------------------------------------------------* 416 * Encode an incomplete character sequence (if possible). 417 * ------------------------------------------------* 418 * 419 * Sideeffects: 420 * As of the called WriteFun. 421 * 422 * Result: 423 * Generated bytes implicitly via WriteFun. 424 * A standard Tcl error code. 425 * 426 *------------------------------------------------------* 427 */ 428 429static int 430FlushEncoder (ctrlBlock, interp, clientData) 431Trf_ControlBlock ctrlBlock; 432Tcl_Interp* interp; 433ClientData clientData; 434{ 435 EncoderControl* c = (EncoderControl*) ctrlBlock; 436 437 /* execute conversion specific code here (qp encode) */ 438 439 int i; 440 441 if (c -> charCount == 0) 442 return TCL_OK; 443 444 if (c -> buf[c -> charCount - 1] == '\n') { 445 if (c -> charCount > 1) 446 switch (c -> buf[c -> charCount - 2]) { 447 case ' ': 448 case '\t': 449 (void) strcpy ((char*) c -> buf + c -> charCount - 1, "=\n\n"); 450 c -> charCount += 2; 451 break; 452 453 default: 454 break; 455 } 456 } else { 457 (void) strcpy ((char*) c -> buf + c -> charCount, "=\n"); 458 c -> charCount += 2; 459 } 460 461 if ((i = c -> write (c -> writeClientData, c -> buf, c -> charCount, 462 interp)) != TCL_OK) 463 return i; 464 465 ClearEncoder (ctrlBlock, clientData); 466 467 return TCL_OK; 468} 469 470/* 471 *------------------------------------------------------* 472 * 473 * ClearEncoder -- 474 * 475 * ------------------------------------------------* 476 * Discard an incomplete character sequence. 477 * ------------------------------------------------* 478 * 479 * Sideeffects: 480 * See above. 481 * 482 * Result: 483 * None. 484 * 485 *------------------------------------------------------* 486 */ 487 488static void 489ClearEncoder (ctrlBlock, clientData) 490Trf_ControlBlock ctrlBlock; 491ClientData clientData; 492{ 493 EncoderControl* c = (EncoderControl*) ctrlBlock; 494 495 /* execute conversion specific code here (qp encode) */ 496 497 c -> charCount = 0; 498 memset (c -> buf, '\0', sizeof c -> buf); 499} 500 501/* 502 *------------------------------------------------------* 503 * 504 * CreateDecoder -- 505 * 506 * ------------------------------------------------* 507 * Allocate and initialize the control block of a 508 * data decoder. 509 * ------------------------------------------------* 510 * 511 * Sideeffects: 512 * Allocates memory. 513 * 514 * Result: 515 * An opaque reference to the control block. 516 * 517 *------------------------------------------------------* 518 */ 519 520static Trf_ControlBlock 521CreateDecoder (writeClientData, fun, optInfo, interp, clientData) 522ClientData writeClientData; 523Trf_WriteProc *fun; 524Trf_Options optInfo; 525Tcl_Interp* interp; 526ClientData clientData; 527{ 528 DecoderControl* c; 529 530 c = (DecoderControl*) ckalloc (sizeof (DecoderControl)); 531 c->write = fun; 532 c->writeClientData = writeClientData; 533 534 /* initialize conversion specific items here (qp decode) */ 535 536 ClearDecoder ((Trf_ControlBlock) c, clientData); 537 538 return ((ClientData) c); 539} 540 541/* 542 *------------------------------------------------------* 543 * 544 * DeleteDecoder -- 545 * 546 * ------------------------------------------------* 547 * Destroy the control block of an decoder. 548 * ------------------------------------------------* 549 * 550 * Sideeffects: 551 * Releases the memory allocated by 'CreateDecoder' 552 * 553 * Result: 554 * None. 555 * 556 *------------------------------------------------------* 557 */ 558 559static void 560DeleteDecoder (ctrlBlock, clientData) 561Trf_ControlBlock ctrlBlock; 562ClientData clientData; 563{ 564 DecoderControl* c = (DecoderControl*) ctrlBlock; 565 566 /* release conversion specific items here (qp decode) */ 567 568 ckfree ((char*) c); 569} 570 571/* 572 *------------------------------------------------------* 573 * 574 * Decode -- 575 * 576 * ------------------------------------------------* 577 * Decode the given character and write the result. 578 * ------------------------------------------------* 579 * 580 * Sideeffects: 581 * As of the called WriteFun. 582 * 583 * Result: 584 * Generated bytes implicitly via WriteFun. 585 * A standard Tcl error code. 586 * 587 *------------------------------------------------------* 588 */ 589 590static int 591Decode (ctrlBlock, character, interp, clientData) 592Trf_ControlBlock ctrlBlock; 593unsigned int character; 594Tcl_Interp* interp; 595ClientData clientData; 596{ 597 DecoderControl* c = (DecoderControl*) ctrlBlock; 598 599 /* execute conversion specific code here (qp decode) */ 600 601 int i; 602 char x = character & 0xff; 603 604 switch (c -> quoted) { 605 case 0: 606 switch (x) { 607 default: 608 if ((x < '!') || ('~' < x)) 609 goto invalid_encoding; 610 /* else fall... */ 611 case ' ': 612 case '\t': 613 case '\n': 614 i = c -> write (c -> writeClientData, (unsigned char*) &x, 1, 615 interp); 616 break; 617 618 case '\r': 619 i = TCL_OK; 620 break; 621 622 case '=': 623 c -> quoted = 1; 624 i = TCL_OK; 625 break; 626 } 627 break; 628 629 case 1: 630 switch (x) { 631 case '\n': 632 c -> quoted = 0; 633 i = TCL_OK; 634 break; 635 636 case '\r': 637 i = TCL_OK; 638 break; 639 640 default: 641 if (!isxdigit (x)) 642 goto invalid_hex; 643 c -> mask = hex2nib[x & 0x7f]; 644 c -> quoted = 2; 645 i = TCL_OK; 646 break; 647 } 648 break; 649 650 default: 651 if (!isxdigit (x)) 652 goto invalid_hex; 653 c -> mask <<= 4; 654 c -> mask |= hex2nib[x & 0x7f]; 655 c -> quoted = 0; 656 i = c -> write (c -> writeClientData, &c -> mask, 1, interp); 657 break; 658 } 659 660 return i; 661 662invalid_hex: ; 663 if (interp) { 664 Tcl_ResetResult (interp); 665 Tcl_AppendResult (interp, "expecting hexadecimal digit", (char *) NULL); 666 } 667 return TCL_ERROR; 668 669invalid_encoding: ; 670 if (interp) { 671 Tcl_ResetResult (interp); 672 Tcl_AppendResult (interp, "expecting character in range [!..~]", 673 (char *) NULL); 674 } 675 return TCL_ERROR; 676} 677 678/* 679 *------------------------------------------------------* 680 * 681 * DecodeBuffer -- 682 * 683 * ------------------------------------------------* 684 * Decode the given buffer and write the result. 685 * ------------------------------------------------* 686 * 687 * Sideeffects: 688 * As of the called WriteFun. 689 * 690 * Result: 691 * Generated bytes implicitly via WriteFun. 692 * A standard Tcl error code. 693 * 694 *------------------------------------------------------* 695 */ 696 697static int 698DecodeBuffer (ctrlBlock, buffer, bufLen, interp, clientData) 699Trf_ControlBlock ctrlBlock; 700unsigned char* buffer; 701int bufLen; 702Tcl_Interp* interp; 703ClientData clientData; 704{ 705 /* DecoderControl* c = (DecoderControl*) ctrlBlock; unused */ 706 /* execute conversion specific code here (qp decode) */ 707 708 int i = TCL_OK; 709 710 while (bufLen-- > 0) { 711 if ((i = Decode (ctrlBlock, *buffer++ & 0xff, interp, clientData)) 712 != TCL_OK) 713 break; 714 } 715 716 return i; 717} 718 719/* 720 *------------------------------------------------------* 721 * 722 * FlushDecoder -- 723 * 724 * ------------------------------------------------* 725 * Decode an incomplete character sequence (if possible). 726 * ------------------------------------------------* 727 * 728 * Sideeffects: 729 * As of the called WriteFun. 730 * 731 * Result: 732 * Generated bytes implicitly via WriteFun. 733 * A standard Tcl error code. 734 * 735 *------------------------------------------------------* 736 */ 737 738static int 739FlushDecoder (ctrlBlock, interp, clientData) 740Trf_ControlBlock ctrlBlock; 741Tcl_Interp* interp; 742ClientData clientData; 743{ 744 DecoderControl* c = (DecoderControl*) ctrlBlock; 745 746 /* execute conversion specific code here (qp decode) */ 747 748 if (c -> quoted) { 749 if (interp) { 750 Tcl_ResetResult (interp); 751 Tcl_AppendResult (interp, c -> quoted > 1 752 ? "expecting another hexadecimal digit" 753 : "expecting addition characters", 754 (char *) NULL); 755 } 756 757 return TCL_ERROR; 758 } 759 760 ClearDecoder (ctrlBlock, clientData); 761 762 return TCL_OK; 763} 764 765/* 766 *------------------------------------------------------* 767 * 768 * ClearDecoder -- 769 * 770 * ------------------------------------------------* 771 * Discard an incomplete character sequence. 772 * ------------------------------------------------* 773 * 774 * Sideeffects: 775 * See above. 776 * 777 * Result: 778 * None. 779 * 780 *------------------------------------------------------* 781 */ 782 783static void 784ClearDecoder (ctrlBlock, clientData) 785Trf_ControlBlock ctrlBlock; 786ClientData clientData; 787{ 788 DecoderControl* c = (DecoderControl*) ctrlBlock; 789 790 /* execute conversion specific code here (qp decode) */ 791 792 c -> quoted = 0; 793 c -> mask = 0; 794} 795 796