/src/php-src/ext/standard/quot_print.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Copyright (c) The PHP Group | |
4 | | +----------------------------------------------------------------------+ |
5 | | | This source file is subject to version 3.01 of the PHP license, | |
6 | | | that is bundled with this package in the file LICENSE, and is | |
7 | | | available through the world-wide-web at the following url: | |
8 | | | https://www.php.net/license/3_01.txt | |
9 | | | If you did not receive a copy of the PHP license and are unable to | |
10 | | | obtain it through the world-wide-web, please send a note to | |
11 | | | license@php.net so we can mail you a copy immediately. | |
12 | | +----------------------------------------------------------------------+ |
13 | | | Author: Kirill Maximov <kir@actimind.com> | |
14 | | +----------------------------------------------------------------------+ |
15 | | */ |
16 | | |
17 | | #include <stdlib.h> |
18 | | |
19 | | #ifdef HAVE_UNISTD_H |
20 | | #include <unistd.h> |
21 | | #endif |
22 | | #include <string.h> |
23 | | #include <errno.h> |
24 | | |
25 | | #include "php.h" |
26 | | #include "quot_print.h" |
27 | | |
28 | | #include <stdio.h> |
29 | | |
30 | | /* |
31 | | * Converting HEX char to INT value |
32 | | */ |
33 | | static char php_hex2int(int c) /* {{{ */ |
34 | 0 | { |
35 | 0 | if (isdigit(c)) { |
36 | 0 | return c - '0'; |
37 | 0 | } else if (c >= 'A' && c <= 'F') { |
38 | 0 | return c - 'A' + 10; |
39 | 0 | } else { |
40 | 0 | ZEND_ASSERT(c >= 'a' && c <= 'f'); |
41 | 0 | return c - 'a' + 10; |
42 | 0 | } |
43 | 0 | } |
44 | | /* }}} */ |
45 | | |
46 | | PHPAPI zend_string *php_quot_print_decode(const unsigned char *str, size_t length, int replace_us_by_ws) /* {{{ */ |
47 | 0 | { |
48 | 0 | size_t i; |
49 | 0 | unsigned const char *p1; |
50 | 0 | unsigned char *p2; |
51 | 0 | unsigned int h_nbl, l_nbl; |
52 | |
|
53 | 0 | size_t decoded_len, buf_size; |
54 | 0 | zend_string *retval; |
55 | |
|
56 | 0 | static unsigned int hexval_tbl[256] = { |
57 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 32, 16, 64, 64, 16, 64, 64, |
58 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
59 | 0 | 32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
60 | 0 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 64, 64, 64, 64, 64, 64, |
61 | 0 | 64, 10, 11, 12, 13, 14, 15, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
62 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
63 | 0 | 64, 10, 11, 12, 13, 14, 15, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
64 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
65 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
66 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
67 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
68 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
69 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
70 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
71 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, |
72 | 0 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 |
73 | 0 | }; |
74 | |
|
75 | 0 | if (replace_us_by_ws) { |
76 | 0 | replace_us_by_ws = '_'; |
77 | 0 | } |
78 | |
|
79 | 0 | i = length, p1 = str; buf_size = length; |
80 | |
|
81 | 0 | while (i > 1 && *p1 != '\0') { |
82 | 0 | if (*p1 == '=') { |
83 | 0 | buf_size -= 2; |
84 | 0 | p1++; |
85 | 0 | i--; |
86 | 0 | } |
87 | 0 | p1++; |
88 | 0 | i--; |
89 | 0 | } |
90 | |
|
91 | 0 | retval = zend_string_alloc(buf_size, 0); |
92 | 0 | i = length; p1 = str; p2 = (unsigned char*)ZSTR_VAL(retval); |
93 | 0 | decoded_len = 0; |
94 | |
|
95 | 0 | while (i > 0 && *p1 != '\0') { |
96 | 0 | if (*p1 == '=') { |
97 | 0 | i--, p1++; |
98 | 0 | if (i == 0 || *p1 == '\0') { |
99 | 0 | break; |
100 | 0 | } |
101 | 0 | h_nbl = hexval_tbl[*p1]; |
102 | 0 | if (h_nbl < 16) { |
103 | | /* next char should be a hexadecimal digit */ |
104 | 0 | if ((--i) == 0 || (l_nbl = hexval_tbl[*(++p1)]) >= 16) { |
105 | 0 | efree(retval); |
106 | 0 | return NULL; |
107 | 0 | } |
108 | 0 | *(p2++) = (h_nbl << 4) | l_nbl, decoded_len++; |
109 | 0 | i--, p1++; |
110 | 0 | } else if (h_nbl < 64) { |
111 | | /* soft line break */ |
112 | 0 | while (h_nbl == 32) { |
113 | 0 | if (--i == 0 || (h_nbl = hexval_tbl[*(++p1)]) == 64) { |
114 | 0 | efree(retval); |
115 | 0 | return NULL; |
116 | 0 | } |
117 | 0 | } |
118 | 0 | if (p1[0] == '\r' && i >= 2 && p1[1] == '\n') { |
119 | 0 | i--, p1++; |
120 | 0 | } |
121 | 0 | i--, p1++; |
122 | 0 | } else { |
123 | 0 | efree(retval); |
124 | 0 | return NULL; |
125 | 0 | } |
126 | 0 | } else { |
127 | 0 | *(p2++) = (replace_us_by_ws == *p1 ? '\x20': *p1); |
128 | 0 | i--, p1++, decoded_len++; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | 0 | *p2 = '\0'; |
133 | 0 | ZSTR_LEN(retval) = decoded_len; |
134 | 0 | return retval; |
135 | 0 | } |
136 | | /* }}} */ |
137 | | |
138 | 119k | #define PHP_QPRINT_MAXL 75 |
139 | | |
140 | | PHPAPI zend_string *php_quot_print_encode(const unsigned char *str, size_t length) /* {{{ */ |
141 | 162 | { |
142 | 162 | zend_ulong lp = 0; |
143 | 162 | unsigned char c, *d; |
144 | 162 | const char *hex = "0123456789ABCDEF"; |
145 | 162 | zend_string *ret; |
146 | | |
147 | 162 | ret = zend_string_safe_alloc(3, (length + (((3 * length)/(PHP_QPRINT_MAXL-9)) + 1)), 0, 0); |
148 | 162 | d = (unsigned char*)ZSTR_VAL(ret); |
149 | | |
150 | 103k | while (length--) { |
151 | 103k | if (((c = *str++) == '\015') && (*str == '\012') && length > 0) { |
152 | 134 | *d++ = '\015'; |
153 | 134 | *d++ = *str++; |
154 | 134 | length--; |
155 | 134 | lp = 0; |
156 | 103k | } else { |
157 | 103k | if (iscntrl (c) || (c == 0x7f) || (c & 0x80) || (c == '=') || ((c == ' ') && (*str == '\015'))) { |
158 | 56.1k | if ((((lp+= 3) > PHP_QPRINT_MAXL) && (c <= 0x7f)) |
159 | 56.1k | || ((c > 0x7f) && (c <= 0xdf) && ((lp + 3) > PHP_QPRINT_MAXL)) |
160 | 56.1k | || ((c > 0xdf) && (c <= 0xef) && ((lp + 6) > PHP_QPRINT_MAXL)) |
161 | 56.1k | || ((c > 0xef) && (c <= 0xf4) && ((lp + 9) > PHP_QPRINT_MAXL))) { |
162 | 1.79k | *d++ = '='; |
163 | 1.79k | *d++ = '\015'; |
164 | 1.79k | *d++ = '\012'; |
165 | 1.79k | lp = 3; |
166 | 1.79k | } |
167 | 56.1k | *d++ = '='; |
168 | 56.1k | *d++ = hex[c >> 4]; |
169 | 56.1k | *d++ = hex[c & 0xf]; |
170 | 56.1k | } else { |
171 | 47.3k | if ((++lp) > PHP_QPRINT_MAXL) { |
172 | 769 | *d++ = '='; |
173 | 769 | *d++ = '\015'; |
174 | 769 | *d++ = '\012'; |
175 | 769 | lp = 1; |
176 | 769 | } |
177 | 47.3k | *d++ = c; |
178 | 47.3k | } |
179 | 103k | } |
180 | 103k | } |
181 | 162 | *d = '\0'; |
182 | 162 | ret = zend_string_truncate(ret, d - (unsigned char*)ZSTR_VAL(ret), 0); |
183 | 162 | return ret; |
184 | 162 | } |
185 | | /* }}} */ |
186 | | |
187 | | /* |
188 | | * |
189 | | * Decoding Quoted-printable string. |
190 | | * |
191 | | */ |
192 | | /* {{{ Convert a quoted-printable string to an 8 bit string */ |
193 | | PHP_FUNCTION(quoted_printable_decode) |
194 | 0 | { |
195 | 0 | zend_string *arg1; |
196 | 0 | char *str_in; |
197 | 0 | zend_string *str_out; |
198 | 0 | size_t i = 0, j = 0, k; |
199 | |
|
200 | 0 | ZEND_PARSE_PARAMETERS_START(1, 1) |
201 | 0 | Z_PARAM_STR(arg1) |
202 | 0 | ZEND_PARSE_PARAMETERS_END(); |
203 | | |
204 | 0 | if (ZSTR_LEN(arg1) == 0) { |
205 | | /* shortcut */ |
206 | 0 | RETURN_EMPTY_STRING(); |
207 | 0 | } |
208 | | |
209 | 0 | str_in = ZSTR_VAL(arg1); |
210 | 0 | str_out = zend_string_alloc(ZSTR_LEN(arg1), 0); |
211 | 0 | while (str_in[i]) { |
212 | 0 | switch (str_in[i]) { |
213 | 0 | case '=': |
214 | 0 | if (str_in[i + 1] && str_in[i + 2] && |
215 | 0 | isxdigit((int) str_in[i + 1]) && |
216 | 0 | isxdigit((int) str_in[i + 2])) |
217 | 0 | { |
218 | 0 | ZSTR_VAL(str_out)[j++] = (php_hex2int((int) str_in[i + 1]) << 4) |
219 | 0 | + php_hex2int((int) str_in[i + 2]); |
220 | 0 | i += 3; |
221 | 0 | } else /* check for soft line break according to RFC 2045*/ { |
222 | 0 | k = 1; |
223 | 0 | while (str_in[i + k] && ((str_in[i + k] == 32) || (str_in[i + k] == 9))) { |
224 | | /* Possibly, skip spaces/tabs at the end of line */ |
225 | 0 | k++; |
226 | 0 | } |
227 | 0 | if (!str_in[i + k]) { |
228 | | /* End of line reached */ |
229 | 0 | i += k; |
230 | 0 | } |
231 | 0 | else if ((str_in[i + k] == 13) && (str_in[i + k + 1] == 10)) { |
232 | | /* CRLF */ |
233 | 0 | i += k + 2; |
234 | 0 | } |
235 | 0 | else if ((str_in[i + k] == 13) || (str_in[i + k] == 10)) { |
236 | | /* CR or LF */ |
237 | 0 | i += k + 1; |
238 | 0 | } |
239 | 0 | else { |
240 | 0 | ZSTR_VAL(str_out)[j++] = str_in[i++]; |
241 | 0 | } |
242 | 0 | } |
243 | 0 | break; |
244 | 0 | default: |
245 | 0 | ZSTR_VAL(str_out)[j++] = str_in[i++]; |
246 | 0 | } |
247 | 0 | } |
248 | 0 | ZSTR_VAL(str_out)[j] = '\0'; |
249 | 0 | ZSTR_LEN(str_out) = j; |
250 | |
|
251 | 0 | RETVAL_NEW_STR(str_out); |
252 | 0 | } |
253 | | /* }}} */ |
254 | | |
255 | | /* {{{ */ |
256 | | PHP_FUNCTION(quoted_printable_encode) |
257 | 162 | { |
258 | 162 | zend_string *str; |
259 | 162 | zend_string *new_str; |
260 | | |
261 | 486 | ZEND_PARSE_PARAMETERS_START(1, 1) |
262 | 648 | Z_PARAM_STR(str) |
263 | 162 | ZEND_PARSE_PARAMETERS_END(); |
264 | | |
265 | 162 | if (!ZSTR_LEN(str)) { |
266 | 0 | RETURN_EMPTY_STRING(); |
267 | 0 | } |
268 | | |
269 | 162 | new_str = php_quot_print_encode((unsigned char *)ZSTR_VAL(str), ZSTR_LEN(str)); |
270 | 162 | RETURN_STR(new_str); |
271 | 162 | } |
272 | | /* }}} */ |