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