WvStreams
|
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2002 Net Integration Technologies, Inc. 00004 * 00005 * Functions for encoding and decoding strings in MIME's Base64 notation. 00006 * 00007 * Base 64 is pretty easy. The input is processed in groups of three bytes. 00008 * These 24 bits are split into 4 groups of 6 bits. Each group of six bits 00009 * is represented by one character in the base64 alphabet, in the encoded 00010 * output. The alphabet is as follows: 00011 * ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= 00012 * Where 'A' through '/' represent 000000 through 011111, the 64 different 00013 * combinations. The '=' (100000) is padding and has no value when decoded. 00014 */ 00015 #include "wvbase64.h" 00016 00017 // maps codes to the Base64 alphabet 00018 static char alphabet[67] = 00019 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"; 00020 00021 // finds codes in the Base64 alphabet 00022 static int lookup(char ch) 00023 { 00024 if (ch >= 'A' && ch <= 'Z') 00025 return ch - 'A'; 00026 if (ch >= 'a' && ch <= 'z') 00027 return ch - 'a' + 26; 00028 if (ch >= '0' && ch <= '9') 00029 return ch - '0' + 52; 00030 if (ch == '+') 00031 return 62; 00032 if (ch == '/') 00033 return 63; 00034 if (ch == '=') 00035 return 64; // padding 00036 if (ch == '\n' || ch == ' ' || ch == '\r' || ch == '\t' || 00037 ch == '\f' || ch == '\v') 00038 return 65; // whitespace 00039 return -1; 00040 } 00041 00042 00043 /***** WvBase64Encoder *****/ 00044 00045 WvBase64Encoder::WvBase64Encoder() 00046 { 00047 _reset(); 00048 } 00049 00050 00051 bool WvBase64Encoder::_reset() 00052 { 00053 state = ATBIT0; 00054 bits = 0; 00055 return true; 00056 } 00057 00058 00059 bool WvBase64Encoder::_encode(WvBuf &in, WvBuf &out, bool flush) 00060 { 00061 // base 64 encode the entire buffer 00062 while (in.used() != 0) 00063 { 00064 unsigned char next = in.getch(); 00065 bits = (bits << 8) | next; 00066 switch (state) 00067 { 00068 case ATBIT0: 00069 out.putch(alphabet[bits >> 2]); 00070 bits &= 0x03; 00071 state = ATBIT2; 00072 break; 00073 case ATBIT2: 00074 out.putch(alphabet[bits >> 4]); 00075 bits &= 0x0f; 00076 state = ATBIT4; 00077 break; 00078 case ATBIT4: 00079 out.putch(alphabet[bits >> 6]); 00080 out.putch(alphabet[bits & 0x3f]); 00081 bits = 0; 00082 state = ATBIT0; 00083 break; 00084 } 00085 } 00086 // do not consider the data flushed if we need padding 00087 if (flush && state != ATBIT0) 00088 return false; 00089 return true; 00090 } 00091 00092 00093 bool WvBase64Encoder::_finish(WvBuf &out) 00094 { 00095 // pad text if needed 00096 switch (state) 00097 { 00098 case ATBIT2: 00099 out.putch(alphabet[bits << 4]); 00100 out.putch('='); 00101 out.putch('='); 00102 break; 00103 case ATBIT4: 00104 out.putch(alphabet[bits << 2]); 00105 out.putch('='); 00106 break; 00107 case ATBIT0: 00108 break; 00109 } 00110 return true; 00111 } 00112 00113 00114 00115 /***** WvBase64Decoder *****/ 00116 00117 WvBase64Decoder::WvBase64Decoder() 00118 { 00119 _reset(); 00120 } 00121 00122 00123 bool WvBase64Decoder::_reset() 00124 { 00125 state = ATBIT0; 00126 bits = 0; 00127 return true; 00128 } 00129 00130 00131 bool WvBase64Decoder::_encode(WvBuf &in, WvBuf &out, bool flush) 00132 { 00133 // base 64 decode the entire buffer 00134 while (in.used() != 0) 00135 { 00136 unsigned char next = in.getch(); 00137 int symbol = lookup(next); 00138 switch (symbol) 00139 { 00140 case -1: // invalid character 00141 seterror("invalid character #%s in base64 input", next); 00142 return false; 00143 00144 case 64: // padding 00145 // strip out any remaining padding 00146 // we are lenient in that we do not track how much padding we skip 00147 setfinished(); 00148 state = PAD; 00149 break; 00150 00151 case 65: // whitespace 00152 break; 00153 00154 default: // other symbol 00155 bits = (bits << 6) | symbol; 00156 switch (state) 00157 { 00158 case ATBIT0: 00159 state = ATBIT2; 00160 break; 00161 case ATBIT2: 00162 out.putch(bits >> 4); 00163 bits &= 0x0f; 00164 state = ATBIT4; 00165 break; 00166 case ATBIT4: 00167 out.putch(bits >> 2); 00168 bits &= 0x03; 00169 state = ATBIT6; 00170 break; 00171 case ATBIT6: 00172 out.putch(bits); 00173 bits = 0; 00174 state = ATBIT0; 00175 break; 00176 00177 case PAD: 00178 seterror("invalid character #%s " 00179 "after base64 padding", next); 00180 return false; 00181 } 00182 break; 00183 } 00184 } 00185 // if flushing and we did not get sufficient padding, then fail 00186 if (flush && (state == ATBIT2 || state == ATBIT4 || state == ATBIT6)) 00187 return false; // insufficient padding to flush! 00188 return true; 00189 }