1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | PHP Version 5 | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1997-2015 The PHP Group | |
6 | +----------------------------------------------------------------------+ |
7 | | This source file is subject to version 3.01 of the PHP license, | |
8 | | that is bundled with this package in the file LICENSE, and is | |
9 | | available through the world-wide-web at the following url: | |
10 | | http://www.php.net/license/3_01.txt | |
11 | | If you did not receive a copy of the PHP license and are unable to | |
12 | | obtain it through the world-wide-web, please send a note to | |
13 | | license@php.net so we can mail you a copy immediately. | |
14 | +----------------------------------------------------------------------+ |
15 | | Author: Jim Winstead <jimw@php.net> | |
16 | +----------------------------------------------------------------------+ |
17 | */ |
18 | /* $Id$ */ |
19 | |
20 | #include <string.h> |
21 | |
22 | #include "php.h" |
23 | #include "base64.h" |
24 | |
25 | /* {{{ base64 tables */ |
26 | static const char base64_table[] = { |
27 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', |
28 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', |
29 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', |
30 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', |
31 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0' |
32 | }; |
33 | |
34 | static const char base64_pad = '='; |
35 | |
36 | static const short base64_reverse_table[256] = { |
37 | -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2, |
38 | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
39 | -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63, |
40 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2, |
41 | -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
42 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2, |
43 | -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
44 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, |
45 | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
46 | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
47 | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
48 | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
49 | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
50 | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
51 | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
52 | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 |
53 | }; |
54 | /* }}} */ |
55 | |
56 | PHPAPI unsigned char *php_base64_encode(const unsigned char *str, int length, int *ret_length) /* {{{ */ |
57 | { |
58 | const unsigned char *current = str; |
59 | unsigned char *p; |
60 | unsigned char *result; |
61 | |
62 | if (length < 0) { |
63 | if (ret_length != NULL) { |
64 | *ret_length = 0; |
65 | } |
66 | return NULL; |
67 | } |
68 | |
69 | result = (unsigned char *) safe_emalloc((length + 2) / 3, 4 * sizeof(char), 1); |
70 | p = result; |
71 | |
72 | while (length > 2) { /* keep going until we have less than 24 bits */ |
73 | *p++ = base64_table[current[0] >> 2]; |
74 | *p++ = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)]; |
75 | *p++ = base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)]; |
76 | *p++ = base64_table[current[2] & 0x3f]; |
77 | |
78 | current += 3; |
79 | length -= 3; /* we just handle 3 octets of data */ |
80 | } |
81 | |
82 | /* now deal with the tail end of things */ |
83 | if (length != 0) { |
84 | *p++ = base64_table[current[0] >> 2]; |
85 | if (length > 1) { |
86 | *p++ = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)]; |
87 | *p++ = base64_table[(current[1] & 0x0f) << 2]; |
88 | *p++ = base64_pad; |
89 | } else { |
90 | *p++ = base64_table[(current[0] & 0x03) << 4]; |
91 | *p++ = base64_pad; |
92 | *p++ = base64_pad; |
93 | } |
94 | } |
95 | if (ret_length != NULL) { |
96 | *ret_length = (int)(p - result); |
97 | } |
98 | *p = '\0'; |
99 | return result; |
100 | } |
101 | /* }}} */ |
102 | |
103 | /* {{{ */ |
104 | /* generate reverse table (do not set index 0 to 64) |
105 | static unsigned short base64_reverse_table[256]; |
106 | #define rt base64_reverse_table |
107 | void php_base64_init(void) |
108 | { |
109 | char *s = emalloc(10240), *sp; |
110 | char *chp; |
111 | short idx; |
112 | |
113 | for(ch = 0; ch < 256; ch++) { |
114 | chp = strchr(base64_table, ch); |
115 | if(ch && chp) { |
116 | idx = chp - base64_table; |
117 | if (idx >= 64) idx = -1; |
118 | rt[ch] = idx; |
119 | } else { |
120 | rt[ch] = -1; |
121 | } |
122 | } |
123 | sp = s; |
124 | sprintf(sp, "static const short base64_reverse_table[256] = {\n"); |
125 | for(ch =0; ch < 256;) { |
126 | sp = s+strlen(s); |
127 | sprintf(sp, "\t% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,\n", rt[ch+0], rt[ch+1], rt[ch+2], rt[ch+3], rt[ch+4], rt[ch+5], rt[ch+6], rt[ch+7], rt[ch+8], rt[ch+9], rt[ch+10], rt[ch+11], rt[ch+12], rt[ch+13], rt[ch+14], rt[ch+15]); |
128 | ch += 16; |
129 | } |
130 | sprintf(sp, "};"); |
131 | php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Reverse_table:\n%s", s); |
132 | efree(s); |
133 | } |
134 | */ |
135 | /* }}} */ |
136 | |
137 | PHPAPI unsigned char *php_base64_decode(const unsigned char *str, int length, int *ret_length) /* {{{ */ |
138 | { |
139 | return php_base64_decode_ex(str, length, ret_length, 0); |
140 | } |
141 | /* }}} */ |
142 | |
143 | PHPAPI unsigned char *php_base64_decode_ex(const unsigned char *str, int length, int *ret_length, zend_bool strict) /* {{{ */ |
144 | { |
145 | const unsigned char *current = str; |
146 | int ch, i = 0, j = 0, k; |
147 | /* this sucks for threaded environments */ |
148 | unsigned char *result; |
149 | |
150 | result = (unsigned char *)safe_emalloc(length, 1, 1); |
151 | |
152 | /* run through the whole string, converting as we go */ |
153 | while ((ch = *current++) != '\0' && length-- > 0) { |
154 | if (ch == base64_pad) { |
155 | if (*current != '=' && ((i % 4) == 1 || (strict && length > 0))) { |
156 | if ((i % 4) != 1) { |
157 | while (isspace(*(++current))) { |
158 | continue; |
159 | } |
160 | if (*current == '\0') { |
161 | continue; |
162 | } |
163 | } |
164 | efree(result); |
165 | return NULL; |
166 | } |
167 | continue; |
168 | } |
169 | |
170 | ch = base64_reverse_table[ch]; |
171 | if ((!strict && ch < 0) || ch == -1) { /* a space or some other separator character, we simply skip over */ |
172 | continue; |
173 | } else if (ch == -2) { |
174 | efree(result); |
175 | return NULL; |
176 | } |
177 | |
178 | switch(i % 4) { |
179 | case 0: |
180 | result[j] = ch << 2; |
181 | break; |
182 | case 1: |
183 | result[j++] |= ch >> 4; |
184 | result[j] = (ch & 0x0f) << 4; |
185 | break; |
186 | case 2: |
187 | result[j++] |= ch >>2; |
188 | result[j] = (ch & 0x03) << 6; |
189 | break; |
190 | case 3: |
191 | result[j++] |= ch; |
192 | break; |
193 | } |
194 | i++; |
195 | } |
196 | |
197 | k = j; |
198 | /* mop things up if we ended on a boundary */ |
199 | if (ch == base64_pad) { |
200 | switch(i % 4) { |
201 | case 1: |
202 | efree(result); |
203 | return NULL; |
204 | case 2: |
205 | k++; |
206 | case 3: |
207 | result[k] = 0; |
208 | } |
209 | } |
210 | if(ret_length) { |
211 | *ret_length = j; |
212 | } |
213 | result[j] = '\0'; |
214 | return result; |
215 | } |
216 | /* }}} */ |
217 | |
218 | /* {{{ proto string base64_encode(string str) |
219 | Encodes string using MIME base64 algorithm */ |
220 | PHP_FUNCTION(base64_encode) |
221 | { |
222 | char *str; |
223 | unsigned char *result; |
224 | int str_len, ret_length; |
225 | |
226 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s" , &str, &str_len) == FAILURE) { |
227 | return; |
228 | } |
229 | result = php_base64_encode((unsigned char*)str, str_len, &ret_length); |
230 | if (result != NULL) { |
231 | RETVAL_STRINGL((char*)result, ret_length, 0); |
232 | } else { |
233 | RETURN_FALSE; |
234 | } |
235 | } |
236 | /* }}} */ |
237 | |
238 | /* {{{ proto string base64_decode(string str[, bool strict]) |
239 | Decodes string using MIME base64 algorithm */ |
240 | PHP_FUNCTION(base64_decode) |
241 | { |
242 | char *str; |
243 | unsigned char *result; |
244 | zend_bool strict = 0; |
245 | int str_len, ret_length; |
246 | |
247 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b" , &str, &str_len, &strict) == FAILURE) { |
248 | return; |
249 | } |
250 | result = php_base64_decode_ex((unsigned char*)str, str_len, &ret_length, strict); |
251 | if (result != NULL) { |
252 | RETVAL_STRINGL((char*)result, ret_length, 0); |
253 | } else { |
254 | RETURN_FALSE; |
255 | } |
256 | } |
257 | /* }}} */ |
258 | |
259 | /* |
260 | * Local variables: |
261 | * tab-width: 4 |
262 | * c-basic-offset: 4 |
263 | * End: |
264 | * vim600: sw=4 ts=4 fdm=marker |
265 | * vim<600: sw=4 ts=4 |
266 | */ |
267 | |