diff --git a/configure.ac b/configure.ac index 77fc8c89cdf40..a515c34f2394b 100644 --- a/configure.ac +++ b/configure.ac @@ -1695,6 +1695,7 @@ PHP_ADD_SOURCES([main/streams], m4_normalize([ memory.c mmap.c plain_wrapper.c + stream_errors.c streams.c transports.c userspace.c diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index f37599e7db117..e364300b0b5f3 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -253,26 +253,31 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, char *error; phar_archive_data *phar; - if ((resource = phar_parse_url(wrapper, path, mode, options)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "phar url \"%s\" is unknown", path); + if ((resource = phar_parse_url(wrapper, context, path, mode, options)) == NULL) { + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar url \"%s\" is unknown", path); return NULL; } /* we must have at the very least phar://alias.phar/ */ if (!resource->scheme || !resource->host || !resource->path) { if (resource->host && !resource->path) { - php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PATH, + "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", + path, ZSTR_VAL(resource->host)); php_url_free(resource); return NULL; } php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); return NULL; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: not a phar url \"%s\"", path); return NULL; } @@ -280,10 +285,11 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar file \"%s\" is unknown", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar file \"%s\" is unknown", ZSTR_VAL(resource->host)); } php_url_free(resource); return NULL; @@ -353,7 +359,8 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo /* pre-readonly check, we need to know if this is a data phar */ if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, NULL, NULL, 2, 2)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", no phar archive specified", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: cannot create directory \"%s\", no phar archive specified", url_from); return 0; } @@ -364,29 +371,34 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo efree(arch); if (PHAR_G(readonly) && (!phar || !phar->is_data)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", write operations disabled", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, + "phar error: cannot create directory \"%s\", write operations disabled", url_from); return 0; } - if ((resource = phar_parse_url(wrapper, url_from, "w", options)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, url_from, "w", options)) == NULL) { return 0; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url \"%s\"", url_from); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: not a phar stream url \"%s\"", url_from); return 0; } if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", + ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -398,13 +410,17 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo zend_string_efree(e->filename); efree(e); } - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_ALREADY_EXISTS, + "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); php_url_free(resource); return 0; } if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -412,13 +428,17 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo if (phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1, 0, &error, true)) { /* entry exists as a file */ - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_ALREADY_EXISTS, + "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); php_url_free(resource); return 0; } if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -447,7 +467,9 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo entry.old_flags = PHAR_ENT_PERM_DEF_DIR; if (NULL == zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info))) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", ZSTR_VAL(entry.filename), phar->fname); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", + ZSTR_VAL(entry.filename), phar->fname); zend_string_efree(entry.filename); return 0; } @@ -455,7 +477,9 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo phar_flush(phar, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry.filename), phar->fname, error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(entry.filename), phar->fname, error); zend_hash_del(&phar->manifest, entry.filename); efree(error); return 0; @@ -479,7 +503,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options /* pre-readonly check, we need to know if this is a data phar */ if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, NULL, NULL, 2, 2)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); return 0; } @@ -490,29 +515,34 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options efree(arch); if (PHAR_G(readonly) && (!phar || !phar->is_data)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot rmdir directory \"%s\", write operations disabled", url); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, + "phar error: cannot rmdir directory \"%s\", write operations disabled", url); return 0; } - if ((resource = phar_parse_url(wrapper, url, "w", options)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, url, "w", options)) == NULL) { return 0; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url \"%s\"", url); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: not a phar stream url \"%s\"", url); return 0; } if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -522,10 +552,14 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options if (!(entry = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, path_len, 2, &error, true))) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); } php_url_free(resource); return 0; @@ -539,7 +573,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len) && IS_SLASH(ZSTR_VAL(str_key)[path_len]) ) { - php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "phar error: Directory not empty"); if (entry->is_temp_dir) { zend_string_efree(entry->filename); efree(entry); @@ -555,7 +590,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len) && IS_SLASH(ZSTR_VAL(str_key)[path_len]) ) { - php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "phar error: Directory not empty"); if (entry->is_temp_dir) { zend_string_efree(entry->filename); efree(entry); @@ -576,7 +612,9 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options phar_flush(phar, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry->filename), phar->fname, error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(entry->filename), phar->fname, error); php_url_free(resource); efree(error); return 0; diff --git a/ext/phar/dirstream.h b/ext/phar/dirstream.h index 4debfecde41a1..42d79c6fc21e9 100644 --- a/ext/phar/dirstream.h +++ b/ext/phar/dirstream.h @@ -24,7 +24,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options #ifdef PHAR_DIRSTREAM #include "ext/standard/url.h" -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options); +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, const char *filename, const char *mode, int options); /* directory handlers */ static ssize_t phar_dir_write(php_stream *stream, const char *buf, size_t count); diff --git a/ext/phar/stream.c b/ext/phar/stream.c index d77dbdcffc74f..3c66ffca75704 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -57,7 +57,8 @@ const php_stream_wrapper php_stream_phar_wrapper = { /** * Open a phar file for streams API */ -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options) /* {{{ */ +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, + const char *filename, const char *mode, int options) { php_url *resource; char *arch = NULL, *entry = NULL, *error; @@ -68,17 +69,21 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (mode[0] == 'a') { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: open mode append not supported"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + "phar error: open mode append not supported"); } return NULL; } if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0)) == FAILURE) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { if (arch && !entry) { - php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PATH, + "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", + filename, arch); arch = NULL; } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url or non-existent phar \"%s\"", filename); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url or non-existent phar \"%s\"", filename); } } return NULL; @@ -111,7 +116,8 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, + "phar error: write operations disabled by the php.ini setting phar.readonly"); } php_url_free(resource); return NULL; @@ -120,7 +126,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_OPEN_FAILED, "%s", error); } efree(error); } @@ -131,7 +137,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (error) { spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", ZSTR_VAL(resource->host)); if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_OPEN_FAILED, "%s", error); } efree(error); } @@ -143,7 +149,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); } efree(error); } @@ -153,7 +159,6 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } return resource; } -/* }}} */ /** * used for fopen('phar://...') and company @@ -169,20 +174,22 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_stream *fpf; zval *pzoption, *metadata; - if ((resource = phar_parse_url(wrapper, path, mode, options)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, path, mode, options)) == NULL) { return NULL; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url \"%s\"", path); return NULL; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: not a phar stream url \"%s\"", path); return NULL; } @@ -193,10 +200,11 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { if (NULL == (idata = phar_get_or_create_entry_data(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), mode, 0, &error, true))) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_CREATE_FAILED, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_CREATE_FAILED, + "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); php_url_free(resource); @@ -236,7 +244,8 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (!*internal_file && (options & STREAM_OPEN_FOR_INCLUDE)) { /* retrieve the stub */ if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, NULL)) { - php_stream_wrapper_log_error(wrapper, options, "file %s is not a valid phar archive", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_FORMAT, + "file %s is not a valid phar archive", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; @@ -255,7 +264,8 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_stream *stream = phar_get_pharfp(phar); if (stream == NULL) { if (UNEXPECTED(FAILURE == phar_open_archive_fp(phar))) { - php_stream_wrapper_log_error(wrapper, options, "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_OPEN_FAILED, + "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; @@ -293,10 +303,11 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if ((FAILURE == phar_get_entry_data(&idata, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), "r", 0, &error, false)) || !idata) { idata_error: if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); php_url_free(resource); @@ -314,7 +325,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* check length, crc32 */ if (!idata->internal_file->is_crc_checked && phar_postprocess_file(idata, idata->internal_file->crc32, &error, 2) != SUCCESS) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_ARCHIVING_FAILED, "%s", error); efree(error); phar_entry_delref(idata); efree(internal_file); @@ -445,7 +456,9 @@ static ssize_t phar_stream_write(php_stream *stream, const char *buf, size_t cou php_stream_seek(data->fp, data->position + data->zero, SEEK_SET); if (count != php_stream_write(data->fp, buf, count)) { - php_stream_wrapper_log_error(stream->wrapper, stream->flags, "phar error: Could not write %zu characters to \"%s\" in phar \"%s\"", count, ZSTR_VAL(data->internal_file->filename), data->phar->fname); + php_stream_warn(stream, STREAM_ERROR_CODE_WRITE_FAILED, + "phar error: Could not write %zu characters to \"%s\" in phar \"%s\"", + count, ZSTR_VAL(data->internal_file->filename), data->phar->fname); return -1; } data->position = php_stream_tell(data->fp) - data->zero; @@ -472,7 +485,7 @@ static int phar_stream_flush(php_stream *stream) /* {{{ */ data->internal_file->timestamp = time(0); ret = phar_flush(data->phar, &error); if (error) { - php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS, "%s", error); + php_stream_warn(stream, STREAM_ERROR_CODE_FLUSH_FAILED, "%s", error); efree(error); } return ret; @@ -562,7 +575,7 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, const char *url, int f phar_entry_info *entry; size_t internal_file_len; - if ((resource = phar_parse_url(wrapper, url, "r", flags|PHP_STREAM_URL_STAT_QUIET)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, url, "r", flags|PHP_STREAM_URL_STAT_QUIET)) == NULL) { return FAILURE; } @@ -662,21 +675,24 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int phar_entry_data *idata; phar_archive_data *pphar; - if ((resource = phar_parse_url(wrapper, url, "rb", options)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "phar error: unlink failed"); + if ((resource = phar_parse_url(wrapper, context, url, "rb", options)) == NULL) { + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, + "phar error: unlink failed"); return 0; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url \"%s\"", url); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: not a phar stream url \"%s\"", url); return 0; } @@ -685,7 +701,8 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int pphar = zend_hash_find_ptr(&(PHAR_G(phar_fname_map)), resource->host); if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, + "phar error: write operations disabled by the php.ini setting phar.readonly"); return 0; } @@ -695,10 +712,12 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int if (FAILURE == phar_get_entry_data(&idata, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, internal_file_len, "r", 0, &error, true)) { /* constraints of fp refcount were not met */ if (error) { - php_stream_wrapper_log_error(wrapper, options, "unlink of \"%s\" failed: %s", url, error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, + "unlink of \"%s\" failed: %s", url, error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "unlink of \"%s\" failed, file does not exist", url); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, + "unlink of \"%s\" failed, file does not exist", url); } efree(internal_file); php_url_free(resource); @@ -709,7 +728,9 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int } if (idata->internal_file->fp_refcount > 1) { /* more than just our fp resource is open for this file */ - php_stream_wrapper_log_error(wrapper, options, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, + "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", + internal_file, ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); phar_entry_delref(idata); @@ -719,7 +740,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int efree(internal_file); phar_entry_remove(idata, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, "%s", error); efree(error); } return 1; @@ -737,8 +758,10 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from error = NULL; - if ((resource_from = phar_parse_url(wrapper, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET)) == NULL) { - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_from); + if ((resource_from = phar_parse_url(wrapper, context, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET)) == NULL) { + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", + url_from, url_to, url_from); return 0; } if (SUCCESS != phar_get_archive(&pfrom, ZSTR_VAL(resource_from->host), ZSTR_LEN(resource_from->host), NULL, 0, &error)) { @@ -749,13 +772,16 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from } if (PHAR_G(readonly) && (!pfrom || !pfrom->is_data)) { php_url_free(resource_from); - php_error_docref(NULL, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, + "phar error: Write operations disabled by the php.ini setting phar.readonly"); return 0; } - if ((resource_to = phar_parse_url(wrapper, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET)) == NULL) { + if ((resource_to = phar_parse_url(wrapper, context, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET)) == NULL) { php_url_free(resource_from); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", + url_from, url_to, url_to); return 0; } if (SUCCESS != phar_get_archive(&pto, ZSTR_VAL(resource_to->host), ZSTR_LEN(resource_to->host), NULL, 0, &error)) { @@ -767,14 +793,17 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (PHAR_G(readonly) && (!pto || !pto->is_data)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, + "phar error: Write operations disabled by the php.ini setting phar.readonly"); return 0; } if (!zend_string_equals(resource_from->host, resource_to->host)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", + url_from, url_to); return 0; } @@ -782,35 +811,44 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (!resource_from->scheme || !resource_from->host || !resource_from->path) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", + url_from, url_to, url_from); return 0; } if (!resource_to->scheme || !resource_to->host || !resource_to->path) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", + url_from, url_to, url_to); return 0; } if (!zend_string_equals_literal_ci(resource_from->scheme, "phar")) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", + url_from, url_to, url_from); return 0; } if (!zend_string_equals_literal_ci(resource_to->scheme, "phar")) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", + url_from, url_to, url_to); return 0; } if (SUCCESS != phar_get_archive(&phar, ZSTR_VAL(resource_from->host), ZSTR_LEN(resource_from->host), NULL, 0, &error)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); return 0; } @@ -818,7 +856,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", + url_from, url_to); return 0; } @@ -829,7 +869,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (entry->is_deleted) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", + url_from, url_to); return 0; } /* transfer all data over to the new entry */ @@ -849,7 +891,8 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (FAILURE == phar_copy_entry_fp(source, entry, &error)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); zend_hash_del(&phar->manifest, entry->filename); return 0; @@ -863,7 +906,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from /* file does not exist */ php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", + url_from, url_to); return 0; } @@ -942,7 +987,8 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (error) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); return 0; } diff --git a/ext/phar/stream.h b/ext/phar/stream.h index 83b395b4cfca3..e39ffb2c10baf 100644 --- a/ext/phar/stream.h +++ b/ext/phar/stream.h @@ -20,7 +20,7 @@ BEGIN_EXTERN_C() #include "ext/standard/url.h" -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options); +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, const char *filename, const char *mode, int options); ZEND_ATTRIBUTE_NONNULL void phar_entry_remove(phar_entry_data *idata, char **error); static php_stream* phar_wrapper_open_url(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC); diff --git a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt index 47813255381e4..dc72254abc076 100644 --- a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt @@ -16,5 +16,6 @@ AssertionError Directory RoundingMode StreamBucket +StreamException __PHP_Incomplete_Class php_user_filter diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index e0c4230dae27c..20781ddddd433 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -336,6 +336,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ #endif BASIC_MINIT_SUBMODULE(exec) + BASIC_MINIT_SUBMODULE(stream_errors) BASIC_MINIT_SUBMODULE(user_streams) php_register_url_stream_wrapper("php", &php_stream_php_wrapper); diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 7913ca0e00194..392551601f0c6 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -3535,6 +3535,12 @@ function stream_resolve_include_path(string $filename): string|false {} */ function stream_get_wrappers(): array {} +/** + * @param resource|string|null $subject + * @return array + */ +function stream_get_errors($subject = null): array {} + /** * @return array * @refcount 1 diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 0a21d7d76426c..60076ea434eca 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f6bf6cdd07080c01d3a0cb08d71409d05b1084f9 */ + * Stub hash: 010f9a285daa0b90bfe254a29c26544b99c4512d */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -1980,6 +1980,10 @@ ZEND_END_ARG_INFO() #define arginfo_stream_get_wrappers arginfo_ob_list_handlers +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_get_errors, 0, 0, IS_ARRAY, 0) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, subject, "null") +ZEND_END_ARG_INFO() + #define arginfo_stream_get_transports arginfo_ob_list_handlers #define arginfo_stream_is_local arginfo_rewind @@ -2815,6 +2819,7 @@ ZEND_FUNCTION(stream_get_meta_data); ZEND_FUNCTION(stream_get_line); ZEND_FUNCTION(stream_resolve_include_path); ZEND_FUNCTION(stream_get_wrappers); +ZEND_FUNCTION(stream_get_errors); ZEND_FUNCTION(stream_get_transports); ZEND_FUNCTION(stream_is_local); ZEND_FUNCTION(stream_isatty); @@ -3422,6 +3427,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(stream_get_line, arginfo_stream_get_line) ZEND_FE(stream_resolve_include_path, arginfo_stream_resolve_include_path) ZEND_FE(stream_get_wrappers, arginfo_stream_get_wrappers) + ZEND_FE(stream_get_errors, arginfo_stream_get_errors) ZEND_FE(stream_get_transports, arginfo_stream_get_transports) ZEND_FE(stream_is_local, arginfo_stream_is_local) ZEND_FE(stream_isatty, arginfo_stream_isatty) diff --git a/ext/standard/file.c b/ext/standard/file.c index fdeabd1872d20..6fb021b44c173 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -1228,7 +1228,7 @@ PHP_FUNCTION(rename) context = php_stream_context_from_zval(zcontext, 0); - RETURN_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, 0, context)); + RETURN_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, REPORT_ERRORS, context)); } /* }}} */ diff --git a/ext/standard/file.h b/ext/standard/file.h index f8faebd028293..a3c84791cc1ee 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -34,6 +34,7 @@ PHPAPI PHP_FUNCTION(ftell); PHPAPI PHP_FUNCTION(fseek); PHPAPI PHP_FUNCTION(fpassthru); +PHP_MINIT_FUNCTION(stream_errors); PHP_MINIT_FUNCTION(user_streams); PHPAPI zend_result php_copy_file(const char *src, const char *dest); @@ -100,7 +101,8 @@ typedef struct { php_stream_context *default_context; HashTable *stream_wrappers; /* per-request copy of url_stream_wrappers_hash */ HashTable *stream_filters; /* per-request copy of stream_filters_hash */ - HashTable *wrapper_errors; /* key: wrapper address; value: linked list of char* */ + HashTable *wrapper_logged_errors; /* key: wrapper address; value: linked list of error entries */ + HashTable *wrapper_stored_errors; /* key: wrapper address; value: linked list of error entries */ int pclose_wait; #ifdef HAVE_GETHOSTBYNAME_R struct hostent tmp_host_info; diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index d1bb3aeeccd68..3e3b74bb2a2e7 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -109,7 +109,9 @@ static int php_stream_ftp_stream_close(php_stream_wrapper *wrapper, php_stream * /* For write modes close data stream first to signal EOF to server */ result = GET_FTP_RESULT(controlstream); if (result != 226 && result != 250) { - php_error_docref(NULL, E_WARNING, "FTP server error %d:%s", result, tmp_line); + php_stream_wrapper_warn(wrapper, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_PROTOCOL_ERROR, + "FTP server error %d:%s", result, tmp_line); ret = EOF; } } @@ -187,7 +189,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char /* get the response */ result = GET_FTP_RESULT(stream); if (result != 334) { - php_stream_wrapper_log_error(wrapper, options, "Server doesn't support FTPS."); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "Server doesn't support FTPS."); goto connect_errexit; } else { /* we must reuse the old SSL session id */ @@ -206,7 +209,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "Unable to activate SSL mode"); php_stream_close(stream); stream = NULL; goto connect_errexit; @@ -237,7 +241,7 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char unsigned char *s = (unsigned char *) val, *e = (unsigned char *) s + val_len; \ while (s < e) { \ if (iscntrl(*s)) { \ - php_stream_wrapper_log_error(wrapper, options, err_msg, val); \ + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_AUTH_FAILED, err_msg, val); \ goto connect_errexit; \ } \ s++; \ @@ -434,7 +438,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa } if (strpbrk(mode, "wa+")) { if (read_write) { - php_stream_wrapper_log_error(wrapper, options, "FTP does not support simultaneous read/write connections"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + "FTP does not support simultaneous read/write connections"); return NULL; } if (strchr(mode, 'a')) { @@ -445,7 +450,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa } if (!read_write) { /* No mode specified? */ - php_stream_wrapper_log_error(wrapper, options, "Unknown file open mode"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_MODE, + "Unknown file open mode"); return NULL; } @@ -456,7 +462,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa return php_stream_url_wrap_http(wrapper, path, mode, options, opened_path, context STREAMS_CC); } else { /* ftp proxy is read-only */ - php_stream_wrapper_log_error(wrapper, options, "FTP proxy may only be used in read mode"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + "FTP proxy may only be used in read mode"); return NULL; } } @@ -508,7 +515,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa goto errexit; } } else { - php_stream_wrapper_log_error(wrapper, options, "Remote file already exists and overwrite context option not specified"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_ALREADY_EXISTS, + "Remote file already exists and overwrite context option not specified"); errno = EEXIST; goto errexit; } @@ -532,7 +540,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_printf(stream, "REST " ZEND_LONG_FMT "\r\n", Z_LVAL_P(tmpzval)); result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { - php_stream_wrapper_log_error(wrapper, options, "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RESUMPTION_FAILED, + "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); goto errexit; } } @@ -577,7 +586,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(datastream, 1) < 0)) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; tmp_line[0]='\0'; @@ -599,10 +609,12 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_close(stream); } if (tmp_line[0] != '\0') - php_stream_wrapper_log_error(wrapper, options, "FTP server reports %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "FTP server reports %s", tmp_line); if (error_message) { - php_stream_wrapper_log_error(wrapper, options, "Failed to set up data channel: %s", ZSTR_VAL(error_message)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NETWORK_SEND_FAILED, + "Failed to set up data channel: %s", ZSTR_VAL(error_message)); zend_string_release(error_message); } return NULL; @@ -747,7 +759,8 @@ static php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const ch STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(datastream, 1) < 0)) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; goto opendir_errexit; @@ -771,7 +784,8 @@ static php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const ch php_stream_close(stream); } if (tmp_line[0] != '\0') { - php_stream_wrapper_log_error(wrapper, options, "FTP server reports %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "FTP server reports %s", tmp_line); } return NULL; } @@ -910,14 +924,16 @@ static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, i stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_AUTH_FAILED, + "Unable to connect to %s", url); } goto unlink_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PATH, + "Invalid path provided in %s", url); } goto unlink_errexit; } @@ -928,7 +944,8 @@ static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, i result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Deleting file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, + "Error Deleting file: %s", tmp_line); } goto unlink_errexit; } @@ -992,7 +1009,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr stream = php_ftp_fopen_connect(wrapper, url_from, "r", 0, NULL, context, NULL, NULL, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", ZSTR_VAL(resource_from->host)); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_AUTH_FAILED, + "Unable to connect to %s", ZSTR_VAL(resource_from->host)); } goto rename_errexit; } @@ -1003,7 +1021,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Renaming file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "Error Renaming file: %s", tmp_line); } goto rename_errexit; } @@ -1014,7 +1033,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Renaming file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "Error Renaming file: %s", tmp_line); } goto rename_errexit; } @@ -1047,14 +1067,16 @@ static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, in stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_AUTH_FAILED, + "Unable to connect to %s", url); } goto mkdir_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PATH, + "Invalid path provided in %s", url); } goto mkdir_errexit; } @@ -1095,7 +1117,8 @@ static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, in result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "%s", tmp_line); } break; } @@ -1139,14 +1162,16 @@ static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, in stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_AUTH_FAILED, + "Unable to connect to %s", url); } goto rmdir_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PATH, + "Invalid path provided in %s", url); } goto rmdir_errexit; } @@ -1156,7 +1181,8 @@ static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, in if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "%s", tmp_line); } goto rmdir_errexit; } diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index da150381f43f3..5852beff7a380 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -246,7 +246,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w while (last_header_name < last_header_value) { if (*last_header_name == ' ' || *last_header_name == '\t') { header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid response format (space in header name)!"); zend_string_efree(last_header_line_str); return NULL; @@ -264,7 +264,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w } else { /* There is no colon which means invalid response so error. */ header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid response format (no colon in header line)!"); zend_string_efree(last_header_line_str); return NULL; @@ -288,7 +288,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w size_t last_header_value_len = strlen(last_header_value); if (last_header_value_len > HTTP_HEADER_MAX_LOCATION_SIZE) { header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP Location header size is over the limit of %d bytes", HTTP_HEADER_MAX_LOCATION_SIZE); zend_string_efree(last_header_line_str); @@ -389,7 +389,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, tmp_line[0] = '\0'; if (redirect_max < 1) { - php_stream_wrapper_log_error(wrapper, options, "Redirection limit reached, aborting"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_REDIRECT_LIMIT, + "Redirection limit reached, aborting"); return NULL; } @@ -422,7 +423,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, /* Normal http request (possibly with proxy) */ if (strpbrk(mode, "awx+")) { - php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper does not support writeable connections"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + "HTTP wrapper does not support writeable connections"); php_uri_struct_free(resource); return NULL; } @@ -451,7 +453,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (request_fulluri && (strchr(path, '\n') != NULL || strchr(path, '\r') != NULL)) { - php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper full URI path does not allow CR or LF characters"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "HTTP wrapper full URI path does not allow CR or LF characters"); php_uri_struct_free(resource); zend_string_release(transport_string); return NULL; @@ -466,7 +469,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, #endif if (d > timeoutmax) { - php_stream_wrapper_log_error(wrapper, options, "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PARAM, + "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); zend_string_release(transport_string); php_uri_struct_free(resource); return NULL; @@ -496,7 +500,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (errstr) { - php_stream_wrapper_log_error(wrapper, options, "%s", ZSTR_VAL(errstr)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "%s", ZSTR_VAL(errstr)); zend_string_release_ex(errstr, 0); errstr = NULL; } @@ -547,7 +552,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, smart_str_appendl(&header, "\r\n", sizeof("\r\n")-1); if (php_stream_write(stream, ZSTR_VAL(header.s), ZSTR_LEN(header.s)) != ZSTR_LEN(header.s)) { - php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } @@ -570,7 +576,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (stream) { if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { - php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } @@ -827,7 +834,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, ua[ua_len] = 0; smart_str_appendl(&req_buf, ua, ua_len); } else { - php_error_docref(NULL, E_WARNING, "Cannot construct User-agent header"); + php_stream_wrapper_warn_nt(wrapper, context, options, STREAM_ERROR_CODE_INVALID_HEADER, + "Cannot construct User-agent header"); } efree(ua); } @@ -866,7 +874,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (!(have_header & HTTP_HEADER_TYPE)) { smart_str_appends(&req_buf, "Content-Type: application/x-www-form-urlencoded\r\n"); - php_error_docref(NULL, E_NOTICE, "Content-type not specified assuming application/x-www-form-urlencoded"); + php_stream_wrapper_notice(wrapper, context, options, STREAM_ERROR_CODE_INVALID_HEADER, + "Content-type not specified assuming application/x-www-form-urlencoded"); } smart_str_appends(&req_buf, "\r\n"); smart_str_appendl(&req_buf, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval)); @@ -953,7 +962,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } else { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, "HTTP request failed!"); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "HTTP request failed!"); goto out; } } @@ -971,7 +981,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (http_header_line[1] != '\n') { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid header name (cannot start with CR character)!"); goto out; } @@ -1002,7 +1012,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (*http_header_line == ' ' || *http_header_line == '\t') { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid response format (folding header at the start)!"); goto out; } @@ -1098,7 +1108,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, php_uri_struct_free(resource); /* check for invalid redirection URLs */ if ((resource = php_uri_parse_to_struct(uri_parser, new_path, strlen(new_path), PHP_URI_COMPONENT_READ_MODE_RAW, true)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "Invalid redirect URL! %s", new_path); efree(new_path); goto out; } @@ -1110,7 +1121,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, s = (unsigned char*)ZSTR_VAL(val); e = s + ZSTR_LEN(val); \ while (s < e) { \ if (iscntrl(*s)) { \ - php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); \ + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, \ + "Invalid redirect URL! %s", new_path); \ efree(new_path); \ goto out; \ } \ @@ -1136,7 +1148,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, --redirect_max, new_flags, response_header STREAMS_CC); efree(new_path); } else { - php_stream_wrapper_log_error(wrapper, options, "HTTP request failed! %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "HTTP request failed! %s", tmp_line); } } out: diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index ea33ba4904346..902b0f853f4e4 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -158,14 +158,18 @@ static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, i if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) { php_stream_filter_append(&stream->readfilters, temp_filter); } else { - php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p); + php_stream_wrapper_warn_nt(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_CREATE_FAILED, + "Unable to create filter (%s)", p); } } if (write_chain) { if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) { php_stream_filter_append(&stream->writefilters, temp_filter); } else { - php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p); + php_stream_wrapper_warn_nt(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_CREATE_FAILED, + "Unable to create filter (%s)", p); } } p = php_strtok_r(NULL, "|", &token); @@ -219,7 +223,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_DISABLED, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -238,7 +244,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (!strcasecmp(path, "stdin")) { if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_DISABLED, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -297,14 +305,18 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (strcmp(sapi_module.name, "cli")) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Direct access to file descriptors is only available from command-line PHP"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_DISABLED, + "Direct access to file descriptors is only available from command-line PHP"); } return NULL; } if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_DISABLED, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -312,7 +324,8 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c start = &path[3]; fildes_ori = ZEND_STRTOL(start, &end, 10); if (end == start || *end != '\0') { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "php://fd/ stream must be specified in the form php://fd/"); return NULL; } @@ -324,14 +337,16 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c #endif if (fildes_ori < 0 || fildes_ori >= dtablesize) { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_PARAM, "The file descriptors must be non-negative numbers smaller than %d", dtablesize); return NULL; } fd = dup((int)fildes_ori); if (fd == -1) { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_DUP_FAILED, "Error duping file descriptor " ZEND_LONG_FMT "; possibly it doesn't exist: " "[%d]: %s", fildes_ori, errno, strerror(errno)); return NULL; @@ -380,7 +395,8 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c return stream; } else { /* invalid php://thingy */ - php_error_docref(NULL, E_WARNING, "Invalid php:// URL specified"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "Invalid php:// URL specified"); return NULL; } diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 506ce0dafed8b..6472bbc23dedc 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -594,6 +594,104 @@ PHP_FUNCTION(stream_get_wrappers) } /* }}} */ +/* Helper function to convert error list to array */ +static void php_stream_errors_list_to_array(zend_llist *list, zval *return_value) +{ + php_stream_error_entry **err_entry_p; + zend_llist_position pos; + + for (err_entry_p = zend_llist_get_first_ex(list, &pos); + err_entry_p; + err_entry_p = zend_llist_get_next_ex(list, &pos)) { + php_stream_error_entry *entry = *err_entry_p; + zval error_array; + array_init(&error_array); + + add_assoc_str(&error_array, "message", zend_string_copy(entry->message)); + add_assoc_long(&error_array, "code", entry->code); + add_assoc_long(&error_array, "severity", entry->severity); + add_assoc_bool(&error_array, "terminal", entry->terminal); + + if (entry->wrapper_name) { + add_assoc_string(&error_array, "wrapper", entry->wrapper_name); + } + if (entry->param) { + add_assoc_string(&error_array, "param", entry->param); + } + if (entry->docref) { + add_assoc_string(&error_array, "docref", entry->docref); + } + + add_next_index_zval(return_value, &error_array); + } +} + +/* Retrieves list of stored stream errors */ +PHP_FUNCTION(stream_get_errors) +{ + zval *subject = NULL; + php_stream *stream = NULL; + char *wrapper_name = NULL; + size_t wrapper_name_len = 0; + bool get_all_wrappers = false; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(subject) + ZEND_PARSE_PARAMETERS_END(); + + /* Determine what we're querying for */ + if (subject == NULL) { + /* No parameter - get all wrapper errors */ + get_all_wrappers = true; + } else if (Z_TYPE_P(subject) == IS_RESOURCE) { + /* Stream resource - get errors for this stream */ + php_stream_from_zval_no_verify(stream, subject); + if (stream == NULL) { + zend_argument_type_error(1, "must be a valid stream resource"); + RETURN_THROWS(); + } + } else if (Z_TYPE_P(subject) == IS_STRING) { + /* Wrapper name - get errors for this wrapper */ + wrapper_name = Z_STRVAL_P(subject); + wrapper_name_len = Z_STRLEN_P(subject); + } else { + zend_argument_type_error(1, "must be a stream resource, string, or null"); + RETURN_THROWS(); + } + + array_init(return_value); + + /* Handle stream errors */ + if (stream) { + if (stream->error_list) { + php_stream_errors_list_to_array(stream->error_list, return_value); + } + } else if (get_all_wrappers) { + /* Get errors from all wrappers */ + if (FG(wrapper_stored_errors)) { + zend_string *key; + zval *val; + ZEND_HASH_FOREACH_STR_KEY_VAL(FG(wrapper_stored_errors), key, val) { + if (key) { + zend_llist *list = (zend_llist *) Z_PTR_P(val); + php_stream_errors_list_to_array(list, return_value); + } + } ZEND_HASH_FOREACH_END(); + } + } else if (wrapper_name) { + /* Get errors for specific wrapper */ + if (FG(wrapper_stored_errors)) { + zend_llist *list = zend_hash_str_find_ptr( + FG(wrapper_stored_errors), wrapper_name, wrapper_name_len); + + if (list) { + php_stream_errors_list_to_array(list, return_value); + } + } + } +} + /* {{{ stream_select related functions */ static int stream_array_to_fd_set(const HashTable *stream_array, fd_set *fds, php_socket_t *max_fd) { diff --git a/ext/standard/tests/file/php_fd_wrapper_03.phpt b/ext/standard/tests/file/php_fd_wrapper_03.phpt index 991c497f5e193..a19d1f5acd94b 100644 --- a/ext/standard/tests/file/php_fd_wrapper_03.phpt +++ b/ext/standard/tests/file/php_fd_wrapper_03.phpt @@ -10,7 +10,6 @@ fopen("php://fd/1/", "w"); echo "\nDone.\n"; ?> --EXPECTF-- -Warning: fopen(): Invalid php:// URL specified in %s on line %d Warning: fopen(php://fd): Failed to open stream: operation failed in %s on line 2 diff --git a/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt new file mode 100644 index 0000000000000..016b989276fa0 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt @@ -0,0 +1,24 @@ +--TEST-- +Stream errors - exception mode for terminal errors +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_EXCEPTION, + ] +]); + +try { + $stream = fopen('php://nonexistent', 'r', false, $context); +} catch (StreamException $e) { + echo "Caught: " . $e->getMessage() . "\n"; + echo "Code: " . $e->getCode() . "\n"; + echo "Wrapper: " . $e->getWrapperName() . "\n"; +} + +?> +--EXPECTF-- +Caught: Failed to open stream: operation failed +Code: 36 +Wrapper: PHP diff --git a/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt b/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt new file mode 100644 index 0000000000000..cb765de62fe07 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt @@ -0,0 +1,194 @@ +--TEST-- +Stream errors - silent mode with mixed error stores +--FILE-- +position += $count; + return str_repeat('x', $count + 10); + } + + public function stream_eof() { + return $this->position >= 100; + } + + public function stream_stat() { + return []; + } +} + +stream_wrapper_register('test', 'TestStream'); + +function stream_test_errors($title, $context) { + $stream = fopen('test://foo', 'r', false, $context); + try { + echo $title . "\n"; + $readin = fopen( 'php://stdin', 'r' ); + $data = fread( $stream, 10 ); + + $read = [$readin, $stream]; + $write = NULL; + $except = NULL; + stream_select($read, $write, $except, 0); + } catch (StreamException $e) { + echo 'EXCEPTION: ' . $e->getMessage() . "\n"; + } + + $errors = stream_get_errors($stream); + var_dump($errors); +} + +stream_test_errors('ALL', stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_store' => STREAM_ERROR_STORE_ALL, + ] +])); +stream_test_errors('NON TERMINAL', stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_store' => STREAM_ERROR_STORE_NON_TERMINAL, + ] +])); +stream_test_errors('TERMINAL', stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_store' => STREAM_ERROR_STORE_TERMINAL, + ] +])); +stream_test_errors('AUTO EXCEPTION', stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_EXCEPTION, + 'error_store' => STREAM_ERROR_STORE_AUTO, + ] +])); +stream_test_errors('AUTO ERROR', stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_ERROR, + 'error_store' => STREAM_ERROR_STORE_AUTO, + ] +])); + +?> +--EXPECTF-- +ALL +array(3) { + [0]=> + array(5) { + ["message"]=> + string(113) "TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost" + ["code"]=> + int(161) + ["severity"]=> + int(2) + ["terminal"]=> + bool(false) + ["wrapper"]=> + string(10) "user-space" + } + [1]=> + array(5) { + ["message"]=> + string(43) "TestStream::stream_cast is not implemented!" + ["code"]=> + int(70) + ["severity"]=> + int(2) + ["terminal"]=> + bool(true) + ["wrapper"]=> + string(10) "user-space" + } + [2]=> + array(5) { + ["message"]=> + string(73) "Cannot represent a stream of type user-space as a select()able descriptor" + ["code"]=> + int(101) + ["severity"]=> + int(2) + ["terminal"]=> + bool(true) + ["wrapper"]=> + string(10) "user-space" + } +} +NON TERMINAL +array(1) { + [0]=> + array(5) { + ["message"]=> + string(113) "TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost" + ["code"]=> + int(161) + ["severity"]=> + int(2) + ["terminal"]=> + bool(false) + ["wrapper"]=> + string(10) "user-space" + } +} +TERMINAL +array(2) { + [0]=> + array(5) { + ["message"]=> + string(43) "TestStream::stream_cast is not implemented!" + ["code"]=> + int(70) + ["severity"]=> + int(2) + ["terminal"]=> + bool(true) + ["wrapper"]=> + string(10) "user-space" + } + [1]=> + array(5) { + ["message"]=> + string(73) "Cannot represent a stream of type user-space as a select()able descriptor" + ["code"]=> + int(101) + ["severity"]=> + int(2) + ["terminal"]=> + bool(true) + ["wrapper"]=> + string(10) "user-space" + } +} +AUTO EXCEPTION +EXCEPTION: Cannot represent a stream of type user-space as a select()able descriptor +array(1) { + [0]=> + array(5) { + ["message"]=> + string(113) "TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost" + ["code"]=> + int(161) + ["severity"]=> + int(2) + ["terminal"]=> + bool(false) + ["wrapper"]=> + string(10) "user-space" + } +} +AUTO ERROR + +Warning: fread(): TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost in %s on line %d + +Warning: stream_select(): TestStream::stream_cast is not implemented! in %s on line %d + +Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d +array(0) { +} diff --git a/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt b/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt new file mode 100644 index 0000000000000..bda445eabe428 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt @@ -0,0 +1,49 @@ +--TEST-- +Stream errors - error_store AUTO mode behavior +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_ERROR, + 'error_store' => STREAM_ERROR_STORE_AUTO, + ] +]); + +@fopen('php://nonexistent', 'r', false, $context1); +$errors1 = stream_get_errors('PHP'); +echo "ERROR mode AUTO: " . count($errors1) . "\n"; + +// AUTO with EXCEPTION mode should store NON_TERM +$context2 = stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_EXCEPTION, + 'error_store' => STREAM_ERROR_STORE_AUTO, + ] +]); + +try { + fopen('php://nonexistent2', 'r', false, $context2); +} catch (StreamException $e) {} + +$errors2 = stream_get_errors('PHP'); +echo "EXCEPTION mode AUTO: " . count($errors2) . "\n"; + +// AUTO with SILENT mode should store ALL +$context3 = stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_store' => STREAM_ERROR_STORE_AUTO, + ] +]); + +fopen('php://nonexistent3', 'r', false, $context3); +$errors3 = stream_get_errors('PHP'); +echo "SILENT mode AUTO: " . count($errors3) . "\n"; + +?> +--EXPECTF-- +ERROR mode AUTO: 0 +EXCEPTION mode AUTO: %d +SILENT mode AUTO: %d diff --git a/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt b/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt new file mode 100644 index 0000000000000..9ce86adb09658 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt @@ -0,0 +1,33 @@ +--TEST-- +Stream errors - custom error handler +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_handler' => function($wrapper, $stream, $code, $message, $param) use (&$handler_called) { + $handler_called = true; + echo "Handler called\n"; + echo "Wrapper: $wrapper\n"; + echo "Code: $code\n"; + echo "Message: $message\n"; + echo "Param: " . ($param ?? 'null') . "\n"; + } + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); + +var_dump($handler_called); + +?> +--EXPECT-- +Handler called +Wrapper: PHP +Code: 36 +Message: Failed to open stream: operation failed +Param: php://nonexistent +bool(true) diff --git a/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt b/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt new file mode 100644 index 0000000000000..563857d308fff --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt @@ -0,0 +1,27 @@ +--TEST-- +Stream errors - silent mode with error storage +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); +var_dump($stream); + +$errors = stream_get_errors(); +echo "Error count: " . count($errors) . "\n"; +if (count($errors) > 0) { + echo "First error code: " . $errors[0]['code'] . "\n"; + echo "First error wrapper: " . $errors[0]['wrapper'] . "\n"; +} + +?> +--EXPECT-- +bool(false) +Error count: 1 +First error code: 36 +First error wrapper: PHP diff --git a/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt b/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt new file mode 100644 index 0000000000000..6ed79378d8226 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt @@ -0,0 +1,20 @@ +--TEST-- +Stream errors - error_store NONE option +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_store' => STREAM_ERROR_STORE_NONE, + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); + +$errors = stream_get_errors(); +echo "Error count: " . count($errors) . "\n"; + +?> +--EXPECT-- +Error count: 0 diff --git a/ext/standard/tests/streams/stream_errors_standard_error.phpt b/ext/standard/tests/streams/stream_errors_standard_error.phpt new file mode 100644 index 0000000000000..a9725f0b89b26 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_standard_error.phpt @@ -0,0 +1,20 @@ +--TEST-- +Stream errors - error mode with standard error reporting +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_ERROR, + ] +]); + +// This will trigger a warning +$stream = fopen('php://nonexistent', 'r', false, $context); + +var_dump($stream); + +?> +--EXPECTF-- +Warning: fopen(php://nonexistent): Failed to open stream: %s in %s on line %d +bool(false) diff --git a/main/php_streams.h b/main/php_streams.h index 1c52539cfcaee..601223bd9a5fd 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -248,6 +248,8 @@ struct _php_stream { #endif struct _php_stream *enclosing_stream; /* this is a private stream owned by enclosing_stream */ + + zend_llist *error_list; }; /* php_stream */ #define PHP_STREAM_CONTEXT(stream) \ @@ -539,6 +541,7 @@ PHPAPI ssize_t _php_stream_passthru(php_stream * src STREAMS_DC); #define php_stream_passthru(stream) _php_stream_passthru((stream) STREAMS_CC) END_EXTERN_C() +#include "streams/php_stream_errors.h" #include "streams/php_stream_transport.h" #include "streams/php_stream_plain_wrapper.h" #include "streams/php_stream_glob_wrapper.h" @@ -642,10 +645,6 @@ PHPAPI const char *php_stream_locate_eol(php_stream *stream, zend_string *buf); #define php_stream_open_wrapper(path, mode, options, opened) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), NULL STREAMS_CC) #define php_stream_open_wrapper_ex(path, mode, options, opened, context) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), (context) STREAMS_CC) - -/* pushes an error message onto the stack for a wrapper instance */ -PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4); - typedef enum { PHP_STREAM_UNCHANGED = 0, /* orig stream was seekable anyway */ PHP_STREAM_RELEASED = 1, /* newstream should be used; origstream is no longer valid */ diff --git a/main/streams/cast.c b/main/streams/cast.c index 4b7183024571b..b7edfe0568825 100644 --- a/main/streams/cast.c +++ b/main/streams/cast.c @@ -189,7 +189,6 @@ void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *resul result[res_curs] = '\0'; } /* }}} */ - /* {{{ php_stream_cast */ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err) { @@ -259,7 +258,7 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, b) no memory -> lets bail */ - php_error_docref(NULL, E_ERROR, "fopencookie failed"); + php_stream_fatal(stream, STREAM_ERROR_CODE_CAST_FAILED, "fopencookie failed"); return FAILURE; #endif @@ -299,7 +298,8 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, if (php_stream_is_filtered(stream)) { if (show_err) { - php_error_docref(NULL, E_WARNING, "Cannot cast a filtered stream on this system"); + php_stream_warn(stream, STREAM_ERROR_CODE_CAST_NOT_SUPPORTED, + "Cannot cast a filtered stream on this system"); } return FAILURE; } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret) == SUCCESS) { @@ -315,7 +315,8 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, "select()able descriptor" }; - php_error_docref(NULL, E_WARNING, "Cannot represent a stream of type %s as a %s", stream->ops->label, cast_names[castas]); + php_stream_warn(stream, STREAM_ERROR_CODE_CAST_NOT_SUPPORTED, + "Cannot represent a stream of type %s as a %s", stream->ops->label, cast_names[castas]); } return FAILURE; @@ -330,7 +331,9 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, * will be accessing the stream. Emit a warning so that the end-user will * know that they should try something else */ - php_error_docref(NULL, E_WARNING, ZEND_LONG_FMT " bytes of buffered data lost during stream conversion!", (zend_long)(stream->writepos - stream->readpos)); + php_stream_warn_nt(stream, STREAM_ERROR_CODE_BUFFERED_DATA_LOST, + ZEND_LONG_FMT " bytes of buffered data lost during stream conversion!", + (zend_long)(stream->writepos - stream->readpos)); } if (castas == PHP_STREAM_AS_STDIO && ret) { diff --git a/main/streams/filter.c b/main/streams/filter.c index b63d789190792..55f1c7ef5ea76 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -343,7 +343,8 @@ PHPAPI zend_result php_stream_filter_append_ex(php_stream_filter_chain *chain, p php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } - php_error_docref(NULL, E_WARNING, "Filter failed to process pre-buffered data"); + php_stream_warn(stream, STREAM_ERROR_CODE_FILTER_FAILED, + "Filter failed to process pre-buffered data"); return FAILURE; case PSFS_FEED_ME: /* We don't actually need data yet, diff --git a/main/streams/memory.c b/main/streams/memory.c index 785109db6582c..1b0bad3410857 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -362,7 +362,9 @@ static ssize_t php_stream_temp_write(php_stream *stream, const char *buf, size_t zend_string *membuf = php_stream_memory_get_buffer(ts->innerstream); php_stream *file = php_stream_fopen_temporary_file(ts->tmpdir, "php", NULL); if (file == NULL) { - php_error_docref(NULL, E_WARNING, "Unable to create temporary file, Check permissions in temporary files directory."); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_PERMISSION_DENIED, + "Unable to create temporary file, Check permissions in temporary files directory."); return 0; } php_stream_write(file, ZSTR_VAL(membuf), ZSTR_LEN(membuf)); @@ -489,7 +491,8 @@ static int php_stream_temp_cast(php_stream *stream, int castas, void **ret) file = php_stream_fopen_tmpfile(); if (file == NULL) { - php_error_docref(NULL, E_WARNING, "Unable to create temporary file."); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_PERMISSION_DENIED, "Unable to create temporary file."); return FAILURE; } @@ -638,7 +641,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con } if ((comma = memchr(path, ',', dlen)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "rfc2397: no comma in URL"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "rfc2397: no comma in URL"); return NULL; } @@ -650,7 +654,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con sep = memchr(path, '/', mlen); if (!semi && !sep) { - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal media type"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "rfc2397: illegal media type"); return NULL; } @@ -665,7 +670,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con path += plen; } else if (semi != path || mlen != sizeof(";base64")-1 || memcmp(path, ";base64", sizeof(";base64")-1)) { /* must be error since parameters are only allowed after mediatype */ zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal media type"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "rfc2397: illegal media type"); return NULL; } /* get parameters and potentially ';base64' */ @@ -678,7 +684,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con if (mlen != sizeof("base64")-1 || memcmp(path, "base64", sizeof("base64")-1)) { /* must be error since parameters are only allowed after mediatype and we have no '=' sign */ zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal parameter"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_PARAM, "rfc2397: illegal parameter"); return NULL; } base64 = 1; @@ -698,7 +705,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con } if (mlen) { zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal URL"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "rfc2397: illegal URL"); return NULL; } } else { @@ -714,7 +722,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con base64_comma = php_base64_decode_ex((const unsigned char *)comma, dlen, 1); if (!base64_comma) { zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: unable to decode"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_DECODING_FAILED, "rfc2397: unable to decode"); return NULL; } comma = ZSTR_VAL(base64_comma); diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h new file mode 100644 index 0000000000000..d53e9a0a5fe1e --- /dev/null +++ b/main/streams/php_stream_errors.h @@ -0,0 +1,226 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | https://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. | + +----------------------------------------------------------------------+ + | Authors: Jakub Zelenka | + +----------------------------------------------------------------------+ + */ + +#ifndef PHP_STREAM_ERRORS_H +#define PHP_STREAM_ERRORS_H + +#include "php.h" +#include "php_streams.h" + +BEGIN_EXTERN_C() + +/* Error mode context options */ +#define PHP_STREAM_ERROR_MODE_ERROR 0 +#define PHP_STREAM_ERROR_MODE_EXCEPTION 1 +#define PHP_STREAM_ERROR_MODE_SILENT 2 + +/* Error store context options */ +#define PHP_STREAM_ERROR_STORE_AUTO 0 +#define PHP_STREAM_ERROR_STORE_NONE 1 +#define PHP_STREAM_ERROR_STORE_NON_TERM 2 +#define PHP_STREAM_ERROR_STORE_TERMINAL 3 +#define PHP_STREAM_ERROR_STORE_ALL 4 + +/* Stream Error Codes*/ +/* No error */ +#define STREAM_ERROR_CODE_NONE 0 +/* Generic unspecified error */ +#define STREAM_ERROR_CODE_GENERIC 1 +/* Stream I/O operations */ +#define STREAM_ERROR_CODE_READ_FAILED 10 +#define STREAM_ERROR_CODE_WRITE_FAILED 11 +#define STREAM_ERROR_CODE_SEEK_FAILED 12 +#define STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED 13 +#define STREAM_ERROR_CODE_FLUSH_FAILED 14 +#define STREAM_ERROR_CODE_TRUNCATE_FAILED 15 +#define STREAM_ERROR_CODE_CONNECT_FAILED 16 +#define STREAM_ERROR_CODE_BIND_FAILED 17 +#define STREAM_ERROR_CODE_LISTEN_FAILED 18 +#define STREAM_ERROR_CODE_NOT_WRITABLE 19 +#define STREAM_ERROR_CODE_NOT_READABLE 20 +/* File system operations */ +#define STREAM_ERROR_CODE_DISABLED 30 +#define STREAM_ERROR_CODE_NOT_FOUND 31 +#define STREAM_ERROR_CODE_PERMISSION_DENIED 32 +#define STREAM_ERROR_CODE_ALREADY_EXISTS 33 +#define STREAM_ERROR_CODE_INVALID_PATH 34 +#define STREAM_ERROR_CODE_PATH_TOO_LONG 35 +#define STREAM_ERROR_CODE_OPEN_FAILED 36 +#define STREAM_ERROR_CODE_CREATE_FAILED 37 +#define STREAM_ERROR_CODE_DUP_FAILED 38 +#define STREAM_ERROR_CODE_UNLINK_FAILED 39 +#define STREAM_ERROR_CODE_RENAME_FAILED 40 +#define STREAM_ERROR_CODE_MKDIR_FAILED 41 +#define STREAM_ERROR_CODE_RMDIR_FAILED 42 +#define STREAM_ERROR_CODE_STAT_FAILED 43 +#define STREAM_ERROR_CODE_META_FAILED 44 +#define STREAM_ERROR_CODE_CHMOD_FAILED 45 +#define STREAM_ERROR_CODE_CHOWN_FAILED 46 +#define STREAM_ERROR_CODE_COPY_FAILED 47 +#define STREAM_ERROR_CODE_TOUCH_FAILED 48 +#define STREAM_ERROR_CODE_INVALID_MODE 49 +#define STREAM_ERROR_CODE_INVALID_META 50 +#define STREAM_ERROR_CODE_MODE_NOT_SUPPORTED 51 +#define STREAM_ERROR_CODE_READONLY 52 +#define STREAM_ERROR_CODE_RECURSION_DETECTED 53 +/* Wrapper/protocol operations */ +#define STREAM_ERROR_CODE_NOT_IMPLEMENTED 70 +#define STREAM_ERROR_CODE_NO_OPENER 71 +#define STREAM_ERROR_CODE_PERSISTENT_NOT_SUPPORTED 72 +#define STREAM_ERROR_CODE_WRAPPER_NOT_FOUND 73 +#define STREAM_ERROR_CODE_WRAPPER_DISABLED 74 +#define STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED 75 +#define STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED 76 +#define STREAM_ERROR_CODE_WRAPPER_UNREGISTRATION_FAILED 77 +#define STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED 78 +/* Filter operations */ +#define STREAM_ERROR_CODE_FILTER_NOT_FOUND 90 +#define STREAM_ERROR_CODE_FILTER_FAILED 91 +/* Cast/conversion operations */ +#define STREAM_ERROR_CODE_CAST_FAILED 100 +#define STREAM_ERROR_CODE_CAST_NOT_SUPPORTED 101 +#define STREAM_ERROR_CODE_MAKE_SEEKABLE_FAILED 102 +#define STREAM_ERROR_CODE_BUFFERED_DATA_LOST 103 +/* Network/socket operations */ +#define STREAM_ERROR_CODE_NETWORK_SEND_FAILED 110 +#define STREAM_ERROR_CODE_NETWORK_RECV_FAILED 111 +#define STREAM_ERROR_CODE_SSL_NOT_SUPPORTED 112 +#define STREAM_ERROR_CODE_RESUMPTION_FAILED 113 +#define STREAM_ERROR_CODE_SOCKET_PATH_TOO_LONG 114 +#define STREAM_ERROR_CODE_OOB_NOT_SUPPORTED 115 +#define STREAM_ERROR_CODE_PROTOCOL_ERROR 116 +#define STREAM_ERROR_CODE_INVALID_URL 117 +#define STREAM_ERROR_CODE_INVALID_RESPONSE 118 +#define STREAM_ERROR_CODE_INVALID_HEADER 119 +#define STREAM_ERROR_CODE_INVALID_PARAM 110 +#define STREAM_ERROR_CODE_REDIRECT_LIMIT 121 +#define STREAM_ERROR_CODE_AUTH_FAILED 122 +/* Encoding/decoding/archiving operations */ +#define STREAM_ERROR_CODE_ARCHIVING_FAILED 130 +#define STREAM_ERROR_CODE_ENCODING_FAILED 131 +#define STREAM_ERROR_CODE_DECODING_FAILED 132 +#define STREAM_ERROR_CODE_INVALID_FORMAT 133 +/* Resource/allocation operations */ +#define STREAM_ERROR_CODE_ALLOCATION_FAILED 140 +#define STREAM_ERROR_CODE_TEMPORARY_FILE_FAILED 141 +/* Locking operations */ +#define STREAM_ERROR_CODE_LOCK_FAILED 150 +#define STREAM_ERROR_CODE_LOCK_NOT_SUPPORTED 151 +/* Userspace stream operations */ +#define STREAM_ERROR_CODE_USERSPACE_NOT_IMPLEMENTED 160 +#define STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN 161 +#define STREAM_ERROR_CODE_USERSPACE_CALL_FAILED 162 + +/* Stored error entry */ +typedef struct { + zend_string *message; + int code; + char *wrapper_name; + char *docref; + char *param; + int severity; + bool terminal; + bool persistent; +} php_stream_error_entry; + +/* Main error reporting functions */ +PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminal, int code, const char *fmt, + ...) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminal, int code, const char *param, + const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 9, 10); + +PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, const char *param1, const char *param2, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 10, 11); + +PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severity, bool terminal, + int code, const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 6, 7); + +/* Legacy wrapper error log - updated API */ +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 7, 8); + +PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *param, const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, + php_stream_context *context, int code, const char *path, const char *caption); + +PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); + +/* Convenience macros */ +#define php_stream_wrapper_warn(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, true, code, __VA_ARGS__) + +#define php_stream_wrapper_warn_name(wrapper_name, context, options, code, ...) \ + php_stream_wrapper_error_with_name(wrapper_name, context, NULL, options, E_WARNING, true, code, __VA_ARGS__) + +#define php_stream_wrapper_warn_nt(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, false, code, __VA_ARGS__) + +#define php_stream_wrapper_notice(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_NOTICE, false, code, __VA_ARGS__) + +#define php_stream_wrapper_warn_param(wrapper, context, options, code, param, ...) \ + php_stream_wrapper_error_param(wrapper, context, NULL, options, E_WARNING, true, code, param, __VA_ARGS__) + +#define php_stream_wrapper_warn_param_nt(wrapper, context, options, code, param, ...) \ + php_stream_wrapper_error_param(wrapper, context, NULL, options, E_WARNING, false, code, param, __VA_ARGS__) + +#define php_stream_wrapper_warn_param2(wrapper, context, options, code, param1, param2, ...) \ + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, true, code, param1, param2, __VA_ARGS__) + +#define php_stream_wrapper_warn_param2_nt(wrapper, context, options, code, param1, param2, ...) \ + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, false, code, param1, param2, __VA_ARGS__) + +#define php_stream_warn(stream, code, ...) \ + php_stream_error(stream, NULL, E_WARNING, true, code, __VA_ARGS__) + +#define php_stream_warn_nt(stream, code, ...) \ + php_stream_error(stream, NULL, E_WARNING, false, code, __VA_ARGS__) + +#define php_stream_warn_docref(stream, docref, code, ...) \ + php_stream_error(stream, docref, E_WARNING, true, code, __VA_ARGS__) + +#define php_stream_notice(stream, code, ...) \ + php_stream_error(stream, NULL, E_NOTICE, false, code, __VA_ARGS__) + +#define php_stream_fatal(stream, code, ...) \ + php_stream_error(stream, NULL, E_ERROR, true, code, __VA_ARGS__) + +/* Legacy log variants */ +#define php_stream_wrapper_log_warn(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_WARNING, true, code, __VA_ARGS__) + +#define php_stream_wrapper_log_warn_nt(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_WARNING, false, code, __VA_ARGS__) + +#define php_stream_wrapper_log_notice(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_NOTICE, false, code, __VA_ARGS__) + +END_EXTERN_C() + +#endif /* PHP_STREAM_ERRORS_H */ diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 688d271db8147..22375713a2a93 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -394,7 +394,8 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun } if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { char errstr[256]; - php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", + php_stream_notice(stream, STREAM_ERROR_CODE_WRITE_FAILED, + "Write of %zu bytes failed with errno=%d %s", count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } @@ -472,7 +473,8 @@ static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count) } else { if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { char errstr[256]; - php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", + php_stream_notice(stream, STREAM_ERROR_CODE_READ_FAILED, + "Read of %zu bytes failed with errno=%d %s", count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } @@ -621,7 +623,8 @@ static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, ze assert(data != NULL); if (!data->is_seekable) { - php_error_docref(NULL, E_WARNING, "Cannot seek on this stream"); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED, "Cannot seek on this stream"); return -1; } @@ -1158,7 +1161,8 @@ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zen char *persistent_id = NULL; if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) { - php_stream_wrapper_log_error(&php_plain_files_wrapper, options, "`%s' is not a valid mode for fopen", mode); + php_stream_wrapper_log_warn(&php_plain_files_wrapper, NULL, options, + STREAM_ERROR_CODE_INVALID_MODE, "`%s' is not a valid mode for fopen", mode); return NULL; } @@ -1311,8 +1315,9 @@ static int php_plain_files_unlink(php_stream_wrapper *wrapper, const char *url, if (ret == -1) { if (options & REPORT_ERRORS) { char errstr[256]; - php_error_docref1(NULL, url, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + STREAM_ERROR_CODE_UNLINK_FAILED, url, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1379,20 +1384,22 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f * access to the file in the meantime. */ if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, + !success, STREAM_ERROR_CODE_CHOWN_FAILED, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } if (success) { if (VCWD_CHMOD(url_to, sb.st_mode)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, + !success, STREAM_ERROR_CODE_CHOWN_FAILED, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } # endif @@ -1400,12 +1407,14 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f VCWD_UNLINK(url_from); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2_nt(wrapper, context, options, + STREAM_ERROR_CODE_STAT_FAILED, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2_nt(wrapper, context, options, + STREAM_ERROR_CODE_COPY_FAILED, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } # if !defined(ZTS) && !defined(TSRM_WIN32) umask(oldmask); @@ -1418,8 +1427,9 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f #ifdef PHP_WIN32 php_win32_docref2_from_error(GetLastError(), url_from, url_to); #else - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2(wrapper, context, options, + STREAM_ERROR_CODE_RENAME_FAILED, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); #endif return 0; } @@ -1443,7 +1453,8 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i int ret = VCWD_MKDIR(dir, (mode_t)mode); if (ret < 0 && (options & REPORT_ERRORS)) { - php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_MKDIR_FAILED, "%s", strerror(errno)); return 0; } @@ -1452,7 +1463,8 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i char buf[MAXPATHLEN]; if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND)) { - php_error_docref(NULL, E_WARNING, "Invalid path"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_PATH, "Invalid path"); return 0; } @@ -1504,7 +1516,9 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i int ret = VCWD_MKDIR(buf, (mode_t) mode); if (ret < 0 && errno != EEXIST) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_MKDIR_FAILED, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1524,7 +1538,9 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i /* issue a warning to client when the last directory was created failed */ if (ret < 0) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_MKDIR_FAILED, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1546,13 +1562,17 @@ static int php_plain_files_rmdir(php_stream_wrapper *wrapper, const char *url, i char errstr[256]; #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + STREAM_ERROR_CODE_NOT_FOUND, url, + "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif if (VCWD_RMDIR(url) < 0) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + STREAM_ERROR_CODE_RMDIR_FAILED, url, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } @@ -1575,7 +1595,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_NOT_FOUND, url, + "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif @@ -1594,7 +1616,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url if (VCWD_ACCESS(url, F_OK) != 0) { FILE *file = VCWD_FOPEN(url, "w"); if (file == NULL) { - php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_PERMISSION_DENIED, url, + "Unable to create file %s because %s", url, php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } @@ -1608,7 +1632,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url case PHP_STREAM_META_OWNER: if(option == PHP_STREAM_META_OWNER_NAME) { if(php_get_uid_by_name((char *)value, &uid) != SUCCESS) { - php_error_docref1(NULL, url, E_WARNING, "Unable to find uid for %s", (char *)value); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_META_FAILED, url, + "Unable to find uid for %s", (char *)value); return 0; } } else { @@ -1620,7 +1646,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url case PHP_STREAM_META_GROUP_NAME: if(option == PHP_STREAM_META_GROUP_NAME) { if(php_get_gid_by_name((char *)value, &gid) != SUCCESS) { - php_error_docref1(NULL, url, E_WARNING, "Unable to find gid for %s", (char *)value); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_META_FAILED, url, + "Unable to find gid for %s", (char *)value); return 0; } } else { @@ -1638,8 +1666,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url return 0; } if (ret == -1) { - php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_META_FAILED, url, + "Operation failed: %s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } php_clear_stat_cache(0, NULL, 0); @@ -1732,7 +1761,9 @@ PHPAPI php_stream *_php_stream_fopen_with_path(const char *filename, const char *(cwd+3) = '\0'; if (snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename) >= MAXPATHLEN) { - php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN); + php_stream_wrapper_notice(NULL, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_PATH_TOO_LONG, + "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN); } efree(cwd); @@ -1787,7 +1818,9 @@ PHPAPI php_stream *_php_stream_fopen_with_path(const char *filename, const char goto stream_skip; } if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) { - php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN); + php_stream_wrapper_notice(NULL, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_PATH_TOO_LONG, + "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN); } if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir_ex(trypath, 0)) { diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c new file mode 100644 index 0000000000000..5acc7cd7d2f41 --- /dev/null +++ b/main/streams/stream_errors.c @@ -0,0 +1,585 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | https://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. | + +----------------------------------------------------------------------+ + | Authors: Jakub Zelenka | + +----------------------------------------------------------------------+ + */ + +#include "php.h" +#include "php_globals.h" +#include "php_streams.h" +#include "php_stream_errors.h" +#include "stream_errors_arginfo.h" +#include "zend_exceptions.h" +#include "ext/standard/file.h" + +/* StreamException class entry */ +static zend_class_entry *php_ce_stream_exception; + +#define PHP_STREAM_ERRORS_WRAPPER_NAME(_wrapper) \ + (_wrapper && _wrapper->wops && _wrapper->wops->label ? _wrapper->wops->label : "unknown") + +static void php_stream_error_entry_dtor(void *error) +{ + php_stream_error_entry *entry = *(php_stream_error_entry **) error; + zend_string_release(entry->message); + pefree(entry->wrapper_name, entry->persistent); + pefree(entry->docref, entry->persistent); + // param is not currently supported for streams so cannot be persistent + ZEND_ASSERT(!entry->persistent || entry->param == NULL); + efree(entry->param); + pefree(entry, entry->persistent); +} + +static void php_stream_error_list_dtor(zval *item) +{ + zend_llist *list = (zend_llist *) Z_PTR_P(item); + zend_llist_destroy(list); + efree(list); +} + +/* Context option helpers */ + +static int php_stream_auto_decide_error_store_mode(int error_mode) +{ + switch (error_mode) { + case PHP_STREAM_ERROR_MODE_ERROR: + return PHP_STREAM_ERROR_STORE_NONE; + case PHP_STREAM_ERROR_MODE_EXCEPTION: + return PHP_STREAM_ERROR_STORE_NON_TERM; + case PHP_STREAM_ERROR_MODE_SILENT: + return PHP_STREAM_ERROR_STORE_ALL; + default: + return PHP_STREAM_ERROR_STORE_NONE; + } +} + +static int php_stream_get_error_mode(php_stream_context *context) +{ + if (!context) { + return PHP_STREAM_ERROR_MODE_ERROR; + } + + zval *option = php_stream_context_get_option(context, "stream", "error_mode"); + if (option && Z_TYPE_P(option) == IS_LONG) { + return Z_LVAL_P(option); + } + + return PHP_STREAM_ERROR_MODE_ERROR; +} + +static int php_stream_get_error_store_mode(php_stream_context *context, int error_mode) +{ + if (!context) { + return php_stream_auto_decide_error_store_mode(error_mode); + } + + zval *option = php_stream_context_get_option(context, "stream", "error_store"); + if (option && Z_TYPE_P(option) == IS_LONG) { + int store_mode = Z_LVAL_P(option); + + if (store_mode == PHP_STREAM_ERROR_STORE_AUTO) { + return php_stream_auto_decide_error_store_mode(error_mode); + } + + return store_mode; + } + + return php_stream_auto_decide_error_store_mode(error_mode); +} + +static bool php_stream_should_store_error(int store_mode, bool terminal) +{ + switch (store_mode) { + case PHP_STREAM_ERROR_STORE_NONE: + return false; + case PHP_STREAM_ERROR_STORE_NON_TERM: + return !terminal; + case PHP_STREAM_ERROR_STORE_TERMINAL: + return terminal; + case PHP_STREAM_ERROR_STORE_ALL: + return true; + default: + return false; + } +} + +/* StreamException methods */ + +static void php_stream_throw_exception( + const char *message, int code, const char *wrapper_name, const char *param) +{ + zval ex; + + object_init_ex(&ex, php_ce_stream_exception); + + zend_update_property_string(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("message"), message); + zend_update_property_long(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("code"), code); + zend_update_property_string( + php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("wrapperName"), wrapper_name); + + if (param) { + zend_update_property_string(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("param"), param); + } + + zend_throw_exception_object(&ex); +} + +/* Core error processing */ + +static void php_stream_process_error(php_stream_context *context, const char *wrapper_name, + php_stream *stream, const char *docref, int code, const char *message, const char *param, + int severity, bool terminal) +{ + int error_mode = php_stream_get_error_mode(context); + + /* Handle error based on error_mode */ + switch (error_mode) { + case PHP_STREAM_ERROR_MODE_ERROR: + if (param) { + php_error_docref1(docref, param, severity, "%s", message); + } else { + php_error_docref(docref, severity, "%s", message); + } + break; + + case PHP_STREAM_ERROR_MODE_EXCEPTION: + if (terminal) { + php_stream_throw_exception(message, code, wrapper_name, param); + } + break; + + case PHP_STREAM_ERROR_MODE_SILENT: + break; + } + + /* Call user error handler if set */ + if (context) { + zval *handler = php_stream_context_get_option(context, "stream", "error_handler"); + if (handler) { + zend_fcall_info_cache fcc; + char *is_callable_error = NULL; + zval retval; + zval args[5]; + + if (!zend_is_callable_ex(handler, NULL, 0, NULL, &fcc, &is_callable_error)) { + if (is_callable_error) { + zend_type_error( + "stream error handler must be a valid callback, %s", is_callable_error); + efree(is_callable_error); + } else { + zend_type_error("stream error must be a valid callback"); + } + return; + } + + /* Arg 0: wrapper name */ + ZVAL_STRING(&args[0], wrapper_name); + + /* Arg 1: stream resource or null */ + if (stream && stream->res) { + ZVAL_RES(&args[1], stream->res); + GC_ADDREF(stream->res); + } else { + ZVAL_NULL(&args[1]); + } + + /* Arg 2: error code */ + ZVAL_LONG(&args[2], code); + + /* Arg 3: message */ + ZVAL_STRING(&args[3], message); + + /* Arg 4: param (or null) */ + if (param) { + ZVAL_STRING(&args[4], param); + } else { + ZVAL_NULL(&args[4]); + } + + call_user_function(NULL, NULL, handler, &retval, 5, args); + + zval_ptr_dtor(&retval); + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[1]); + zval_ptr_dtor(&args[3]); + zval_ptr_dtor(&args[4]); + } + } +} + +/* Helper to create error entry */ +static php_stream_error_entry *php_stream_create_error_entry(zend_string *message, int code, + const char *wrapper_name, const char *docref, char *param, int severity, bool terminal, + bool persistent) +{ + if (persistent) { + message = zend_string_dup(message, true); + } else { + zend_string_addref(message); + } + + php_stream_error_entry *entry = pemalloc(sizeof(php_stream_error_entry), persistent); + entry->message = message; + entry->code = code; + entry->wrapper_name = wrapper_name ? pestrdup(wrapper_name, persistent) : NULL; + entry->docref = docref ? pestrdup(docref, persistent) : NULL; + entry->param = param; + entry->severity = severity; + entry->terminal = terminal; + entry->persistent = persistent; + + return entry; +} + +/* Common storage function*/ +static void php_stream_store_error_common(php_stream_context *context, php_stream *stream, + zend_string *message, const char *docref, int code, const char *wrapper_name, char *param, + int severity, bool terminal) +{ + int error_mode = php_stream_get_error_mode(context); + int store_mode = php_stream_get_error_store_mode(context, error_mode); + + if (!php_stream_should_store_error(store_mode, terminal)) { + efree(param); + return; + } + + zend_llist *list; + bool persistent = false; + if (stream) { + persistent = stream->is_persistent; + /* Store in stream's error list */ + if (!stream->error_list) { + stream->error_list = pemalloc(sizeof(zend_llist), persistent); + zend_llist_init(stream->error_list, sizeof(php_stream_error_entry *), + php_stream_error_entry_dtor, persistent); + } + list = stream->error_list; + } else { + /* Store in FG(stream_errors) for wrapper errors */ + if (!FG(wrapper_stored_errors)) { + ALLOC_HASHTABLE(FG(wrapper_stored_errors)); + zend_hash_init(FG(wrapper_stored_errors), 8, NULL, php_stream_error_list_dtor, 0); + list = NULL; + } else { + list = zend_hash_str_find_ptr( + FG(wrapper_stored_errors), wrapper_name, strlen(wrapper_name)); + } + + if (!list) { + zend_llist new_list; + zend_llist_init( + &new_list, sizeof(php_stream_error_entry *), php_stream_error_entry_dtor, 0); + list = zend_hash_str_update_mem(FG(wrapper_stored_errors), wrapper_name, + strlen(wrapper_name), &new_list, sizeof(new_list)); + } + } + + php_stream_error_entry *entry = php_stream_create_error_entry( + message, code, wrapper_name, docref, param, severity, terminal, persistent); + + zend_llist_add_element(list, &entry); +} + +/* Wrapper error reporting - stores in FG(wrapper_stored_errors) */ +static void php_stream_wrapper_error_internal_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, char *param, const char *fmt, va_list args) +{ + zend_string *message = vstrpprintf(0, fmt, args); + + php_stream_process_error(context, wrapper_name, NULL, docref, code, ZSTR_VAL(message), param, + severity, terminal); + + php_stream_store_error_common( + context, NULL, message, docref, code, wrapper_name, param, severity, terminal); + + zend_string_release(message); +} + +static void php_stream_wrapper_error_internal(php_stream_wrapper *wrapper, + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, char *param, const char *fmt, va_list args) +{ + const char *wrapper_name = PHP_STREAM_ERRORS_WRAPPER_NAME(wrapper); + + php_stream_wrapper_error_internal_with_name( + wrapper_name, context, docref, options, severity, terminal, code, param, fmt, args); +} + +PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, const char *fmt, ...) +{ + if (options & REPORT_ERRORS) { + va_list args; + va_start(args, fmt); + php_stream_wrapper_error_internal_with_name( + wrapper_name, context, docref, options, severity, terminal, code, NULL, fmt, args); + va_end(args); + } +} + +PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminal, int code, const char *fmt, + ...) +{ + if (options & REPORT_ERRORS) { + va_list args; + va_start(args, fmt); + php_stream_wrapper_error_internal( + wrapper, context, docref, options, severity, terminal, code, NULL, fmt, args); + va_end(args); + } +} + +PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminal, int code, const char *param, + const char *fmt, ...) +{ + if (options & REPORT_ERRORS) { + va_list args; + va_start(args, fmt); + char *param_copy = param ? estrdup(param) : NULL; + php_stream_wrapper_error_internal( + wrapper, context, docref, options, severity, terminal, code, param_copy, fmt, args); + va_end(args); + } +} + +PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, const char *param1, const char *param2, const char *fmt, ...) +{ + if (options & REPORT_ERRORS) { + char *combined_param; + spprintf(&combined_param, 0, "%s,%s", param1, param2); + + va_list args; + va_start(args, fmt); + php_stream_wrapper_error_internal(wrapper, context, docref, options, severity, terminal, + code, combined_param, fmt, args); + va_end(args); + } +} + +/* Wrapper error logging - stores in FG(wrapper_logged_errors) */ + +static void php_stream_wrapper_log_store_error(const php_stream_wrapper *wrapper, + zend_string *message, int code, const char *param, int severity, bool terminal) +{ + char *param_copy = param ? estrdup(param) : NULL; + php_stream_error_entry *entry = php_stream_create_error_entry( + message, code, NULL, NULL, param_copy, severity, terminal, false); + + if (!FG(wrapper_logged_errors)) { + ALLOC_HASHTABLE(FG(wrapper_logged_errors)); + zend_hash_init(FG(wrapper_logged_errors), 8, NULL, php_stream_error_list_dtor, 0); + } + + zend_llist *list = zend_hash_str_find_ptr( + FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); + + if (!list) { + zend_llist new_list; + zend_llist_init( + &new_list, sizeof(php_stream_error_entry *), php_stream_error_entry_dtor, 0); + list = zend_hash_str_update_mem(FG(wrapper_logged_errors), (const char *) &wrapper, + sizeof(wrapper), &new_list, sizeof(new_list)); + } + + zend_llist_add_element(list, &entry); +} + +static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + char *param, const char *fmt, va_list args) +{ + zend_string *message = vstrpprintf(0, fmt, args); + const char *wrapper_name = PHP_STREAM_ERRORS_WRAPPER_NAME(wrapper); + + if (options & REPORT_ERRORS) { + /* Report immediately using standard error functions */ + php_stream_wrapper_error_internal_with_name( + wrapper_name, context, NULL, options, severity, terminal, code, param, fmt, args); + } else { + /* Store for later display in FG(wrapper_logged_errors) */ + php_stream_wrapper_log_store_error(wrapper, message, code, param, severity, terminal); + } + zend_string_release(message); +} + +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + php_stream_wrapper_log_error_internal( + wrapper, context, options, severity, terminal, code, NULL, fmt, args); + va_end(args); +} + +PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *param, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + char *param_copy = param ? estrdup(param) : NULL; + php_stream_wrapper_log_error_internal( + wrapper, context, options, severity, terminal, code, param_copy, fmt, args); + va_end(args); +} + +static zend_llist *php_stream_get_wrapper_errors_list(php_stream_wrapper *wrapper) +{ + if (!FG(wrapper_logged_errors)) { + return NULL; + } else { + return (zend_llist *) zend_hash_str_find_ptr( + FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); + } +} + +void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, php_stream_context *context, + int code, const char *path, const char *caption) +{ + char *msg; + char errstr[256]; + int free_msg = 0; + + if (EG(exception)) { + /* Don't emit additional warnings if an exception has already been thrown. */ + return; + } + + char *tmp = estrdup(path); + if (wrapper) { + zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper); + if (err_list) { + size_t l = 0; + int brlen; + int i; + int count = (int) zend_llist_count(err_list); + const char *br; + php_stream_error_entry **err_entry_p; + zend_llist_position pos; + + if (PG(html_errors)) { + brlen = 7; + br = "
\n"; + } else { + brlen = 1; + br = "\n"; + } + + for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p; + err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) { + l += ZSTR_LEN((*err_entry_p)->message); + if (i < count - 1) { + l += brlen; + } + } + msg = emalloc(l + 1); + msg[0] = '\0'; + for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p; + err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) { + strcat(msg, ZSTR_VAL((*err_entry_p)->message)); + if (i < count - 1) { + strcat(msg, br); + } + } + + free_msg = 1; + } else { + if (wrapper == &php_plain_files_wrapper) { + msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); + } else { + msg = "operation failed"; + } + } + } else { + msg = "no suitable wrapper could be found"; + } + + php_strip_url_passwd(tmp); + php_stream_wrapper_warn_param( + wrapper, context, REPORT_ERRORS, code, tmp, "%s: %s", caption, msg); + efree(tmp); + if (free_msg) { + efree(msg); + } +} + +void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) +{ + if (wrapper && FG(wrapper_logged_errors)) { + zend_hash_str_del(FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); + } +} + +/* Stream error reporting */ + +PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severity, bool terminal, + int code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + php_stream_wrapper *wrapper = stream->wrapper; + const char *wrapper_name = wrapper ? wrapper->wops->label : "stream"; + + php_stream_context *context = PHP_STREAM_CONTEXT(stream); + + /* Process the error - always report for stream errors */ + php_stream_process_error(context, wrapper_name, stream, docref, code, ZSTR_VAL(message), NULL, + severity, terminal); + + /* Store error */ + php_stream_store_error_common( + context, stream, message, docref, code, wrapper_name, NULL, severity, terminal); + + zend_string_release(message); +} + +/* StreamException class and error constants registration */ + +PHP_MINIT_FUNCTION(stream_errors) +{ + register_stream_errors_symbols(module_number); + + php_ce_stream_exception = register_class_StreamException(zend_ce_exception); + + return SUCCESS; +} + +PHP_METHOD(StreamException, getParam) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zval *param = zend_read_property( + php_ce_stream_exception, Z_OBJ_P(getThis()), ZEND_STRL("param"), 1, NULL); + RETURN_COPY(param); +} + +PHP_METHOD(StreamException, getWrapperName) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zval *wrapper_name = zend_read_property( + php_ce_stream_exception, Z_OBJ_P(getThis()), ZEND_STRL("wrapperName"), 1, NULL); + RETURN_COPY(wrapper_name); +} diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php new file mode 100644 index 0000000000000..fb6c5584a5f48 --- /dev/null +++ b/main/streams/stream_errors.stub.php @@ -0,0 +1,435 @@ +\n"; - } else { - brlen = 1; - br = "\n"; - } - - for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0; - err_buf_p; - err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) { - l += strlen(*err_buf_p); - if (i < count - 1) { - l += brlen; - } - } - msg = emalloc(l + 1); - msg[0] = '\0'; - for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0; - err_buf_p; - err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) { - strcat(msg, *err_buf_p); - if (i < count - 1) { - strcat(msg, br); - } - } - - free_msg = 1; - } else { - if (wrapper == &php_plain_files_wrapper) { - msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); - } else { - msg = "operation failed"; - } - } - } else { - msg = "no suitable wrapper could be found"; - } - - php_strip_url_passwd(tmp); - php_error_docref1(NULL, tmp, E_WARNING, "%s: %s", caption, msg); - efree(tmp); - if (free_msg) { - efree(msg); - } -} - -static void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) -{ - if (wrapper && FG(wrapper_errors)) { - zend_hash_str_del(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper)); - } -} - -static void wrapper_error_dtor(void *error) -{ - efree(*(char**)error); -} - -static void wrapper_list_dtor(zval *item) { - zend_llist *list = (zend_llist*)Z_PTR_P(item); - zend_llist_destroy(list); - efree(list); -} - -PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, const char *fmt, ...) -{ - va_list args; - char *buffer = NULL; - - va_start(args, fmt); - vspprintf(&buffer, 0, fmt, args); - va_end(args); - - if ((options & REPORT_ERRORS) || wrapper == NULL) { - php_error_docref(NULL, E_WARNING, "%s", buffer); - efree(buffer); - } else { - zend_llist *list = NULL; - if (!FG(wrapper_errors)) { - ALLOC_HASHTABLE(FG(wrapper_errors)); - zend_hash_init(FG(wrapper_errors), 8, NULL, wrapper_list_dtor, 0); - } else { - list = zend_hash_str_find_ptr(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper)); - } - - if (!list) { - zend_llist new_list; - zend_llist_init(&new_list, sizeof(buffer), wrapper_error_dtor, 0); - list = zend_hash_str_update_mem(FG(wrapper_errors), (const char*)&wrapper, - sizeof(wrapper), &new_list, sizeof(new_list)); - } - - /* append to linked list */ - zend_llist_add_element(list, &buffer); - } -} - - /* }}} */ /* allocate a new stream for a particular ops */ @@ -512,6 +377,11 @@ fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remov ZVAL_UNDEF(&stream->wrapperdata); } + if (stream->error_list) { + zend_llist_destroy(stream->error_list); + pefree(stream->error_list, stream->is_persistent); + } + if (stream->readbuf) { pefree(stream->readbuf, stream->is_persistent); stream->readbuf = NULL; @@ -1319,7 +1189,7 @@ PHPAPI ssize_t _php_stream_write(php_stream *stream, const char *buf, size_t cou ZEND_ASSERT(buf != NULL); if (stream->ops->write == NULL) { - php_error_docref(NULL, E_NOTICE, "Stream is not writable"); + php_stream_notice(stream, STREAM_ERROR_CODE_NOT_WRITABLE, "Stream is not writable"); return (ssize_t) -1; } @@ -1453,7 +1323,8 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) return 0; } - php_error_docref(NULL, E_WARNING, "Stream does not support seeking"); + php_stream_warn(stream, STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED, + "Stream does not support seeking"); return -1; } @@ -1877,10 +1748,16 @@ void php_shutdown_stream_hashes(void) FG(stream_filters) = NULL; } - if (FG(wrapper_errors)) { - zend_hash_destroy(FG(wrapper_errors)); - efree(FG(wrapper_errors)); - FG(wrapper_errors) = NULL; + if (FG(wrapper_logged_errors)) { + zend_hash_destroy(FG(wrapper_logged_errors)); + efree(FG(wrapper_logged_errors)); + FG(wrapper_logged_errors) = NULL; + } + + if (FG(wrapper_stored_errors)) { + zend_hash_destroy(FG(wrapper_stored_errors)); + efree(FG(wrapper_stored_errors)); + FG(wrapper_stored_errors) = NULL; } } @@ -2048,7 +1925,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const if (!localhost && path[n+3] != '\0' && path[n+3] != '/') { #endif if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Remote host file access not supported, %s", path); + php_stream_wrapper_warn(plain_files_wrapper, NULL, options, + STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "Remote host file access not supported, %s", path); } return NULL; } @@ -2087,7 +1966,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const } if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "file:// wrapper is disabled in the server configuration"); + php_stream_wrapper_warn(plain_files_wrapper, NULL, options, + STREAM_ERROR_CODE_DISABLED, + "file:// wrapper is disabled in the server configuration"); } return NULL; } @@ -2185,10 +2066,12 @@ PHPAPI php_stream *_php_stream_opendir(const char *path, int options, stream->flags |= PHP_STREAM_FLAG_NO_BUFFER | PHP_STREAM_FLAG_IS_DIR; } } else if (wrapper) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, "not implemented"); + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + STREAM_ERROR_CODE_NO_OPENER, "not implemented"); } if (stream == NULL && (options & REPORT_ERRORS)) { - php_stream_display_wrapper_errors(wrapper, path, "Failed to open directory"); + php_stream_display_wrapper_errors(wrapper, context, STREAM_ERROR_CODE_OPEN_FAILED, path, + "Failed to open directory"); } php_stream_tidy_wrapper_error_log(wrapper); @@ -2255,7 +2138,13 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options); if ((options & STREAM_USE_URL) && (!wrapper || !wrapper->is_url)) { - php_error_docref(NULL, E_WARNING, "This function may only be used against URLs"); + if (wrapper) { + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "This function may only be used against URLs"); + } else { + php_error_docref(NULL, E_WARNING, "This function may only be used against URLs"); + } if (resolved_path) { zend_string_release_ex(resolved_path, 0); } @@ -2264,7 +2153,8 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (wrapper) { if (!wrapper->wops->stream_opener) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + STREAM_ERROR_CODE_NO_OPENER, "wrapper does not support stream open"); } else { stream = wrapper->wops->stream_opener(wrapper, @@ -2275,7 +2165,8 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod /* if the caller asked for a persistent stream but the wrapper did not * return one, force an error here */ if (stream && persistent && !stream->is_persistent) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + STREAM_ERROR_CODE_PERSISTENT_NOT_SUPPORTED, "wrapper does not support persistent streams"); php_stream_close(stream); stream = NULL; @@ -2299,6 +2190,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; #endif + if (stream->ctx == NULL && context != NULL && !persistent) { + php_stream_context_set(stream, context); + } } if (stream != NULL && (options & STREAM_MUST_SEEK)) { @@ -2327,8 +2221,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (options & REPORT_ERRORS) { char *tmp = estrdup(path); php_strip_url_passwd(tmp); - php_error_docref1(NULL, tmp, E_WARNING, "could not make seekable - %s", - tmp); + php_stream_wrapper_warn_param(wrapper, context, options, + STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED, tmp, + "could not make seekable - %s", tmp); efree(tmp); options &= ~REPORT_ERRORS; @@ -2346,7 +2241,8 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod } if (stream == NULL && (options & REPORT_ERRORS)) { - php_stream_display_wrapper_errors(wrapper, path, "Failed to open stream"); + php_stream_display_wrapper_errors(wrapper, context, STREAM_ERROR_CODE_OPEN_FAILED, path, + "Failed to open stream"); if (opened_path && *opened_path) { zend_string_release_ex(*opened_path, 0); *opened_path = NULL; diff --git a/main/streams/transports.c b/main/streams/transports.c index 83297d9a06ceb..ceab9a083de82 100644 --- a/main/streams/transports.c +++ b/main/streams/transports.c @@ -39,13 +39,13 @@ PHPAPI int php_stream_xport_unregister(const char *protocol) return zend_hash_str_del(&xport_hash, protocol, strlen(protocol)); } -#define ERR_REPORT(out_err, fmt, arg) \ +#define ERR_REPORT(code, out_err, fmt, arg) \ if (out_err) { *out_err = strpprintf(0, fmt, arg); } \ - else { php_error_docref(NULL, E_WARNING, fmt, arg); } + else { php_stream_wrapper_warn(NULL, NULL, REPORT_ERRORS, code, fmt, arg); } -#define ERR_RETURN(out_err, local_err, fmt) \ +#define ERR_RETURN(code, out_err, local_err, fmt) \ if (out_err) { *out_err = local_err; } \ - else { php_error_docref(NULL, E_WARNING, fmt, local_err ? ZSTR_VAL(local_err) : "Unspecified error"); \ + else { php_stream_wrapper_warn(NULL, NULL, REPORT_ERRORS, code, fmt, local_err ? ZSTR_VAL(local_err) : "Unspecified error"); \ if (local_err) { zend_string_release_ex(local_err, 0); local_err = NULL; } \ } @@ -116,7 +116,8 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in n = sizeof(wrapper_name) - 1; PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n); - ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?", + ERR_REPORT(STREAM_ERROR_CODE_WRAPPER_NOT_FOUND, error_string, + "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name); return NULL; @@ -125,7 +126,8 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in if (factory == NULL) { /* should never happen */ - php_error_docref(NULL, E_WARNING, "Could not find a factory !?"); + php_stream_wrapper_warn(NULL, context, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_NOT_FOUND, "Could not find a factory !?"); return NULL; } @@ -146,7 +148,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0, timeout, &error_text, error_code)) { - ERR_RETURN(error_string, error_text, "connect() failed: %s"); + ERR_RETURN(STREAM_ERROR_CODE_CONNECT_FAILED, error_string, error_text, "connect() failed: %s"); failed = true; } @@ -156,7 +158,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in /* server */ if (flags & STREAM_XPORT_BIND) { if (0 != php_stream_xport_bind(stream, name, namelen, &error_text)) { - ERR_RETURN(error_string, error_text, "bind() failed: %s"); + ERR_RETURN(STREAM_ERROR_CODE_BIND_FAILED, error_string, error_text, "bind() failed: %s"); failed = true; } else if (flags & STREAM_XPORT_LISTEN) { zval *zbacklog = NULL; @@ -167,7 +169,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in } if (0 != php_stream_xport_listen(stream, backlog, &error_text)) { - ERR_RETURN(error_string, error_text, "listen() failed: %s"); + ERR_RETURN(STREAM_ERROR_CODE_LISTEN_FAILED, error_string, error_text, "listen() failed: %s"); failed = true; } } @@ -370,7 +372,8 @@ PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_cr return param.outputs.returncode; } - php_error_docref("streams.crypto", E_WARNING, "This stream does not support SSL/crypto"); + php_stream_warn_docref(stream, "streams.crypto", STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "This stream does not support SSL/crypto"); return ret; } @@ -390,7 +393,8 @@ PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate) return param.outputs.returncode; } - php_error_docref("streams.crypto", E_WARNING, "This stream does not support SSL/crypto"); + php_stream_warn_docref(stream, "streams.crypto", STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "This stream does not support SSL/crypto"); return ret; } @@ -412,7 +416,8 @@ PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t bufle } if (stream->readfilters.head) { - php_error_docref(NULL, E_WARNING, "Cannot peek or fetch OOB data from a filtered stream"); + php_stream_warn(stream, STREAM_ERROR_CODE_FILTER_FAILED, + "Cannot peek or fetch OOB data from a filtered stream"); return -1; } @@ -482,7 +487,8 @@ PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t b oob = (flags & STREAM_OOB) == STREAM_OOB; if ((oob || addr) && stream->writefilters.head) { - php_error_docref(NULL, E_WARNING, "Cannot write OOB data, or data to a targeted address on a filtered stream"); + php_stream_warn(stream, STREAM_ERROR_CODE_FILTER_FAILED, + "Cannot write OOB data, or data to a targeted address on a filtered stream"); return -1; } diff --git a/main/streams/userspace.c b/main/streams/userspace.c index f5e25aa96c772..4ba3bed83d3be 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -293,7 +293,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * /* Try to catch bad usage without preventing flexibility */ if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { - php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_RECURSION_DETECTED, "infinite recursion prevented"); return NULL; } FG(user_stream_current_filename) = filename; @@ -334,8 +335,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" is not implemented", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options,STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "\"%s::" USERSTREAM_OPEN "\" is not implemented", ZSTR_VAL(us->wrapper->ce->name)); zval_ptr_dtor(&args[3]); goto end; } @@ -357,8 +358,9 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * /* set wrapper data to be a reference to our object */ ZVAL_COPY(&stream->wrapperdata, &us->object); } else { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" call failed", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_USERSPACE_CALL_FAILED, + "\"%s::" USERSTREAM_OPEN "\" call failed", ZSTR_VAL(us->wrapper->ce->name)); } zval_ptr_dtor(&zretval); @@ -394,7 +396,8 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char /* Try to catch bad usage without preventing flexibility */ if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { - php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_RECURSION_DETECTED, "infinite recursion prevented"); return NULL; } FG(user_stream_current_filename) = filename; @@ -419,8 +422,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" is not implemented", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "\"%s::" USERSTREAM_DIR_OPEN "\" is not implemented", + ZSTR_VAL(us->wrapper->ce->name)); goto end; } /* Exception occurred in call */ @@ -435,8 +439,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char /* set wrapper data to be a reference to our object */ ZVAL_COPY(&stream->wrapperdata, &us->object); } else { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_USERSPACE_CALL_FAILED, + "\"%s::" USERSTREAM_DIR_OPEN "\" call failed", ZSTR_VAL(us->wrapper->ce->name)); } zval_ptr_dtor(&zretval); @@ -479,10 +484,15 @@ PHP_FUNCTION(stream_wrapper_register) /* We failed. But why? */ if (zend_hash_exists(php_stream_get_url_stream_wrappers_hash(), protocol)) { - php_error_docref(NULL, E_WARNING, "Protocol %s:// is already defined.", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(&uwrap->wrapper, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED, + "Protocol %s:// is already defined.", ZSTR_VAL(protocol)); } else { /* Hash doesn't exist so it must have been an invalid protocol scheme */ - php_error_docref(NULL, E_WARNING, "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", ZSTR_VAL(uwrap->ce->name), ZSTR_VAL(protocol)); + php_stream_wrapper_warn(&uwrap->wrapper, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED, + "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", + ZSTR_VAL(uwrap->ce->name), ZSTR_VAL(protocol)); } zend_list_delete(rsrc); @@ -502,7 +512,9 @@ PHP_FUNCTION(stream_wrapper_unregister) php_stream_wrapper *wrapper = zend_hash_find_ptr(php_stream_get_url_stream_wrappers_hash(), protocol); if (php_unregister_url_stream_wrapper_volatile(protocol) == FAILURE) { /* We failed */ - php_error_docref(NULL, E_WARNING, "Unable to unregister protocol %s://", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(wrapper, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_UNREGISTRATION_FAILED, + "Unable to unregister protocol %s://", ZSTR_VAL(protocol)); RETURN_FALSE; } @@ -530,13 +542,17 @@ PHP_FUNCTION(stream_wrapper_restore) global_wrapper_hash = php_stream_get_url_stream_wrappers_hash_global(); if ((wrapper = zend_hash_find_ptr(global_wrapper_hash, protocol)) == NULL) { - php_error_docref(NULL, E_WARNING, "%s:// never existed, nothing to restore", ZSTR_VAL(protocol)); + php_stream_wrapper_warn_name(user_stream_wops.label, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_NOT_FOUND, + "%s:// never existed, nothing to restore", ZSTR_VAL(protocol)); RETURN_FALSE; } wrapper_hash = php_stream_get_url_stream_wrappers_hash(); if (wrapper_hash == global_wrapper_hash || zend_hash_find_ptr(wrapper_hash, protocol) == wrapper) { - php_error_docref(NULL, E_NOTICE, "%s:// was never changed, nothing to restore", ZSTR_VAL(protocol)); + php_stream_wrapper_notice(wrapper, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED, + "%s:// was never changed, nothing to restore", ZSTR_VAL(protocol)); RETURN_TRUE; } @@ -544,7 +560,9 @@ PHP_FUNCTION(stream_wrapper_restore) php_unregister_url_stream_wrapper_volatile(protocol); if (php_register_url_stream_wrapper_volatile(protocol, wrapper) == FAILURE) { - php_error_docref(NULL, E_WARNING, "Unable to restore original %s:// wrapper", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(wrapper, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED, + "Unable to restore original %s:// wrapper", ZSTR_VAL(protocol)); RETURN_FALSE; } @@ -572,8 +590,8 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_WRITE " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } stream->flags &= ~PHP_STREAM_FLAG_NO_FCLOSE; @@ -593,7 +611,9 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ /* don't allow strange buffer overruns due to bogus return */ if (didwrite > 0 && didwrite > count) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)", + php_stream_warn_nt(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" + ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)", ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didwrite - count), (zend_long)didwrite, (zend_long)count); didwrite = count; @@ -624,8 +644,8 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count } if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_READ " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); goto err; } @@ -641,8 +661,12 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count didread = Z_STRLEN(retval); if (didread > 0) { if (didread > count) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " read, " ZEND_LONG_FMT " max) - excess data will be lost", - ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didread - count), (zend_long)didread, (zend_long)count); + php_stream_warn_nt(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT + " bytes more data than requested (" ZEND_LONG_FMT " read, " + ZEND_LONG_FMT " max) - excess data will be lost", + ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didread - count), + (zend_long)didread, (zend_long)count); didread = count; } memcpy(buf, Z_STRVAL(retval), didread); @@ -658,7 +682,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); stream->eof = 1; @@ -774,7 +798,8 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when *newoffs = Z_LVAL(retval); ret = 0; } else if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_TELL " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); ret = -1; } else { ret = -1; @@ -838,8 +863,8 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_STAT " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -857,7 +882,7 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) return ret; } -static int user_stream_set_check_liveliness(const php_userstream_data_t *us) +static int user_stream_set_check_liveliness(php_stream *stream, const php_userstream_data_t *us) { zval retval; @@ -866,7 +891,7 @@ static int user_stream_set_check_liveliness(const php_userstream_data_t *us) zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -877,15 +902,15 @@ static int user_stream_set_check_liveliness(const php_userstream_data_t *us) if (EXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) { return Z_TYPE(retval) == IS_TRUE ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; } else { - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_EOF " value must be of type bool, %s given", + php_stream_warn(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%s::" USERSTREAM_EOF " value must be of type bool, %s given", ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval)); zval_ptr_dtor(&retval); return PHP_STREAM_OPTION_RETURN_ERR; } } -static int user_stream_set_locking(const php_userstream_data_t *us, int value) +static int user_stream_set_locking(php_stream *stream, const php_userstream_data_t *us, int value) { zval retval; zval zlock; @@ -920,9 +945,8 @@ static int user_stream_set_locking(const php_userstream_data_t *us, int value) /* lock support test (TODO: more check) */ return PHP_STREAM_OPTION_RETURN_OK; } - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_LOCK " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_LOCK " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -934,14 +958,15 @@ static int user_stream_set_locking(const php_userstream_data_t *us, int value) } // TODO: ext/standard/tests/file/userstreams_004.phpt returns null implicitly for function // Should this warn or not? And should this be considered an error? - //php_error_docref(NULL, E_WARNING, - // "%s::" USERSTREAM_LOCK " value must be of type bool, %s given", + //php_stream_warn(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + // "%s::" USERSTREAM_LOCK " value must be of type bool, %s given", // ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval)); zval_ptr_dtor(&retval); return PHP_STREAM_OPTION_RETURN_NOTIMPL; } -static int user_stream_set_truncation(const php_userstream_data_t *us, int value, void *ptrparam) { +static int user_stream_set_truncation(php_stream *stream, const php_userstream_data_t *us, + int value, void *ptrparam) { zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_TRUNCATE, false); if (value == PHP_STREAM_TRUNCATE_SUPPORTED) { @@ -969,9 +994,8 @@ static int user_stream_set_truncation(const php_userstream_data_t *us, int value zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_TRUNCATE " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_TRUNCATE " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -980,15 +1004,16 @@ static int user_stream_set_truncation(const php_userstream_data_t *us, int value if (EXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) { return Z_TYPE(retval) == IS_TRUE ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; } else { - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_TRUNCATE " value must be of type bool, %s given", + php_stream_warn(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%s::" USERSTREAM_TRUNCATE " value must be of type bool, %s given", ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval)); zval_ptr_dtor(&retval); return PHP_STREAM_OPTION_RETURN_ERR; } } -static int user_stream_set_option(const php_userstream_data_t *us, int option, int value, void *ptrparam) +static int user_stream_set_option(php_stream *stream, const php_userstream_data_t *us, int option, + int value, void *ptrparam) { zval args[3]; ZVAL_LONG(&args[0], option); @@ -1013,7 +1038,7 @@ static int user_stream_set_option(const php_userstream_data_t *us, int option, i zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_SET_OPTION " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -1038,19 +1063,19 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value switch (option) { case PHP_STREAM_OPTION_CHECK_LIVENESS: - return user_stream_set_check_liveliness(us); + return user_stream_set_check_liveliness(stream, us); case PHP_STREAM_OPTION_LOCKING: - return user_stream_set_locking(us, value); + return user_stream_set_locking(stream, us, value); case PHP_STREAM_OPTION_TRUNCATE_API: - return user_stream_set_truncation(us, value, ptrparam); + return user_stream_set_truncation(stream, us, value, ptrparam); case PHP_STREAM_OPTION_READ_BUFFER: case PHP_STREAM_OPTION_WRITE_BUFFER: case PHP_STREAM_OPTION_READ_TIMEOUT: case PHP_STREAM_OPTION_BLOCKING: - return user_stream_set_option(us, option, value, ptrparam); + return user_stream_set_option(stream, us, option, value, ptrparam); default: return PHP_STREAM_OPTION_RETURN_NOTIMPL; @@ -1083,7 +1108,8 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_UNLINK " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1121,7 +1147,8 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_RENAME " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1159,7 +1186,8 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_MKDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_MKDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1196,7 +1224,8 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_RMDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1235,7 +1264,9 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i ZVAL_STRING(&args[2], value); break; default: - php_error_docref(NULL, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_INVALID_META, + "Unknown option %d for " USERSTREAM_METADATA, option); return ret; } @@ -1258,7 +1289,8 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_METADATA " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_METADATA " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1296,8 +1328,8 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!", - ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_STATURL " is not implemented!", ZSTR_VAL(uwrap->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(zretval))) { @@ -1332,8 +1364,9 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_DIR_READ " is not implemented!", + ZSTR_VAL(us->wrapper->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -1418,7 +1451,8 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) if (UNEXPECTED(call_result == FAILURE)) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!", + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_CAST " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } goto out; @@ -1432,14 +1466,16 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) php_stream_from_zval_no_verify(intstream, &retval); if (!intstream) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource", + php_stream_warn(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%s::" USERSTREAM_CAST " must return a stream resource", ZSTR_VAL(us->wrapper->ce->name)); } break; } if (intstream == stream) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself", + php_stream_warn(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%s::" USERSTREAM_CAST " must not return itself", ZSTR_VAL(us->wrapper->ce->name)); } intstream = NULL; diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index db35a9b7952c8..4d4428442a867 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -106,9 +106,8 @@ static ssize_t php_sockop_write(php_stream *stream, const char *buf, size_t coun if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { estr = php_socket_strerror(err, NULL, 0); - php_error_docref(NULL, E_NOTICE, - "Send of %zu bytes failed with errno=%d %s", - count, err, estr); + php_stream_warn(stream, STREAM_ERROR_CODE_NETWORK_SEND_FAILED, + "Send of %zu bytes failed with errno=%d %s", count, err, estr); efree(estr); } } @@ -444,8 +443,7 @@ static int php_sockop_set_option(php_stream *stream, int option, int value, void xparam->inputs.addrlen); if (xparam->outputs.returncode == -1) { char *err = php_socket_strerror(php_socket_errno(), NULL, 0); - php_error_docref(NULL, E_WARNING, - "%s\n", err); + php_stream_warn(stream, STREAM_ERROR_CODE_NETWORK_SEND_FAILED, "%s", err); efree(err); } return PHP_STREAM_OPTION_RETURN_OK; @@ -585,7 +583,8 @@ static const php_stream_ops php_stream_unixdg_socket_ops = { /* network socket operations */ #ifdef AF_UNIX -static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr) +static inline int parse_unix_address(php_stream *stream, php_stream_xport_param *xparam, + struct sockaddr_un *unix_addr) { memset(unix_addr, 0, sizeof(*unix_addr)); unix_addr->sun_family = AF_UNIX; @@ -604,9 +603,9 @@ static inline int parse_unix_address(php_stream_xport_param *xparam, struct sock * BUT, to get into this branch of code, the name is too long, * so we don't care. */ xparam->inputs.namelen = max_length; - php_error_docref(NULL, E_NOTICE, - "socket path exceeded the maximum allowed length of %lu bytes " - "and was truncated", max_length); + php_stream_notice(stream, STREAM_ERROR_CODE_INVALID_PATH, + "socket path exceeded the maximum allowed length of %lu bytes and was truncated", + max_length); } memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen); @@ -686,7 +685,7 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t * return -1; } - parse_unix_address(xparam, &unix_addr); + parse_unix_address(stream, xparam, &unix_addr); return bind(sock->socket, (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen); @@ -775,7 +774,7 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_ return -1; } - parse_unix_address(xparam, &unix_addr); + parse_unix_address(stream, xparam, &unix_addr); ret = php_network_connect_socket(sock->socket, (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen, diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 403f0aa6efbfe..6e02ae5c07341 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -298,7 +298,7 @@ AC_DEFINE('HAVE_STRNLEN', 1); AC_DEFINE('ZEND_CHECK_STACK_LIMIT', 1) -ADD_SOURCES("main/streams", "streams.c cast.c memory.c filter.c plain_wrapper.c \ +ADD_SOURCES("main/streams", "streams.c stream_errors.c cast.c memory.c filter.c plain_wrapper.c \ userspace.c transports.c xp_socket.c mmap.c glob_wrapper.c"); ADD_FLAG("CFLAGS_BD_MAIN_STREAMS", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");