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