From 31ac172fd00e2028212cd77925d67927038df3b1 Mon Sep 17 00:00:00 2001 From: Valery Ivashchanka Date: Thu, 15 Oct 2026 20:05:38 +0000 Subject: [PATCH] ext/soap: report WSDL and schema errors without a bailout WSDL and XML schema errors were raised with soap_errorN(E_ERROR, ...). SoapClient turned the fatal error into a SoapFault and caught the bailout in SOAP_CLIENT_END_CODE(), but zend_bailout() had already called gc_protect(1), so the cycle collector stayed off for the rest of the request (GH-10026). The WSDL and schema loaders now throw the SoapFault with soap_throw_error() and return an error to their callers, which release what they own on the way out. The message and the fault code ("WSDL") are the same as before. SoapServer and SoapClient with the "exceptions" option disabled keep reporting these errors as fatal errors: the constructor turns the SoapFault back into the same E_ERROR once the loader has cleaned up. Since nothing bails out of the loaders any more, the error paths that existed only to survive a bailout are gone: the zend_try in load_wsdl_ex(), requestify_string() and the copies of targetNamespace in schema_load_file(). delete_type() now also frees an unresolved "ref", which an error before schema_pass2() can leave behind. Errors from encoding and decoding values still bail out and are not covered by this change. Co-Authored-By: Claude Opus 5.5 --- NEWS | 3 + ext/soap/php_schema.c | 723 +++++++++++----- ext/soap/php_schema.h | 5 +- ext/soap/php_sdl.c | 946 ++++++++++++--------- ext/soap/php_soap.h | 2 + ext/soap/soap.c | 64 ++ ext/soap/tests/bugs/gh10026.phpt | 41 + ext/soap/tests/wsdl_errors_no_bailout.phpt | 176 ++++ 8 files changed, 1353 insertions(+), 607 deletions(-) create mode 100644 ext/soap/tests/bugs/gh10026.phpt create mode 100644 ext/soap/tests/wsdl_errors_no_bailout.phpt diff --git a/NEWS b/NEWS index 67c44a4420e9..7eeb4b54f1dd 100644 --- a/NEWS +++ b/NEWS @@ -103,6 +103,9 @@ PHP NEWS - SOAP: . Fixed the SOAP client accepting a response body shorter than its Content-Length and reusing the connection. (Ilia Alshanetsky) + . SoapClient no longer bails out on WSDL and XML schema errors, which kept + the cycle collector disabled for the rest of the request (GH-10026). + (Valery Ivashchanka) - SPL: . Fixed bug GH-23385 (SplDoublyLinkedList::serialize() use-after-free when diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c index 195c5abe45eb..d5bd90390703 100644 --- a/ext/soap/php_schema.c +++ b/ext/soap/php_schema.c @@ -39,7 +39,7 @@ static int schema_restriction_var_int(xmlNodePtr val, sdlRestrictionIntPtr *valp static int schema_restriction_var_char(xmlNodePtr val, sdlRestrictionCharPtr *valptr); -static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type); +static bool schema_type_fixup(sdlCtx *ctx, sdlTypePtr type); static bool node_is_equal_xsd(xmlNodePtr node, const char *name) { @@ -53,7 +53,7 @@ static bool node_is_equal_xsd(xmlNodePtr node, const char *name) return node_is_equal_ex_one_of(node, name, ns); } -static int schema_parse_int(const xmlChar *value, const char *name, bool allow_negative) +static bool schema_parse_int(const xmlChar *value, const char *name, bool allow_negative, int *result) { const char *str = (const char *) value; zend_long lval = 0; @@ -64,15 +64,18 @@ static int schema_parse_int(const xmlChar *value, const char *name, bool allow_n errno = 0; lval = ZEND_STRTOL(str, NULL, 10); if (UNEXPECTED(oflow_info || (errno == ERANGE && lval != 0))) { - soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name); + soap_throw_error("Parsing Schema: %s value is out of range", name); + return false; } } if (UNEXPECTED(ZEND_LONG_EXCEEDS_INT(lval) || (!allow_negative && lval < 0))) { - soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name); + soap_throw_error("Parsing Schema: %s value is out of range", name); + return false; } - return (int) lval; + *result = (int) lval; + return true; } static encodePtr create_encoder(sdlPtr sdl, sdlTypePtr cur_type, const xmlChar *ns, const xmlChar *type) @@ -130,14 +133,7 @@ static encodePtr get_create_encoder(sdlPtr sdl, sdlTypePtr cur_type, const xmlCh return enc; } -/* Necessary for some error paths to avoid leaking persistent memory. */ -static void requestify_string(xmlChar **str) { - xmlChar *copy = (xmlChar *) estrdup((const char *) *str); - xmlFree(*str); - *str = copy; -} - -static void schema_load_file(sdlCtx *ctx, xmlAttrPtr ns, xmlChar *location, xmlAttrPtr tns, int import) { +static bool schema_load_file(sdlCtx *ctx, xmlAttrPtr ns, xmlChar *location, xmlAttrPtr tns, int import) { if (location != NULL && !zend_hash_str_exists(&ctx->docs, (char*)location, xmlStrlen(location))) { xmlDocPtr doc; @@ -149,35 +145,30 @@ static void schema_load_file(sdlCtx *ctx, xmlAttrPtr ns, xmlChar *location, xmlA sdl_restore_uri_credentials(ctx); if (doc == NULL) { - requestify_string(&location); - soap_error1(E_ERROR, "Parsing Schema: can't import schema from '%s'", location); + soap_throw_error("Parsing Schema: can't import schema from '%s'", location); + return false; } schema = get_node(doc->children, "schema"); if (schema == NULL) { - requestify_string(&location); + soap_throw_error("Parsing Schema: can't import schema from '%s'", location); xmlFreeDoc(doc); - soap_error1(E_ERROR, "Parsing Schema: can't import schema from '%s'", location); + return false; } new_tns = get_attribute(schema->properties, "targetNamespace"); if (import) { if (ns != NULL && (new_tns == NULL || xmlStrcmp(ns->children->content, new_tns->children->content) != 0)) { - requestify_string(&location); if (new_tns == NULL) { - xmlFreeDoc(doc); - soap_error2(E_ERROR, "Parsing Schema: can't import schema from '%s', missing 'targetNamespace', expected '%s'", location, ns->children->content); + soap_throw_error("Parsing Schema: can't import schema from '%s', missing 'targetNamespace', expected '%s'", location, ns->children->content); } else { - /* Have to make a copy to avoid a UAF after freeing `doc` */ - const char *target_ns_copy = estrdup((const char *) new_tns->children->content); - xmlFreeDoc(doc); - soap_error3(E_ERROR, "Parsing Schema: can't import schema from '%s', unexpected 'targetNamespace'='%s', expected '%s'", location, target_ns_copy, ns->children->content); + soap_throw_error("Parsing Schema: can't import schema from '%s', unexpected 'targetNamespace'='%s', expected '%s'", location, new_tns->children->content, ns->children->content); } + xmlFreeDoc(doc); + return false; } if (ns == NULL && new_tns != NULL) { - requestify_string(&location); - /* Have to make a copy to avoid a UAF after freeing `doc` */ - const char *target_ns_copy = estrdup((const char *) new_tns->children->content); + soap_throw_error("Parsing Schema: can't import schema from '%s', unexpected 'targetNamespace'='%s', expected no 'targetNamespace'", location, new_tns->children->content); xmlFreeDoc(doc); - soap_error2(E_ERROR, "Parsing Schema: can't import schema from '%s', unexpected 'targetNamespace'='%s', expected no 'targetNamespace'", location, target_ns_copy); + return false; } } else { new_tns = get_attribute(schema->properties, "targetNamespace"); @@ -186,14 +177,15 @@ static void schema_load_file(sdlCtx *ctx, xmlAttrPtr ns, xmlChar *location, xmlA xmlSetProp(schema, BAD_CAST("targetNamespace"), tns->children->content); } } else if (tns != NULL && xmlStrcmp(tns->children->content, new_tns->children->content) != 0) { - requestify_string(&location); + soap_throw_error("Parsing Schema: can't include schema from '%s', different 'targetNamespace'", location); xmlFreeDoc(doc); - soap_error1(E_ERROR, "Parsing Schema: can't include schema from '%s', different 'targetNamespace'", location); + return false; } } zend_hash_str_add_ptr(&ctx->docs, (char*)location, xmlStrlen(location), doc); - load_schema(ctx, schema); + return load_schema(ctx, schema); } + return true; } /* Returned uri must be freed by the caller. */ @@ -232,7 +224,7 @@ xmlChar *schema_location_construct_uri(const xmlAttr *attribute) Content: ((include | import | redefine | annotation)*, (((simpleType | complexType | group | attributeGroup) | element | attribute | notation), annotation*)*) */ -int load_schema(sdlCtx *ctx, xmlNodePtr schema) +bool load_schema(sdlCtx *ctx, xmlNodePtr schema) { xmlNodePtr trav; xmlAttrPtr tns; @@ -263,11 +255,15 @@ int load_schema(sdlCtx *ctx, xmlNodePtr schema) location = get_attribute(trav->properties, "schemaLocation"); if (location == NULL) { - soap_error0(E_ERROR, "Parsing Schema: include has no 'schemaLocation' attribute"); + soap_throw_error("Parsing Schema: include has no 'schemaLocation' attribute"); + return false; } else { xmlChar *uri = schema_location_construct_uri(location); - schema_load_file(ctx, NULL, uri, tns, 0); + bool loaded = schema_load_file(ctx, NULL, uri, tns, 0); xmlFree(uri); + if (!loaded) { + return false; + } } } else if (node_is_equal_xsd(trav,"redefine")) { @@ -275,11 +271,15 @@ int load_schema(sdlCtx *ctx, xmlNodePtr schema) location = get_attribute(trav->properties, "schemaLocation"); if (location == NULL) { - soap_error0(E_ERROR, "Parsing Schema: redefine has no 'schemaLocation' attribute"); + soap_throw_error("Parsing Schema: redefine has no 'schemaLocation' attribute"); + return false; } else { xmlChar *uri = schema_location_construct_uri(location); - schema_load_file(ctx, NULL, uri, tns, 0); + bool loaded = schema_load_file(ctx, NULL, uri, tns, 0); xmlFree(uri); + if (!loaded) { + return false; + } /* TODO: support */ } @@ -292,16 +292,21 @@ int load_schema(sdlCtx *ctx, xmlNodePtr schema) if (ns != NULL && tns != NULL && xmlStrcmp(ns->children->content, tns->children->content) == 0) { if (location) { - soap_error1(E_ERROR, "Parsing Schema: can't import schema from '%s', namespace must not match the enclosing schema 'targetNamespace'", location->children->content); + soap_throw_error("Parsing Schema: can't import schema from '%s', namespace must not match the enclosing schema 'targetNamespace'", location->children->content); + return false; } else { - soap_error0(E_ERROR, "Parsing Schema: can't import schema. Namespace must not match the enclosing schema 'targetNamespace'"); + soap_throw_error("Parsing Schema: can't import schema. Namespace must not match the enclosing schema 'targetNamespace'"); + return false; } } if (location) { uri = schema_location_construct_uri(location); } - schema_load_file(ctx, ns, uri, tns, 1); + bool loaded = schema_load_file(ctx, ns, uri, tns, 1); if (uri != NULL) {xmlFree(uri);} + if (!loaded) { + return false; + } } else if (node_is_equal_xsd(trav,"annotation")) { /* TODO: support */ /* annotation cleanup @@ -319,27 +324,40 @@ int load_schema(sdlCtx *ctx, xmlNodePtr schema) while (trav != NULL) { if (node_is_equal_xsd(trav,"simpleType")) { - schema_simpleType(ctx->sdl, tns, trav, NULL); + if (!schema_simpleType(ctx->sdl, tns, trav, NULL)) { + return false; + } } else if (node_is_equal_xsd(trav,"complexType")) { - schema_complexType(ctx->sdl, tns, trav, NULL); + if (!schema_complexType(ctx->sdl, tns, trav, NULL)) { + return false; + } } else if (node_is_equal_xsd(trav,"group")) { - schema_group(ctx->sdl, tns, trav, NULL, NULL); + if (!schema_group(ctx->sdl, tns, trav, NULL, NULL)) { + return false; + } } else if (node_is_equal_xsd(trav,"attributeGroup")) { - schema_attributeGroup(ctx->sdl, tns, trav, NULL, ctx); + if (!schema_attributeGroup(ctx->sdl, tns, trav, NULL, ctx)) { + return false; + } } else if (node_is_equal_xsd(trav,"element")) { - schema_element(ctx->sdl, tns, trav, NULL, NULL); + if (!schema_element(ctx->sdl, tns, trav, NULL, NULL)) { + return false; + } } else if (node_is_equal_xsd(trav,"attribute")) { - schema_attribute(ctx->sdl, tns, trav, NULL, ctx); + if (!schema_attribute(ctx->sdl, tns, trav, NULL, ctx)) { + return false; + } } else if (node_is_equal_xsd(trav,"notation")) { /* TODO: support */ } else if (node_is_equal_xsd(trav,"annotation")) { /* TODO: support */ } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in schema", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in schema", trav->name); + return false; } trav = trav->next; } - return TRUE; + return true; } /* @@ -419,7 +437,8 @@ static int schema_simpleType(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr simpleType, create_encoder(sdl, cur_type, ns->children->content, name->children->content); } else { - soap_error0(E_ERROR, "Parsing Schema: simpleType has no 'name' attribute"); + soap_throw_error("Parsing Schema: simpleType has no 'name' attribute"); + return FALSE; } trav = simpleType->children; @@ -429,24 +448,33 @@ static int schema_simpleType(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr simpleType, } if (trav != NULL) { if (node_is_equal_xsd(trav,"restriction")) { - schema_restriction_simpleContent(sdl, tns, trav, cur_type, 1); + if (!schema_restriction_simpleContent(sdl, tns, trav, cur_type, 1)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"list")) { cur_type->kind = XSD_TYPEKIND_LIST; - schema_list(sdl, tns, trav, cur_type); + if (!schema_list(sdl, tns, trav, cur_type)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"union")) { cur_type->kind = XSD_TYPEKIND_UNION; - schema_union(sdl, tns, trav, cur_type); + if (!schema_union(sdl, tns, trav, cur_type)) { + return FALSE; + } trav = trav->next; } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in simpleType", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in simpleType", trav->name); + return FALSE; } } else { - soap_error0(E_ERROR, "Parsing Schema: expected , or in simpleType"); + soap_throw_error("Parsing Schema: expected , or in simpleType"); + return FALSE; } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in simpleType", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in simpleType", trav->name); + return FALSE; } return TRUE; @@ -502,7 +530,8 @@ static int schema_list(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr listType, sdlTypeP sdlTypePtr newType; if (itemType != NULL) { - soap_error0(E_ERROR, "Parsing Schema: element has both 'itemType' attribute and subtype"); + soap_throw_error("Parsing Schema: element has both 'itemType' attribute and subtype"); + return FALSE; } newType = emalloc(sizeof(sdlType)); @@ -525,12 +554,15 @@ static int schema_list(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr listType, sdlTypeP } zend_hash_next_index_insert_ptr(cur_type->elements, newType); - schema_simpleType(sdl, tns, trav, newType); + if (!schema_simpleType(sdl, tns, trav, newType)) { + return FALSE; + } trav = trav->next; } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in list", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in list", trav->name); + return FALSE; } return TRUE; } @@ -622,15 +654,19 @@ static int schema_union(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr unionType, sdlTyp } zend_hash_next_index_insert_ptr(cur_type->elements, newType); - schema_simpleType(sdl, tns, trav, newType); + if (!schema_simpleType(sdl, tns, trav, newType)) { + return FALSE; + } } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in union", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in union", trav->name); + return FALSE; } trav = trav->next; } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in union", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in union", trav->name); + return FALSE; } return TRUE; } @@ -654,20 +690,27 @@ static int schema_simpleContent(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr simpCompT if (trav != NULL) { if (node_is_equal_xsd(trav, "restriction")) { cur_type->kind = XSD_TYPEKIND_RESTRICTION; - schema_restriction_simpleContent(sdl, tns, trav, cur_type, 0); + if (!schema_restriction_simpleContent(sdl, tns, trav, cur_type, 0)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav, "extension")) { cur_type->kind = XSD_TYPEKIND_EXTENSION; - schema_extension_simpleContent(sdl, tns, trav, cur_type); + if (!schema_extension_simpleContent(sdl, tns, trav, cur_type)) { + return FALSE; + } trav = trav->next; } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in simpleContent", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in simpleContent", trav->name); + return FALSE; } } else { - soap_error0(E_ERROR, "Parsing Schema: expected or in simpleContent"); + soap_throw_error("Parsing Schema: expected or in simpleContent"); + return FALSE; } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in simpleContent", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in simpleContent", trav->name); + return FALSE; } return TRUE; @@ -705,7 +748,8 @@ static int schema_restriction_simpleContent(sdlPtr sdl, xmlAttrPtr tns, xmlNodeP } if (ns) {efree(ns);} } else if (!simpleType) { - soap_error0(E_ERROR, "Parsing Schema: restriction has no 'base' attribute"); + soap_throw_error("Parsing Schema: restriction has no 'base' attribute"); + return FALSE; } if (cur_type->restrictions == NULL) { @@ -719,36 +763,63 @@ static int schema_restriction_simpleContent(sdlPtr sdl, xmlAttrPtr tns, xmlNodeP trav = trav->next; } if (trav != NULL && node_is_equal_xsd(trav, "simpleType")) { - schema_simpleType(sdl, tns, trav, cur_type); + if (!schema_simpleType(sdl, tns, trav, cur_type)) { + return FALSE; + } trav = trav->next; } while (trav != NULL) { if (node_is_equal_xsd(trav, "minExclusive")) { - schema_restriction_var_int(trav, &cur_type->restrictions->minExclusive); + if (!schema_restriction_var_int(trav, &cur_type->restrictions->minExclusive)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "minInclusive")) { - schema_restriction_var_int(trav, &cur_type->restrictions->minInclusive); + if (!schema_restriction_var_int(trav, &cur_type->restrictions->minInclusive)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "maxExclusive")) { - schema_restriction_var_int(trav, &cur_type->restrictions->maxExclusive); + if (!schema_restriction_var_int(trav, &cur_type->restrictions->maxExclusive)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "maxInclusive")) { - schema_restriction_var_int(trav, &cur_type->restrictions->maxInclusive); + if (!schema_restriction_var_int(trav, &cur_type->restrictions->maxInclusive)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "totalDigits")) { - schema_restriction_var_int(trav, &cur_type->restrictions->totalDigits); + if (!schema_restriction_var_int(trav, &cur_type->restrictions->totalDigits)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "fractionDigits")) { - schema_restriction_var_int(trav, &cur_type->restrictions->fractionDigits); + if (!schema_restriction_var_int(trav, &cur_type->restrictions->fractionDigits)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "length")) { - schema_restriction_var_int(trav, &cur_type->restrictions->length); + if (!schema_restriction_var_int(trav, &cur_type->restrictions->length)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "minLength")) { - schema_restriction_var_int(trav, &cur_type->restrictions->minLength); + if (!schema_restriction_var_int(trav, &cur_type->restrictions->minLength)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "maxLength")) { - schema_restriction_var_int(trav, &cur_type->restrictions->maxLength); + if (!schema_restriction_var_int(trav, &cur_type->restrictions->maxLength)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "whiteSpace")) { - schema_restriction_var_char(trav, &cur_type->restrictions->whiteSpace); + if (!schema_restriction_var_char(trav, &cur_type->restrictions->whiteSpace)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "pattern")) { - schema_restriction_var_char(trav, &cur_type->restrictions->pattern); + if (!schema_restriction_var_char(trav, &cur_type->restrictions->pattern)) { + return FALSE; + } } else if (node_is_equal_xsd(trav, "enumeration")) { sdlRestrictionCharPtr enumval = NULL; - schema_restriction_var_char(trav, &enumval); + if (!schema_restriction_var_char(trav, &enumval)) { + delete_restriction_var_char_int(enumval); + return FALSE; + } if (cur_type->restrictions->enumeration == NULL) { cur_type->restrictions->enumeration = emalloc(sizeof(HashTable)); zend_hash_init(cur_type->restrictions->enumeration, 0, NULL, delete_restriction_var_char, 0); @@ -764,21 +835,27 @@ static int schema_restriction_simpleContent(sdlPtr sdl, xmlAttrPtr tns, xmlNodeP if (!simpleType) { while (trav != NULL) { if (node_is_equal_xsd(trav,"attribute")) { - schema_attribute(sdl, tns, trav, cur_type, NULL); + if (!schema_attribute(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"attributeGroup")) { - schema_attributeGroup(sdl, tns, trav, cur_type, NULL); + if (!schema_attributeGroup(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"anyAttribute")) { /* TODO: support */ trav = trav->next; break; } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in restriction", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in restriction", trav->name); + return FALSE; } trav = trav->next; } } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in restriction", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in restriction", trav->name); + return FALSE; } return TRUE; @@ -810,7 +887,8 @@ static int schema_restriction_complexContent(sdlPtr sdl, xmlAttrPtr tns, xmlNode } if (ns) {efree(ns);} } else { - soap_error0(E_ERROR, "Parsing Schema: restriction has no 'base' attribute"); + soap_throw_error("Parsing Schema: restriction has no 'base' attribute"); + return FALSE; } trav = restType->children; @@ -820,35 +898,49 @@ static int schema_restriction_complexContent(sdlPtr sdl, xmlAttrPtr tns, xmlNode } if (trav != NULL) { if (node_is_equal_xsd(trav,"group")) { - schema_group(sdl, tns, trav, cur_type, NULL); + if (!schema_group(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"all")) { - schema_all(sdl, tns, trav, cur_type, NULL); + if (!schema_all(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"choice")) { - schema_choice(sdl, tns, trav, cur_type, NULL); + if (!schema_choice(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"sequence")) { - schema_sequence(sdl, tns, trav, cur_type, NULL); + if (!schema_sequence(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } } while (trav != NULL) { if (node_is_equal_xsd(trav,"attribute")) { - schema_attribute(sdl, tns, trav, cur_type, NULL); + if (!schema_attribute(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"attributeGroup")) { - schema_attributeGroup(sdl, tns, trav, cur_type, NULL); + if (!schema_attributeGroup(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"anyAttribute")) { /* TODO: support */ trav = trav->next; break; } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in restriction", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in restriction", trav->name); + return FALSE; } trav = trav->next; } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in restriction", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in restriction", trav->name); + return FALSE; } return TRUE; @@ -873,12 +965,11 @@ static int schema_restriction_var_int(xmlNodePtr val, sdlRestrictionIntPtr *valp value = get_attribute(val->properties, "value"); if (value == NULL) { - soap_error0(E_ERROR, "Parsing Schema: missing restriction value"); + soap_throw_error("Parsing Schema: missing restriction value"); + return FALSE; } - (*valptr)->value = schema_parse_int(value->children->content, (const char *) val->name, true); - - return TRUE; + return schema_parse_int(value->children->content, (const char *) val->name, true, &(*valptr)->value); } static int schema_restriction_var_char(xmlNodePtr val, sdlRestrictionCharPtr *valptr) @@ -901,7 +992,8 @@ static int schema_restriction_var_char(xmlNodePtr val, sdlRestrictionCharPtr *va value = get_attribute(val->properties, "value"); if (value == NULL) { - soap_error0(E_ERROR, "Parsing Schema: missing restriction value"); + soap_throw_error("Parsing Schema: missing restriction value"); + return FALSE; } (*valptr)->value = estrdup((char*)value->children->content); @@ -935,7 +1027,8 @@ static int schema_extension_simpleContent(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr } if (ns) {efree(ns);} } else { - soap_error0(E_ERROR, "Parsing Schema: extension has no 'base' attribute"); + soap_throw_error("Parsing Schema: extension has no 'base' attribute"); + return FALSE; } trav = extType->children; @@ -945,20 +1038,26 @@ static int schema_extension_simpleContent(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr } while (trav != NULL) { if (node_is_equal_xsd(trav,"attribute")) { - schema_attribute(sdl, tns, trav, cur_type, NULL); + if (!schema_attribute(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"attributeGroup")) { - schema_attributeGroup(sdl, tns, trav, cur_type, NULL); + if (!schema_attributeGroup(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"anyAttribute")) { /* TODO: support */ trav = trav->next; break; } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in extension", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in extension", trav->name); + return FALSE; } trav = trav->next; } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in extension", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in extension", trav->name); + return FALSE; } return TRUE; } @@ -990,7 +1089,8 @@ static int schema_extension_complexContent(sdlPtr sdl, xmlAttrPtr tns, xmlNodePt } if (ns) {efree(ns);} } else { - soap_error0(E_ERROR, "Parsing Schema: extension has no 'base' attribute"); + soap_throw_error("Parsing Schema: extension has no 'base' attribute"); + return FALSE; } trav = extType->children; @@ -1000,45 +1100,61 @@ static int schema_extension_complexContent(sdlPtr sdl, xmlAttrPtr tns, xmlNodePt } if (trav != NULL) { if (node_is_equal_xsd(trav,"group")) { - schema_group(sdl, tns, trav, cur_type, NULL); + if (!schema_group(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"all")) { - schema_all(sdl, tns, trav, cur_type, NULL); + if (!schema_all(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"choice")) { - schema_choice(sdl, tns, trav, cur_type, NULL); + if (!schema_choice(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"sequence")) { - schema_sequence(sdl, tns, trav, cur_type, NULL); + if (!schema_sequence(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } } while (trav != NULL) { if (node_is_equal_xsd(trav,"attribute")) { - schema_attribute(sdl, tns, trav, cur_type, NULL); + if (!schema_attribute(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"attributeGroup")) { - schema_attributeGroup(sdl, tns, trav, cur_type, NULL); + if (!schema_attributeGroup(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"anyAttribute")) { /* TODO: support */ trav = trav->next; break; } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in extension", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in extension", trav->name); + return FALSE; } trav = trav->next; } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in extension", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in extension", trav->name); + return FALSE; } return TRUE; } -void schema_min_max(xmlNodePtr node, sdlContentModelPtr model) +static bool schema_min_max(xmlNodePtr node, sdlContentModelPtr model) { xmlAttrPtr attr = get_attribute(node->properties, "minOccurs"); if (attr) { - model->min_occurs = schema_parse_int(attr->children->content, "minOccurs", false); + if (!schema_parse_int(attr->children->content, "minOccurs", false, &model->min_occurs)) { + return false; + } } else { model->min_occurs = 1; } @@ -1047,12 +1163,13 @@ void schema_min_max(xmlNodePtr node, sdlContentModelPtr model) if (attr) { if (!strncmp((char*)attr->children->content, "unbounded", sizeof("unbounded"))) { model->max_occurs = -1; - } else { - model->max_occurs = schema_parse_int(attr->children->content, "maxOccurs", false); + } else if (!schema_parse_int(attr->children->content, "maxOccurs", false, &model->max_occurs)) { + return false; } } else { model->max_occurs = 1; } + return true; } /* @@ -1079,7 +1196,9 @@ static int schema_all(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr all, sdlTypePtr cur zend_hash_next_index_insert_ptr(model->u.content, newModel); } - schema_min_max(all, newModel); + if (!schema_min_max(all, newModel)) { + return FALSE; + } trav = all->children; if (trav != NULL && node_is_equal_xsd(trav,"annotation")) { @@ -1088,9 +1207,12 @@ static int schema_all(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr all, sdlTypePtr cur } while (trav != NULL) { if (node_is_equal_xsd(trav,"element")) { - schema_element(sdl, tns, trav, cur_type, newModel); + if (!schema_element(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in all", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in all", trav->name); + return FALSE; } trav = trav->next; } @@ -1177,7 +1299,15 @@ static int schema_group(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr groupType, sdlTyp zend_hash_init(sdl->groups, 0, NULL, delete_type, 0); } if (zend_hash_add_ptr(sdl->groups, key.s, newType) == NULL) { - soap_error1(E_ERROR, "Parsing Schema: group '%s' already defined", ZSTR_VAL(key.s)); + zval zv; + + soap_throw_error("Parsing Schema: group '%s' already defined", ZSTR_VAL(key.s)); + smart_str_free(&key); + ZVAL_PTR(&zv, newType); + delete_type(&zv); + ZVAL_PTR(&zv, newModel); + delete_model(&zv); + return FALSE; } cur_type = newType; @@ -1190,10 +1320,13 @@ static int schema_group(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr groupType, sdlTyp zend_hash_next_index_insert_ptr(model->u.content, newModel); } } else { - soap_error0(E_ERROR, "Parsing Schema: group has no 'name' nor 'ref' attributes"); + soap_throw_error("Parsing Schema: group has no 'name' nor 'ref' attributes"); + return FALSE; } - schema_min_max(groupType, newModel); + if (!schema_min_max(groupType, newModel)) { + return FALSE; + } trav = groupType->children; if (trav != NULL && node_is_equal_xsd(trav,"annotation")) { @@ -1203,31 +1336,42 @@ static int schema_group(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr groupType, sdlTyp if (trav != NULL) { if (node_is_equal_xsd(trav,"choice")) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: group has both 'ref' attribute and subcontent"); + soap_throw_error("Parsing Schema: group has both 'ref' attribute and subcontent"); + return FALSE; } newModel->kind = XSD_CONTENT_CHOICE; - schema_choice(sdl, tns, trav, cur_type, newModel); + if (!schema_choice(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"sequence")) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: group has both 'ref' attribute and subcontent"); + soap_throw_error("Parsing Schema: group has both 'ref' attribute and subcontent"); + return FALSE; } newModel->kind = XSD_CONTENT_SEQUENCE; - schema_sequence(sdl, tns, trav, cur_type, newModel); + if (!schema_sequence(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"all")) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: group has both 'ref' attribute and subcontent"); + soap_throw_error("Parsing Schema: group has both 'ref' attribute and subcontent"); + return FALSE; } newModel->kind = XSD_CONTENT_ALL; - schema_all(sdl, tns, trav, cur_type, newModel); + if (!schema_all(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } trav = trav->next; } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in group", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in group", trav->name); + return FALSE; } } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in group", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in group", trav->name); + return FALSE; } return TRUE; } @@ -1255,7 +1399,9 @@ static int schema_choice(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr choiceType, sdlT zend_hash_next_index_insert_ptr(model->u.content, newModel); } - schema_min_max(choiceType, newModel); + if (!schema_min_max(choiceType, newModel)) { + return FALSE; + } trav = choiceType->children; if (trav != NULL && node_is_equal_xsd(trav,"annotation")) { @@ -1264,17 +1410,28 @@ static int schema_choice(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr choiceType, sdlT } while (trav != NULL) { if (node_is_equal_xsd(trav,"element")) { - schema_element(sdl, tns, trav, cur_type, newModel); + if (!schema_element(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"group")) { - schema_group(sdl, tns, trav, cur_type, newModel); + if (!schema_group(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"choice")) { - schema_choice(sdl, tns, trav, cur_type, newModel); + if (!schema_choice(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"sequence")) { - schema_sequence(sdl, tns, trav, cur_type, newModel); + if (!schema_sequence(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"any")) { - schema_any(sdl, tns, trav, cur_type, newModel); + if (!schema_any(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in choice", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in choice", trav->name); + return FALSE; } trav = trav->next; } @@ -1305,7 +1462,9 @@ static int schema_sequence(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr seqType, sdlTy zend_hash_next_index_insert_ptr(model->u.content, newModel); } - schema_min_max(seqType, newModel); + if (!schema_min_max(seqType, newModel)) { + return FALSE; + } trav = seqType->children; if (trav != NULL && node_is_equal_xsd(trav,"annotation")) { @@ -1314,17 +1473,28 @@ static int schema_sequence(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr seqType, sdlTy } while (trav != NULL) { if (node_is_equal_xsd(trav,"element")) { - schema_element(sdl, tns, trav, cur_type, newModel); + if (!schema_element(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"group")) { - schema_group(sdl, tns, trav, cur_type, newModel); + if (!schema_group(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"choice")) { - schema_choice(sdl, tns, trav, cur_type, newModel); + if (!schema_choice(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"sequence")) { - schema_sequence(sdl, tns, trav, cur_type, newModel); + if (!schema_sequence(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"any")) { - schema_any(sdl, tns, trav, cur_type, newModel); + if (!schema_any(sdl, tns, trav, cur_type, newModel)) { + return FALSE; + } } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in sequence", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in sequence", trav->name); + return FALSE; } trav = trav->next; } @@ -1350,7 +1520,10 @@ static int schema_any(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr anyType, sdlTypePtr newModel = emalloc(sizeof(sdlContentModel)); newModel->kind = XSD_CONTENT_ANY; - schema_min_max(anyType, newModel); + if (!schema_min_max(anyType, newModel)) { + efree(newModel); + return FALSE; + } zend_hash_next_index_insert_ptr(model->u.content, newModel); } @@ -1377,20 +1550,27 @@ static int schema_complexContent(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr compCont if (trav != NULL) { if (node_is_equal_xsd(trav, "restriction")) { cur_type->kind = XSD_TYPEKIND_RESTRICTION; - schema_restriction_complexContent(sdl, tns, trav, cur_type); + if (!schema_restriction_complexContent(sdl, tns, trav, cur_type)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav, "extension")) { cur_type->kind = XSD_TYPEKIND_EXTENSION; - schema_extension_complexContent(sdl, tns, trav, cur_type); + if (!schema_extension_complexContent(sdl, tns, trav, cur_type)) { + return FALSE; + } trav = trav->next; } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in complexContent", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in complexContent", trav->name); + return FALSE; } } else { - soap_error0(E_ERROR, "Parsing Schema: or expected in complexContent"); + soap_throw_error("Parsing Schema: or expected in complexContent"); + return FALSE; } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in complexContent", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in complexContent", trav->name); + return FALSE; } return TRUE; @@ -1469,7 +1649,7 @@ static int schema_complexType(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr compType, s cur_type = ptr; create_encoder(sdl, cur_type, ns->children->content, name->children->content); } else { - soap_error0(E_ERROR, "Parsing Schema: complexType has no 'name' attribute"); + soap_throw_error("Parsing Schema: complexType has no 'name' attribute"); return FALSE; } @@ -1480,43 +1660,61 @@ static int schema_complexType(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr compType, s } if (trav != NULL) { if (node_is_equal_xsd(trav,"simpleContent")) { - schema_simpleContent(sdl, tns, trav, cur_type); + if (!schema_simpleContent(sdl, tns, trav, cur_type)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"complexContent")) { - schema_complexContent(sdl, tns, trav, cur_type); + if (!schema_complexContent(sdl, tns, trav, cur_type)) { + return FALSE; + } trav = trav->next; } else { if (node_is_equal_xsd(trav,"group")) { - schema_group(sdl, tns, trav, cur_type, NULL); + if (!schema_group(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"all")) { - schema_all(sdl, tns, trav, cur_type, NULL); + if (!schema_all(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"choice")) { - schema_choice(sdl, tns, trav, cur_type, NULL); + if (!schema_choice(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } else if (node_is_equal_xsd(trav,"sequence")) { - schema_sequence(sdl, tns, trav, cur_type, NULL); + if (!schema_sequence(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } trav = trav->next; } while (trav != NULL) { if (node_is_equal_xsd(trav,"attribute")) { - schema_attribute(sdl, tns, trav, cur_type, NULL); + if (!schema_attribute(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"attributeGroup")) { - schema_attributeGroup(sdl, tns, trav, cur_type, NULL); + if (!schema_attributeGroup(sdl, tns, trav, cur_type, NULL)) { + return FALSE; + } } else if (node_is_equal_xsd(trav,"anyAttribute")) { /* TODO: support */ trav = trav->next; break; } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in complexType", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in complexType", trav->name); + return FALSE; } trav = trav->next; } } } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in complexType", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in complexType", trav->name); + return FALSE; } return TRUE; } @@ -1619,7 +1817,13 @@ static int schema_element(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr element, sdlTyp smart_str_0(&key); if (zend_hash_add_ptr(addHash, key.s, newType) == NULL) { if (cur_type == NULL) { - soap_error1(E_ERROR, "Parsing Schema: element '%s' already defined", ZSTR_VAL(key.s)); + zval zv; + + soap_throw_error("Parsing Schema: element '%s' already defined", ZSTR_VAL(key.s)); + smart_str_free(&key); + ZVAL_PTR(&zv, newType); + delete_type(&zv); + return FALSE; } else { zend_hash_next_index_insert_ptr(addHash, newType); } @@ -1632,14 +1836,18 @@ static int schema_element(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr element, sdlTyp newModel->kind = XSD_CONTENT_ELEMENT; newModel->u.element = newType; - schema_min_max(element, newModel); + if (!schema_min_max(element, newModel)) { + efree(newModel); + return FALSE; + } zend_hash_next_index_insert_ptr(model->u.content, newModel); } cur_type = newType; } else { - soap_error0(E_ERROR, "Parsing Schema: element has no 'name' nor 'ref' attributes"); + soap_throw_error("Parsing Schema: element has no 'name' nor 'ref' attributes"); + return FALSE; } /* nillable = boolean : false */ @@ -1647,7 +1855,8 @@ static int schema_element(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr element, sdlTyp attr = get_attribute(attrs, "nillable"); if (attr) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: element has both 'ref' and 'nillable' attributes"); + soap_throw_error("Parsing Schema: element has both 'ref' and 'nillable' attributes"); + return FALSE; } if (!stricmp((char*)attr->children->content, "true") || !stricmp((char*)attr->children->content, "1")) { @@ -1662,7 +1871,8 @@ static int schema_element(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr element, sdlTyp attr = get_attribute(attrs, "fixed"); if (attr) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: element has both 'ref' and 'fixed' attributes"); + soap_throw_error("Parsing Schema: element has both 'ref' and 'fixed' attributes"); + return FALSE; } cur_type->fixed = estrdup((char*)attr->children->content); } @@ -1670,7 +1880,8 @@ static int schema_element(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr element, sdlTyp attr = get_attribute(attrs, "default"); if (attr) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: element has both 'default' and 'fixed' attributes"); + soap_throw_error("Parsing Schema: element has both 'default' and 'fixed' attributes"); + return FALSE; } cur_type->def = estrdup((char*)attr->children->content); } @@ -1716,7 +1927,8 @@ static int schema_element(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr element, sdlTyp xmlNsPtr nsptr; if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: element has both 'ref' and 'type' attributes"); + soap_throw_error("Parsing Schema: element has both 'ref' and 'type' attributes"); + return FALSE; } parse_namespace(type->children->content, &cptype, &str_ns); nsptr = xmlSearchNs(element->doc, element, BAD_CAST(str_ns)); @@ -1734,19 +1946,27 @@ static int schema_element(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr element, sdlTyp if (trav != NULL) { if (node_is_equal_xsd(trav,"simpleType")) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: element has both 'ref' attribute and subtype"); + soap_throw_error("Parsing Schema: element has both 'ref' attribute and subtype"); + return FALSE; } else if (type != NULL) { - soap_error0(E_ERROR, "Parsing Schema: element has both 'type' attribute and subtype"); + soap_throw_error("Parsing Schema: element has both 'type' attribute and subtype"); + return FALSE; + } + if (!schema_simpleType(sdl, tns, trav, cur_type)) { + return FALSE; } - schema_simpleType(sdl, tns, trav, cur_type); trav = trav->next; } else if (node_is_equal_xsd(trav,"complexType")) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: element has both 'ref' attribute and subtype"); + soap_throw_error("Parsing Schema: element has both 'ref' attribute and subtype"); + return FALSE; } else if (type != NULL) { - soap_error0(E_ERROR, "Parsing Schema: element has both 'type' attribute and subtype"); + soap_throw_error("Parsing Schema: element has both 'type' attribute and subtype"); + return FALSE; + } + if (!schema_complexType(sdl, tns, trav, cur_type)) { + return FALSE; } - schema_complexType(sdl, tns, trav, cur_type); trav = trav->next; } } @@ -1758,7 +1978,8 @@ static int schema_element(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr element, sdlTyp } else if (node_is_equal_xsd(trav,"keyref")) { /* TODO: support */ } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in element", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in element", trav->name); + return FALSE; } trav = trav->next; } @@ -1848,12 +2069,18 @@ static int schema_attribute(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr attrType, sdl } if (zend_hash_add_ptr(addHash, key.s, newAttr) == NULL) { - soap_error1(E_ERROR, "Parsing Schema: attribute '%s' already defined", ZSTR_VAL(key.s)); + zval zv; + + soap_throw_error("Parsing Schema: attribute '%s' already defined", ZSTR_VAL(key.s)); + smart_str_free(&key); + ZVAL_PTR(&zv, newAttr); + delete_attribute(&zv); + return FALSE; } smart_str_free(&key); } else{ - soap_error0(E_ERROR, "Parsing Schema: attribute has no 'name' nor 'ref' attributes"); - return FALSE; /* the above call is noreturn, but not marked as such */ + soap_throw_error("Parsing Schema: attribute has no 'name' nor 'ref' attributes"); + return FALSE; } /* type = QName */ @@ -1864,7 +2091,8 @@ static int schema_attribute(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr attrType, sdl xmlNsPtr nsptr; if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: attribute has both 'ref' and 'type' attributes"); + soap_throw_error("Parsing Schema: attribute has both 'ref' and 'type' attributes"); + return FALSE; } parse_namespace(type->children->content, &cptype, &str_ns); nsptr = xmlSearchNs(attrType->doc, attrType, BAD_CAST(str_ns)); @@ -1973,9 +2201,11 @@ static int schema_attribute(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr attrType, sdl zval zv; if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: attribute has both 'ref' attribute and subtype"); + soap_throw_error("Parsing Schema: attribute has both 'ref' attribute and subtype"); + return FALSE; } else if (type != NULL) { - soap_error0(E_ERROR, "Parsing Schema: attribute has both 'type' attribute and subtype"); + soap_throw_error("Parsing Schema: attribute has both 'type' attribute and subtype"); + return FALSE; } dummy_type = emalloc(sizeof(sdlType)); memset(dummy_type, 0, sizeof(sdlType)); @@ -1989,15 +2219,19 @@ static int schema_attribute(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr attrType, sdl dummy_type->name = str; } dummy_type->namens = estrdup((char*)tns->children->content); - schema_simpleType(sdl, tns, trav, dummy_type); + bool parsed = schema_simpleType(sdl, tns, trav, dummy_type); newAttr->encode = dummy_type->encode; ZVAL_PTR(&zv, dummy_type); delete_type(&zv); + if (!parsed) { + return FALSE; + } trav = trav->next; } } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in attribute", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in attribute", trav->name); + return FALSE; } return TRUE; } @@ -2033,7 +2267,13 @@ static int schema_attributeGroup(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr attrGrou smart_str_0(&key); if (zend_hash_add_ptr(ctx->attributeGroups, key.s, newType) == NULL) { - soap_error1(E_ERROR, "Parsing Schema: attributeGroup '%s' already defined", ZSTR_VAL(key.s)); + zval zv; + + soap_throw_error("Parsing Schema: attributeGroup '%s' already defined", ZSTR_VAL(key.s)); + smart_str_free(&key); + ZVAL_PTR(&zv, newType); + delete_type(&zv); + return FALSE; } cur_type = newType; smart_str_free(&key); @@ -2067,7 +2307,8 @@ static int schema_attributeGroup(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr attrGrou cur_type = NULL; } } else{ - soap_error0(E_ERROR, "Parsing Schema: attributeGroup has no 'name' nor 'ref' attributes"); + soap_throw_error("Parsing Schema: attributeGroup has no 'name' nor 'ref' attributes"); + return FALSE; } trav = attrGroup->children; @@ -2078,28 +2319,37 @@ static int schema_attributeGroup(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr attrGrou while (trav != NULL) { if (node_is_equal_xsd(trav,"attribute")) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: attributeGroup has both 'ref' attribute and subattribute"); + soap_throw_error("Parsing Schema: attributeGroup has both 'ref' attribute and subattribute"); + return FALSE; + } + if (!schema_attribute(sdl, tns, trav, cur_type, NULL)) { + return FALSE; } - schema_attribute(sdl, tns, trav, cur_type, NULL); } else if (node_is_equal_xsd(trav,"attributeGroup")) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: attributeGroup has both 'ref' attribute and subattribute"); + soap_throw_error("Parsing Schema: attributeGroup has both 'ref' attribute and subattribute"); + return FALSE; + } + if (!schema_attributeGroup(sdl, tns, trav, cur_type, NULL)) { + return FALSE; } - schema_attributeGroup(sdl, tns, trav, cur_type, NULL); } else if (node_is_equal_xsd(trav,"anyAttribute")) { if (ref != NULL) { - soap_error0(E_ERROR, "Parsing Schema: attributeGroup has both 'ref' attribute and subattribute"); + soap_throw_error("Parsing Schema: attributeGroup has both 'ref' attribute and subattribute"); + return FALSE; } /* TODO: support */ trav = trav->next; break; } else { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in attributeGroup", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in attributeGroup", trav->name); + return FALSE; } trav = trav->next; } if (trav != NULL) { - soap_error1(E_ERROR, "Parsing Schema: unexpected <%s> in attributeGroup", trav->name); + soap_throw_error("Parsing Schema: unexpected <%s> in attributeGroup", trav->name); + return FALSE; } return TRUE; } @@ -2184,7 +2434,7 @@ static void schema_attribute_fixup(sdlCtx *ctx, sdlAttributePtr attr) } } -static void schema_attributegroup_fixup(sdlCtx *ctx, sdlAttributePtr attr, HashTable *ht) +static bool schema_attributegroup_fixup(sdlCtx *ctx, sdlAttributePtr attr, HashTable *ht) { sdlTypePtr tmp; sdlAttributePtr tmp_attr; @@ -2194,7 +2444,8 @@ static void schema_attributegroup_fixup(sdlCtx *ctx, sdlAttributePtr attr, HashT tmp = (sdlTypePtr)schema_find_by_ref(ctx->attributeGroups, attr->ref); if (tmp) { if (zend_hash_index_find_ptr(&ctx->fixupInProgress, (zend_ulong)(uintptr_t)tmp) != NULL) { - soap_error1(E_ERROR, "Parsing Schema: recursive attributeGroup 'ref' attribute '%s'", attr->ref); + soap_throw_error("Parsing Schema: recursive attributeGroup 'ref' attribute '%s'", attr->ref); + return false; } zend_hash_index_add_ptr(&ctx->fixupInProgress, (zend_ulong)(uintptr_t)tmp, tmp); if (tmp->attributes) { @@ -2226,7 +2477,9 @@ static void schema_attributegroup_fixup(sdlCtx *ctx, sdlAttributePtr attr, HashT } else { zend_ulong index; - schema_attributegroup_fixup(ctx, tmp_attr, ht); + if (!schema_attributegroup_fixup(ctx, tmp_attr, ht)) { + return false; + } zend_hash_get_current_key(tmp->attributes, NULL, &index); zend_hash_index_del(tmp->attributes, index); } @@ -2238,9 +2491,10 @@ static void schema_attributegroup_fixup(sdlCtx *ctx, sdlAttributePtr attr, HashT efree(attr->ref); attr->ref = NULL; } + return true; } -static void schema_content_model_fixup(sdlCtx *ctx, sdlContentModelPtr model) +static bool schema_content_model_fixup(sdlCtx *ctx, sdlContentModelPtr model) { switch (model->kind) { case XSD_CONTENT_GROUP_REF: { @@ -2248,14 +2502,18 @@ static void schema_content_model_fixup(sdlCtx *ctx, sdlContentModelPtr model) if (ctx->sdl->groups && (tmp = zend_hash_str_find_ptr(ctx->sdl->groups, model->u.group_ref, strlen(model->u.group_ref))) != NULL) { if (zend_hash_index_find_ptr(&ctx->fixupInProgress, (zend_ulong)(uintptr_t)tmp) != NULL) { - soap_error1(E_ERROR, "Parsing Schema: recursive group 'ref' attribute '%s'", model->u.group_ref); + soap_throw_error("Parsing Schema: recursive group 'ref' attribute '%s'", model->u.group_ref); + return false; + } + if (!schema_type_fixup(ctx, tmp)) { + return false; } - schema_type_fixup(ctx, tmp); efree(model->u.group_ref); model->kind = XSD_CONTENT_GROUP; model->u.group = tmp; } else { - soap_error1(E_ERROR, "Parsing Schema: unresolved group 'ref' attribute '%s'", model->u.group_ref); + soap_throw_error("Parsing Schema: unresolved group 'ref' attribute '%s'", model->u.group_ref); + return false; } break; } @@ -2279,16 +2537,19 @@ static void schema_content_model_fixup(sdlCtx *ctx, sdlContentModelPtr model) sdlContentModelPtr tmp; ZEND_HASH_FOREACH_PTR(model->u.content, tmp) { - schema_content_model_fixup(ctx, tmp); + if (!schema_content_model_fixup(ctx, tmp)) { + return false; + } } ZEND_HASH_FOREACH_END(); break; } default: break; } + return true; } -static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type) +static bool schema_type_fixup(sdlCtx *ctx, sdlTypePtr type) { sdlTypePtr tmp; sdlAttributePtr attr; @@ -2314,7 +2575,8 @@ static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type) } else if (strcmp(type->ref, XSD_NAMESPACE ":schema") == 0) { type->encode = get_conversion(XSD_ANYXML); } else { - soap_error1(E_ERROR, "Parsing Schema: unresolved element 'ref' attribute '%s'", type->ref); + soap_throw_error("Parsing Schema: unresolved element 'ref' attribute '%s'", type->ref); + return false; } } efree(type->ref); @@ -2322,11 +2584,13 @@ static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type) } if (type->elements) { ZEND_HASH_FOREACH_PTR(type->elements, tmp) { - schema_type_fixup(ctx, tmp); + if (!schema_type_fixup(ctx, tmp)) { + return false; + } } ZEND_HASH_FOREACH_END(); } - if (type->model) { - schema_content_model_fixup(ctx, type->model); + if (type->model && !schema_content_model_fixup(ctx, type->model)) { + return false; } if (type->attributes) { HashPosition pos; @@ -2341,23 +2605,24 @@ static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type) zend_result result = zend_hash_move_forward_ex(type->attributes, &pos); ZEND_ASSERT(result == SUCCESS); } else { - schema_attributegroup_fixup(ctx, attr, type->attributes); + if (!schema_attributegroup_fixup(ctx, attr, type->attributes)) { + return false; + } zend_result result = zend_hash_index_del(type->attributes, index); ZEND_ASSERT(result == SUCCESS); } } } zend_hash_index_del(&ctx->fixupInProgress, (zend_ulong)(uintptr_t)type); + return true; } -void schema_pass2(sdlCtx *ctx) +static bool schema_pass2_fixup(sdlCtx *ctx) { sdlPtr sdl = ctx->sdl; sdlAttributePtr attr; sdlTypePtr type; - zend_hash_init(&ctx->fixupInProgress, 0, NULL, NULL, 0); - if (ctx->attributes) { ZEND_HASH_FOREACH_PTR(ctx->attributes, attr) { schema_attribute_fixup(ctx, attr); @@ -2365,34 +2630,60 @@ void schema_pass2(sdlCtx *ctx) } if (ctx->attributeGroups) { ZEND_HASH_MAP_FOREACH_PTR(ctx->attributeGroups, type) { - schema_type_fixup(ctx, type); + if (!schema_type_fixup(ctx, type)) { + return false; + } } ZEND_HASH_FOREACH_END(); } if (sdl->elements) { ZEND_HASH_MAP_FOREACH_PTR(sdl->elements, type) { - schema_type_fixup(ctx, type); + if (!schema_type_fixup(ctx, type)) { + return false; + } } ZEND_HASH_FOREACH_END(); } if (sdl->groups) { ZEND_HASH_MAP_FOREACH_PTR(sdl->groups, type) { - schema_type_fixup(ctx, type); + if (!schema_type_fixup(ctx, type)) { + return false; + } } ZEND_HASH_FOREACH_END(); } if (sdl->types) { ZEND_HASH_FOREACH_PTR(sdl->types, type) { - schema_type_fixup(ctx, type); + if (!schema_type_fixup(ctx, type)) { + return false; + } } ZEND_HASH_FOREACH_END(); } + return true; +} + +bool schema_pass2(sdlCtx *ctx) +{ + bool result; + + zend_hash_init(&ctx->fixupInProgress, 0, NULL, NULL, 0); + result = schema_pass2_fixup(ctx); + zend_hash_destroy(&ctx->fixupInProgress); + schema_free_ctx(ctx); + + return result; +} + +/* Frees the parts of the context that only load_schema() and schema_pass2() use. */ +void schema_free_ctx(sdlCtx *ctx) +{ if (ctx->attributes) { zend_hash_destroy(ctx->attributes); efree(ctx->attributes); + ctx->attributes = NULL; } if (ctx->attributeGroups) { zend_hash_destroy(ctx->attributeGroups); efree(ctx->attributeGroups); + ctx->attributeGroups = NULL; } - - zend_hash_destroy(&ctx->fixupInProgress); } void delete_model(zval *zv) @@ -2459,6 +2750,10 @@ void delete_type(zval *zv) if (type->fixed) { efree(type->fixed); } + if (type->ref) { + /* Only set when parsing failed before schema_pass2() resolved it */ + efree(type->ref); + } if (type->elements) { zend_hash_destroy(type->elements); efree(type->elements); diff --git a/ext/soap/php_schema.h b/ext/soap/php_schema.h index b92c8a8fb4c4..ec8c155bdf0f 100644 --- a/ext/soap/php_schema.h +++ b/ext/soap/php_schema.h @@ -17,8 +17,9 @@ #ifndef PHP_SCHEMA_H #define PHP_SCHEMA_H -int load_schema(sdlCtx *ctx, xmlNodePtr schema); -void schema_pass2(sdlCtx *ctx); +bool load_schema(sdlCtx *ctx, xmlNodePtr schema); +bool schema_pass2(sdlCtx *ctx); +void schema_free_ctx(sdlCtx *ctx); xmlChar *schema_location_construct_uri(const xmlAttr *attribute); diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c index 3ffa67cfb910..e331502b77e9 100644 --- a/ext/soap/php_sdl.c +++ b/ext/soap/php_sdl.c @@ -183,6 +183,9 @@ encodePtr get_encoder_ex(sdlPtr sdl, const char *nscat, size_t len) return NULL; } +/* Returns 1 for an element of the WSDL namespace, 0 for an extension element + * that can be ignored, and -1 after throwing a SoapFault for an unknown + * extension element that is marked as required. */ static int is_wsdl_element(xmlNodePtr node) { if (node->ns && strcmp((char*)node->ns->href, WSDL_NAMESPACE) != 0) { @@ -191,7 +194,8 @@ static int is_wsdl_element(xmlNodePtr node) attr->children && attr->children->content && (strcmp((char*)attr->children->content, "1") == 0 || strcmp((char*)attr->children->content, "true") == 0)) { - soap_error1(E_ERROR, "Parsing WSDL: Unknown required WSDL extension '%s'", node->ns->href); + soap_throw_error("Parsing WSDL: Unknown required WSDL extension '%s'", node->ns->href); + return -1; } return 0; } @@ -292,14 +296,14 @@ void sdl_restore_uri_credentials(sdlCtx *ctx) #define SAFE_STR(a) ((a)?((const char *)a):"") -static void load_wsdl_ex(zval *this_ptr, char *struri, sdlCtx *ctx, bool include) +static bool load_wsdl_ex(zval *this_ptr, char *struri, sdlCtx *ctx, bool include) { sdlPtr tmpsdl = ctx->sdl; xmlDocPtr wsdl; xmlNodePtr root, definitions, trav; if (zend_hash_str_exists(&ctx->docs, struri, strlen(struri))) { - return; + return true; } sdl_set_uri_credentials(ctx, struri); @@ -310,10 +314,11 @@ static void load_wsdl_ex(zval *this_ptr, char *struri, sdlCtx *ctx, bool include const xmlError *xmlErrorPtr = xmlGetLastError(); if (xmlErrorPtr) { - soap_error2(E_ERROR, "Parsing WSDL: Couldn't load from '%s' : %s", struri, xmlErrorPtr->message); + soap_throw_error("Parsing WSDL: Couldn't load from '%s' : %s", struri, xmlErrorPtr->message); } else { - soap_error1(E_ERROR, "Parsing WSDL: Couldn't load from '%s'", struri); + soap_throw_error("Parsing WSDL: Couldn't load from '%s'", struri); } + return false; } zend_hash_str_add_ptr(&ctx->docs, struri, strlen(struri), wsdl); @@ -324,11 +329,11 @@ static void load_wsdl_ex(zval *this_ptr, char *struri, sdlCtx *ctx, bool include if (include) { xmlNodePtr schema = get_node_ex(root, "schema", XSD_NAMESPACE); if (schema) { - load_schema(ctx, schema); - return; + return load_schema(ctx, schema); } } - soap_error1(E_ERROR, "Parsing WSDL: Couldn't find in '%s'", struri); + soap_throw_error("Parsing WSDL: Couldn't find in '%s'", struri); + return false; } if (!include) { @@ -340,7 +345,11 @@ static void load_wsdl_ex(zval *this_ptr, char *struri, sdlCtx *ctx, bool include trav = definitions->children; while (trav != NULL) { - if (!is_wsdl_element(trav)) { + int wsdl_element = is_wsdl_element(trav); + if (wsdl_element < 0) { + return false; + } + if (!wsdl_element) { trav = trav->next; continue; } @@ -350,9 +359,18 @@ static void load_wsdl_ex(zval *this_ptr, char *struri, sdlCtx *ctx, bool include while (trav2 != NULL) { if (node_is_equal_ex(trav2, "schema", XSD_NAMESPACE)) { - load_schema(ctx, trav2); - } else if (is_wsdl_element(trav2) && !node_is_equal(trav2,"documentation")) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav2->name)); + if (!load_schema(ctx, trav2)) { + return false; + } + } else { + wsdl_element = is_wsdl_element(trav2); + if (wsdl_element < 0) { + return false; + } + if (wsdl_element && !node_is_equal(trav2,"documentation")) { + soap_throw_error("Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav2->name)); + return false; + } } trav2 = trav2->next; } @@ -361,59 +379,67 @@ static void load_wsdl_ex(zval *this_ptr, char *struri, sdlCtx *ctx, bool include xmlAttrPtr tmp = get_attribute(trav->properties, "location"); if (tmp) { xmlChar *uri = schema_location_construct_uri(tmp); - zend_try { - load_wsdl_ex(this_ptr, (char*)uri, ctx, true); - } zend_catch { - xmlFree(uri); - zend_bailout(); - } zend_end_try(); + bool loaded = load_wsdl_ex(this_ptr, (char*)uri, ctx, true); xmlFree(uri); + if (!loaded) { + return false; + } } } else if (node_is_equal(trav,"message")) { xmlAttrPtr name = get_attribute(trav->properties, "name"); if (name && name->children && name->children->content) { if (zend_hash_str_add_ptr(&ctx->messages, (char*)name->children->content, xmlStrlen(name->children->content), trav) == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: '%s' already defined", name->children->content); + soap_throw_error("Parsing WSDL: '%s' already defined", name->children->content); + return false; } } else { - soap_error0(E_ERROR, "Parsing WSDL: has no name attribute"); + soap_throw_error("Parsing WSDL: has no name attribute"); + return false; } } else if (node_is_equal(trav,"portType")) { xmlAttrPtr name = get_attribute(trav->properties, "name"); if (name && name->children && name->children->content) { if (zend_hash_str_add_ptr(&ctx->portTypes, (char*)name->children->content, xmlStrlen(name->children->content), trav) == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: '%s' already defined", name->children->content); + soap_throw_error("Parsing WSDL: '%s' already defined", name->children->content); + return false; } } else { - soap_error0(E_ERROR, "Parsing WSDL: has no name attribute"); + soap_throw_error("Parsing WSDL: has no name attribute"); + return false; } } else if (node_is_equal(trav,"binding")) { xmlAttrPtr name = get_attribute(trav->properties, "name"); if (name && name->children && name->children->content) { if (zend_hash_str_add_ptr(&ctx->bindings, (char*)name->children->content, xmlStrlen(name->children->content), trav) == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: '%s' already defined", name->children->content); + soap_throw_error("Parsing WSDL: '%s' already defined", name->children->content); + return false; } } else { - soap_error0(E_ERROR, "Parsing WSDL: has no name attribute"); + soap_throw_error("Parsing WSDL: has no name attribute"); + return false; } } else if (node_is_equal(trav,"service")) { xmlAttrPtr name = get_attribute(trav->properties, "name"); if (name && name->children && name->children->content) { if (zend_hash_str_add_ptr(&ctx->services, (char*)name->children->content, xmlStrlen(name->children->content), trav) == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: '%s' already defined", name->children->content); + soap_throw_error("Parsing WSDL: '%s' already defined", name->children->content); + return false; } } else { - soap_error0(E_ERROR, "Parsing WSDL: has no name attribute"); + soap_throw_error("Parsing WSDL: has no name attribute"); + return false; } } else if (!node_is_equal(trav,"documentation")) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav->name)); + soap_throw_error("Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav->name)); + return false; } trav = trav->next; } + return true; } static sdlSoapBindingFunctionHeaderPtr wsdl_soap_binding_header(sdlCtx* ctx, xmlNodePtr header, char* wsdl_soap_namespace, int fault) @@ -425,7 +451,8 @@ static sdlSoapBindingFunctionHeaderPtr wsdl_soap_binding_header(sdlCtx* ctx, xml tmp = get_attribute(header->properties, "message"); if (!tmp) { - soap_error0(E_ERROR, "Parsing WSDL: Missing message attribute for
"); + soap_throw_error("Parsing WSDL: Missing message attribute for
"); + return NULL; } ctype = strrchr((char*)tmp->children->content,':'); @@ -435,16 +462,19 @@ static sdlSoapBindingFunctionHeaderPtr wsdl_soap_binding_header(sdlCtx* ctx, xml ++ctype; } if ((message = zend_hash_str_find_ptr(&ctx->messages, ctype, strlen(ctype))) == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: Missing with name '%s'", tmp->children->content); + soap_throw_error("Parsing WSDL: Missing with name '%s'", tmp->children->content); + return NULL; } tmp = get_attribute(header->properties, "part"); if (!tmp) { - soap_error0(E_ERROR, "Parsing WSDL: Missing part attribute for
"); + soap_throw_error("Parsing WSDL: Missing part attribute for
"); + return NULL; } part = get_node_with_attribute_ex(message->children, "part", WSDL_NAMESPACE, "name", (char*)tmp->children->content, NULL); if (!part) { - soap_error1(E_ERROR, "Parsing WSDL: Missing part '%s' in ", tmp->children->content); + soap_throw_error("Parsing WSDL: Missing part '%s' in ", tmp->children->content); + return NULL; } h = emalloc(sizeof(sdlSoapBindingFunctionHeader)); @@ -471,10 +501,14 @@ static sdlSoapBindingFunctionHeaderPtr wsdl_soap_binding_header(sdlCtx* ctx, xml } else if (strncmp((char*)tmp->children->content, SOAP_1_2_ENC_NAMESPACE, sizeof(SOAP_1_2_ENC_NAMESPACE)) == 0) { h->encodingStyle = SOAP_ENCODING_1_2; } else { - soap_error1(E_ERROR, "Parsing WSDL: Unknown encodingStyle '%s'", tmp->children->content); + soap_throw_error("Parsing WSDL: Unknown encodingStyle '%s'", tmp->children->content); + delete_header_int(h); + return NULL; } } else { - soap_error0(E_ERROR, "Parsing WSDL: Unspecified encodingStyle"); + soap_throw_error("Parsing WSDL: Unspecified encodingStyle"); + delete_header_int(h); + return NULL; } } @@ -504,6 +538,11 @@ static sdlSoapBindingFunctionHeaderPtr wsdl_soap_binding_header(sdlCtx* ctx, xml sdlSoapBindingFunctionHeaderPtr hf = wsdl_soap_binding_header(ctx, trav, wsdl_soap_namespace, 1); smart_str key = {0}; + if (hf == NULL) { + delete_header_int(h); + return NULL; + } + if (h->headerfaults == NULL) { h->headerfaults = emalloc(sizeof(HashTable)); zend_hash_init(h->headerfaults, 0, NULL, delete_header, 0); @@ -519,8 +558,17 @@ static sdlSoapBindingFunctionHeaderPtr wsdl_soap_binding_header(sdlCtx* ctx, xml delete_header_int(hf); } smart_str_free(&key); - } else if (is_wsdl_element(trav) && !node_is_equal(trav,"documentation")) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav->name)); + } else { + int wsdl_element = is_wsdl_element(trav); + if (wsdl_element < 0) { + delete_header_int(h); + return NULL; + } + if (wsdl_element && !node_is_equal(trav,"documentation")) { + soap_throw_error("Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav->name)); + delete_header_int(h); + return NULL; + } } trav = trav->next; } @@ -528,7 +576,7 @@ static sdlSoapBindingFunctionHeaderPtr wsdl_soap_binding_header(sdlCtx* ctx, xml return h; } -static void wsdl_soap_binding_body(sdlCtx* ctx, xmlNodePtr node, char* wsdl_soap_namespace, sdlSoapBindingFunctionBody *binding, HashTable* params) +static bool wsdl_soap_binding_body(sdlCtx* ctx, xmlNodePtr node, char* wsdl_soap_namespace, sdlSoapBindingFunctionBody *binding, HashTable* params) { xmlNodePtr trav; @@ -577,7 +625,10 @@ static void wsdl_soap_binding_body(sdlCtx* ctx, xmlNodePtr node, char* wsdl_soap } } ZEND_HASH_FOREACH_END(); if (!found) { - soap_error1(E_ERROR, "Parsing WSDL: Missing part '%s' in ", parts); + soap_throw_error("Parsing WSDL: Missing part '%s' in ", parts); + if (end) *end = ' '; + zend_hash_destroy(&ht); + return false; } parts += strlen(parts); if (end) *end = ' '; @@ -594,16 +645,22 @@ static void wsdl_soap_binding_body(sdlCtx* ctx, xmlNodePtr node, char* wsdl_soap } else if (strncmp((char*)encodingStyleAttribute->children->content, SOAP_1_2_ENC_NAMESPACE, sizeof(SOAP_1_2_ENC_NAMESPACE)) == 0) { binding->encodingStyle = SOAP_ENCODING_1_2; } else { - soap_error1(E_ERROR, "Parsing WSDL: Unknown encodingStyle '%s'", encodingStyleAttribute->children->content); + soap_throw_error("Parsing WSDL: Unknown encodingStyle '%s'", encodingStyleAttribute->children->content); + return false; } } else { - soap_error0(E_ERROR, "Parsing WSDL: Unspecified encodingStyle"); + soap_throw_error("Parsing WSDL: Unspecified encodingStyle"); + return false; } } } else if (node_is_equal_ex(trav, "header", wsdl_soap_namespace)) { sdlSoapBindingFunctionHeaderPtr h = wsdl_soap_binding_header(ctx, trav, wsdl_soap_namespace, 0); smart_str key = {0}; + if (h == NULL) { + return false; + } + if (binding->headers == NULL) { binding->headers = emalloc(sizeof(HashTable)); zend_hash_init(binding->headers, 0, NULL, delete_header, 0); @@ -619,11 +676,19 @@ static void wsdl_soap_binding_body(sdlCtx* ctx, xmlNodePtr node, char* wsdl_soap delete_header_int(h); } smart_str_free(&key); - } else if (is_wsdl_element(trav) && !node_is_equal(trav,"documentation")) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav->name)); + } else { + int wsdl_element = is_wsdl_element(trav); + if (wsdl_element < 0) { + return false; + } + if (wsdl_element && !node_is_equal(trav,"documentation")) { + soap_throw_error("Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav->name)); + return false; + } } trav = trav->next; } + return true; } static HashTable* wsdl_message(const sdlCtx *ctx, const xmlChar* message_name) @@ -639,7 +704,8 @@ static HashTable* wsdl_message(const sdlCtx *ctx, const xmlChar* message_name) xmlNodePtr message = zend_hash_str_find_ptr(&ctx->messages, ctype, strlen(ctype)); if (message == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: Missing with name '%s'", (const char*)message_name); + soap_throw_error("Parsing WSDL: Missing with name '%s'", (const char*)message_name); + return NULL; } parameters = emalloc(sizeof(HashTable)); @@ -651,25 +717,29 @@ static HashTable* wsdl_message(const sdlCtx *ctx, const xmlChar* message_name) sdlParamPtr param; if (trav->ns != NULL && strcmp((char*)trav->ns->href, WSDL_NAMESPACE) != 0) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected extensibility element <%s>", SAFE_STR(trav->name)); + soap_throw_error("Parsing WSDL: Unexpected extensibility element <%s>", SAFE_STR(trav->name)); + goto error; } if (node_is_equal(trav,"documentation")) { trav = trav->next; continue; } if (!node_is_equal(trav,"part")) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav->name)); + soap_throw_error("Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav->name)); + goto error; } xmlNodePtr part = trav; - param = emalloc(sizeof(sdlParam)); - memset(param,0,sizeof(sdlParam)); - param->order = 0; name = get_attribute(part->properties, "name"); if (name == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: No name associated with '%s'", SAFE_STR(message->name)); + soap_throw_error("Parsing WSDL: No name associated with '%s'", SAFE_STR(message->name)); + goto error; } + param = emalloc(sizeof(sdlParam)); + memset(param,0,sizeof(sdlParam)); + param->order = 0; + param->paramName = estrdup((char*)name->children->content); type = get_attribute(part->properties, "type"); @@ -690,11 +760,22 @@ static HashTable* wsdl_message(const sdlCtx *ctx, const xmlChar* message_name) trav = trav->next; } return parameters; + +error: + zend_hash_destroy(parameters); + efree(parameters); + return NULL; } static sdlPtr load_wsdl(zval *this_ptr, char *struri) { sdlCtx ctx = {0}; + /* Not yet owned by ctx.sdl, freed on error */ + sdlBindingPtr tmpbinding = NULL; + sdlFunctionPtr function = NULL; + sdlFaultPtr f = NULL; + uint32_t n; + zval zv; ctx.sdl = emalloc(sizeof(sdl)); memset(ctx.sdl, 0, sizeof(sdl)); @@ -707,438 +788,498 @@ static sdlPtr load_wsdl(zval *this_ptr, char *struri) zend_hash_init(&ctx.portTypes, 0, NULL, NULL, 0); zend_hash_init(&ctx.services, 0, NULL, NULL, 0); - zend_try { - load_wsdl_ex(this_ptr, struri, &ctx, false); - schema_pass2(&ctx); + if (!load_wsdl_ex(this_ptr, struri, &ctx, false) || !schema_pass2(&ctx)) { + goto error; + } - uint32_t n = zend_hash_num_elements(&ctx.services); - if (n == 0) { - soap_error0(E_ERROR, "Parsing WSDL: Couldn't bind to service"); - } + n = zend_hash_num_elements(&ctx.services); + if (n == 0) { + soap_throw_error("Parsing WSDL: Couldn't bind to service"); + goto error; + } - zend_hash_internal_pointer_reset(&ctx.services); - for (uint32_t i = 0; i < n; i++) { - xmlNodePtr service, tmp; - xmlNodePtr trav, port; - bool has_soap_port = false; + zend_hash_internal_pointer_reset(&ctx.services); + for (uint32_t i = 0; i < n; i++) { + xmlNodePtr service, tmp; + xmlNodePtr trav, port; + bool has_soap_port = false; - service = tmp = zend_hash_get_current_data_ptr(&ctx.services); + service = tmp = zend_hash_get_current_data_ptr(&ctx.services); - trav = service->children; - while (trav != NULL) { - xmlAttrPtr type, name, bindingAttr, location; - xmlNodePtr portType, operation; - xmlNodePtr address, binding, trav2; - char *ctype; - sdlBindingPtr tmpbinding; - char *wsdl_soap_namespace = NULL; + trav = service->children; + while (trav != NULL) { + xmlAttrPtr type, name, bindingAttr, location; + xmlNodePtr portType, operation; + xmlNodePtr address, binding, trav2; + char *ctype; + char *wsdl_soap_namespace = NULL; + int wsdl_element = is_wsdl_element(trav); - if (!is_wsdl_element(trav) || node_is_equal(trav,"documentation")) { - trav = trav->next; - continue; - } - if (!node_is_equal(trav,"port")) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav->name)); - } + if (wsdl_element < 0) { + goto error; + } + if (!wsdl_element || node_is_equal(trav,"documentation")) { + trav = trav->next; + continue; + } + if (!node_is_equal(trav,"port")) { + soap_throw_error("Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav->name)); + goto error; + } - port = trav; + port = trav; - tmpbinding = emalloc(sizeof(sdlBinding)); - memset(tmpbinding, 0, sizeof(sdlBinding)); + tmpbinding = emalloc(sizeof(sdlBinding)); + memset(tmpbinding, 0, sizeof(sdlBinding)); - bindingAttr = get_attribute(port->properties, "binding"); - if (bindingAttr == NULL) { - soap_error0(E_ERROR, "Parsing WSDL: No binding associated with "); - } + bindingAttr = get_attribute(port->properties, "binding"); + if (bindingAttr == NULL) { + soap_throw_error("Parsing WSDL: No binding associated with "); + goto error; + } - /* find address and figure out binding type */ - address = NULL; - trav2 = port->children; - while (trav2 != NULL) { - if (node_is_equal(trav2,"address") && trav2->ns) { - if (!strncmp((char*)trav2->ns->href, WSDL_SOAP11_NAMESPACE, sizeof(WSDL_SOAP11_NAMESPACE))) { - address = trav2; - wsdl_soap_namespace = WSDL_SOAP11_NAMESPACE; - tmpbinding->bindingType = BINDING_SOAP; - } else if (!strncmp((char*)trav2->ns->href, WSDL_SOAP12_NAMESPACE, sizeof(WSDL_SOAP12_NAMESPACE))) { - address = trav2; - wsdl_soap_namespace = WSDL_SOAP12_NAMESPACE; - tmpbinding->bindingType = BINDING_SOAP; - } else if (!strncmp((char*)trav2->ns->href, RPC_SOAP12_NAMESPACE, sizeof(RPC_SOAP12_NAMESPACE))) { - address = trav2; - wsdl_soap_namespace = RPC_SOAP12_NAMESPACE; - tmpbinding->bindingType = BINDING_SOAP; - } else if (!strncmp((char*)trav2->ns->href, WSDL_HTTP11_NAMESPACE, sizeof(WSDL_HTTP11_NAMESPACE))) { - address = trav2; - tmpbinding->bindingType = BINDING_HTTP; - } else if (!strncmp((char*)trav2->ns->href, WSDL_HTTP12_NAMESPACE, sizeof(WSDL_HTTP12_NAMESPACE))) { - address = trav2; - tmpbinding->bindingType = BINDING_HTTP; - } - } - if (trav2 != address && is_wsdl_element(trav2) && !node_is_equal(trav2,"documentation")) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav2->name)); + /* find address and figure out binding type */ + address = NULL; + trav2 = port->children; + while (trav2 != NULL) { + if (node_is_equal(trav2,"address") && trav2->ns) { + if (!strncmp((char*)trav2->ns->href, WSDL_SOAP11_NAMESPACE, sizeof(WSDL_SOAP11_NAMESPACE))) { + address = trav2; + wsdl_soap_namespace = WSDL_SOAP11_NAMESPACE; + tmpbinding->bindingType = BINDING_SOAP; + } else if (!strncmp((char*)trav2->ns->href, WSDL_SOAP12_NAMESPACE, sizeof(WSDL_SOAP12_NAMESPACE))) { + address = trav2; + wsdl_soap_namespace = WSDL_SOAP12_NAMESPACE; + tmpbinding->bindingType = BINDING_SOAP; + } else if (!strncmp((char*)trav2->ns->href, RPC_SOAP12_NAMESPACE, sizeof(RPC_SOAP12_NAMESPACE))) { + address = trav2; + wsdl_soap_namespace = RPC_SOAP12_NAMESPACE; + tmpbinding->bindingType = BINDING_SOAP; + } else if (!strncmp((char*)trav2->ns->href, WSDL_HTTP11_NAMESPACE, sizeof(WSDL_HTTP11_NAMESPACE))) { + address = trav2; + tmpbinding->bindingType = BINDING_HTTP; + } else if (!strncmp((char*)trav2->ns->href, WSDL_HTTP12_NAMESPACE, sizeof(WSDL_HTTP12_NAMESPACE))) { + address = trav2; + tmpbinding->bindingType = BINDING_HTTP; } - trav2 = trav2->next; } - if (!address || tmpbinding->bindingType == BINDING_HTTP) { - if (has_soap_port || trav->next || i < n-1) { - efree(tmpbinding); - trav = trav->next; - continue; - } else if (!address) { - soap_error0(E_ERROR, "Parsing WSDL: No address associated with "); + if (trav2 != address) { + wsdl_element = is_wsdl_element(trav2); + if (wsdl_element < 0) { + goto error; + } + if (wsdl_element && !node_is_equal(trav2,"documentation")) { + soap_throw_error("Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav2->name)); + goto error; } } - has_soap_port = true; - - location = get_attribute(address->properties, "location"); - if (!location) { - soap_error0(E_ERROR, "Parsing WSDL: No location associated with "); + trav2 = trav2->next; + } + if (!address || tmpbinding->bindingType == BINDING_HTTP) { + if (has_soap_port || trav->next || i < n-1) { + efree(tmpbinding); + tmpbinding = NULL; + trav = trav->next; + continue; + } else if (!address) { + soap_throw_error("Parsing WSDL: No address associated with "); + goto error; } + } + has_soap_port = true; - tmpbinding->location = estrdup((char*)location->children->content); + location = get_attribute(address->properties, "location"); + if (!location) { + soap_throw_error("Parsing WSDL: No location associated with "); + goto error; + } - ctype = strrchr((char*)bindingAttr->children->content,':'); - if (ctype == NULL) { - ctype = (char*)bindingAttr->children->content; - } else { - ++ctype; - } - if ((tmp = zend_hash_str_find_ptr(&ctx.bindings, ctype, strlen(ctype))) == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: No element with name '%s'", ctype); - } - binding = tmp; + tmpbinding->location = estrdup((char*)location->children->content); - if (tmpbinding->bindingType == BINDING_SOAP) { - sdlSoapBindingPtr soapBinding; - xmlNodePtr soapBindingNode; + ctype = strrchr((char*)bindingAttr->children->content,':'); + if (ctype == NULL) { + ctype = (char*)bindingAttr->children->content; + } else { + ++ctype; + } + if ((tmp = zend_hash_str_find_ptr(&ctx.bindings, ctype, strlen(ctype))) == NULL) { + soap_throw_error("Parsing WSDL: No element with name '%s'", ctype); + goto error; + } + binding = tmp; - soapBinding = emalloc(sizeof(sdlSoapBinding)); - memset(soapBinding, 0, sizeof(sdlSoapBinding)); - soapBinding->style = SOAP_DOCUMENT; + if (tmpbinding->bindingType == BINDING_SOAP) { + sdlSoapBindingPtr soapBinding; + xmlNodePtr soapBindingNode; - soapBindingNode = get_node_ex(binding->children, "binding", wsdl_soap_namespace); - if (soapBindingNode) { - xmlAttrPtr styleAttribute = get_attribute(soapBindingNode->properties, "style"); - if (styleAttribute && !strncmp((char*)styleAttribute->children->content, "rpc", sizeof("rpc"))) { - soapBinding->style = SOAP_RPC; - } + soapBinding = emalloc(sizeof(sdlSoapBinding)); + memset(soapBinding, 0, sizeof(sdlSoapBinding)); + soapBinding->style = SOAP_DOCUMENT; - xmlAttrPtr transportAttribute = get_attribute(soapBindingNode->properties, "transport"); - if (transportAttribute) { - if (strncmp((char*)transportAttribute->children->content, WSDL_HTTP_TRANSPORT, sizeof(WSDL_HTTP_TRANSPORT)) == 0) { - soapBinding->transport = SOAP_TRANSPORT_HTTP; - } else { - /* try the next binding */ - efree(soapBinding); - efree(tmpbinding->location); - efree(tmpbinding); - trav = trav->next; - continue; - } + soapBindingNode = get_node_ex(binding->children, "binding", wsdl_soap_namespace); + if (soapBindingNode) { + xmlAttrPtr styleAttribute = get_attribute(soapBindingNode->properties, "style"); + if (styleAttribute && !strncmp((char*)styleAttribute->children->content, "rpc", sizeof("rpc"))) { + soapBinding->style = SOAP_RPC; + } + + xmlAttrPtr transportAttribute = get_attribute(soapBindingNode->properties, "transport"); + if (transportAttribute) { + if (strncmp((char*)transportAttribute->children->content, WSDL_HTTP_TRANSPORT, sizeof(WSDL_HTTP_TRANSPORT)) == 0) { + soapBinding->transport = SOAP_TRANSPORT_HTTP; + } else { + /* try the next binding */ + efree(soapBinding); + efree(tmpbinding->location); + efree(tmpbinding); + tmpbinding = NULL; + trav = trav->next; + continue; } } - tmpbinding->bindingAttributes = (void *)soapBinding; } + tmpbinding->bindingAttributes = (void *)soapBinding; + } - name = get_attribute(binding->properties, "name"); - if (name == NULL) { - soap_error0(E_ERROR, "Parsing WSDL: Missing 'name' attribute for "); - } - tmpbinding->name = estrdup((char*)name->children->content); + name = get_attribute(binding->properties, "name"); + if (name == NULL) { + soap_throw_error("Parsing WSDL: Missing 'name' attribute for "); + goto error; + } + tmpbinding->name = estrdup((char*)name->children->content); - type = get_attribute(binding->properties, "type"); - if (type == NULL) { - soap_error0(E_ERROR, "Parsing WSDL: Missing 'type' attribute for "); - } + type = get_attribute(binding->properties, "type"); + if (type == NULL) { + soap_throw_error("Parsing WSDL: Missing 'type' attribute for "); + goto error; + } - ctype = strchr((char*)type->children->content,':'); - if (ctype == NULL) { - ctype = (char*)type->children->content; - } else { - ++ctype; + ctype = strchr((char*)type->children->content,':'); + if (ctype == NULL) { + ctype = (char*)type->children->content; + } else { + ++ctype; + } + if ((tmp = zend_hash_str_find_ptr(&ctx.portTypes, ctype, strlen(ctype))) == NULL) { + soap_throw_error("Parsing WSDL: Missing with name '%s'", name->children->content); + goto error; + } + portType = tmp; + + trav2 = binding->children; + while (trav2 != NULL) { + xmlNodePtr input, output, fault, portTypeOperation, trav3; + xmlAttrPtr op_name, paramOrder; + + if (tmpbinding->bindingType == BINDING_SOAP && + node_is_equal_ex(trav2, "binding", wsdl_soap_namespace)) { + trav2 = trav2->next; + continue; } - if ((tmp = zend_hash_str_find_ptr(&ctx.portTypes, ctype, strlen(ctype))) == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: Missing with name '%s'", name->children->content); + wsdl_element = is_wsdl_element(trav2); + if (wsdl_element < 0) { + goto error; + } + if (!wsdl_element || node_is_equal(trav2,"documentation")) { + trav2 = trav2->next; + continue; + } + if (!node_is_equal(trav2,"operation")) { + soap_throw_error("Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav2->name)); + goto error; } - portType = tmp; - - trav2 = binding->children; - while (trav2 != NULL) { - sdlFunctionPtr function; - xmlNodePtr input, output, fault, portTypeOperation, trav3; - xmlAttrPtr op_name, paramOrder; - - if ((tmpbinding->bindingType == BINDING_SOAP && - node_is_equal_ex(trav2, "binding", wsdl_soap_namespace)) || - !is_wsdl_element(trav2) || - node_is_equal(trav2,"documentation")) { - trav2 = trav2->next; - continue; - } - if (!node_is_equal(trav2,"operation")) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav2->name)); - } - operation = trav2; + operation = trav2; - op_name = get_attribute(operation->properties, "name"); - if (op_name == NULL) { - soap_error0(E_ERROR, "Parsing WSDL: Missing 'name' attribute for "); - } + op_name = get_attribute(operation->properties, "name"); + if (op_name == NULL) { + soap_throw_error("Parsing WSDL: Missing 'name' attribute for "); + goto error; + } - trav3 = operation->children; - while (trav3 != NULL) { - if (tmpbinding->bindingType == BINDING_SOAP && - node_is_equal_ex(trav3, "operation", wsdl_soap_namespace)) { - } else if (is_wsdl_element(trav3) && - !node_is_equal(trav3,"input") && - !node_is_equal(trav3,"output") && - !node_is_equal(trav3,"fault") && - !node_is_equal(trav3,"documentation")) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav3->name)); + trav3 = operation->children; + while (trav3 != NULL) { + if (tmpbinding->bindingType == BINDING_SOAP && + node_is_equal_ex(trav3, "operation", wsdl_soap_namespace)) { + } else { + wsdl_element = is_wsdl_element(trav3); + if (wsdl_element < 0) { + goto error; + } + if (wsdl_element && + !node_is_equal(trav3,"input") && + !node_is_equal(trav3,"output") && + !node_is_equal(trav3,"fault") && + !node_is_equal(trav3,"documentation")) { + soap_throw_error("Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(trav3->name)); + goto error; } - trav3 = trav3->next; } + trav3 = trav3->next; + } - portTypeOperation = get_node_with_attribute_ex(portType->children, "operation", WSDL_NAMESPACE, "name", (char*)op_name->children->content, NULL); - if (portTypeOperation == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: Missing / with name '%s'", op_name->children->content); - } + portTypeOperation = get_node_with_attribute_ex(portType->children, "operation", WSDL_NAMESPACE, "name", (char*)op_name->children->content, NULL); + if (portTypeOperation == NULL) { + soap_throw_error("Parsing WSDL: Missing / with name '%s'", op_name->children->content); + goto error; + } - function = emalloc(sizeof(sdlFunction)); - memset(function, 0, sizeof(sdlFunction)); - function->functionName = estrdup((char*)op_name->children->content); + function = emalloc(sizeof(sdlFunction)); + memset(function, 0, sizeof(sdlFunction)); + function->functionName = estrdup((char*)op_name->children->content); + function->binding = tmpbinding; - if (tmpbinding->bindingType == BINDING_SOAP) { - sdlSoapBindingFunctionPtr soapFunctionBinding; - sdlSoapBindingPtr soapBinding; - xmlNodePtr soapOperation; - - soapFunctionBinding = emalloc(sizeof(sdlSoapBindingFunction)); - memset(soapFunctionBinding, 0, sizeof(sdlSoapBindingFunction)); - soapBinding = (sdlSoapBindingPtr)tmpbinding->bindingAttributes; - soapFunctionBinding->style = soapBinding->style; - - soapOperation = get_node_ex(operation->children, "operation", wsdl_soap_namespace); - if (soapOperation) { - xmlAttrPtr soapActionAttribute = get_attribute(soapOperation->properties, "soapAction"); - if (soapActionAttribute) { - soapFunctionBinding->soapAction = estrdup((char*)soapActionAttribute->children->content); - } + if (tmpbinding->bindingType == BINDING_SOAP) { + sdlSoapBindingFunctionPtr soapFunctionBinding; + sdlSoapBindingPtr soapBinding; + xmlNodePtr soapOperation; + + soapFunctionBinding = emalloc(sizeof(sdlSoapBindingFunction)); + memset(soapFunctionBinding, 0, sizeof(sdlSoapBindingFunction)); + soapBinding = (sdlSoapBindingPtr)tmpbinding->bindingAttributes; + soapFunctionBinding->style = soapBinding->style; + + soapOperation = get_node_ex(operation->children, "operation", wsdl_soap_namespace); + if (soapOperation) { + xmlAttrPtr soapActionAttribute = get_attribute(soapOperation->properties, "soapAction"); + if (soapActionAttribute) { + soapFunctionBinding->soapAction = estrdup((char*)soapActionAttribute->children->content); + } - xmlAttrPtr styleAttribute = get_attribute(soapOperation->properties, "style"); - if (styleAttribute) { - if (!strncmp((char*)styleAttribute->children->content, "rpc", sizeof("rpc"))) { - soapFunctionBinding->style = SOAP_RPC; - } else { - soapFunctionBinding->style = SOAP_DOCUMENT; - } + xmlAttrPtr styleAttribute = get_attribute(soapOperation->properties, "style"); + if (styleAttribute) { + if (!strncmp((char*)styleAttribute->children->content, "rpc", sizeof("rpc"))) { + soapFunctionBinding->style = SOAP_RPC; } else { - soapFunctionBinding->style = soapBinding->style; + soapFunctionBinding->style = SOAP_DOCUMENT; } + } else { + soapFunctionBinding->style = soapBinding->style; } - - function->bindingAttributes = (void *)soapFunctionBinding; } - input = get_node_ex(portTypeOperation->children, "input", WSDL_NAMESPACE); - if (input != NULL) { - xmlAttrPtr message; + function->bindingAttributes = (void *)soapFunctionBinding; + } - message = get_attribute(input->properties, "message"); - if (message == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: Missing name for of '%s'", op_name->children->content); - } - function->requestParameters = wsdl_message(&ctx, message->children->content); + input = get_node_ex(portTypeOperation->children, "input", WSDL_NAMESPACE); + if (input != NULL) { + xmlAttrPtr message; + + message = get_attribute(input->properties, "message"); + if (message == NULL) { + soap_throw_error("Parsing WSDL: Missing name for of '%s'", op_name->children->content); + goto error; + } + function->requestParameters = wsdl_message(&ctx, message->children->content); + if (function->requestParameters == NULL) { + goto error; + } /* FIXME - xmlAttrPtr name = get_attribute(input->properties, "name"); - if (name != NULL) { - function->requestName = estrdup(name->children->content); - } else { + xmlAttrPtr name = get_attribute(input->properties, "name"); + if (name != NULL) { + function->requestName = estrdup(name->children->content); + } else { */ - { - function->requestName = estrdup(function->functionName); - } + { + function->requestName = estrdup(function->functionName); + } - if (tmpbinding->bindingType == BINDING_SOAP) { - input = get_node_ex(operation->children, "input", WSDL_NAMESPACE); - if (input != NULL) { - sdlSoapBindingFunctionPtr soapFunctionBinding = function->bindingAttributes; - wsdl_soap_binding_body(&ctx, input, wsdl_soap_namespace, &soapFunctionBinding->input, function->requestParameters); + if (tmpbinding->bindingType == BINDING_SOAP) { + input = get_node_ex(operation->children, "input", WSDL_NAMESPACE); + if (input != NULL) { + sdlSoapBindingFunctionPtr soapFunctionBinding = function->bindingAttributes; + if (!wsdl_soap_binding_body(&ctx, input, wsdl_soap_namespace, &soapFunctionBinding->input, function->requestParameters)) { + goto error; } } } + } - output = get_node_ex(portTypeOperation->children, "output", WSDL_NAMESPACE); - if (output != NULL) { - xmlAttrPtr message; + output = get_node_ex(portTypeOperation->children, "output", WSDL_NAMESPACE); + if (output != NULL) { + xmlAttrPtr message; - message = get_attribute(output->properties, "message"); - if (message == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: Missing name for of '%s'", op_name->children->content); - } - function->responseParameters = wsdl_message(&ctx, message->children->content); + message = get_attribute(output->properties, "message"); + if (message == NULL) { + soap_throw_error("Parsing WSDL: Missing name for of '%s'", op_name->children->content); + goto error; + } + function->responseParameters = wsdl_message(&ctx, message->children->content); + if (function->responseParameters == NULL) { + goto error; + } /* FIXME - xmlAttrPtr name = get_attribute(output->properties, "name"); - if (name != NULL) { - function->responseName = estrdup(name->children->content); - } else if (input == NULL) { - function->responseName = estrdup(function->functionName); - } else { + xmlAttrPtr name = get_attribute(output->properties, "name"); + if (name != NULL) { + function->responseName = estrdup(name->children->content); + } else if (input == NULL) { + function->responseName = estrdup(function->functionName); + } else { */ - { - size_t len = strlen(function->functionName); - function->responseName = emalloc(len + sizeof("Response")); - memcpy(function->responseName, function->functionName, len); - memcpy(function->responseName+len, "Response", sizeof("Response")); - } + { + size_t len = strlen(function->functionName); + function->responseName = emalloc(len + sizeof("Response")); + memcpy(function->responseName, function->functionName, len); + memcpy(function->responseName+len, "Response", sizeof("Response")); + } - if (tmpbinding->bindingType == BINDING_SOAP) { - output = get_node_ex(operation->children, "output", WSDL_NAMESPACE); - if (output != NULL) { - sdlSoapBindingFunctionPtr soapFunctionBinding = function->bindingAttributes; - wsdl_soap_binding_body(&ctx, output, wsdl_soap_namespace, &soapFunctionBinding->output, function->responseParameters); + if (tmpbinding->bindingType == BINDING_SOAP) { + output = get_node_ex(operation->children, "output", WSDL_NAMESPACE); + if (output != NULL) { + sdlSoapBindingFunctionPtr soapFunctionBinding = function->bindingAttributes; + if (!wsdl_soap_binding_body(&ctx, output, wsdl_soap_namespace, &soapFunctionBinding->output, function->responseParameters)) { + goto error; } } } + } - paramOrder = get_attribute(portTypeOperation->properties, "parameterOrder"); - if (paramOrder) { - /* FIXME: */ - } - - fault = portTypeOperation->children; - while (fault != NULL) { - if (node_is_equal_ex(fault, "fault", WSDL_NAMESPACE)) { - xmlAttrPtr message; - sdlFaultPtr f; - - xmlAttrPtr faultNameAttribute = get_attribute(fault->properties, "name"); - if (faultNameAttribute == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: Missing name for of '%s'", op_name->children->content); - } - message = get_attribute(fault->properties, "message"); - if (message == NULL) { - soap_error1(E_ERROR, "Parsing WSDL: Missing name for of '%s'", op_name->children->content); - } + paramOrder = get_attribute(portTypeOperation->properties, "parameterOrder"); + if (paramOrder) { + /* FIXME: */ + } - f = emalloc(sizeof(sdlFault)); - memset(f, 0, sizeof(sdlFault)); + fault = portTypeOperation->children; + while (fault != NULL) { + if (node_is_equal_ex(fault, "fault", WSDL_NAMESPACE)) { + xmlAttrPtr message; - f->name = estrdup((char*)faultNameAttribute->children->content); - f->details = wsdl_message(&ctx, message->children->content); - if (f->details == NULL || zend_hash_num_elements(f->details) > 1) { - soap_error1(E_ERROR, "Parsing WSDL: The fault message '%s' must have a single part", message->children->content); - } + xmlAttrPtr faultNameAttribute = get_attribute(fault->properties, "name"); + if (faultNameAttribute == NULL) { + soap_throw_error("Parsing WSDL: Missing name for of '%s'", op_name->children->content); + goto error; + } + message = get_attribute(fault->properties, "message"); + if (message == NULL) { + soap_throw_error("Parsing WSDL: Missing name for of '%s'", op_name->children->content); + goto error; + } - if (tmpbinding->bindingType == BINDING_SOAP) { - xmlNodePtr soapFault = get_node_with_attribute_ex(operation->children, "fault", WSDL_NAMESPACE, "name", f->name, NULL); - if (soapFault != NULL) { - xmlNodePtr faultNodes = soapFault->children; - while (faultNodes != NULL) { - if (node_is_equal_ex(faultNodes, "fault", wsdl_soap_namespace)) { - sdlSoapBindingFunctionFaultPtr faultBinding; + f = emalloc(sizeof(sdlFault)); + memset(f, 0, sizeof(sdlFault)); - faultBinding = f->bindingAttributes = emalloc(sizeof(sdlSoapBindingFunctionFault)); - memset(f->bindingAttributes, 0, sizeof(sdlSoapBindingFunctionFault)); + f->name = estrdup((char*)faultNameAttribute->children->content); + f->details = wsdl_message(&ctx, message->children->content); + if (f->details == NULL) { + goto error; + } + if (zend_hash_num_elements(f->details) > 1) { + soap_throw_error("Parsing WSDL: The fault message '%s' must have a single part", message->children->content); + goto error; + } - xmlAttrPtr faultUseAttribute = get_attribute(faultNodes->properties, "use"); - if (faultUseAttribute && !strncmp((char*)faultUseAttribute->children->content, "encoded", sizeof("encoded"))) { - faultBinding->use = SOAP_ENCODED; - } else { - faultBinding->use = SOAP_LITERAL; - } + if (tmpbinding->bindingType == BINDING_SOAP) { + xmlNodePtr soapFault = get_node_with_attribute_ex(operation->children, "fault", WSDL_NAMESPACE, "name", f->name, NULL); + if (soapFault != NULL) { + xmlNodePtr faultNodes = soapFault->children; + while (faultNodes != NULL) { + if (node_is_equal_ex(faultNodes, "fault", wsdl_soap_namespace)) { + sdlSoapBindingFunctionFaultPtr faultBinding; + + faultBinding = f->bindingAttributes = emalloc(sizeof(sdlSoapBindingFunctionFault)); + memset(f->bindingAttributes, 0, sizeof(sdlSoapBindingFunctionFault)); + + xmlAttrPtr faultUseAttribute = get_attribute(faultNodes->properties, "use"); + if (faultUseAttribute && !strncmp((char*)faultUseAttribute->children->content, "encoded", sizeof("encoded"))) { + faultBinding->use = SOAP_ENCODED; + } else { + faultBinding->use = SOAP_LITERAL; + } - xmlAttrPtr faultNamespaceAttribute = get_attribute(faultNodes->properties, "namespace"); - if (faultNamespaceAttribute) { - faultBinding->ns = estrdup((char*)faultNamespaceAttribute->children->content); - } + xmlAttrPtr faultNamespaceAttribute = get_attribute(faultNodes->properties, "namespace"); + if (faultNamespaceAttribute) { + faultBinding->ns = estrdup((char*)faultNamespaceAttribute->children->content); + } - if (faultBinding->use == SOAP_ENCODED) { - xmlAttrPtr faultEncodingStyleAttribute = get_attribute(faultNodes->properties, "encodingStyle"); - if (faultEncodingStyleAttribute) { - if (strncmp((char*)faultEncodingStyleAttribute->children->content, SOAP_1_1_ENC_NAMESPACE, sizeof(SOAP_1_1_ENC_NAMESPACE)) == 0) { - faultBinding->encodingStyle = SOAP_ENCODING_1_1; - } else if (strncmp((char*)faultEncodingStyleAttribute->children->content, SOAP_1_2_ENC_NAMESPACE, sizeof(SOAP_1_2_ENC_NAMESPACE)) == 0) { - faultBinding->encodingStyle = SOAP_ENCODING_1_2; - } else { - soap_error1(E_ERROR, "Parsing WSDL: Unknown encodingStyle '%s'", faultEncodingStyleAttribute->children->content); - } + if (faultBinding->use == SOAP_ENCODED) { + xmlAttrPtr faultEncodingStyleAttribute = get_attribute(faultNodes->properties, "encodingStyle"); + if (faultEncodingStyleAttribute) { + if (strncmp((char*)faultEncodingStyleAttribute->children->content, SOAP_1_1_ENC_NAMESPACE, sizeof(SOAP_1_1_ENC_NAMESPACE)) == 0) { + faultBinding->encodingStyle = SOAP_ENCODING_1_1; + } else if (strncmp((char*)faultEncodingStyleAttribute->children->content, SOAP_1_2_ENC_NAMESPACE, sizeof(SOAP_1_2_ENC_NAMESPACE)) == 0) { + faultBinding->encodingStyle = SOAP_ENCODING_1_2; } else { - soap_error0(E_ERROR, "Parsing WSDL: Unspecified encodingStyle"); + soap_throw_error("Parsing WSDL: Unknown encodingStyle '%s'", faultEncodingStyleAttribute->children->content); + goto error; } + } else { + soap_throw_error("Parsing WSDL: Unspecified encodingStyle"); + goto error; } - } else if (is_wsdl_element(faultNodes) && !node_is_equal(faultNodes,"documentation")) { - soap_error1(E_ERROR, "Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(faultNodes->name)); } - faultNodes = faultNodes->next; + } else { + wsdl_element = is_wsdl_element(faultNodes); + if (wsdl_element < 0) { + goto error; + } + if (wsdl_element && !node_is_equal(faultNodes,"documentation")) { + soap_throw_error("Parsing WSDL: Unexpected WSDL element <%s>", SAFE_STR(faultNodes->name)); + goto error; + } } + faultNodes = faultNodes->next; } } - if (function->faults == NULL) { - function->faults = emalloc(sizeof(HashTable)); - zend_hash_init(function->faults, 0, NULL, delete_fault, 0); - } - if (zend_hash_str_add_ptr(function->faults, f->name, strlen(f->name), f) == NULL) { - soap_error2(E_ERROR, "Parsing WSDL: with name '%s' already defined in '%s'", f->name, op_name->children->content); - } } - fault = fault->next; + if (function->faults == NULL) { + function->faults = emalloc(sizeof(HashTable)); + zend_hash_init(function->faults, 0, NULL, delete_fault, 0); + } + if (zend_hash_str_add_ptr(function->faults, f->name, strlen(f->name), f) == NULL) { + soap_throw_error("Parsing WSDL: with name '%s' already defined in '%s'", f->name, op_name->children->content); + goto error; + } + f = NULL; } + fault = fault->next; + } - function->binding = tmpbinding; + { + size_t function_name_len = strlen(function->functionName); + char *lc_function_name = zend_str_tolower_dup(function->functionName, function_name_len); - { - size_t function_name_len = strlen(function->functionName); - char *lc_function_name = zend_str_tolower_dup(function->functionName, function_name_len); + if (zend_hash_str_add_ptr(&ctx.sdl->functions, lc_function_name, function_name_len, function) == NULL) { + zend_hash_next_index_insert_ptr(&ctx.sdl->functions, function); + } + efree(lc_function_name); - if (zend_hash_str_add_ptr(&ctx.sdl->functions, lc_function_name, function_name_len, function) == NULL) { - zend_hash_next_index_insert_ptr(&ctx.sdl->functions, function); + if (function->requestName != NULL && strcmp(function->requestName,function->functionName) != 0) { + if (ctx.sdl->requests == NULL) { + ctx.sdl->requests = emalloc(sizeof(HashTable)); + zend_hash_init(ctx.sdl->requests, 0, NULL, NULL, 0); } - efree(lc_function_name); - if (function->requestName != NULL && strcmp(function->requestName,function->functionName) != 0) { - if (ctx.sdl->requests == NULL) { - ctx.sdl->requests = emalloc(sizeof(HashTable)); - zend_hash_init(ctx.sdl->requests, 0, NULL, NULL, 0); - } - - size_t request_name_len = strlen(function->requestName); - char *lc_request_name = zend_str_tolower_dup(function->requestName, request_name_len); - zend_hash_str_add_ptr(ctx.sdl->requests, lc_request_name, request_name_len, function); - efree(lc_request_name); - } + size_t request_name_len = strlen(function->requestName); + char *lc_request_name = zend_str_tolower_dup(function->requestName, request_name_len); + zend_hash_str_add_ptr(ctx.sdl->requests, lc_request_name, request_name_len, function); + efree(lc_request_name); } - trav2 = trav2->next; - } - - if (!ctx.sdl->bindings) { - ctx.sdl->bindings = emalloc(sizeof(HashTable)); - zend_hash_init(ctx.sdl->bindings, 0, NULL, delete_binding, 0); } + function = NULL; + trav2 = trav2->next; + } - if (!zend_hash_str_add_ptr(ctx.sdl->bindings, tmpbinding->name, strlen(tmpbinding->name), tmpbinding)) { - zend_hash_next_index_insert_ptr(ctx.sdl->bindings, tmpbinding); - } - trav= trav->next; + if (!ctx.sdl->bindings) { + ctx.sdl->bindings = emalloc(sizeof(HashTable)); + zend_hash_init(ctx.sdl->bindings, 0, NULL, delete_binding, 0); } - zend_hash_move_forward(&ctx.services); + if (!zend_hash_str_add_ptr(ctx.sdl->bindings, tmpbinding->name, strlen(tmpbinding->name), tmpbinding)) { + zend_hash_next_index_insert_ptr(ctx.sdl->bindings, tmpbinding); + } + tmpbinding = NULL; + trav= trav->next; } - if (ctx.sdl->bindings == NULL || ctx.sdl->bindings->nNumOfElements == 0) { - soap_error0(E_ERROR, "Parsing WSDL: Could not find any usable binding services in WSDL."); - } + zend_hash_move_forward(&ctx.services); + } - } zend_catch { - /* Avoid persistent memory leak. */ - zend_hash_destroy(&ctx.docs); - zend_bailout(); - } zend_end_try(); + if (ctx.sdl->bindings == NULL || ctx.sdl->bindings->nNumOfElements == 0) { + soap_throw_error("Parsing WSDL: Could not find any usable binding services in WSDL."); + goto error; + } zend_hash_destroy(&ctx.messages); zend_hash_destroy(&ctx.bindings); @@ -1147,6 +1288,29 @@ static sdlPtr load_wsdl(zval *this_ptr, char *struri) zend_hash_destroy(&ctx.docs); return ctx.sdl; + +error: + if (f) { + ZVAL_PTR(&zv, f); + delete_fault(&zv); + } + if (function) { + ZVAL_PTR(&zv, function); + delete_function(&zv); + } + schema_free_ctx(&ctx); + zend_hash_destroy(&ctx.messages); + zend_hash_destroy(&ctx.bindings); + zend_hash_destroy(&ctx.portTypes); + zend_hash_destroy(&ctx.services); + zend_hash_destroy(&ctx.docs); + /* Functions of the binding being parsed point to it, so delete it after them. */ + delete_sdl(ctx.sdl); + if (tmpbinding) { + ZVAL_PTR(&zv, tmpbinding); + delete_binding(&zv); + } + return NULL; } #define WSDL_CACHE_VERSION 0x11 diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index d3c273615eb6..aea3ab0a9415 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -200,6 +200,8 @@ extern HashTable php_soap_defEncNs, php_soap_defEnc, php_soap_defEncIndex; void add_soap_fault(zval *obj, const char *fault_code, const char *fault_string, zend_string *fault_actor, zval *fault_detail, zend_string *lang); +ZEND_COLD void soap_throw_error(const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 1, 2); + #define soap_error0(severity, format) \ php_error(severity, "SOAP-ERROR: " format) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 7266b06f8294..d445da7bf584 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -69,6 +69,7 @@ static void delete_service(soapServicePtr service); static void delete_argv(struct _soap_class *class); static void soap_error_handler(int error_num, zend_string *error_filename, uint32_t error_lineno, zend_string *message); +ZEND_NORETURN static ZEND_COLD void soap_fault_to_error(char *code); #define SOAP_SERVER_BEGIN_CODE() \ bool _old_handler = SOAP_GLOBAL(use_soap_error_handler);\ @@ -1108,6 +1109,9 @@ PHP_METHOD(SoapServer, __construct) if (wsdl) { zend_try { service->sdl = get_sdl(ZEND_THIS, ZSTR_VAL(wsdl), cache_wsdl); + if (service->sdl == NULL) { + soap_fault_to_error("WSDL"); + } } zend_catch { xmlCharEncCloseFunc(service->encoding); service->encoding = NULL; @@ -1990,6 +1994,58 @@ ZEND_NORETURN static void soap_server_fault_en(const char *code, const char *str soap_server_fault(code, string, NULL, NULL, NULL, soap_lang_en); } +/* Throws a SoapFault for an error detected by ext/soap itself, with the same + * "SOAP-ERROR: " message that soap_errorN() produces. + * Unlike soap_errorN(E_ERROR, ...) this does not bail out: the caller must + * release what it owns and report the failure to its own caller. */ +ZEND_COLD void soap_throw_error(const char *format, ...) /* {{{ */ +{ + va_list args; + zend_string *details, *message; + zval fault; + const char *code = SOAP_GLOBAL(error_code); + + va_start(args, format); + details = zend_vstrpprintf(0, format, args); + va_end(args); + message = zend_string_concat2(ZEND_STRL("SOAP-ERROR: "), ZSTR_VAL(details), ZSTR_LEN(details)); + zend_string_release(details); + + if (code == NULL) { + code = "Client"; + } + + if (Z_OBJ(SOAP_GLOBAL(error_object))) { + add_soap_fault_ex_en(&fault, &SOAP_GLOBAL(error_object), code, ZSTR_VAL(message)); + Z_ADDREF(fault); + } else { + ZVAL_NULL(&fault); + set_soap_fault(&fault, NULL, code, ZSTR_VAL(message), NULL, NULL, NULL, soap_lang_en); + } + zend_string_release(message); + zend_throw_exception_object(&fault); +} +/* }}} */ + +/* SoapServer and SoapClient with the "exceptions" option disabled report + * errors as fatal errors. Turns the SoapFault thrown by soap_throw_error() + * into the same fatal error that soap_errorN(E_ERROR, ...) raises. */ +ZEND_NORETURN static ZEND_COLD void soap_fault_to_error(char *code) /* {{{ */ +{ + zval fault; + zend_string *message; + + ZEND_ASSERT(EG(exception) != NULL && instanceof_function(EG(exception)->ce, soap_fault_class_entry)); + ZVAL_OBJ(&fault, EG(exception)); + message = zval_get_string(Z_FAULT_STRING_P(&fault)); + zend_clear_exception(); + + SOAP_GLOBAL(error_code) = code; + php_error(E_ERROR, "%s", ZSTR_VAL(message)); + ZEND_UNREACHABLE(); +} +/* }}} */ + static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, zend_string *error_filename, const uint32_t error_lineno, zend_string *message) /* {{{ */ { bool _old_in_compilation; @@ -2345,6 +2401,13 @@ PHP_METHOD(SoapClient, __construct) SOAP_GLOBAL(soap_version) = soap_version; sdl = get_sdl(this_ptr, ZSTR_VAL(wsdl), cache_wsdl); + if (sdl == NULL) { + if (Z_TYPE_P(Z_CLIENT_EXCEPTIONS_P(this_ptr)) == IS_FALSE) { + soap_fault_to_error("WSDL"); + } + SOAP_GLOBAL(soap_version) = old_soap_version; + goto end; + } zval *sdl_zval = Z_CLIENT_SDL_P(this_ptr); if (Z_TYPE_P(sdl_zval) == IS_OBJECT) { @@ -2362,6 +2425,7 @@ PHP_METHOD(SoapClient, __construct) soap_client_object_fetch(Z_OBJ_P(this_ptr))->typemap = soap_create_typemap(sdl, typemap_ht); } +end: ; SOAP_CLIENT_END_CODE(); } /* }}} */ diff --git a/ext/soap/tests/bugs/gh10026.phpt b/ext/soap/tests/bugs/gh10026.phpt new file mode 100644 index 000000000000..cb9e74c0695a --- /dev/null +++ b/ext/soap/tests/bugs/gh10026.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-10026 (Garbage collection stops after exception in SoapClient) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--FILE-- +self = $node; + } +} + +$wsdl = __DIR__ . '/gh10026.wsdl'; +file_put_contents($wsdl, ''); + +try { + new SoapClient($wsdl); +} catch (SoapFault $e) { + echo $e->faultcode, ": ", $e->getMessage(), "\n"; +} + +var_dump(gc_status()['protected']); +make_cycles(20000); +gc_collect_cycles(); +var_dump(gc_status()['collected']); +?> +--CLEAN-- + +--EXPECT-- +WSDL: SOAP-ERROR: Parsing WSDL: has no name attribute +bool(false) +int(20000) diff --git a/ext/soap/tests/wsdl_errors_no_bailout.phpt b/ext/soap/tests/wsdl_errors_no_bailout.phpt new file mode 100644 index 000000000000..62ba19e75d71 --- /dev/null +++ b/ext/soap/tests/wsdl_errors_no_bailout.phpt @@ -0,0 +1,176 @@ +--TEST-- +SoapClient reports WSDL and schema errors as SoapFault without a bailout +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--FILE-- + '{schema}', + 'schema' => '', + 'messages' => '', + 'port_type' => '{pt_extra}', + 'pt_extra' => '', + 'binding' => '{b_extra}{op_extra}{in_body}{out_body}{b_fault}', + 'b_extra' => '', + 'op_extra' => '', + 'in_body' => '', + 'out_body' => '', + 'b_fault' => '', + 'service' => '{port_body}{svc_extra}', + 'port_body' => '', + 'svc_extra' => '', + 'extra' => '', + ]; + $pairs = []; + foreach ($parts as $k => $v) { + $pairs['{' . $k . '}'] = $v; + } + // Placeholders can nest up to three levels deep + $body = '{types}{messages}{port_type}{binding}{service}{extra}'; + for ($i = 0; $i < 4; $i++) { + $body = strtr($body, $pairs); + } + return '' + . '' + . $body . ''; +} + +$dir = __DIR__ . '/wsdl_errors_no_bailout'; +@mkdir($dir); +$helpers = [ + 'not-a-schema.xml' => '', + 'other-tns.xsd' => '', + 'broken-schema.xsd' => '', + 'broken-schema-notns.xsd' => '', + 'broken-import.wsdl' => '', +]; +foreach ($helpers as $name => $content) { + file_put_contents("$dir/$name", $content); +} + +$cases = [ + 'sdl_required_ext_defs' => ['extra' => ''], + 'sdl_required_ext_port' => ['port_body' => ''], + 'sdl_unexpected_in_types' => ['types' => ''], + 'sdl_import_broken' => ['extra' => ''], + 'sdl_import_schema_broken' => ['extra' => ''], + 'sdl_message_dup' => ['messages' => ''], + 'sdl_no_service' => ['service' => ''], + 'sdl_port_no_location' => ['port_body' => ''], + 'sdl_no_usable_binding' => ['binding' => ''], + 'sdl_operation_not_in_porttype' => ['b_extra' => ''], + 'sdl_output_unknown_message' => ['port_type' => ''], + 'sdl_part_no_name_second' => ['messages' => ''], + 'sdl_body_missing_part' => ['in_body' => ''], + 'sdl_body_unknown_encoding' => ['in_body' => ''], + 'sdl_header_unknown_encoding' => ['in_body' => ''], + 'sdl_headerfault_broken' => ['in_body' => ''], + 'sdl_header_required_ext' => ['in_body' => ''], + 'sdl_fault_two_parts' => ['messages' => '', 'pt_extra' => ''], + 'sdl_fault_dup' => ['pt_extra' => ''], + 'sdl_fault_unknown_encoding' => ['pt_extra' => '', 'b_fault' => ''], + 'sdl_fault_required_ext' => ['pt_extra' => '', 'b_fault' => ''], + 'sdl_second_operation_broken' => ['port_type' => '', 'b_extra' => ''], + 'sdl_second_port_broken' => ['svc_extra' => ''], + 'xsd_minoccurs_overflow' => ['schema' => ''], + 'xsd_any_minoccurs' => ['schema' => ''], + 'xsd_restriction_int_overflow' => ['schema' => ''], + 'xsd_enumeration_no_value' => ['schema' => ''], + 'xsd_import_no_schema' => ['schema' => ''], + 'xsd_import_wrong_tns' => ['schema' => ''], + 'xsd_include_wrong_tns' => ['schema' => ''], + 'xsd_import_broken_content' => ['schema' => ''], + 'xsd_list_subtype_broken' => ['schema' => ''], + 'xsd_union_subtype_broken' => ['schema' => ''], + 'xsd_complex_extension_choice_broken' => ['schema' => ''], + 'xsd_group_dup' => ['schema' => ''], + 'xsd_element_dup' => ['schema' => ''], + 'xsd_element_anon_complex_broken' => ['schema' => ''], + 'xsd_attribute_dup_in_type' => ['schema' => ''], + 'xsd_attribute_subtype_broken' => ['schema' => ''], + 'xsd_attributegroup_dup' => ['schema' => ''], + 'xsd_attributegroup_attr_broken' => ['schema' => ''], + 'xsd_recursive_attributegroup' => ['schema' => ''], + 'xsd_recursive_group' => ['schema' => ''], + 'xsd_unresolved_group' => ['schema' => ''], + 'xsd_unresolved_element_deep' => ['schema' => ''], + 'xsd_p_choice_any' => ['schema' => ''], + 'xsd_p_recursive_attributegroup_pair' => ['schema' => ''], + 'xsd_p_group_ref_later_broken_group' => ['schema' => ''], +]; +foreach ($cases as $name => $parts) { + $file = "$dir/$name.wsdl"; + file_put_contents($file, make_wsdl($parts)); + try { + new SoapClient($file); + echo "$name: no error\n"; + } catch (SoapFault $e) { + echo "$name: ", str_replace([$dir, strtr($dir, '\\', '/')], 'DIR', $e->getMessage()), "\n"; + } + unlink($file); +} +var_dump(gc_status()['protected']); +?> +--CLEAN-- + +--EXPECT-- +sdl_required_ext_defs: SOAP-ERROR: Parsing WSDL: Unknown required WSDL extension 'urn:ext' +sdl_required_ext_port: SOAP-ERROR: Parsing WSDL: Unknown required WSDL extension 'urn:ext' +sdl_unexpected_in_types: SOAP-ERROR: Parsing WSDL: Unexpected WSDL element +sdl_import_broken: SOAP-ERROR: Parsing WSDL: has no name attribute +sdl_import_schema_broken: SOAP-ERROR: Parsing Schema: complexType has no 'name' attribute +sdl_message_dup: SOAP-ERROR: Parsing WSDL: 'Req' already defined +sdl_no_service: SOAP-ERROR: Parsing WSDL: Couldn't bind to service +sdl_port_no_location: SOAP-ERROR: Parsing WSDL: No location associated with +sdl_no_usable_binding: SOAP-ERROR: Parsing WSDL: Could not find any usable binding services in WSDL. +sdl_operation_not_in_porttype: SOAP-ERROR: Parsing WSDL: Missing / with name 'nope' +sdl_output_unknown_message: SOAP-ERROR: Parsing WSDL: Missing with name 'tns:Nope' +sdl_part_no_name_second: SOAP-ERROR: Parsing WSDL: No name associated with 'message' +sdl_body_missing_part: SOAP-ERROR: Parsing WSDL: Missing part 'nope' in +sdl_body_unknown_encoding: SOAP-ERROR: Parsing WSDL: Unknown encodingStyle 'urn:nope' +sdl_header_unknown_encoding: SOAP-ERROR: Parsing WSDL: Unknown encodingStyle 'urn:nope' +sdl_headerfault_broken: SOAP-ERROR: Parsing WSDL: Missing with name 'tns:Nope' +sdl_header_required_ext: SOAP-ERROR: Parsing WSDL: Unknown required WSDL extension 'urn:ext' +sdl_fault_two_parts: SOAP-ERROR: Parsing WSDL: The fault message 'tns:F' must have a single part +sdl_fault_dup: SOAP-ERROR: Parsing WSDL: with name 'f' already defined in 'op' +sdl_fault_unknown_encoding: SOAP-ERROR: Parsing WSDL: Unknown encodingStyle 'urn:nope' +sdl_fault_required_ext: SOAP-ERROR: Parsing WSDL: Unknown required WSDL extension 'urn:ext' +sdl_second_operation_broken: SOAP-ERROR: Parsing WSDL: Missing with name 'tns:Nope' +sdl_second_port_broken: SOAP-ERROR: Parsing WSDL: No element with name 'Nope' +xsd_minoccurs_overflow: SOAP-ERROR: Parsing Schema: minOccurs value is out of range +xsd_any_minoccurs: SOAP-ERROR: Parsing Schema: minOccurs value is out of range +xsd_restriction_int_overflow: SOAP-ERROR: Parsing Schema: maxInclusive value is out of range +xsd_enumeration_no_value: SOAP-ERROR: Parsing Schema: missing restriction value +xsd_import_no_schema: SOAP-ERROR: Parsing Schema: can't import schema from 'DIR/not-a-schema.xml' +xsd_import_wrong_tns: SOAP-ERROR: Parsing Schema: can't import schema from 'DIR/other-tns.xsd', unexpected 'targetNamespace'='urn:other', expected 'urn:i' +xsd_include_wrong_tns: SOAP-ERROR: Parsing Schema: can't include schema from 'DIR/other-tns.xsd', different 'targetNamespace' +xsd_import_broken_content: SOAP-ERROR: Parsing Schema: complexType has no 'name' attribute +xsd_list_subtype_broken: SOAP-ERROR: Parsing Schema: unexpected in simpleType +xsd_union_subtype_broken: SOAP-ERROR: Parsing Schema: unexpected in simpleType +xsd_complex_extension_choice_broken: SOAP-ERROR: Parsing Schema: unexpected in choice +xsd_group_dup: SOAP-ERROR: Parsing Schema: group 'urn:t:G' already defined +xsd_element_dup: SOAP-ERROR: Parsing Schema: element 'urn:t:E' already defined +xsd_element_anon_complex_broken: SOAP-ERROR: Parsing Schema: unexpected in sequence +xsd_attribute_dup_in_type: SOAP-ERROR: Parsing Schema: attribute 'urn:t:a' already defined +xsd_attribute_subtype_broken: SOAP-ERROR: Parsing Schema: unexpected in simpleType +xsd_attributegroup_dup: SOAP-ERROR: Parsing Schema: attributeGroup 'urn:t:AG' already defined +xsd_attributegroup_attr_broken: SOAP-ERROR: Parsing Schema: attribute has no 'name' nor 'ref' attributes +xsd_recursive_attributegroup: SOAP-ERROR: Parsing Schema: recursive attributeGroup 'ref' attribute 'urn:t:AG' +xsd_recursive_group: SOAP-ERROR: Parsing Schema: recursive group 'ref' attribute 'urn:t:G' +xsd_unresolved_group: SOAP-ERROR: Parsing Schema: unresolved group 'ref' attribute 'urn:t:Nope' +xsd_unresolved_element_deep: SOAP-ERROR: Parsing Schema: unresolved element 'ref' attribute 'urn:t:Nope' +xsd_p_choice_any: SOAP-ERROR: Parsing Schema: minOccurs value is out of range +xsd_p_recursive_attributegroup_pair: SOAP-ERROR: Parsing Schema: recursive attributeGroup 'ref' attribute 'urn:t:AG1' +xsd_p_group_ref_later_broken_group: SOAP-ERROR: Parsing Schema: unresolved element 'ref' attribute 'urn:t:Nope' +bool(false)