mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00

This implements an SVG handler using the libxml reader API. This does not parse the entire document but instead uses a pull parser to locate the root element, check whether it's an svg root, do some extra sanity checks on the attribute, and fill in the php_gfxinfo structure.
178 lines
4.8 KiB
C
178 lines
4.8 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| 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: Niels Dossche <nielsdos@php.net> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "php.h"
|
|
#include "image_svg.h"
|
|
#include "php_libxml.h"
|
|
|
|
#include "ext/standard/php_image.h"
|
|
|
|
#include <libxml/xmlreader.h>
|
|
|
|
#ifdef HAVE_LIBXML
|
|
|
|
static int svg_image_type_id;
|
|
|
|
static int php_libxml_svg_stream_read(void *context, char *buffer, int len)
|
|
{
|
|
return php_stream_read(context, buffer, len);
|
|
}
|
|
|
|
/* Sanity check that the input only contains characters valid for a dimension (numbers with units, e.g. 5cm).
|
|
* This also protects the user against injecting XSS.
|
|
* Only accept [0-9]+[a-zA-Z]* */
|
|
static bool php_libxml_parse_dimension(const xmlChar *input, const xmlChar **unit_position)
|
|
{
|
|
if (!(*input >= '0' && *input <= '9')) {
|
|
return false;
|
|
}
|
|
|
|
input++;
|
|
|
|
while (*input) {
|
|
if (!(*input >= '0' && *input <= '9')) {
|
|
if ((*input >= 'a' && *input <= 'z') || (*input >= 'A' && *input <= 'Z')) {
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
input++;
|
|
}
|
|
|
|
*unit_position = input;
|
|
|
|
while (*input) {
|
|
if (!((*input >= 'a' && *input <= 'z') || (*input >= 'A' && *input <= 'Z'))) {
|
|
return false;
|
|
}
|
|
input++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
zend_result php_libxml_svg_image_handle(php_stream *stream, struct php_gfxinfo **result)
|
|
{
|
|
if (php_stream_rewind(stream)) {
|
|
return FAILURE;
|
|
}
|
|
|
|
/* Early check before doing more expensive work */
|
|
if (php_stream_getc(stream) != '<') {
|
|
return FAILURE;
|
|
}
|
|
|
|
if (php_stream_rewind(stream)) {
|
|
return FAILURE;
|
|
}
|
|
|
|
PHP_LIBXML_SANITIZE_GLOBALS(reader_for_stream);
|
|
xmlTextReaderPtr reader = xmlReaderForIO(
|
|
php_libxml_svg_stream_read,
|
|
NULL,
|
|
stream,
|
|
NULL,
|
|
NULL,
|
|
XML_PARSE_NOWARNING | XML_PARSE_NOERROR | XML_PARSE_NONET
|
|
);
|
|
PHP_LIBXML_RESTORE_GLOBALS(reader_for_stream);
|
|
|
|
if (!reader) {
|
|
return FAILURE;
|
|
}
|
|
|
|
bool is_svg = false;
|
|
while (xmlTextReaderRead(reader) == 1) {
|
|
if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
|
|
/* Root must be an svg element */
|
|
const xmlChar *name = xmlTextReaderConstLocalName(reader);
|
|
if (!name || strcasecmp((const char *) name, "svg") != 0) {
|
|
break;
|
|
}
|
|
|
|
xmlChar *width = xmlTextReaderGetAttribute(reader, BAD_CAST "width");
|
|
xmlChar *height = xmlTextReaderGetAttribute(reader, BAD_CAST "height");
|
|
const xmlChar *width_unit_position, *height_unit_position;
|
|
if (!width || !height
|
|
|| !php_libxml_parse_dimension(width, &width_unit_position)
|
|
|| !php_libxml_parse_dimension(height, &height_unit_position)) {
|
|
xmlFree(width);
|
|
xmlFree(height);
|
|
break;
|
|
}
|
|
|
|
is_svg = true;
|
|
if (result) {
|
|
*result = ecalloc(1, sizeof(**result));
|
|
(*result)->width = ZEND_STRTOL((const char *) width, NULL, 10);
|
|
(*result)->height = ZEND_STRTOL((const char *) height, NULL, 10);
|
|
if (*width_unit_position) {
|
|
(*result)->width_unit = zend_string_init((const char*) width_unit_position,
|
|
xmlStrlen(width_unit_position), false);
|
|
}
|
|
if (*height_unit_position) {
|
|
(*result)->height_unit = zend_string_init((const char*) height_unit_position,
|
|
xmlStrlen(height_unit_position), false);
|
|
}
|
|
}
|
|
|
|
xmlFree(width);
|
|
xmlFree(height);
|
|
break;
|
|
}
|
|
}
|
|
|
|
xmlFreeTextReader(reader);
|
|
|
|
return is_svg ? SUCCESS : FAILURE;
|
|
}
|
|
|
|
zend_result php_libxml_svg_image_identify(php_stream *stream)
|
|
{
|
|
return php_libxml_svg_image_handle(stream, NULL);
|
|
}
|
|
|
|
struct php_gfxinfo *php_libxml_svg_image_get_info(php_stream *stream)
|
|
{
|
|
struct php_gfxinfo *result = NULL;
|
|
zend_result status = php_libxml_svg_image_handle(stream, &result);
|
|
ZEND_ASSERT((status == SUCCESS) == (result != NULL));
|
|
return result;
|
|
}
|
|
|
|
static const struct php_image_handler svg_image_handler = {
|
|
"image/svg+xml",
|
|
".svg",
|
|
PHP_IMAGE_CONST_NAME("SVG"),
|
|
php_libxml_svg_image_identify,
|
|
php_libxml_svg_image_get_info,
|
|
};
|
|
|
|
void php_libxml_register_image_svg_handler(void)
|
|
{
|
|
svg_image_type_id = php_image_register_handler(&svg_image_handler);
|
|
}
|
|
|
|
zend_result php_libxml_unregister_image_svg_handler(void)
|
|
{
|
|
return php_image_unregister_handler(svg_image_type_id);
|
|
}
|
|
|
|
#endif
|