* base64.c (base64_reader_cb): Try to detect an S/MIME body part.
[gnupg.git] / sm / base64.c
1 /* base64.c 
2  *      Copyright (C) 2001 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h> 
27 #include <time.h>
28 #include <assert.h>
29
30 #include <ksba.h>
31
32 #include "gpgsm.h"
33 #include "i18n.h"
34
35 #ifdef HAVE_DOSISH_SYSTEM
36   #define LF "\r\n"
37 #else
38   #define LF "\n"
39 #endif
40
41 /* data used by the reader callbacks */
42 struct reader_cb_parm_s {
43   FILE *fp;
44   unsigned char line[1024];
45   int linelen;
46   int readpos;
47   int have_lf;
48   unsigned long line_counter;
49
50   int autodetect; /* try to detect the input encoding */
51   int assume_pem; /* assume input encoding is PEM */
52   int assume_base64; /* assume input is base64 encoded */
53
54   int identified;
55   int is_pem;
56   int is_base64;
57   int stop_seen;
58   int might_be_smime;
59
60   struct {
61     int idx;
62     unsigned char val;
63     int stop_seen;
64   } base64;
65 };
66
67 /* data used by the writer callbacks */
68 struct writer_cb_parm_s {
69   FILE *fp;
70   const char *pem_name;
71   
72   int wrote_begin;
73   int did_finish;
74
75   struct {
76     int idx;
77     int quad_count;
78     unsigned char radbuf[4];
79   } base64;
80
81 };
82
83
84 /* context for this module's functions */
85 struct base64_context_s {
86   union {
87     struct reader_cb_parm_s rparm;
88     struct writer_cb_parm_s wparm;
89   } u;
90 };
91
92
93 /* The base-64 character list */
94 static unsigned char bintoasc[64] = 
95        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
96        "abcdefghijklmnopqrstuvwxyz" 
97        "0123456789+/"; 
98 /* The reverse base-64 list */
99 static unsigned char asctobin[256] = {
100   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
101   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
102   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
103   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 
104   0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 
105   0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
106   0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 
107   0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 
108   0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 
109   0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 
110   0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
111   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
112   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
113   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
114   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
115   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
116   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
117   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
118   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
119   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
120   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
121   0xff, 0xff, 0xff, 0xff
122 };
123
124
125 static int
126 has_only_base64 (const unsigned char *line, int linelen)
127 {
128   if (linelen < 20)
129     return 0;
130   for (; linelen; line++, linelen--)
131     {
132       if (*line == '\n' || (linelen > 1 && *line == '\r' && line[1] == '\n'))
133           break;
134       if ( !strchr (bintoasc, *line) )
135         return 0;
136     }
137   return 1;  /* yes */
138 }
139
140 static int
141 is_empty_line (const unsigned char *line, int linelen)
142 {
143   if (linelen >= 2 && *line == '\r' && line[1] == '\n')
144     return 1;
145   if (linelen >= 1 && *line == '\n')
146     return 1;
147   return 0;
148 }
149
150
151 static int
152 base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
153 {
154   struct reader_cb_parm_s *parm = cb_value;
155   size_t n;
156   int c, c2;
157
158   *nread = 0;
159   if (!buffer)
160     return -1; /* not supported */
161
162  next:
163   if (!parm->linelen)
164     {
165       /* read an entire line or up to the size of the buffer */
166       parm->line_counter++;
167       parm->have_lf = 0;
168       for (n=0; n < DIM(parm->line);)
169         {
170           c = getc (parm->fp);
171           if (c == EOF)
172             {
173               if (ferror (parm->fp))
174                 return -1;
175               break; 
176             }
177           parm->line[n++] = c;
178           if (c == '\n')
179             {
180               parm->have_lf = 1;
181               /* FIXME: we need to skip overlong lines while detecting
182                  the dashed lines */
183               break;
184             }
185         }
186       parm->linelen = n;
187       if (!n)
188         return -1; /* eof */
189       parm->readpos = 0;
190     }
191
192   if (!parm->identified)
193     {
194       if (!parm->autodetect)
195         {
196           if (parm->assume_pem)
197             {
198               /* wait for the header line */
199               parm->linelen = parm->readpos = 0;
200               if (!parm->have_lf || strncmp (parm->line, "-----BEGIN ", 11)
201                   || !strncmp (parm->line+11, "PGP ", 4))
202                 goto next;
203               parm->is_pem = 1;
204             }
205           else if (parm->assume_base64)
206             parm->is_base64 = 1;
207         }
208       else if (parm->line_counter == 1 && !parm->have_lf)
209         {
210           /* first line too long - assume DER encoding */
211           parm->is_pem = 0;
212         }
213       else if (parm->line_counter == 1 && parm->linelen && *parm->line == 0x30)
214         {
215           /* the very first byte does pretty much look like a SEQUENCE tag*/
216           parm->is_pem = 0;
217         }
218       else if ( parm->have_lf && !strncmp (parm->line, "-----BEGIN ", 11)
219                 && strncmp (parm->line+11, "PGP ", 4) )
220         {
221           /* Fixme: we must only compare if the line really starts at
222              the beginning */
223           parm->is_pem = 1;
224           parm->linelen = parm->readpos = 0;
225         }
226       else if ( parm->have_lf && parm->line_counter == 1
227                 && !strncmp (parm->line, "Content-Type:", 13))
228         { /* Might be a S/MIME body */
229           parm->might_be_smime = 1;
230           parm->linelen = parm->readpos = 0;
231           goto next;
232         }
233       else if (parm->might_be_smime == 1
234                && is_empty_line (parm->line, parm->linelen))
235         {
236           parm->might_be_smime = 2;
237           parm->linelen = parm->readpos = 0;
238           goto next;
239         }
240       else if (parm->might_be_smime == 2)
241         {
242           parm->might_be_smime = 0;
243           if ( !has_only_base64 (parm->line, parm->linelen))
244             {
245               parm->linelen = parm->readpos = 0;
246               goto next;
247             }
248           parm->is_pem = 1;
249         }
250       else
251         {
252           parm->linelen = parm->readpos = 0;
253           goto next;
254         }
255       parm->identified = 1;
256       parm->base64.stop_seen = 0;
257       parm->base64.idx = 0;
258     }
259   
260
261   n = 0;
262   if (parm->is_pem || parm->is_base64)
263     {  
264       if (parm->is_pem && parm->have_lf
265           && !strncmp (parm->line, "-----END ", 9))
266         { 
267           parm->identified = 0;
268           parm->linelen = parm->readpos = 0;
269           /* let us return 0 */
270         }
271       else if (parm->stop_seen)
272         { /* skip the rest of the line */
273           parm->linelen = parm->readpos = 0;
274         }
275       else
276         {
277           int idx = parm->base64.idx;
278           unsigned char val = parm->base64.val;
279
280           while (n < count && parm->readpos < parm->linelen )
281             {
282               c = parm->line[parm->readpos++];
283               if (c == '\n' || c == ' ' || c == '\r' || c == '\t')
284                 continue;
285               if (c == '=')
286                 { /* pad character: stop */
287                   if (idx == 1)
288                     buffer[n++] = val; 
289                   parm->stop_seen = 1;
290                   break;
291                 }
292               if( (c = asctobin[(c2=c)]) == 255 ) 
293                 {
294                   log_error (_("invalid radix64 character %02x skipped\n"),
295                              c2);
296                   continue;
297                 }
298               switch (idx) 
299                 {
300                 case 0: 
301                   val = c << 2;
302                   break;
303                 case 1: 
304                   val |= (c>>4)&3;
305                   buffer[n++] = val;
306                   val = (c<<4)&0xf0;
307                   break;
308                 case 2: 
309                   val |= (c>>2)&15;
310                   buffer[n++] = val;
311                   val = (c<<6)&0xc0;
312                   break;
313                 case 3: 
314                   val |= c&0x3f;
315                   buffer[n++] = val;
316                   break;
317                 }
318               idx = (idx+1) % 4;
319             }
320           if (parm->readpos == parm->linelen)
321             parm->linelen = parm->readpos = 0;
322
323           parm->base64.idx = idx;
324           parm->base64.val = val;
325         }
326     }
327   else
328     { /* DER encoded */
329       while (n < count && parm->readpos < parm->linelen)
330           buffer[n++] = parm->line[parm->readpos++];
331       if (parm->readpos == parm->linelen)
332         parm->linelen = parm->readpos = 0;
333     }
334
335   *nread = n;
336   return 0;
337 }
338
339
340
341 static int
342 simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
343 {
344   struct reader_cb_parm_s *parm = cb_value;
345   size_t n;
346   int c = 0;
347
348   *nread = 0;
349   if (!buffer)
350     return -1; /* not supported */
351
352   for (n=0; n < count; n++)
353     {
354       c = getc (parm->fp);
355       if (c == EOF)
356         {
357           if ( ferror (parm->fp) )
358             return -1;
359           if (n)
360             break; /* return what we have before an EOF */
361           return -1;
362         }
363       *(byte *)buffer++ = c;
364     }
365
366   *nread = n;
367   return 0;
368 }
369
370
371
372 \f
373 static int
374 base64_writer_cb (void *cb_value, const void *buffer, size_t count)
375 {
376   struct writer_cb_parm_s *parm = cb_value;
377   unsigned char radbuf[4];
378   int i, c, idx, quad_count;
379   const unsigned char *p;
380   FILE *fp = parm->fp;
381
382   if (!count)
383     return 0;
384
385   if (!parm->wrote_begin)
386     {
387       if (parm->pem_name)
388         {
389           fputs ("-----BEGIN ", fp);
390           fputs (parm->pem_name, fp);
391           fputs ("-----\n", fp);
392         }
393       parm->wrote_begin = 1;
394       parm->base64.idx = 0;
395       parm->base64.quad_count = 0;
396     }
397
398   idx = parm->base64.idx;
399   quad_count = parm->base64.quad_count;
400   for (i=0; i < idx; i++)
401     radbuf[i] = parm->base64.radbuf[i];
402
403   for (p=buffer; count; p++, count--)
404     {
405       radbuf[idx++] = *p;
406       if (idx > 2)
407         {
408           idx = 0;
409           c = bintoasc[(*radbuf >> 2) & 077];
410           putc (c, fp);
411           c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
412           putc (c, fp);
413           c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
414           putc (c, fp);
415           c = bintoasc[radbuf[2]&077];
416           putc (c, fp);
417           if (++quad_count >= (64/4)) 
418             {
419               fputs (LF, fp);
420               quad_count = 0;
421             }
422         }
423     }
424   for (i=0; i < idx; i++)
425     parm->base64.radbuf[i] = radbuf[i];
426   parm->base64.idx = idx;
427   parm->base64.quad_count = quad_count;
428
429   return ferror (fp) ? KSBA_Write_Error:0;
430 }
431
432 static int
433 base64_finish_write (struct writer_cb_parm_s *parm)
434 {
435   unsigned char radbuf[4];
436   int i, c, idx, quad_count;
437   FILE *fp = parm->fp;
438
439   if (!parm->wrote_begin)
440     return 0; /* nothing written */
441
442   /* flush the base64 encoding */
443   idx = parm->base64.idx;
444   quad_count = parm->base64.quad_count;
445   for (i=0; i < idx; i++)
446     radbuf[i] = parm->base64.radbuf[i];
447
448   if (idx)
449     {
450       c = bintoasc[(*radbuf>>2)&077];
451       putc (c, fp);
452       if (idx == 1)
453         {
454           c = bintoasc[((*radbuf << 4) & 060) & 077];
455           putc (c, fp);
456           putc ('=', fp);
457           putc ('=', fp);
458         }
459       else 
460         { 
461           c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
462           putc (c, fp);
463           c = bintoasc[((radbuf[1] << 2) & 074) & 077];
464           putc (c, fp);
465           putc ('=', fp);
466
467         }
468       if (++quad_count >= (64/4)) 
469         {
470           fputs (LF, fp);
471           quad_count = 0;
472         }
473     }
474
475   if (quad_count)
476     fputs (LF, fp);
477
478   if (parm->pem_name)
479     {
480       fputs ("-----END ", fp);
481       fputs (parm->pem_name, fp);
482       fputs ("-----\n", fp);
483     }
484   return ferror (fp)? GNUPG_Write_Error : 0;
485 }
486
487
488
489 \f
490 /* Create a reader for the given file descriptor.  Depending on the
491    control information an input decoding is automagically choosen.
492    The function returns a Base64Context object which must be passed to
493    the gpgme_destroy_reader function.  The created KsbaReader object
494    is also returned, but the caller must not call the
495    ksba_reader_release function on. */
496 int
497 gpgsm_create_reader (Base64Context *ctx,
498                      CTRL ctrl, FILE *fp, KsbaReader *r_reader)
499 {
500   int rc;
501   KsbaReader r;
502
503   *r_reader = NULL;
504   *ctx = xtrycalloc (1, sizeof **ctx);
505   if (!*ctx)
506     return seterr (Out_Of_Core);
507
508   r = ksba_reader_new ();
509   if (!r)
510     {
511       xfree (*ctx); *ctx = NULL;
512       return seterr (Out_Of_Core);
513     }
514
515   (*ctx)->u.rparm.fp = fp;
516   if (ctrl->is_pem)
517     {
518       (*ctx)->u.rparm.assume_pem = 1;
519       (*ctx)->u.rparm.assume_base64 = 1;
520       rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
521     }
522   else if (ctrl->is_base64)
523     {
524       (*ctx)->u.rparm.assume_base64 = 1;
525       rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
526     }
527   else if (ctrl->autodetect_encoding)
528     {
529       (*ctx)->u.rparm.autodetect = 1;
530       rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
531     }
532   else
533       rc = ksba_reader_set_cb (r, simple_reader_cb, &(*ctx)->u.rparm);
534
535   if (rc)
536     {
537       ksba_reader_release (r);
538       xfree (*ctx); *ctx = NULL;
539       return map_ksba_err (rc);
540     }
541
542   *r_reader = r;
543   return 0;
544 }
545
546
547 void
548 gpgsm_destroy_reader (Base64Context ctx)
549 {
550   xfree (ctx);
551 }
552
553
554 \f
555 /* Create a writer for the given stream.  Depending on the control
556    information an output encoding is automagically choosen.  The
557    function returns a Base64Context object which must be passed to the
558    gpgme_destroy_writer function.  The created KsbaWriter object is
559    also returned, but the caller must not call the ksba_reader_release
560    function on. */
561 int
562 gpgsm_create_writer (Base64Context *ctx,
563                      CTRL ctrl, FILE *fp, KsbaWriter *r_writer)
564 {
565   int rc;
566   KsbaWriter w;
567
568   *r_writer = NULL;
569   *ctx = xtrycalloc (1, sizeof **ctx);
570   if (!*ctx)
571     return seterr (Out_Of_Core);
572
573   w = ksba_writer_new ();
574   if (!w)
575     {
576       xfree (*ctx); *ctx = NULL;
577       return seterr (Out_Of_Core);
578     }
579
580   if (ctrl->create_pem || ctrl->create_base64)
581     {
582       (*ctx)->u.wparm.fp = fp;
583       if (ctrl->create_pem)
584         (*ctx)->u.wparm.pem_name = "CMS OBJECT"; /* fixme */
585       rc = ksba_writer_set_cb (w, base64_writer_cb, &(*ctx)->u.wparm);
586     }
587   else
588     rc = ksba_writer_set_file (w, fp);
589
590   if (rc)
591     {
592       ksba_writer_release (w);
593       xfree (*ctx); *ctx = NULL;
594       return map_ksba_err (rc);
595     }
596
597   *r_writer = w;
598   return 0;
599 }
600
601
602 int
603 gpgsm_finish_writer (Base64Context ctx)
604 {
605   struct writer_cb_parm_s *parm;
606   
607   if (!ctx)
608     return GNUPG_Invalid_Value;
609   parm = &ctx->u.wparm;
610   if (parm->did_finish)
611     return 0; /* already done */
612   parm->did_finish = 1;
613   if (!parm->fp)
614     return 0; /* callback was not used */
615   return base64_finish_write (parm);
616 }
617
618 void
619 gpgsm_destroy_writer (Base64Context ctx)
620 {
621   xfree (ctx);
622 }