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