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