1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 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: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
16 +----------------------------------------------------------------------+
17 */
18 /* $Id$ */
19
20 #include <stdio.h>
21 #include "php.h"
22 #include "ext/standard/php_standard.h"
23 #include "ext/date/php_date.h"
24 #include "SAPI.h"
25 #include "php_main.h"
26 #include "head.h"
27 #ifdef TM_IN_SYS_TIME
28 #include <sys/time.h>
29 #else
30 #include <time.h>
31 #endif
32
33 #include "php_globals.h"
34
35
36 /* Implementation of the language Header() function */
37 /* {{{ proto void header(string header [, bool replace, [int http_response_code]])
38 Sends a raw HTTP header */
PHP_FUNCTION(header)39 PHP_FUNCTION(header)
40 {
41 zend_bool rep = 1;
42 sapi_header_line ctr = {0};
43 size_t len;
44
45 ZEND_PARSE_PARAMETERS_START(1, 3)
46 Z_PARAM_STRING(ctr.line, len)
47 Z_PARAM_OPTIONAL
48 Z_PARAM_BOOL(rep)
49 Z_PARAM_LONG(ctr.response_code)
50 ZEND_PARSE_PARAMETERS_END();
51
52 ctr.line_len = (uint32_t)len;
53 sapi_header_op(rep ? SAPI_HEADER_REPLACE:SAPI_HEADER_ADD, &ctr);
54 }
55 /* }}} */
56
57 /* {{{ proto void header_remove([string name])
58 Removes an HTTP header previously set using header() */
PHP_FUNCTION(header_remove)59 PHP_FUNCTION(header_remove)
60 {
61 sapi_header_line ctr = {0};
62 size_t len = 0;
63
64 ZEND_PARSE_PARAMETERS_START(0, 1)
65 Z_PARAM_OPTIONAL
66 Z_PARAM_STRING(ctr.line, len)
67 ZEND_PARSE_PARAMETERS_END();
68
69 ctr.line_len = (uint32_t)len;
70 sapi_header_op(ZEND_NUM_ARGS() == 0 ? SAPI_HEADER_DELETE_ALL : SAPI_HEADER_DELETE, &ctr);
71 }
72 /* }}} */
73
php_header(void)74 PHPAPI int php_header(void)
75 {
76 if (sapi_send_headers()==FAILURE || SG(request_info).headers_only) {
77 return 0; /* don't allow output */
78 } else {
79 return 1; /* allow output */
80 }
81 }
82
83
php_setcookie(zend_string * name,zend_string * value,time_t expires,zend_string * path,zend_string * domain,int secure,int url_encode,int httponly)84 PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int url_encode, int httponly)
85 {
86 char *cookie;
87 size_t len = sizeof("Set-Cookie: ");
88 zend_string *dt;
89 sapi_header_line ctr = {0};
90 int result;
91 zend_string *encoded_value = NULL;
92
93 if (!ZSTR_LEN(name)) {
94 zend_error( E_WARNING, "Cookie names must not be empty" );
95 return FAILURE;
96 } else if (strpbrk(ZSTR_VAL(name), "=,; \t\r\n\013\014") != NULL) { /* man isspace for \013 and \014 */
97 zend_error(E_WARNING, "Cookie names cannot contain any of the following '=,; \\t\\r\\n\\013\\014'" );
98 return FAILURE;
99 }
100
101 if (!url_encode && value &&
102 strpbrk(ZSTR_VAL(value), ",; \t\r\n\013\014") != NULL) { /* man isspace for \013 and \014 */
103 zend_error(E_WARNING, "Cookie values cannot contain any of the following ',; \\t\\r\\n\\013\\014'" );
104 return FAILURE;
105 }
106
107 len += ZSTR_LEN(name);
108 if (value) {
109 if (url_encode) {
110 encoded_value = php_url_encode(ZSTR_VAL(value), ZSTR_LEN(value));
111 len += ZSTR_LEN(encoded_value);
112 } else {
113 encoded_value = zend_string_copy(value);
114 len += ZSTR_LEN(encoded_value);
115 }
116 }
117
118 if (path) {
119 len += ZSTR_LEN(path);
120 }
121 if (domain) {
122 len += ZSTR_LEN(domain);
123 }
124
125 cookie = emalloc(len + 100);
126
127 if (value == NULL || ZSTR_LEN(value) == 0) {
128 /*
129 * MSIE doesn't delete a cookie when you set it to a null value
130 * so in order to force cookies to be deleted, even on MSIE, we
131 * pick an expiry date in the past
132 */
133 dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, 1, 0);
134 snprintf(cookie, len + 100, "Set-Cookie: %s=deleted; expires=%s; Max-Age=0", ZSTR_VAL(name), ZSTR_VAL(dt));
135 zend_string_free(dt);
136 } else {
137 snprintf(cookie, len + 100, "Set-Cookie: %s=%s", ZSTR_VAL(name), value ? ZSTR_VAL(encoded_value) : "");
138 if (expires > 0) {
139 const char *p;
140 char tsdelta[13];
141 double diff;
142
143 strlcat(cookie, COOKIE_EXPIRES, len + 100);
144 dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0);
145 /* check to make sure that the year does not exceed 4 digits in length */
146 p = zend_memrchr(ZSTR_VAL(dt), '-', ZSTR_LEN(dt));
147 if (!p || *(p + 5) != ' ') {
148 zend_string_free(dt);
149 efree(cookie);
150 zend_string_release(encoded_value);
151 zend_error(E_WARNING, "Expiry date cannot have a year greater than 9999");
152 return FAILURE;
153 }
154 strlcat(cookie, ZSTR_VAL(dt), len + 100);
155 zend_string_free(dt);
156
157 diff = difftime(expires, php_time());
158 if (diff < 0) {
159 diff = 0;
160 }
161 snprintf(tsdelta, sizeof(tsdelta), ZEND_LONG_FMT, (zend_long) diff);
162 strlcat(cookie, COOKIE_MAX_AGE, len + 100);
163 strlcat(cookie, tsdelta, len + 100);
164 }
165 }
166
167 if (encoded_value) {
168 zend_string_release(encoded_value);
169 }
170
171 if (path && ZSTR_LEN(path)) {
172 strlcat(cookie, COOKIE_PATH, len + 100);
173 strlcat(cookie, ZSTR_VAL(path), len + 100);
174 }
175 if (domain && ZSTR_LEN(domain)) {
176 strlcat(cookie, COOKIE_DOMAIN, len + 100);
177 strlcat(cookie, ZSTR_VAL(domain), len + 100);
178 }
179 if (secure) {
180 strlcat(cookie, COOKIE_SECURE, len + 100);
181 }
182 if (httponly) {
183 strlcat(cookie, COOKIE_HTTPONLY, len + 100);
184 }
185
186 ctr.line = cookie;
187 ctr.line_len = (uint32_t)strlen(cookie);
188
189 result = sapi_header_op(SAPI_HEADER_ADD, &ctr);
190 efree(cookie);
191 return result;
192 }
193
194
195 /* php_set_cookie(name, value, expires, path, domain, secure) */
196 /* {{{ proto bool setcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])
197 Send a cookie */
PHP_FUNCTION(setcookie)198 PHP_FUNCTION(setcookie)
199 {
200 zend_string *name, *value = NULL, *path = NULL, *domain = NULL;
201 zend_long expires = 0;
202 zend_bool secure = 0, httponly = 0;
203
204 ZEND_PARSE_PARAMETERS_START(1, 7)
205 Z_PARAM_STR(name)
206 Z_PARAM_OPTIONAL
207 Z_PARAM_STR(value)
208 Z_PARAM_LONG(expires)
209 Z_PARAM_STR(path)
210 Z_PARAM_STR(domain)
211 Z_PARAM_BOOL(secure)
212 Z_PARAM_BOOL(httponly)
213 ZEND_PARSE_PARAMETERS_END();
214
215 if (php_setcookie(name, value, expires, path, domain, secure, 1, httponly) == SUCCESS) {
216 RETVAL_TRUE;
217 } else {
218 RETVAL_FALSE;
219 }
220 }
221 /* }}} */
222
223 /* {{{ proto bool setrawcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])
224 Send a cookie with no url encoding of the value */
PHP_FUNCTION(setrawcookie)225 PHP_FUNCTION(setrawcookie)
226 {
227 zend_string *name, *value = NULL, *path = NULL, *domain = NULL;
228 zend_long expires = 0;
229 zend_bool secure = 0, httponly = 0;
230
231 ZEND_PARSE_PARAMETERS_START(1, 7)
232 Z_PARAM_STR(name)
233 Z_PARAM_OPTIONAL
234 Z_PARAM_STR(value)
235 Z_PARAM_LONG(expires)
236 Z_PARAM_STR(path)
237 Z_PARAM_STR(domain)
238 Z_PARAM_BOOL(secure)
239 Z_PARAM_BOOL(httponly)
240 ZEND_PARSE_PARAMETERS_END();
241
242 if (php_setcookie(name, value, expires, path, domain, secure, 0, httponly) == SUCCESS) {
243 RETVAL_TRUE;
244 } else {
245 RETVAL_FALSE;
246 }
247 }
248 /* }}} */
249
250
251 /* {{{ proto bool headers_sent([string &$file [, int &$line]])
252 Returns true if headers have already been sent, false otherwise */
PHP_FUNCTION(headers_sent)253 PHP_FUNCTION(headers_sent)
254 {
255 zval *arg1 = NULL, *arg2 = NULL;
256 const char *file="";
257 int line=0;
258
259 ZEND_PARSE_PARAMETERS_START(0, 2)
260 Z_PARAM_OPTIONAL
261 Z_PARAM_ZVAL_DEREF(arg1)
262 Z_PARAM_ZVAL_DEREF(arg2)
263 ZEND_PARSE_PARAMETERS_END();
264
265 if (SG(headers_sent)) {
266 line = php_output_get_start_lineno();
267 file = php_output_get_start_filename();
268 }
269
270 switch(ZEND_NUM_ARGS()) {
271 case 2:
272 zval_ptr_dtor(arg2);
273 ZVAL_LONG(arg2, line);
274 case 1:
275 zval_ptr_dtor(arg1);
276 if (file) {
277 ZVAL_STRING(arg1, file);
278 } else {
279 ZVAL_EMPTY_STRING(arg1);
280 }
281 break;
282 }
283
284 if (SG(headers_sent)) {
285 RETURN_TRUE;
286 } else {
287 RETURN_FALSE;
288 }
289 }
290 /* }}} */
291
292 /* {{{ php_head_apply_header_list_to_hash
293 Turn an llist of sapi_header_struct headers into a numerically indexed zval hash */
php_head_apply_header_list_to_hash(void * data,void * arg)294 static void php_head_apply_header_list_to_hash(void *data, void *arg)
295 {
296 sapi_header_struct *sapi_header = (sapi_header_struct *)data;
297
298 if (arg && sapi_header) {
299 add_next_index_string((zval *)arg, (char *)(sapi_header->header));
300 }
301 }
302
303 /* {{{ proto array headers_list(void)
304 Return list of headers to be sent / already sent */
PHP_FUNCTION(headers_list)305 PHP_FUNCTION(headers_list)
306 {
307 if (zend_parse_parameters_none() == FAILURE) {
308 return;
309 }
310
311 array_init(return_value);
312 zend_llist_apply_with_argument(&SG(sapi_headers).headers, php_head_apply_header_list_to_hash, return_value);
313 }
314 /* }}} */
315
316 /* {{{ proto long http_response_code([int response_code])
317 Sets a response code, or returns the current HTTP response code */
PHP_FUNCTION(http_response_code)318 PHP_FUNCTION(http_response_code)
319 {
320 zend_long response_code = 0;
321
322 ZEND_PARSE_PARAMETERS_START(0, 1)
323 Z_PARAM_OPTIONAL
324 Z_PARAM_LONG(response_code)
325 ZEND_PARSE_PARAMETERS_END();
326
327 if (response_code)
328 {
329 zend_long old_response_code;
330
331 old_response_code = SG(sapi_headers).http_response_code;
332 SG(sapi_headers).http_response_code = (int)response_code;
333
334 if (old_response_code) {
335 RETURN_LONG(old_response_code);
336 }
337
338 RETURN_TRUE;
339 }
340
341 if (!SG(sapi_headers).http_response_code) {
342 RETURN_FALSE;
343 }
344
345 RETURN_LONG(SG(sapi_headers).http_response_code);
346 }
347 /* }}} */
348
349 /*
350 * Local variables:
351 * tab-width: 4
352 * c-basic-offset: 4
353 * vim600: sw=4 ts=4 fdm=marker
354 * vim<600: sw=4 ts=4 * End:
355 */
356