]> Pileus Git - ~andy/fetchmail/blob - mime64/mime64.c
Add files from ESR's dev directory that weren't under version control
[~andy/fetchmail] / mime64 / mime64.c
1 /* mime64 */
2 /* MIME base64 encoder/decoder by Karl Hahn  hahn@lds.loral.com  3-Aug-94 */
3 /* modified 30-Sep-94 by Karl Hahn hahn@lds.loral.com: handle multiple
4    content */
5 /* modified 12-Jan-95 by Karl Hahn hahn@lds.loral.com: handle file names
6    that are encased in quotes */
7 /* modified 18-Jan-95 by Karl Hahn hahn@lds.loral.com: prevent complete
8    failure if filename in name field matches name of input file */
9 /* modified 19-Jan-95 by Karl Hahn hahn@lds.loral.com: prevent early exit
10    if last decoded character falls on a multiple of 3 -- would cause error
11    message and failure to rename output file if rename was necessary */
12 /* modified 19-Jan-95 by Karl Hahn hahn@lds.loral.com: prevent complete
13    failure if a line of text preceding the MIME64 stuff contains no
14    non-base64 characters */
15 /* modified 19-Jan-95 by Karl Hahn hahn@lds.loral.com: fixed command
16    line parser to prevent missing a name field preceded by another
17    name field */
18 /* modified 19-Jan-95 by Karl Hahn hahn@lds.loral.com: prevent error
19    message at the end of decoding each section.  Terminates output
20    file now on a blank line as well as the conditions that did so
21    previously */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #ifdef __FreeBSD__
28 #define strcmpi(x,y)    strcasecmp(x,y)
29 #endif
30
31 char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
32                   "0123456789+/";
33
34 enum TOKENTYPE { NONE, BLANKS, PUNCT, TAG, NAME, CONTENT };
35
36 struct TOKEN {
37                 char *text;
38                 int  length;
39                 int  index;
40                 enum TOKENTYPE type;
41              };
42
43 int compare_token( struct TOKEN *token, char *text )
44 {
45    int index=0;
46    int count;
47    int result;
48    char blivit1, blivit2;
49
50    count = token->length;
51
52    if ( count > 0 )
53    {
54       result = 1;
55    }
56    else
57    {
58       result = 0;
59    }
60
61    while ( (count > 0) && ( result != 0 ) )
62    {
63       blivit1 = token->text[index++];
64       if ( (blivit1 >= 'a' ) && (blivit1 <= 'z') )
65       {
66          blivit1 -= ' ';
67       }
68
69       blivit2 = *text++;
70       if ( (blivit2 >= 'a' ) && (blivit2 <= 'z') )
71       {
72          blivit2 -= ' ';
73       }
74
75       if ( blivit1 != blivit2 )
76       {
77          result = 0;
78       }
79
80       count--;
81    }
82
83    return result;
84 }
85
86 int ispunct( char blivit )
87 {
88    if ( ( blivit >= 'a' ) && (blivit <= 'z' ) )
89    {
90       blivit -= ' ';
91    }
92
93    if ( ( ( blivit < '0' ) ||
94           ( ( blivit > '9' ) && (blivit < 'A') ) ||
95           ( blivit > 'Z' ) ) &&
96         ( blivit != '-') && (blivit != '/') && (blivit != '.') )
97    {
98       return 1;
99    }
100    else
101    {
102       return 0;
103    }
104 }
105
106 void fixname( char *name )
107 {
108    while ( *name != '\0' )
109    {
110
111      if (ispunct( *name ) )
112      {
113         *name = '\0';
114      }
115
116      name++;
117    }
118 }
119
120 void acquire_token( char *line, enum TOKENTYPE type, struct TOKEN *token )
121 {
122    int doneflag=0, startflag=1;
123    int index;
124    enum TOKENTYPE nextstate=NONE;
125    char blivit;
126
127    if (token->type == NONE)
128    {
129       token->index = 0;
130       token->length = 0;
131    }
132
133    index = token->index + token->length;
134
135    token->text = 0;
136
137    while ( doneflag == 0 )
138    {
139       blivit = line[index];
140       if ( (blivit >= 'a') && (blivit <= 'z') )
141       {
142          blivit -= ' ';
143       }
144
145       switch (token->type)
146       {
147          case NONE:
148          if ( blivit == ' ')
149          {
150             index++;
151             token->index++;
152          }
153          else
154          {
155             token->type = TAG;
156             nextstate = TAG;
157          }
158          break;
159
160          case BLANKS:
161          if      ( blivit == ' ')
162          {
163             index++;
164          }
165          else if ( ispunct( blivit ) )
166          {
167             token->type = PUNCT;
168             token->index = index;
169          }
170          else
171          {
172             token->type = nextstate;
173             token->index = index;
174          }
175          break;
176
177          case PUNCT:
178          if      ( blivit <  ' ')
179          {
180             doneflag = 1;
181             token->type = NONE;
182             token->index = index;
183             token->text = line + index;
184             token->length = 0;
185          }
186          else if ( blivit == ' ' )
187          {
188             token->type = BLANKS;
189             token->index = index;
190             if      ( line[ token->index ] == ';' )
191             {
192                nextstate = NAME;
193             }
194             else if ( line[ token->index ] == '=' )
195             {
196                nextstate = CONTENT;
197             }
198
199          }
200          else if ( ispunct( blivit ) )
201          {
202             index++;
203          }
204          else
205          {
206             if      ( line[ token->index ] == ';' )
207             {
208                nextstate = NAME;
209             }
210             else if ( line[ token->index ] == '=' )
211             {
212                nextstate = CONTENT;
213             }
214
215             token->type = nextstate;
216             token->index = index;
217          }
218          break;
219
220          case TAG:
221          if ( ispunct( blivit ) )
222          {
223             token->length = index - token->index;
224             token->text = line + token->index;
225             nextstate = NAME;
226
227             if ( ( ( type == TAG ) || ( type == NONE ) ) && !startflag)
228             {
229                doneflag = 1;
230             }
231             else if (blivit == ' ')
232             {
233                token->type = BLANKS;
234                token->index = index;
235             }
236             else
237             {
238                token->type = PUNCT;
239                token->index = index;
240             }
241          }
242          else
243          {
244             index++;
245          }
246          break;
247
248          case NAME:
249          if ( ispunct( blivit ) )
250          {
251             token->length = index - token->index;
252             token->text = line + token->index;
253
254             if ( blivit != ';' )
255             {
256                nextstate = CONTENT;
257             }
258             else
259             {
260                nextstate = NAME;
261             }
262
263             if ( ( ( type == NAME ) || ( type == NONE ) ) && !startflag )
264             {
265                doneflag = 1;
266             }
267             else if (blivit == ' ')
268             {
269                token->type = BLANKS;
270                token->index = index;
271             }
272             else
273             {
274                token->type = PUNCT;
275                token->index = index;
276             }
277          }
278          else
279          {
280             index++;
281          }
282          break;
283
284          case CONTENT:
285          if ( ispunct( blivit ) )
286          {
287             token->length = index - token->index;
288             token->text = line + token->index;
289             nextstate = NAME;
290
291             if ( ( ( type == CONTENT ) || ( type == NONE ) ) && !startflag )
292             {
293                doneflag = 1;
294             }
295             else if (blivit == ' ')
296             {
297                token->type = BLANKS;
298                token->index = index;
299             }
300             else
301             {
302                token->type = PUNCT;
303                token->index = index;
304             }
305          }
306          else
307          {
308             index++;
309          }
310          break;
311       }
312       startflag = 0;
313    }
314 }
315
316 void fputch( char blivit, FILE *f )
317 {
318 /*   if (blivit == '\n') fputc( '\r', f );*/
319    fputc( blivit, f );
320 }
321
322 int classify_args( int narg,
323                    char *rawargs[], char *fileargs[], char *optargs[] )
324 {
325    int index, jndex, kndex;
326    char *argptr;
327
328    for ( index = 0, jndex = 0, kndex = 0; index < narg; index++ )
329    {
330       argptr = rawargs[index];
331       if (*argptr == '-')
332       {
333          argptr++;
334          optargs[kndex++] = argptr;
335       }
336       else
337       {
338          fileargs[jndex++] = argptr;
339       }
340    }
341
342    return kndex;
343 }
344
345 int cvt_ascii( unsigned char alpha )
346 {
347    if      ( (alpha >= 'A') && (alpha <= 'Z') ) return (int)(alpha - 'A');
348    else if ( (alpha >= 'a') && (alpha <= 'z') )
349         return 26 + (int)(alpha - 'a');
350    else if ( (alpha >= '0') && (alpha <= '9' ) )
351         return 52 + (int)(alpha - '0');
352    else if ( alpha == '+' ) return 62;
353    else if ( alpha == '/' ) return 63;
354    else if ( alpha == '=' ) return -2;
355    else                     return -1;
356 }
357
358 char *fileargs[64], *optargs[64];
359
360 struct STATE64 {
361                   unsigned long int accum;
362                   int               shift;
363                };
364
365
366 int main( int nargs, char *cargs[] )
367 {
368    int n_options, n_files, index, jndex, shift, save_shift;
369    enum { ENCODE, DECODE } whattodo = DECODE;
370    int help_flag = 0, replace_flag = 0, perm_replace_flag = 0, quit = 0;
371    int cycle_flag = 0;
372    FILE *fin, *fout = NULL, *dummy;
373    unsigned char blivit;
374    unsigned long accum, value;
375    char buf[80], dumname[80];
376    char *cptr, *altptr;
377    int decode_state;
378    struct TOKEN token;
379    int firsttime = 1;
380    int skipflag = 0;
381    int printmsg = 1;
382    int outcount = 0;
383
384    n_options = classify_args( nargs, cargs, fileargs, optargs );
385
386    n_files = nargs - n_options;
387
388    if ( n_files < 2 ) help_flag = 1;
389
390    for ( index = 0; index < n_options; index++ )
391    {
392       if ( ( optargs[index][0] == 'e' ) ||
393            ( optargs[index][0] == 'E' ) ) whattodo = ENCODE;
394       if ( optargs[index][0] == '?' ) help_flag = 1;
395    }
396
397    if ( help_flag )
398    {
399       printf( "mime64 infile [outfile] [-option] [-option] etc.\n\n"
400               "convert between binary and MIME BASE64 format\n\n"
401               "        -e       MIME base64 encode (default is decode)\n"
402               "        -?       display help message\n\n"
403               "if no outfile given, output file replaces infile\n" );
404    }
405
406    if ( n_files < 2 ) exit(0);
407
408    if ( whattodo == DECODE )
409    {
410       fin = fopen( fileargs[1], "r" );
411    }
412    else
413    {
414       fin = fopen( fileargs[1], "rb" );
415    }
416
417    if ( fin == 0 )
418    {
419       printf( "%s file not found\n", fileargs[1] );
420       exit(-1);
421    }
422
423    if ( n_files > 2 )
424    {
425       if ( whattodo == DECODE )
426       {
427          sprintf( dumname, "%s", fileargs[2] );
428       }
429       else
430       {
431          fout = fopen( fileargs[2], "w" );
432
433          if ( fout == 0 )
434          {
435             printf( "Couldn't open %s for output\n", fileargs[2] );
436          }
437       }
438    }
439    else
440    {
441       if ( whattodo == DECODE )
442       {
443          sprintf( dumname, "%s", fileargs[1] );
444       }
445       else
446       {
447          fout = fopen( "$$$$$$$$.$$$", "w" );
448       }
449
450       replace_flag = 1;
451    }
452
453
454 do {
455    quit = 0;
456    printmsg = 1;
457
458    if ( whattodo == DECODE )
459    {
460       shift = 0;
461       accum = 0;
462       decode_state = 0;
463
464       while ( ( !feof( fin ) ) && (quit == 0) )
465       {
466          fgets( buf, 80, fin );
467          if ( feof( fin ) )
468          {
469             if ( ( dumname[0] != '\0' ) && ( shift != 0 ) )
470             {
471                printf( "Unexpected end of file encountered in %s\n"
472                        "last few bytes may have been lost\n", dumname );
473                quit = 1;
474                decode_state = 1;
475                continue;
476             }
477             else if ( cycle_flag == 0 )
478             {
479                quit = 1;
480                decode_state = 1;
481                continue;
482             }
483          }
484          else
485          {
486             cycle_flag = 1;
487
488             if ( (decode_state == 1) &&
489                  ( (buf[0] == '\n') || (buf[0] < '+') ) )
490             {
491                quit = 1;
492
493                if ( shift != 0 )
494                {
495                   printf( "Unexpected end of section in %s\n"
496                           "last few bytes may have been lost\n", dumname );
497                }
498
499                continue;
500             }
501          }
502
503
504          if ( decode_state == 0 )
505          {
506             for ( index = 0;
507                   (buf[index] != '\n') && (buf[index] != '\0') &&
508                   (decode_state >= 0);
509                   index++ )
510             {
511                if ( ( (buf[index] >= 'A') && (buf[index] <= 'Z') ) ||
512                     ( (buf[index] >= 'a') && (buf[index] <= 'z') ) ||
513                     ( (buf[index] >= '0') && (buf[index] <= '9') ) ||
514                     (buf[index] == '+') ||
515                     (buf[index] == '/') ||
516                     (buf[index] == '=') )
517                {
518                   decode_state = 1;
519                }
520                else
521                {
522                   decode_state = -2;
523                }
524             }
525
526             if ( decode_state <= 0 )
527             {
528
529                decode_state = 0;
530                token.type = NONE;
531
532                acquire_token( buf, TAG, &token );
533                if      ( compare_token( &token, "Content-Type") )
534                {
535                   do
536                   {
537                      acquire_token( buf, NAME, &token );
538                      if ( compare_token( &token, "name" ) )
539                      {
540                         acquire_token( buf, CONTENT, &token );
541
542                         if ( ( replace_flag ) ||
543                              ( firsttime == 0 ) )
544                         {
545                            sscanf( token.text, "%s", dumname );
546                            fixname( dumname );
547
548                            if ( strcmpi( dumname, fileargs[1] ) != 0 )
549                            {
550                               replace_flag = 0;
551                            }
552                            else
553                            {
554                               if ( perm_replace_flag )
555                               {
556                                  printf( 
557                                  "More than one output file named %s\n",
558                                  dumname );
559
560                                  exit(-1);
561                               }
562                            }
563                         }
564                      }
565                   } while ( token.type != NONE );
566                }
567                else if ( compare_token( &token, "Content-transfer-encoding" ) )
568                {
569                   skipflag = 1;
570
571                   do
572                   {
573                      acquire_token( buf, NAME, &token );
574                      if ( compare_token( &token, "base64" ) )
575                      {
576                         skipflag = 0;
577                      }
578                   } while ( token.type != NONE );
579                }
580                continue;
581             }
582             else if ( skipflag != 0 )
583             {
584                continue;
585             }
586          }
587
588          if ( printmsg )
589          {
590             if ( skipflag )
591             {
592                printf( "Section %s not MIME base64\n", dumname );
593             }
594             else
595             {
596                printf( "Creating %s\n", dumname );
597                if ( strcmpi( dumname, fileargs[1] ) == 0 )
598                {
599                   replace_flag = 1;
600                }
601
602                if ( replace_flag )
603                {
604                   fout = fopen( "$$$$$$$$.$$$", "wb" );
605                }
606                else
607                {
608                   fout = fopen( dumname, "wb" );
609                }
610
611                if ( fout == 0 )
612                {
613                   printf( "Couldn't open %s for output\n", dumname );
614                }
615             }
616
617             printmsg = 0;
618          }
619
620          if ( fout == 0 )
621          {
622             printf( "No filename given for subsequent section\n" );
623             exit(-1);
624          }
625
626          if ( feof(fin) )
627          {
628             quit = 1;
629          }
630
631          if ( quit != 0 )
632          {
633             buf[0] = '\0';
634          }
635
636          for ( index = 0; (buf[index] != '\n') && (buf[index] != '\0'); index++)
637          {
638             value = cvt_ascii( buf[index] );
639
640             if ( value < 64 )
641             {
642                accum <<= 6;
643                shift += 6;
644                accum |= value;
645                if ( shift >= 8 )
646                {
647                   shift -= 8;
648                   value = accum >> shift;
649                   blivit = (unsigned char)value & 0xFFl;
650                   fputc( blivit, fout );
651                }
652             }
653             else
654             {
655                quit = 1;
656                break;
657             }
658          }
659       }
660    }
661    else
662    {
663       fprintf ( fout,
664        "Content-Type: text/plain; charset=US-ASCII; name=%s\n"
665        "Content-transfer-encoding: base64\n\n", fileargs[1] );
666
667       shift = 0;
668       accum = 0;
669       index = 0;
670       while ( ( !feof( fin ) ) || (shift != 0) )
671       {
672          if ( ( !feof( fin ) ) && ( quit == 0 ) )
673          {
674             blivit = fgetc( fin );
675
676             if ( feof( fin ) )
677             {
678                quit = 1;
679                save_shift = shift;
680                blivit = 0;
681             }
682          }
683          else
684          {
685             quit = 1;
686             save_shift = shift;
687             blivit = 0;
688          }
689
690          if ( (quit == 0) || (shift != 0) )
691          {
692             value = (unsigned long)blivit;
693             accum <<= 8;
694             shift += 8;
695             accum |= value;
696          } /* ENDIF */
697
698          while ( shift >= 6 )
699          {
700             shift -= 6;
701             value = (accum >> shift) & 0x3Fl;
702             blivit = alphabet[value];
703
704             buf[index++] = blivit;
705             if ( index >= 60 )
706             {
707                buf[index] = '\0';
708                fprintf( fout, "%s\n", buf );
709                index = 0;
710             }
711
712             if ( quit != 0 )
713             {
714                shift = 0;
715             }
716          }
717       }
718
719       if      ( save_shift == 2 )
720       {
721          buf[index++] = '=';
722          if ( index >= 60 )
723          {
724             buf[index] = '\0';
725             fprintf( fout, "%s\n", buf );
726             index = 0;
727          }
728
729          buf[index++] = '=';
730          if ( index >= 60 )
731          {
732             buf[index] = '\0';
733             fprintf( fout, "%s\n", buf );
734             index = 0;
735          }
736       }
737       else if ( save_shift == 4 )
738       {
739          buf[index++] = '=';
740          if ( index >= 60 )
741          {
742             buf[index] = '\0';
743             fprintf( fout, "%s\n", buf );
744             index = 0;
745          }
746       }
747
748       if ( index != 0 )
749       {
750          buf[index] = '\0';
751          fprintf( fout, "%s\n", buf );
752       }
753    }
754
755    if ( fout )
756    {
757       ++outcount;
758       fclose( fout );
759    }
760
761    if ( replace_flag )
762    {
763       perm_replace_flag = 1;
764
765       if ( ( whattodo == DECODE ) && ( decode_state <= 0 ) && ( outcount == 0 ) )
766       {
767          remove( "$$$$$$$$.$$$" );
768          printf( "No MIME base64 lines found in %s\n", fileargs[1] );
769       }
770    }
771    else
772    {
773       if ( ( whattodo == DECODE ) && ( decode_state <= 0 ) && ( outcount == 0 ) )
774       {
775          remove( fileargs[2] );
776          printf( "No MIME base64 lines found in %s\n", fileargs[1] );
777       }
778    }
779
780    fout = 0;
781    firsttime = 0;
782    dumname[0] = '\0';
783    cycle_flag = 0;
784
785 } while ( !feof( fin ) );
786
787
788 if ( perm_replace_flag )
789 {
790    remove( fileargs[1] );
791    rename( "$$$$$$$$.$$$", fileargs[1] );
792 }
793
794 fclose( fin );
795 }