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)