mirror of
https://github.com/php/php-src.git
synced 2025-08-18 15:08:55 +02:00

This patch removes the so called local variables defined per file basis for certain editors to properly show tab width, and similar settings. These are mainly used by Vim and Emacs editors yet with recent changes the once working definitions don't work anymore in Vim without custom plugins or additional configuration. Neither are these settings synced across the PHP code base. A simpler and better approach is EditorConfig and fixing code using some code style fixing tools in the future instead. This patch also removes the so called modelines for Vim. Modelines allow Vim editor specifically to set some editor configuration such as syntax highlighting, indentation style and tab width to be set in the first line or the last 5 lines per file basis. Since the php test files have syntax highlighting already set in most editors properly and EditorConfig takes care of the indentation settings, this patch removes these as well for the Vim 6.0 and newer versions. With the removal of local variables for certain editors such as Emacs and Vim, the footer is also probably not needed anymore when creating extensions using ext_skel.php script. Additionally, Vim modelines for setting php syntax and some editor settings has been removed from some *.phpt files. All these are mostly not relevant for phpt files neither work properly in the middle of the file.
445 lines
13 KiB
C
445 lines
13 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP Version 7 |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Author: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include "php.h"
|
|
#include "ext/standard/php_standard.h"
|
|
#include "ext/date/php_date.h"
|
|
#include "SAPI.h"
|
|
#include "php_main.h"
|
|
#include "head.h"
|
|
#include <time.h>
|
|
|
|
#include "php_globals.h"
|
|
|
|
|
|
/* Implementation of the language Header() function */
|
|
/* {{{ proto void header(string header [, bool replace, [int http_response_code]])
|
|
Sends a raw HTTP header */
|
|
PHP_FUNCTION(header)
|
|
{
|
|
zend_bool rep = 1;
|
|
sapi_header_line ctr = {0};
|
|
size_t len;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(1, 3)
|
|
Z_PARAM_STRING(ctr.line, len)
|
|
Z_PARAM_OPTIONAL
|
|
Z_PARAM_BOOL(rep)
|
|
Z_PARAM_LONG(ctr.response_code)
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
ctr.line_len = (uint32_t)len;
|
|
sapi_header_op(rep ? SAPI_HEADER_REPLACE:SAPI_HEADER_ADD, &ctr);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void header_remove([string name])
|
|
Removes an HTTP header previously set using header() */
|
|
PHP_FUNCTION(header_remove)
|
|
{
|
|
sapi_header_line ctr = {0};
|
|
size_t len = 0;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(0, 1)
|
|
Z_PARAM_OPTIONAL
|
|
Z_PARAM_STRING(ctr.line, len)
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
ctr.line_len = (uint32_t)len;
|
|
sapi_header_op(ZEND_NUM_ARGS() == 0 ? SAPI_HEADER_DELETE_ALL : SAPI_HEADER_DELETE, &ctr);
|
|
}
|
|
/* }}} */
|
|
|
|
PHPAPI int php_header(void)
|
|
{
|
|
if (sapi_send_headers()==FAILURE || SG(request_info).headers_only) {
|
|
return 0; /* don't allow output */
|
|
} else {
|
|
return 1; /* allow output */
|
|
}
|
|
}
|
|
|
|
PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int httponly, zend_string *samesite, int url_encode)
|
|
{
|
|
char *cookie;
|
|
size_t len = sizeof("Set-Cookie: ");
|
|
zend_string *dt;
|
|
sapi_header_line ctr = {0};
|
|
int result;
|
|
zend_string *encoded_value = NULL;
|
|
|
|
if (!ZSTR_LEN(name)) {
|
|
zend_error( E_WARNING, "Cookie names must not be empty" );
|
|
return FAILURE;
|
|
} else if (strpbrk(ZSTR_VAL(name), "=,; \t\r\n\013\014") != NULL) { /* man isspace for \013 and \014 */
|
|
zend_error(E_WARNING, "Cookie names cannot contain any of the following '=,; \\t\\r\\n\\013\\014'" );
|
|
return FAILURE;
|
|
}
|
|
|
|
if (!url_encode && value &&
|
|
strpbrk(ZSTR_VAL(value), ",; \t\r\n\013\014") != NULL) { /* man isspace for \013 and \014 */
|
|
zend_error(E_WARNING, "Cookie values cannot contain any of the following ',; \\t\\r\\n\\013\\014'" );
|
|
return FAILURE;
|
|
}
|
|
|
|
if (path && strpbrk(ZSTR_VAL(path), ",; \t\r\n\013\014") != NULL) { /* man isspace for \013 and \014 */
|
|
zend_error(E_WARNING, "Cookie paths cannot contain any of the following ',; \\t\\r\\n\\013\\014'" );
|
|
return FAILURE;
|
|
}
|
|
|
|
if (domain && strpbrk(ZSTR_VAL(domain), ",; \t\r\n\013\014") != NULL) { /* man isspace for \013 and \014 */
|
|
zend_error(E_WARNING, "Cookie domains cannot contain any of the following ',; \\t\\r\\n\\013\\014'" );
|
|
return FAILURE;
|
|
}
|
|
|
|
len += ZSTR_LEN(name);
|
|
if (value) {
|
|
if (url_encode) {
|
|
encoded_value = php_url_encode(ZSTR_VAL(value), ZSTR_LEN(value));
|
|
len += ZSTR_LEN(encoded_value);
|
|
} else {
|
|
encoded_value = zend_string_copy(value);
|
|
len += ZSTR_LEN(encoded_value);
|
|
}
|
|
}
|
|
|
|
if (path) {
|
|
len += ZSTR_LEN(path);
|
|
}
|
|
if (domain) {
|
|
len += ZSTR_LEN(domain);
|
|
}
|
|
if (samesite) {
|
|
len += ZSTR_LEN(samesite);
|
|
}
|
|
|
|
cookie = emalloc(len + 100);
|
|
|
|
if (value == NULL || ZSTR_LEN(value) == 0) {
|
|
/*
|
|
* MSIE doesn't delete a cookie when you set it to a null value
|
|
* so in order to force cookies to be deleted, even on MSIE, we
|
|
* pick an expiry date in the past
|
|
*/
|
|
dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, 1, 0);
|
|
snprintf(cookie, len + 100, "Set-Cookie: %s=deleted; expires=%s; Max-Age=0", ZSTR_VAL(name), ZSTR_VAL(dt));
|
|
zend_string_free(dt);
|
|
} else {
|
|
snprintf(cookie, len + 100, "Set-Cookie: %s=%s", ZSTR_VAL(name), value ? ZSTR_VAL(encoded_value) : "");
|
|
if (expires > 0) {
|
|
const char *p;
|
|
char tsdelta[13];
|
|
double diff;
|
|
|
|
strlcat(cookie, COOKIE_EXPIRES, len + 100);
|
|
dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0);
|
|
/* check to make sure that the year does not exceed 4 digits in length */
|
|
p = zend_memrchr(ZSTR_VAL(dt), '-', ZSTR_LEN(dt));
|
|
if (!p || *(p + 5) != ' ') {
|
|
zend_string_free(dt);
|
|
efree(cookie);
|
|
zend_string_release_ex(encoded_value, 0);
|
|
zend_error(E_WARNING, "Expiry date cannot have a year greater than 9999");
|
|
return FAILURE;
|
|
}
|
|
strlcat(cookie, ZSTR_VAL(dt), len + 100);
|
|
zend_string_free(dt);
|
|
|
|
diff = difftime(expires, time(NULL));
|
|
if (diff < 0) {
|
|
diff = 0;
|
|
}
|
|
snprintf(tsdelta, sizeof(tsdelta), ZEND_LONG_FMT, (zend_long) diff);
|
|
strlcat(cookie, COOKIE_MAX_AGE, len + 100);
|
|
strlcat(cookie, tsdelta, len + 100);
|
|
}
|
|
}
|
|
|
|
if (encoded_value) {
|
|
zend_string_release_ex(encoded_value, 0);
|
|
}
|
|
|
|
if (path && ZSTR_LEN(path)) {
|
|
strlcat(cookie, COOKIE_PATH, len + 100);
|
|
strlcat(cookie, ZSTR_VAL(path), len + 100);
|
|
}
|
|
if (domain && ZSTR_LEN(domain)) {
|
|
strlcat(cookie, COOKIE_DOMAIN, len + 100);
|
|
strlcat(cookie, ZSTR_VAL(domain), len + 100);
|
|
}
|
|
if (secure) {
|
|
strlcat(cookie, COOKIE_SECURE, len + 100);
|
|
}
|
|
if (httponly) {
|
|
strlcat(cookie, COOKIE_HTTPONLY, len + 100);
|
|
}
|
|
if (samesite && ZSTR_LEN(samesite)) {
|
|
strlcat(cookie, COOKIE_SAMESITE, len + 100);
|
|
strlcat(cookie, ZSTR_VAL(samesite), len + 100);
|
|
}
|
|
|
|
ctr.line = cookie;
|
|
ctr.line_len = (uint32_t)strlen(cookie);
|
|
|
|
result = sapi_header_op(SAPI_HEADER_ADD, &ctr);
|
|
efree(cookie);
|
|
return result;
|
|
}
|
|
|
|
static void php_head_parse_cookie_options_array(zval *options, zend_long *expires, zend_string **path, zend_string **domain, zend_bool *secure, zend_bool *httponly, zend_string **samesite) {
|
|
int found = 0;
|
|
zend_string *key;
|
|
zval *value;
|
|
|
|
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), key, value) {
|
|
if (key) {
|
|
if (zend_string_equals_literal_ci(key, "expires")) {
|
|
*expires = zval_get_long(value);
|
|
found++;
|
|
} else if (zend_string_equals_literal_ci(key, "path")) {
|
|
*path = zval_get_string(value);
|
|
found++;
|
|
} else if (zend_string_equals_literal_ci(key, "domain")) {
|
|
*domain = zval_get_string(value);
|
|
found++;
|
|
} else if (zend_string_equals_literal_ci(key, "secure")) {
|
|
*secure = zval_is_true(value);
|
|
found++;
|
|
} else if (zend_string_equals_literal_ci(key, "httponly")) {
|
|
*httponly = zval_is_true(value);
|
|
found++;
|
|
} else if (zend_string_equals_literal_ci(key, "samesite")) {
|
|
*samesite = zval_get_string(value);
|
|
found++;
|
|
} else {
|
|
php_error_docref(NULL, E_WARNING, "Unrecognized key '%s' found in the options array", ZSTR_VAL(key));
|
|
}
|
|
} else {
|
|
php_error_docref(NULL, E_WARNING, "Numeric key found in the options array");
|
|
}
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
/* Array is not empty but no valid keys were found */
|
|
if (found == 0 && zend_hash_num_elements(Z_ARRVAL_P(options)) > 0) {
|
|
php_error_docref(NULL, E_WARNING, "No valid options were found in the given array");
|
|
}
|
|
}
|
|
|
|
/* {{{ proto bool setcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])
|
|
setcookie(string name [, string value [, array options]])
|
|
Send a cookie */
|
|
PHP_FUNCTION(setcookie)
|
|
{
|
|
zval *expires_or_options = NULL;
|
|
zend_string *name, *value = NULL, *path = NULL, *domain = NULL, *samesite = NULL;
|
|
zend_long expires = 0;
|
|
zend_bool secure = 0, httponly = 0;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(1, 7)
|
|
Z_PARAM_STR(name)
|
|
Z_PARAM_OPTIONAL
|
|
Z_PARAM_STR(value)
|
|
Z_PARAM_ZVAL(expires_or_options)
|
|
Z_PARAM_STR(path)
|
|
Z_PARAM_STR(domain)
|
|
Z_PARAM_BOOL(secure)
|
|
Z_PARAM_BOOL(httponly)
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
if (expires_or_options) {
|
|
if (Z_TYPE_P(expires_or_options) == IS_ARRAY) {
|
|
if (UNEXPECTED(ZEND_NUM_ARGS() > 3)) {
|
|
php_error_docref(NULL, E_WARNING, "Cannot pass arguments after the options array");
|
|
RETURN_FALSE;
|
|
}
|
|
php_head_parse_cookie_options_array(expires_or_options, &expires, &path, &domain, &secure, &httponly, &samesite);
|
|
} else {
|
|
expires = zval_get_long(expires_or_options);
|
|
}
|
|
}
|
|
|
|
if (php_setcookie(name, value, expires, path, domain, secure, httponly, samesite, 1) == SUCCESS) {
|
|
RETVAL_TRUE;
|
|
} else {
|
|
RETVAL_FALSE;
|
|
}
|
|
|
|
if (expires_or_options && Z_TYPE_P(expires_or_options) == IS_ARRAY) {
|
|
if (path) {
|
|
zend_string_release(path);
|
|
}
|
|
if (domain) {
|
|
zend_string_release(domain);
|
|
}
|
|
if (samesite) {
|
|
zend_string_release(samesite);
|
|
}
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool setrawcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])
|
|
setrawcookie(string name [, string value [, array options]])
|
|
Send a cookie with no url encoding of the value */
|
|
PHP_FUNCTION(setrawcookie)
|
|
{
|
|
zval *expires_or_options = NULL;
|
|
zend_string *name, *value = NULL, *path = NULL, *domain = NULL, *samesite = NULL;
|
|
zend_long expires = 0;
|
|
zend_bool secure = 0, httponly = 0;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(1, 7)
|
|
Z_PARAM_STR(name)
|
|
Z_PARAM_OPTIONAL
|
|
Z_PARAM_STR(value)
|
|
Z_PARAM_ZVAL(expires_or_options)
|
|
Z_PARAM_STR(path)
|
|
Z_PARAM_STR(domain)
|
|
Z_PARAM_BOOL(secure)
|
|
Z_PARAM_BOOL(httponly)
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
if (expires_or_options) {
|
|
if (Z_TYPE_P(expires_or_options) == IS_ARRAY) {
|
|
if (UNEXPECTED(ZEND_NUM_ARGS() > 3)) {
|
|
php_error_docref(NULL, E_WARNING, "Cannot pass arguments after the options array");
|
|
RETURN_FALSE;
|
|
}
|
|
php_head_parse_cookie_options_array(expires_or_options, &expires, &path, &domain, &secure, &httponly, &samesite);
|
|
} else {
|
|
expires = zval_get_long(expires_or_options);
|
|
}
|
|
}
|
|
|
|
if (php_setcookie(name, value, expires, path, domain, secure, httponly, samesite, 0) == SUCCESS) {
|
|
RETVAL_TRUE;
|
|
} else {
|
|
RETVAL_FALSE;
|
|
}
|
|
|
|
if (expires_or_options && Z_TYPE_P(expires_or_options) == IS_ARRAY) {
|
|
if (path) {
|
|
zend_string_release(path);
|
|
}
|
|
if (domain) {
|
|
zend_string_release(domain);
|
|
}
|
|
if (samesite) {
|
|
zend_string_release(samesite);
|
|
}
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto bool headers_sent([string &$file [, int &$line]])
|
|
Returns true if headers have already been sent, false otherwise */
|
|
PHP_FUNCTION(headers_sent)
|
|
{
|
|
zval *arg1 = NULL, *arg2 = NULL;
|
|
const char *file="";
|
|
int line=0;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(0, 2)
|
|
Z_PARAM_OPTIONAL
|
|
Z_PARAM_ZVAL(arg1)
|
|
Z_PARAM_ZVAL(arg2)
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
if (SG(headers_sent)) {
|
|
line = php_output_get_start_lineno();
|
|
file = php_output_get_start_filename();
|
|
}
|
|
|
|
switch(ZEND_NUM_ARGS()) {
|
|
case 2:
|
|
ZEND_TRY_ASSIGN_LONG(arg2, line);
|
|
case 1:
|
|
if (file) {
|
|
ZEND_TRY_ASSIGN_STRING(arg1, file);
|
|
} else {
|
|
ZEND_TRY_ASSIGN_EMPTY_STRING(arg1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (SG(headers_sent)) {
|
|
RETURN_TRUE;
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ php_head_apply_header_list_to_hash
|
|
Turn an llist of sapi_header_struct headers into a numerically indexed zval hash */
|
|
static void php_head_apply_header_list_to_hash(void *data, void *arg)
|
|
{
|
|
sapi_header_struct *sapi_header = (sapi_header_struct *)data;
|
|
|
|
if (arg && sapi_header) {
|
|
add_next_index_string((zval *)arg, (char *)(sapi_header->header));
|
|
}
|
|
}
|
|
|
|
/* {{{ proto array headers_list(void)
|
|
Return list of headers to be sent / already sent */
|
|
PHP_FUNCTION(headers_list)
|
|
{
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
array_init(return_value);
|
|
zend_llist_apply_with_argument(&SG(sapi_headers).headers, php_head_apply_header_list_to_hash, return_value);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int http_response_code([int response_code])
|
|
Sets a response code, or returns the current HTTP response code */
|
|
PHP_FUNCTION(http_response_code)
|
|
{
|
|
zend_long response_code = 0;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(0, 1)
|
|
Z_PARAM_OPTIONAL
|
|
Z_PARAM_LONG(response_code)
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
if (response_code)
|
|
{
|
|
zend_long old_response_code;
|
|
|
|
old_response_code = SG(sapi_headers).http_response_code;
|
|
SG(sapi_headers).http_response_code = (int)response_code;
|
|
|
|
if (old_response_code) {
|
|
RETURN_LONG(old_response_code);
|
|
}
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
|
|
if (!SG(sapi_headers).http_response_code) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
RETURN_LONG(SG(sapi_headers).http_response_code);
|
|
}
|
|
/* }}} */
|