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