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: Kirill Maximov <kir@actimind.com> | |
16 | +----------------------------------------------------------------------+ |
17 | */ |
18 | |
19 | /* $Id$ */ |
20 | |
21 | #include <stdlib.h> |
22 | |
23 | #ifdef HAVE_UNISTD_H |
24 | #include <unistd.h> |
25 | #endif |
26 | #include <string.h> |
27 | #include <errno.h> |
28 | |
29 | #include "php.h" |
30 | #include "quot_print.h" |
31 | |
32 | #include <stdio.h> |
33 | |
34 | /* |
35 | * Converting HEX char to INT value |
36 | */ |
37 | static char php_hex2int(int c) /* {{{ */ |
38 | { |
39 | if (isdigit(c)) { |
40 | return c - '0'; |
41 | } |
42 | else if (c >= 'A' && c <= 'F') { |
43 | return c - 'A' + 10; |
44 | } |
45 | else if (c >= 'a' && c <= 'f') { |
46 | return c - 'a' + 10; |
47 | } |
48 | else { |
49 | return -1; |
50 | } |
51 | } |
52 | /* }}} */ |
53 | |
54 | PHPAPI unsigned char *php_quot_print_decode(const unsigned char *str, size_t length, size_t *ret_length, int replace_us_by_ws) /* {{{ */ |
55 | { |
56 | register unsigned int i; |
57 | register unsigned const char *p1; |
58 | register unsigned char *p2; |
59 | register unsigned int h_nbl, l_nbl; |
60 | |
61 | size_t decoded_len, buf_size; |
62 | unsigned char *retval; |
63 | |
64 | static unsigned int hexval_tbl[256] = { |
65 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 32, 16, 64, 64, 16, 64, 64, |
66 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
67 | 32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
68 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 64, 64, 64, 64, 64, 64, |
69 | 64, 10, 11, 12, 13, 14, 15, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
70 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
71 | 64, 10, 11, 12, 13, 14, 15, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
72 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
73 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
74 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
75 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
76 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
77 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
78 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
79 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
80 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 |
81 | }; |
82 | |
83 | if (replace_us_by_ws) { |
84 | replace_us_by_ws = '_'; |
85 | } |
86 | |
87 | i = length, p1 = str; buf_size = length; |
88 | |
89 | while (i > 1 && *p1 != '\0') { |
90 | if (*p1 == '=') { |
91 | buf_size -= 2; |
92 | p1++; |
93 | i--; |
94 | } |
95 | p1++; |
96 | i--; |
97 | } |
98 | |
99 | retval = emalloc(buf_size + 1); |
100 | i = length; p1 = str; p2 = retval; |
101 | decoded_len = 0; |
102 | |
103 | while (i > 0 && *p1 != '\0') { |
104 | if (*p1 == '=') { |
105 | i--, p1++; |
106 | if (i == 0 || *p1 == '\0') { |
107 | break; |
108 | } |
109 | h_nbl = hexval_tbl[*p1]; |
110 | if (h_nbl < 16) { |
111 | /* next char should be a hexadecimal digit */ |
112 | if ((--i) == 0 || (l_nbl = hexval_tbl[*(++p1)]) >= 16) { |
113 | efree(retval); |
114 | return NULL; |
115 | } |
116 | *(p2++) = (h_nbl << 4) | l_nbl, decoded_len++; |
117 | i--, p1++; |
118 | } else if (h_nbl < 64) { |
119 | /* soft line break */ |
120 | while (h_nbl == 32) { |
121 | if (--i == 0 || (h_nbl = hexval_tbl[*(++p1)]) == 64) { |
122 | efree(retval); |
123 | return NULL; |
124 | } |
125 | } |
126 | if (p1[0] == '\r' && i >= 2 && p1[1] == '\n') { |
127 | i--, p1++; |
128 | } |
129 | i--, p1++; |
130 | } else { |
131 | efree(retval); |
132 | return NULL; |
133 | } |
134 | } else { |
135 | *(p2++) = (replace_us_by_ws == *p1 ? '\x20': *p1); |
136 | i--, p1++, decoded_len++; |
137 | } |
138 | } |
139 | |
140 | *p2 = '\0'; |
141 | *ret_length = decoded_len; |
142 | return retval; |
143 | } |
144 | /* }}} */ |
145 | |
146 | #define PHP_QPRINT_MAXL 75 |
147 | |
148 | PHPAPI unsigned char *php_quot_print_encode(const unsigned char *str, size_t length, size_t *ret_length) /* {{{ */ |
149 | { |
150 | unsigned long lp = 0; |
151 | unsigned char c, *ret, *d; |
152 | char *hex = "0123456789ABCDEF" ; |
153 | |
154 | ret = safe_emalloc(3, length + (((3 * length)/(PHP_QPRINT_MAXL-9)) + 1), 1); |
155 | d = ret; |
156 | |
157 | while (length--) { |
158 | if (((c = *str++) == '\015') && (*str == '\012') && length > 0) { |
159 | *d++ = '\015'; |
160 | *d++ = *str++; |
161 | length--; |
162 | lp = 0; |
163 | } else { |
164 | if (iscntrl (c) || (c == 0x7f) || (c & 0x80) || (c == '=') || ((c == ' ') && (*str == '\015'))) { |
165 | if ((((lp+= 3) > PHP_QPRINT_MAXL) && (c <= 0x7f)) |
166 | || ((c > 0x7f) && (c <= 0xdf) && ((lp + 3) > PHP_QPRINT_MAXL)) |
167 | || ((c > 0xdf) && (c <= 0xef) && ((lp + 6) > PHP_QPRINT_MAXL)) |
168 | || ((c > 0xef) && (c <= 0xf4) && ((lp + 9) > PHP_QPRINT_MAXL))) { |
169 | *d++ = '='; |
170 | *d++ = '\015'; |
171 | *d++ = '\012'; |
172 | lp = 3; |
173 | } |
174 | *d++ = '='; |
175 | *d++ = hex[c >> 4]; |
176 | *d++ = hex[c & 0xf]; |
177 | } else { |
178 | if ((++lp) > PHP_QPRINT_MAXL) { |
179 | *d++ = '='; |
180 | *d++ = '\015'; |
181 | *d++ = '\012'; |
182 | lp = 1; |
183 | } |
184 | *d++ = c; |
185 | } |
186 | } |
187 | } |
188 | *d = '\0'; |
189 | *ret_length = d - ret; |
190 | |
191 | ret = erealloc(ret, *ret_length + 1); |
192 | return ret; |
193 | } |
194 | /* }}} */ |
195 | |
196 | /* |
197 | * |
198 | * Decoding Quoted-printable string. |
199 | * |
200 | */ |
201 | /* {{{ proto string quoted_printable_decode(string str) |
202 | Convert a quoted-printable string to an 8 bit string */ |
203 | PHP_FUNCTION(quoted_printable_decode) |
204 | { |
205 | char *arg1, *str_in, *str_out; |
206 | int arg1_len, i = 0, j = 0, k; |
207 | |
208 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s" , &arg1, &arg1_len) == FAILURE) { |
209 | return; |
210 | } |
211 | |
212 | if (arg1_len == 0) { |
213 | /* shortcut */ |
214 | RETURN_EMPTY_STRING(); |
215 | } |
216 | |
217 | str_in = arg1; |
218 | str_out = emalloc(arg1_len + 1); |
219 | while (str_in[i]) { |
220 | switch (str_in[i]) { |
221 | case '=': |
222 | if (str_in[i + 1] && str_in[i + 2] && |
223 | isxdigit((int) str_in[i + 1]) && |
224 | isxdigit((int) str_in[i + 2])) |
225 | { |
226 | str_out[j++] = (php_hex2int((int) str_in[i + 1]) << 4) |
227 | + php_hex2int((int) str_in[i + 2]); |
228 | i += 3; |
229 | } else /* check for soft line break according to RFC 2045*/ { |
230 | k = 1; |
231 | while (str_in[i + k] && ((str_in[i + k] == 32) || (str_in[i + k] == 9))) { |
232 | /* Possibly, skip spaces/tabs at the end of line */ |
233 | k++; |
234 | } |
235 | if (!str_in[i + k]) { |
236 | /* End of line reached */ |
237 | i += k; |
238 | } |
239 | else if ((str_in[i + k] == 13) && (str_in[i + k + 1] == 10)) { |
240 | /* CRLF */ |
241 | i += k + 2; |
242 | } |
243 | else if ((str_in[i + k] == 13) || (str_in[i + k] == 10)) { |
244 | /* CR or LF */ |
245 | i += k + 1; |
246 | } |
247 | else { |
248 | str_out[j++] = str_in[i++]; |
249 | } |
250 | } |
251 | break; |
252 | default: |
253 | str_out[j++] = str_in[i++]; |
254 | } |
255 | } |
256 | str_out[j] = '\0'; |
257 | |
258 | RETVAL_STRINGL(str_out, j, 0); |
259 | } |
260 | /* }}} */ |
261 | |
262 | /* {{{ proto string quoted_printable_encode(string str) */ |
263 | PHP_FUNCTION(quoted_printable_encode) |
264 | { |
265 | char *str, *new_str; |
266 | int str_len; |
267 | size_t new_str_len; |
268 | |
269 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s" , &str, &str_len) != SUCCESS) { |
270 | return; |
271 | } |
272 | |
273 | if (!str_len) { |
274 | RETURN_EMPTY_STRING(); |
275 | } |
276 | |
277 | new_str = (char *)php_quot_print_encode((unsigned char *)str, (size_t)str_len, &new_str_len); |
278 | RETURN_STRINGL(new_str, new_str_len, 0); |
279 | } |
280 | /* }}} */ |
281 | |
282 | /* |
283 | * Local variables: |
284 | * tab-width: 4 |
285 | * c-basic-offset: 4 |
286 | * End: |
287 | * vim600: sw=4 ts=4 fdm=marker |
288 | * vim<600: sw=4 ts=4 |
289 | */ |
290 | |