# base64.cc

    00001 /*
    00002  * Encode or decode file as MIME base64 (RFC 1341)
    00003  * Public domain by John Walker, August 11 1997
    00004  *           http://www.fourmilab.ch/
    00005  * Modified slightly for the Citadel/UX system, June 1999
    00006  *
    00007  * Taken from the Citadel/UX GPL source tree, at version 6.01
    00008  * Modified into a C++ API by Chris Frey for Net Direct Inc., November 2005
    00009  *           http://www.netdirect.ca/
    00010  *
    00011  */
    00012
    00013 #include "base64.h"
    00014 #include <string>
    00015 #include <iterator>
    00016 #include <stdio.h>
    00017
    00018 #define TRUE  1
    00019 #define FALSE 0
    00020
    00021 #define LINELEN 72                    /* Encoded line length (max 76) */
    00022
    00023 typedef unsigned char byte;           /* Byte type */
    00024
    00025 static byte dtable[256];              /* Encode / decode table */
    00026 //static char eol[] = "\r\n";           /* End of line sequence */
    00027 static int errcheck = TRUE;           /* Check decode input for errors ? */
    00028
    00029
    00030 /*  INCHAR  --  Return next character from input  */
    00031
    00032 class base64_input
    00033 {
    00034         std::string::const_iterator begin, end;
    00035 public:
    00036         base64_input(const std::string &input)
    00037                 : begin(input.begin()), end(input.end()) {}
    00038
    00039         int operator()()
    00040         {
    00041                 if (begin == end) {
    00042                         return EOF;
    00043                 }
    00044                 return (int)((unsigned int)(unsigned char) *begin++);
    00045         }
    00046 };
    00047
    00048
    00049 /*  OCHAR  --  Output an encoded character, inserting line breaks
    00050                where required.  */
    00051
    00052 class base64_output
    00053 {
    00054         std::back_insert_iterator<std::string> insert;
    00055         int linelength;                 /* Length of encoded output line */
    00056
    00057 public:
    00058         base64_output(std::string &output)
    00059                 : insert(back_inserter(output)),
    00060                 linelength(0)
    00061         {}
    00062
    00063         void operator()(int c)
    00064         {
    00065                 if (linelength >= LINELEN) {
    00066                         *insert++ = '\n';
    00067                         *insert++ = ' ';
    00068                         linelength = 0;
    00069                 }
    00070                 *insert++ = (unsigned char) c;
    00071                 linelength++;
    00072         }
    00073 };
    00074
    00075 /*  ENCODE  --  Encode binary file into base64.  */
    00076
    00077 static bool encode(base64_input &inchar, base64_output &ochar)
    00078 {
    00079     int i, hiteof = FALSE;
    00080
    00081     /*  Fill dtable with character encodings.  */
    00082
    00083     for (i = 0; i < 26; i++) {
    00084         dtable[i] = 'A' + i;
    00085         dtable[26 + i] = 'a' + i;
    00086     }
    00087     for (i = 0; i < 10; i++) {
    00088         dtable[52 + i] = '0' + i;
    00089     }
    00090     dtable[62] = '+';
    00091     dtable[63] = '/';
    00092
    00093     while (!hiteof) {
    00094         byte igroup[3], ogroup[4];
    00095         int c, n;
    00096
    00097         igroup[0] = igroup[1] = igroup[2] = 0;
    00098         for (n = 0; n < 3; n++) {
    00099             c = inchar();
    00100             if (c == EOF) {
    00101                 hiteof = TRUE;
    00102                 break;
    00103             }
    00104             igroup[n] = (byte) c;
    00105         }
    00106         if (n > 0) {
    00107             ogroup[0] = dtable[igroup[0] >> 2];
    00108             ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
    00109             ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
    00110             ogroup[3] = dtable[igroup[2] & 0x3F];
    00111
    00112             /* Replace characters in output stream with "=" pad
    00113                characters if fewer than three characters were
    00114                read from the end of the input stream. */
    00115
    00116             if (n < 3) {
    00117                 ogroup[3] = '=';
    00118                 if (n < 2) {
    00119                     ogroup[2] = '=';
    00120                 }
    00121             }
    00122             for (i = 0; i < 4; i++) {
    00123                 ochar(ogroup[i]);
    00124             }
    00125         }
    00126     }
    00127     return true;
    00128 }
    00129
    00130 /*  INSIG  --  Return next significant input  */
    00131
    00132 static int insig(base64_input &inchar)
    00133 {
    00134     int c;
    00135
    00136     /*CONSTANTCONDITION*/
    00137     while (TRUE) {
    00138         c = inchar();
    00139         if (c == EOF || (c > ' ')) {
    00140             return c;
    00141         }
    00142     }
    00143     /*NOTREACHED*/
    00144 }
    00145
    00146 /*  DECODE  --  Decode base64.  */
    00147
    00148 static bool decode(base64_input &inchar, base64_output &ochar)
    00149 {
    00150     int i;
    00151
    00152     for (i = 0; i < 255; i++) {
    00153         dtable[i] = 0x80;
    00154     }
    00155     for (i = 'A'; i <= 'Z'; i++) {
    00156         dtable[i] = 0 + (i - 'A');
    00157     }
    00158     for (i = 'a'; i <= 'z'; i++) {
    00159         dtable[i] = 26 + (i - 'a');
    00160     }
    00161     for (i = '0'; i <= '9'; i++) {
    00162         dtable[i] = 52 + (i - '0');
    00163     }
    00164     dtable[(int)'+'] = 62;
    00165     dtable[(int)'/'] = 63;
    00166     dtable[(int)'='] = 0;
    00167
    00168     /*CONSTANTCONDITION*/
    00169     while (TRUE) {
    00170         byte a[4], b[4], o[3];
    00171
    00172         for (i = 0; i < 4; i++) {
    00173             int c = insig(inchar);
    00174
    00175             if (c == EOF) {
    00176                 // fprintf(stderr, "Input file incomplete.\n");
    00177                 return false;
    00178             }
    00179             if (dtable[c] & 0x80) {
    00180                 if (errcheck) {
    00181                     //fprintf(stderr, "Illegal character '%c' in input file.\n", c);
    00182                     return false;
    00183                 }
    00184                 /* Ignoring errors: discard invalid character. */
    00185                 i--;
    00186                 continue;
    00187             }
    00188             a[i] = (byte) c;
    00189             b[i] = (byte) dtable[c];
    00190         }
    00191         o[0] = (b[0] << 2) | (b[1] >> 4);
    00192         o[1] = (b[1] << 4) | (b[2] >> 2);
    00193         o[2] = (b[2] << 6) | b[3];
    00194         i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
    00195         for (int w = 0; w < i; w++ )
    00196             ochar(o[w]);
    00197         if (i < 3) {
    00198             return true;
    00199         }
    00200     }
    00201 }
    00202
    00203 // in-memory encode / decode API
    00204 bool base64_encode(const std::string &in, std::string &out)
    00205 {
    00206         out.clear();
    00207         base64_input input(in);
    00208         base64_output output(out);
    00209         return encode(input, output);
    00210 }
    00211
    00212 bool base64_decode(const std::string &in, std::string &out)
    00213 {
    00214         out.clear();
    00215         base64_input input(in);
    00216         base64_output output(out);
    00217         return decode(input, output);
    00218 }
    00219
    00220
    00221 #ifdef __TEST_MODE__
    00222
    00223 #include <iostream>
    00224 using namespace std;
    00225
    00226 /*  Main program  */
    00227
    00228 int main()
    00229 {
    00230         string test = "This is a test.", encoded, decoded;
    00231         base64_encode(test, encoded);
    00232         base64_decode(encoded, decoded);
    00233         if( test != decoded )
    00234                 cerr << "Test failed" << endl;
    00235         else
    00236                 cerr << "Success" << endl;
    00237 }
    00238
    00239 #endif
    00240