Back to index

citadel  8.12
base64.c
Go to the documentation of this file.
00001 /*
00002  * Encode or decode file as MIME base64 (RFC 1341)
00003  * Public domain by John Walker, August 11 1997
00004  * Modified slightly for the Citadel system, June 1999
00005  *
00006  *  This program is open source software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License version 3.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  */
00014 
00015 
00016 #include <stdio.h>
00017 #include <stdlib.h>
00018 #include <ctype.h>
00019 #include <string.h>
00020 
00021 #define TRUE  1
00022 #define FALSE 0
00023 
00024 #define LINELEN 72                /* Encoded line length (max 76) */
00025 
00026 typedef unsigned char byte;       /* Byte type */
00027 
00028 FILE *fi;                         /* Input file */
00029 FILE *fo;                         /* Output file */
00030 static byte iobuf[256];           /* I/O buffer */
00031 static int iolen = 0;                    /* Bytes left in I/O buffer */
00032 static int iocp = 256;                   /* Character removal pointer */
00033 static int ateof = FALSE;         /* EOF encountered */
00034 static byte dtable[256];          /* Encode / decode table */
00035 static int linelength = 0;        /* Length of encoded output line */
00036 static char eol[] = "\r\n";           /* End of line sequence */
00037 static int errcheck = TRUE;       /* Check decode input for errors ? */
00038 
00039 /*  INBUF  --  Fill input buffer with data  */
00040 
00041 static int inbuf(void)
00042 {
00043     int l;
00044 
00045     if (ateof) {
00046        return FALSE;
00047     }
00048     l = fread(iobuf, 1, sizeof iobuf, fi);     /* Read input buffer */
00049     if (l <= 0) {
00050        if (ferror(fi)) {
00051            exit(1);
00052        }
00053        ateof = TRUE;
00054        return FALSE;
00055     }
00056     iolen = l;
00057     iocp = 0;
00058     return TRUE;
00059 }
00060 
00061 /*  INCHAR  --       Return next character from input  */
00062 
00063 static int inchar(void)
00064 {
00065     if (iocp >= iolen) {
00066        if (!inbuf()) {
00067          return EOF;
00068        }
00069     }
00070 
00071     return iobuf[iocp++];
00072 }
00073 
00074 /*  OCHAR  --  Output an encoded character, inserting line breaks
00075               where required.      */
00076 
00077 static void ochar(int c)
00078 {
00079     if (linelength >= LINELEN) {
00080        if (fputs(eol, fo) == EOF) {
00081            exit(1);
00082        }
00083        linelength = 0;
00084     }
00085     if (putc(((byte) c), fo) == EOF) {
00086        exit(1);
00087     }
00088     linelength++;
00089 }
00090 
00091 /*  ENCODE  --       Encode binary file into base64.  */
00092 
00093 static void encode(void)
00094 {
00095     int i, hiteof = FALSE;
00096 
00097     /* Fill dtable with character encodings.  */
00098 
00099     for (i = 0; i < 26; i++) {
00100         dtable[i] = 'A' + i;
00101         dtable[26 + i] = 'a' + i;
00102     }
00103     for (i = 0; i < 10; i++) {
00104         dtable[52 + i] = '0' + i;
00105     }
00106     dtable[62] = '+';
00107     dtable[63] = '/';
00108 
00109     while (!hiteof) {
00110        byte igroup[3], ogroup[4];
00111        int c, n;
00112 
00113        igroup[0] = igroup[1] = igroup[2] = 0;
00114        for (n = 0; n < 3; n++) {
00115            c = inchar();
00116            if (c == EOF) {
00117               hiteof = TRUE;
00118               break;
00119            }
00120            igroup[n] = (byte) c;
00121        }
00122        if (n > 0) {
00123            ogroup[0] = dtable[igroup[0] >> 2];
00124            ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
00125            ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
00126            ogroup[3] = dtable[igroup[2] & 0x3F];
00127 
00128             /* Replace characters in output stream with "=" pad
00129               characters if fewer than three characters were
00130               read from the end of the input stream. */
00131 
00132            if (n < 3) {
00133                 ogroup[3] = '=';
00134               if (n < 2) {
00135                     ogroup[2] = '=';
00136               }
00137            }
00138            for (i = 0; i < 4; i++) {
00139               ochar(ogroup[i]);
00140            }
00141        }
00142     }
00143     if (fputs(eol, fo) == EOF) {
00144        exit(1);
00145     }
00146 }
00147 
00148 /*  INSIG  --  Return next significant input  */
00149 
00150 static int insig(void)
00151 {
00152     int c;
00153 
00154     /*CONSTANTCONDITION*/
00155     while (TRUE) {
00156        c = inchar();
00157         if (c == EOF || (c > ' ')) {
00158            return c;
00159        }
00160     }
00161     /*NOTREACHED*/
00162 }
00163 
00164 /*  DECODE  --       Decode base64.       */
00165 
00166 static void decode(void)
00167 {
00168     int i;
00169 
00170     for (i = 0; i < 255; i++) {
00171        dtable[i] = 0x80;
00172     }
00173     for (i = 'A'; i <= 'Z'; i++) {
00174         dtable[i] = 0 + (i - 'A');
00175     }
00176     for (i = 'a'; i <= 'z'; i++) {
00177         dtable[i] = 26 + (i - 'a');
00178     }
00179     for (i = '0'; i <= '9'; i++) {
00180         dtable[i] = 52 + (i - '0');
00181     }
00182     dtable['+'] = 62;
00183     dtable['/'] = 63;
00184     dtable['='] = 0;
00185 
00186     /*CONSTANTCONDITION*/
00187     while (TRUE) {
00188        byte a[4], b[4], o[3];
00189 
00190        for (i = 0; i < 4; i++) {
00191            int c = insig();
00192 
00193            if (c == EOF) {
00194               if (errcheck && (i > 0)) {
00195                     fprintf(stderr, "Input file incomplete.\n");
00196                   exit(1);
00197               }
00198               return;
00199            }
00200            if (dtable[c] & 0x80) {
00201               if (errcheck) {
00202                     fprintf(stderr, "Illegal character '%c' in input file.\n", c);
00203                   exit(1);
00204               }
00205               /* Ignoring errors: discard invalid character. */
00206               i--;
00207               continue;
00208            }
00209            a[i] = (byte) c;
00210            b[i] = (byte) dtable[c];
00211        }
00212        o[0] = (b[0] << 2) | (b[1] >> 4);
00213        o[1] = (b[1] << 4) | (b[2] >> 2);
00214        o[2] = (b[2] << 6) | b[3];
00215         i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
00216        if (fwrite(o, i, 1, fo) == EOF) {
00217            exit(1);
00218        }
00219        if (i < 3) {
00220            return;
00221        }
00222     }
00223 }
00224 
00225 /*  USAGE  --  Print how-to-call information.  */
00226 
00227 static void usage(char *pname)
00228 {
00229     fprintf(stderr, "%s  --  Encode/decode file as base64.  Call:\n", pname);
00230     fprintf(stderr,
00231     "            %s [-e[ncode] / -d[ecode]] [-n] [infile] [outfile]\n", pname);
00232     fprintf(stderr, "\n");
00233     fprintf(stderr, "Options:\n");
00234     fprintf(stderr, "           -D         Decode base64 encoded file\n");
00235     fprintf(stderr, "           -E         Encode file into base64\n");
00236     fprintf(stderr, "           -N         Ignore errors when decoding\n");
00237     fprintf(stderr, "           -U         Print this message\n");
00238     fprintf(stderr, "\n");
00239     fprintf(stderr, "by John Walker\n");
00240     fprintf(stderr, "   WWW:    http://www.fourmilab.ch/\n");
00241 }
00242 
00243 /*  Main program  */
00244 
00245 int main(int argc, char *argv[])
00246 {
00247     int i, f = 0, decoding = FALSE;
00248     char *cp, opt;
00249 
00250     fi = stdin;
00251     fo = stdout;
00252 
00253     for (i = 1; i < argc; i++) {
00254        cp = argv[i];
00255         if (*cp == '-') {
00256            opt = *(++cp);
00257            if (islower(opt)) {
00258               opt = toupper(opt);
00259            }
00260            switch (opt) {
00261 
00262                 case 'D':             /* -D  Decode */
00263                   decoding = TRUE;
00264                   break;
00265 
00266                 case 'E':             /* -E  Encode */
00267                   decoding = FALSE;
00268                   break;
00269 
00270                 case 'N':             /* -N  Suppress error checking */
00271                   errcheck = FALSE;
00272                   break;
00273 
00274                 case 'U':             /* -U  Print how-to-call information */
00275                 case '?':
00276                   usage(argv[0]);
00277                   return 0;
00278           }
00279        } else {
00280            switch (f) {
00281 
00294               case 0:
00295                     if (strcmp(cp, "-") != 0) {
00296                         if ((fi = fopen(cp, "r")) == NULL) {
00297                             fprintf(stderr, "Cannot open input file %s\n", cp);
00298                          return 2;
00299                      }
00300                   }
00301                   f++;
00302                   break;
00303 
00304               case 1:
00305                     if (strcmp(cp, "-") != 0) {
00306                         if ((fo = fopen(cp, "w")) == NULL) {
00307                             fprintf(stderr, "Cannot open output file %s\n", cp);
00308                          return 2;
00309                      }
00310                   }
00311                   f++;
00312                   break;
00313 
00314               default:
00315                     fprintf(stderr, "Too many file names specified.\n");
00316                   usage(argv[0]);
00317                   return 2;
00318            }
00319        }
00320     }
00321 
00322     if (decoding) {
00323        decode();
00324     } else {
00325        encode();
00326     }
00327     return 0;
00328 }