diff --git a/NEWS b/NEWS index 67c44a4420e9..8ab2ddfec235 100644 --- a/NEWS +++ b/NEWS @@ -103,6 +103,12 @@ 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) + . SoapClient no longer bails out on errors in encoding a request or decoding + a response, 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_encoding.c b/ext/soap/php_encoding.c index 5d52ba7d7798..9884361c8f6e 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -366,6 +366,13 @@ static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xml xmlNodePtr node = NULL; int add_type = 0; + /* The request is discarded, skip the rest of it */ + if (SOAP_ENCODING_ERROR_THROWN()) { + node = xmlNewDocNode(parent->doc, NULL, BAD_CAST("BOGUS"), NULL); + xmlAddChild(parent, node); + return node; + } + if (data) { ZVAL_DEREF(data); } @@ -376,7 +383,10 @@ static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xml zval *ztype = Z_VAR_ENC_TYPE_P(data); if (Z_TYPE_P(ztype) != IS_LONG) { - soap_error0(E_ERROR, "Encoding: SoapVar has no 'enc_type' property"); + soap_encoding_error("Encoding: SoapVar has no 'enc_type' property"); + node = xmlNewDocNode(parent->doc, NULL, BAD_CAST("BOGUS"), NULL); + xmlAddChild(parent, node); + return node; } zval *zstype = Z_VAR_ENC_STYPE_P(data); @@ -497,6 +507,13 @@ xmlNodePtr master_to_xml(encodePtr encode, zval *data, int style, xmlNodePtr par static zval *master_to_zval_int(zval *ret, encodePtr encode, xmlNodePtr data) { + /* The response is discarded, skip the rest of it. Going on could take + * exponential time on references that fail with "Nesting level too deep". */ + if (SOAP_ENCODING_ERROR_THROWN()) { + ZVAL_NULL(ret); + return ret; + } + if (SOAP_GLOBAL(typemap)) { if (encode->details.type_str) { smart_str nscat = {0}; @@ -540,7 +557,9 @@ static zval *master_to_zval_int(zval *ret, encodePtr encode, xmlNodePtr data) } if (encode->to_zval) { if (SOAP_GLOBAL(decode_depth) >= SOAP_MAX_DECODE_DEPTH) { - soap_error0(E_ERROR, "Encoding: Nesting level too deep"); + soap_encoding_error("Encoding: Nesting level too deep"); + ZVAL_NULL(ret); + return ret; } SOAP_GLOBAL(decode_depth)++; ret = encode->to_zval(ret, &encode->details, data); @@ -592,7 +611,7 @@ xmlNodePtr to_xml_user(encodeTypePtr type, zval *data, int style, xmlNodePtr par ZVAL_NULL(&return_value); if (call_user_function(NULL, NULL, &type->map->to_xml, &return_value, 1, data) == FAILURE) { - soap_error0(E_ERROR, "Encoding: Error calling to_xml callback"); + soap_encoding_error("Encoding: Error calling to_xml callback"); } if (Z_TYPE(return_value) == IS_STRING) { xmlDocPtr doc = soap_xmlParseMemory(Z_STRVAL(return_value), Z_STRLEN(return_value)); @@ -629,7 +648,8 @@ zval *to_zval_user(zval *ret, encodeTypePtr type, xmlNodePtr node) xmlFreeNode(copy); if (call_user_function(NULL, NULL, &type->map->to_zval, ret, 1, &data) == FAILURE) { - soap_error0(E_ERROR, "Encoding: Error calling from_xml callback"); + soap_encoding_error("Encoding: Error calling from_xml callback"); + ZVAL_NULL(ret); } else if (EG(exception)) { ZVAL_NULL(ret); } @@ -666,7 +686,7 @@ static zval *to_zval_string(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) { ZVAL_STRING(ret, (char*)data->children->content); } else { - soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); + soap_encoding_error("Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_EMPTY_STRING(ret); @@ -699,7 +719,7 @@ static zval *to_zval_stringr(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) { ZVAL_STRING(ret, (char*)data->children->content); } else { - soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); + soap_encoding_error("Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_EMPTY_STRING(ret); @@ -732,7 +752,7 @@ static zval *to_zval_stringc(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) { ZVAL_STRING(ret, (char*)data->children->content); } else { - soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); + soap_encoding_error("Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_EMPTY_STRING(ret); @@ -751,17 +771,19 @@ static zval *to_zval_base64(zval *ret, encodeTypePtr type, xmlNodePtr data) whiteSpace_collapse(data->children->content); str = php_base64_decode(data->children->content, strlen((char*)data->children->content)); if (!str) { - soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); + soap_encoding_error("Encoding: Invalid value for type '%s'", soap_type_name(type)); + return ret; } ZVAL_STR(ret, str); } else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) { str = php_base64_decode(data->children->content, strlen((char*)data->children->content)); if (!str) { - soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); + soap_encoding_error("Encoding: Invalid value for type '%s'", soap_type_name(type)); + return ret; } ZVAL_STR(ret, str); } else { - soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); + soap_encoding_error("Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_EMPTY_STRING(ret); @@ -782,12 +804,12 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data) if (data->children->type == XML_TEXT_NODE && data->children->next == NULL) { whiteSpace_collapse(data->children->content); } else if (data->children->type != XML_CDATA_SECTION_NODE || data->children->next != NULL) { - soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); + soap_encoding_error("Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); return ret; } content_len = strlen((char*) data->children->content); if (content_len % 2 != 0) { - soap_error1(E_ERROR, "Encoding: Type '%s' value must contain an even number of hexadecimal digits", soap_type_name(type)); + soap_encoding_error("Encoding: Type '%s' value must contain an even number of hexadecimal digits", soap_type_name(type)); return ret; } str = zend_string_alloc(content_len / 2, 0); @@ -800,7 +822,9 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (c >= 'A' && c <= 'F') { ZSTR_VAL(str)[i] = (c - 'A' + 10) << 4; } else { - soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); + soap_encoding_error("Encoding: Invalid value for type '%s'", soap_type_name(type)); + zend_string_efree(str); + return ret; } c = data->children->content[j++]; if (c >= '0' && c <= '9') { @@ -810,7 +834,9 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (c >= 'A' && c <= 'F') { ZSTR_VAL(str)[i] |= c - 'A' + 10; } else { - soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); + soap_encoding_error("Encoding: Invalid value for type '%s'", soap_type_name(type)); + zend_string_efree(str); + return ret; } } ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0'; @@ -929,7 +955,8 @@ static xmlNodePtr to_xml_string(encodeTypePtr type, zval *data, int style, xmlNo err[i++] = 0; } - soap_error1(E_ERROR, "Encoding: string '%s' is not a valid utf-8 string", err); + soap_encoding_error("Encoding: string '%s' is not a valid utf-8 string", err); + efree(err); } text = xmlNewTextLen(BAD_CAST(str), new_len); @@ -1031,11 +1058,11 @@ static zval *to_zval_double(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (strncasecmp((char*)data->children->content, "-INF", sizeof("-INF")-1) == 0) { ZVAL_DOUBLE(ret, -php_get_inf()); } else { - soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); + soap_encoding_error("Encoding: Invalid value for type '%s'", soap_type_name(type)); } } } else { - soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); + soap_encoding_error("Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_NULL(ret); @@ -1064,10 +1091,10 @@ static zval *to_zval_long(zval *ret, encodeTypePtr type, xmlNodePtr data) ZVAL_DOUBLE(ret, dval); break; default: - soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); + soap_encoding_error("Encoding: Invalid value for type '%s'", soap_type_name(type)); } } else { - soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); + soap_encoding_error("Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_NULL(ret); @@ -1132,8 +1159,9 @@ static zval *to_zval_bool(zval *ret, encodeTypePtr type, xmlNodePtr data) return ret; } if (data->children->type != XML_TEXT_NODE || data->children->next != NULL) { - // TODO Convert to exception? - soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); + soap_encoding_error("Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); + ZVAL_NULL(ret); + return ret; } whiteSpace_collapse(data->children->content); @@ -1191,6 +1219,11 @@ static xmlNodePtr to_xml_null(encodeTypePtr type, zval *data, int style, xmlNode static void set_zval_property(zval* object, char* name, zval* val) { + if (SOAP_ENCODING_ERROR_THROWN()) { + /* The object is discarded; a typed property could throw again and hide the error */ + zval_ptr_dtor(val); + return; + } zend_update_property(Z_OBJCE_P(object), Z_OBJ_P(object), name, strlen(name), val); Z_TRY_DELREF_P(val); } @@ -1315,7 +1348,7 @@ static void model_to_zval_object(zval *ret, sdlContentModelPtr model, xmlNodePtr r_node = check_and_resolve_href(node); if (r_node && r_node->children && r_node->children->content) { if (model->u.element->fixed && strcmp(model->u.element->fixed, (char*)r_node->children->content) != 0) { - soap_error3(E_ERROR, "Encoding: Element '%s' has fixed value '%s' (value '%s' is not allowed)", model->u.element->name, model->u.element->fixed, r_node->children->content); + soap_encoding_error("Encoding: Element '%s' has fixed value '%s' (value '%s' is not allowed)", model->u.element->name, model->u.element->fixed, r_node->children->content); } master_to_zval(&val, model->u.element->encode, r_node); } else if (model->u.element->fixed) { @@ -1340,7 +1373,7 @@ static void model_to_zval_object(zval *ret, sdlContentModelPtr model, xmlNodePtr ZVAL_NULL(&val); if (node && node->children && node->children->content) { if (model->u.element->fixed && strcmp(model->u.element->fixed, (char*)node->children->content) != 0) { - soap_error3(E_ERROR, "Encoding: Element '%s' has fixed value '%s' (value '%s' is not allowed)", model->u.element->name, model->u.element->fixed, node->children->content); + soap_encoding_error("Encoding: Element '%s' has fixed value '%s' (value '%s' is not allowed)", model->u.element->name, model->u.element->fixed, node->children->content); } master_to_zval(&val, model->u.element->encode, node); } else if (model->u.element->fixed) { @@ -1543,7 +1576,7 @@ static zval *to_zval_object_ex(zval *ret, encodeTypePtr type, xmlNodePtr data, z if (val && val->children && val->children->content) { str_val = (char*)val->children->content; if (attr->fixed && strcmp(attr->fixed, str_val) != 0) { - soap_error3(E_ERROR, "Encoding: Attribute '%s' has fixed value '%s' (value '%s' is not allowed)", attr->name, attr->fixed, str_val); + soap_encoding_error("Encoding: Attribute '%s' has fixed value '%s' (value '%s' is not allowed)", attr->name, attr->fixed, str_val); } } else if (attr->fixed) { str_val = attr->fixed; @@ -1666,7 +1699,7 @@ static int model_to_xml_object(xmlNodePtr node, sdlContentModelPtr model, zval * property = master_to_xml(enc, val, style, node); if (property->children && property->children->content && model->u.element->fixed && strcmp(model->u.element->fixed, (char*)property->children->content) != 0) { - soap_error3(E_ERROR, "Encoding: Element '%s' has fixed value '%s' (value '%s' is not allowed)", model->u.element->name, model->u.element->fixed, property->children->content); + soap_encoding_error("Encoding: Element '%s' has fixed value '%s' (value '%s' is not allowed)", model->u.element->name, model->u.element->fixed, property->children->content); } } xmlNodeSetName(property, BAD_CAST(model->u.element->name)); @@ -1688,7 +1721,7 @@ static int model_to_xml_object(xmlNodePtr node, sdlContentModelPtr model, zval * property = master_to_xml(enc, data, style, node); if (property->children && property->children->content && model->u.element->fixed && strcmp(model->u.element->fixed, (char*)property->children->content) != 0) { - soap_error3(E_ERROR, "Encoding: Element '%s' has fixed value '%s' (value '%s' is not allowed)", model->u.element->name, model->u.element->fixed, property->children->content); + soap_encoding_error("Encoding: Element '%s' has fixed value '%s' (value '%s' is not allowed)", model->u.element->name, model->u.element->fixed, property->children->content); } } xmlNodeSetName(property, BAD_CAST(model->u.element->name)); @@ -1715,7 +1748,7 @@ static int model_to_xml_object(xmlNodePtr node, sdlContentModelPtr model, zval * return 2; } else { if (strict) { - soap_error1(E_ERROR, "Encoding: object has no '%s' property", model->u.element->name); + soap_encoding_error("Encoding: object has no '%s' property", model->u.element->name); } return 0; } @@ -1746,7 +1779,7 @@ static int model_to_xml_object(xmlNodePtr node, sdlContentModelPtr model, zval * return 2; } else { if (strict) { - soap_error0(E_ERROR, "Encoding: object has no 'any' property"); + soap_encoding_error("Encoding: object has no 'any' property"); } return 0; } @@ -1951,7 +1984,7 @@ static xmlNodePtr to_xml_object(encodeTypePtr type, zval *data, int style, xmlNo dummy = master_to_xml(attr->encode, zattr, SOAP_LITERAL, xmlParam); if (dummy->children && dummy->children->content) { if (attr->fixed && strcmp(attr->fixed, (char*)dummy->children->content) != 0) { - soap_error3(E_ERROR, "Encoding: Attribute '%s' has fixed value '%s' (value '%s' is not allowed)", attr->name, attr->fixed, dummy->children->content); + soap_encoding_error("Encoding: Attribute '%s' has fixed value '%s' (value '%s' is not allowed)", attr->name, attr->fixed, dummy->children->content); } /* we need to handle xml: namespace specially, since it is an implicit schema. Otherwise, use form. @@ -2053,7 +2086,7 @@ static int calc_dimension_12(const char* str) flag = 1; } } else if (*str == '*') { - soap_error0(E_ERROR, "Encoding: '*' may only be first arraySize value in list"); + soap_encoding_error("Encoding: '*' may only be first arraySize value in list"); } else { flag = 0; } @@ -2065,7 +2098,8 @@ static int calc_dimension_12(const char* str) static void soap_array_position_add_digit(int *position, int digit) { if (UNEXPECTED(*position > (INT_MAX - digit) / 10)) { - soap_error0(E_ERROR, "Encoding: array index out of range"); + soap_encoding_error("Encoding: array index out of range"); + return; } *position = (*position * 10) + digit; @@ -2093,7 +2127,7 @@ static int* get_position_12(int dimension, const char* str) } soap_array_position_add_digit(&pos[i], *str - '0'); } else if (*str == '*') { - soap_error0(E_ERROR, "Encoding: '*' may only be first arraySize value in list"); + soap_encoding_error("Encoding: '*' may only be first arraySize value in list"); } else { flag = 0; } @@ -2715,8 +2749,9 @@ static zval *to_zval_array(zval *ret, encodeTypePtr type, xmlNodePtr data) efree(dims); efree(pos); zval_ptr_dtor(ret); - ZVAL_UNDEF(ret); - soap_error0(E_ERROR, "Encoding: array index out of range"); + ZVAL_NULL(ret); + soap_encoding_error("Encoding: array index out of range"); + return ret; } pos[i]++; if (pos[i] < dims[i]) { @@ -2809,12 +2844,14 @@ static zval *to_zval_map(zval *ret, encodeTypePtr type, xmlNodePtr data) FOREACHNODE(trav, "item", item) { xmlKey = get_node(item->children, "key"); if (!xmlKey) { - soap_error0(E_ERROR, "Encoding: Can't decode apache map, missing key"); + soap_encoding_error("Encoding: Can't decode apache map, missing key"); + continue; } xmlValue = get_node(item->children, "value"); if (!xmlValue) { - soap_error0(E_ERROR, "Encoding: Can't decode apache map, missing value"); + soap_encoding_error("Encoding: Can't decode apache map, missing value"); + continue; } ZVAL_NULL(&key); @@ -2827,7 +2864,8 @@ static zval *to_zval_map(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (Z_TYPE(key) == IS_LONG) { zend_hash_index_update(Z_ARRVAL_P(ret), Z_LVAL(key), &value); } else { - soap_error0(E_ERROR, "Encoding: Can't decode apache map, only Strings or Longs are allowed as keys"); + soap_encoding_error("Encoding: Can't decode apache map, only Strings or Longs are allowed as keys"); + zval_ptr_dtor(&value); } zval_ptr_dtor(&key); } @@ -2849,6 +2887,12 @@ static xmlNodePtr guess_xml_convert(encodeTypePtr type, zval *data, int style, x } else { enc = get_conversion(IS_NULL); } + if (!enc) { + /* Not an encodable type, the error has been thrown */ + ret = xmlNewDocNode(parent->doc, NULL, BAD_CAST("BOGUS"), NULL); + xmlAddChild(parent, ret); + return ret; + } ret = master_to_xml_int(enc, data, style, parent, 0); /* if (style == SOAP_LITERAL && SOAP_GLOBAL(sdl)) { @@ -2959,7 +3003,8 @@ static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *forma ta = php_localtime_r(×tamp, &tmbuf); /*ta = php_gmtime_r(×tamp, &tmbuf);*/ if (!ta) { - soap_error1(E_ERROR, "Encoding: Invalid timestamp " ZEND_LONG_FMT, Z_LVAL_P(data)); + soap_encoding_error("Encoding: Invalid timestamp " ZEND_LONG_FMT, Z_LVAL_P(data)); + return xmlParam; } buf = (char *) emalloc(buf_len); @@ -3004,7 +3049,7 @@ static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *forma xmlNodeSetContentLen(xmlParam, BAD_CAST(ZSTR_VAL(formatted_date_string)), ZSTR_LEN(formatted_date_string)); zend_string_release_ex(formatted_date_string, false); } else { - soap_error0(E_ERROR, "Encoding: Invalid DateTimeInterface"); + soap_encoding_error("Encoding: Invalid DateTimeInterface"); } } } @@ -3104,7 +3149,7 @@ static xmlNodePtr to_xml_list(encodeTypePtr enc, zval *data, int style, xmlNodeP } smart_str_appends(&list, (char*)dummy->children->content); } else { - soap_error2(E_ERROR, + soap_encoding_error( "Encoding: Failed to encode list item of type '%s' for list type '%s'", soap_type_name(&list_enc->details), soap_type_name(enc)); } @@ -3148,7 +3193,7 @@ static xmlNodePtr to_xml_list(encodeTypePtr enc, zval *data, int style, xmlNodeP } smart_str_appends(&list, (char*)dummy->children->content); } else { - soap_error2(E_ERROR, + soap_encoding_error( "Encoding: Failed to encode list item of type '%s' for list type '%s'", soap_type_name(&list_enc->details), soap_type_name(enc)); } @@ -3315,7 +3360,7 @@ zval *sdl_guess_convert_zval(zval *ret, encodeTypePtr enc, xmlNodePtr data) } return to_zval_object(ret, enc, data); default: - soap_error0(E_ERROR, "Encoding: Internal Error"); + soap_encoding_error("Encoding: Internal Error"); return guess_zval_convert(ret, enc, data); } } @@ -3383,7 +3428,9 @@ xmlNodePtr sdl_guess_convert_xml(encodeTypePtr enc, zval *data, int style, xmlNo } break; default: - soap_error0(E_ERROR, "Encoding: Internal Error"); + soap_encoding_error("Encoding: Internal Error"); + ret = xmlNewDocNode(parent->doc, NULL, BAD_CAST("BOGUS"), NULL); + xmlAddChild(parent, ret); break; } if (style == SOAP_ENCODED) { @@ -3401,12 +3448,13 @@ static xmlNodePtr check_and_resolve_href(xmlNodePtr data) if (href->children->content[0] == '#') { xmlNodePtr ret = get_node_with_attribute_recursive(data->doc->children, NULL, "id", (char*)&href->children->content[1]); if (!ret) { - soap_error1(E_ERROR, "Encoding: Unresolved reference '%s'", href->children->content); + soap_encoding_error("Encoding: Unresolved reference '%s'", href->children->content); + return data; } return ret; } else { /* TODO: External href....? */ - soap_error1(E_ERROR, "Encoding: External reference '%s'", href->children->content); + soap_encoding_error("Encoding: External reference '%s'", href->children->content); } } /* SOAP 1.2 enc:id enc:ref */ @@ -3422,9 +3470,10 @@ static xmlNodePtr check_and_resolve_href(xmlNodePtr data) } ret = get_node_with_attribute_recursive_ex(data->doc->children, NULL, NULL, "id", (char*)id, SOAP_1_2_ENC_NAMESPACE); if (!ret) { - soap_error1(E_ERROR, "Encoding: Unresolved reference '%s'", href->children->content); + soap_encoding_error("Encoding: Unresolved reference '%s'", href->children->content); + return data; } else if (ret == data) { - soap_error1(E_ERROR, "Encoding: Violation of id and ref information items '%s'", href->children->content); + soap_encoding_error("Encoding: Violation of id and ref information items '%s'", href->children->content); } return ret; } @@ -3575,7 +3624,7 @@ encodePtr get_conversion(int encode) encodePtr enc; if ((enc = zend_hash_index_find_ptr(&php_soap_defEncIndex, encode)) == NULL) { - soap_error0(E_ERROR, "Encoding: Cannot find encoding"); + soap_encoding_error("Encoding: Cannot find encoding"); return NULL; } else { return enc; @@ -3605,7 +3654,9 @@ static encodePtr get_array_type(xmlNodePtr node, zval *array, smart_str *type) Z_OBJCE_P(tmp) == soap_var_class_entry) { zval *ztype = Z_VAR_ENC_TYPE_P(tmp); if (Z_TYPE_P(ztype) != IS_LONG) { - soap_error0(E_ERROR, "Encoding: SoapVar has no 'enc_type' property"); + soap_encoding_error("Encoding: SoapVar has no 'enc_type' property"); + different = TRUE; + break; } cur_type = Z_LVAL_P(ztype); if (cur_type == UNKNOWN_TYPE) { @@ -3682,6 +3733,10 @@ static encodePtr get_array_type(xmlNodePtr node, zval *array, smart_str *type) return enc; } else { enc = get_conversion(cur_type); + if (!enc) { + smart_str_appendl(type, "xsd:anyType", sizeof("xsd:anyType")-1); + return get_conversion(XSD_ANYTYPE); + } get_type_str(node, enc->details.ns, enc->details.type_str, type); return enc; } diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c index 29ed1294f364..70afc763cf91 100644 --- a/ext/soap/php_packet_soap.c +++ b/ext/soap/php_packet_soap.c @@ -261,7 +261,15 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z master_to_zval_with_doc_cleanup(&details, NULL, tmp, response); } } - add_soap_fault(this_ptr, faultcode, faultstring ? ZSTR_VAL(faultstring) : NULL, faultactor, &details, lang); + if (SOAP_ENCODING_ERROR_THROWN()) { + /* Decoding the fault failed, the client reports that error instead */ + zval_ptr_dtor(&details); + } else { + add_soap_fault(this_ptr, faultcode, faultstring ? ZSTR_VAL(faultstring) : NULL, faultactor, &details, lang); + if (Z_REFCOUNTED(details)) { + Z_DELREF(details); + } + } if (faultstring) { zend_string_release_ex(faultstring, 0); } @@ -269,9 +277,6 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z zend_string_release_ex(faultactor, 0); } zend_string_release_ex(lang, false); - if (Z_REFCOUNTED(details)) { - Z_DELREF(details); - } xmlFreeDoc(response); return false; } @@ -442,12 +447,23 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z smart_str_free(&key); } master_to_zval_with_doc_cleanup(&val, enc, trav, response); + if (SOAP_ENCODING_ERROR_THROWN()) { + /* The trace of the SoapFault shares the array of output headers */ + zval_ptr_dtor(&val); + break; + } add_assoc_zval(soap_headers, (const char*)trav->name, &val); } trav = trav->next; } } + if (SOAP_ENCODING_ERROR_THROWN()) { + /* The result is incomplete */ + zval_ptr_dtor(return_value); + ZVAL_NULL(return_value); + } + xmlFreeDoc(response); return true; } 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..2809c14f73bd 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -200,6 +200,36 @@ 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); + +typedef enum { + SOAP_ENCODING_ERROR_FATAL, + SOAP_ENCODING_ERROR_THROW, + SOAP_ENCODING_ERROR_IGNORE, +} soap_encoding_error_mode; + +soap_encoding_error_mode soap_get_encoding_error_mode(void); + +/* Reports an error in encoding or decoding a value. It is a fatal error, as with soap_errorN(E_ERROR, ...), except in a + * call of a SoapClient that uses exceptions: there it is thrown as a SoapFault and does not bail out, so the encoder + * must return a value its callers can handle. The call discards the result once encoding is done. */ +#define soap_encoding_error(...) do { \ + switch (soap_get_encoding_error_mode()) { \ + case SOAP_ENCODING_ERROR_FATAL: \ + php_error(E_ERROR, "SOAP-ERROR: " __VA_ARGS__); \ + break; \ + case SOAP_ENCODING_ERROR_THROW: \ + soap_throw_error(__VA_ARGS__); \ + break; \ + case SOAP_ENCODING_ERROR_IGNORE: \ + break; \ + } \ + } while (0) + +/* An encoding error has been thrown as a SoapFault: the rest of the call is skipped */ +#define SOAP_ENCODING_ERROR_THROWN() \ + (UNEXPECTED(EG(exception)) && soap_get_encoding_error_mode() == SOAP_ENCODING_ERROR_IGNORE) + #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..c087b2c96cc6 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,76 @@ 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); +} +/* }}} */ + +/* Encoding errors are thrown only where a fatal error used to become a SoapFault: in a call of a SoapClient that uses + * exceptions. Only the first one is thrown, the encoding that is still running after it reports nothing. */ +soap_encoding_error_mode soap_get_encoding_error_mode(void) /* {{{ */ +{ + zval *client = &SOAP_GLOBAL(error_object); + + if (!Z_OBJ_P(client) || + !instanceof_function(Z_OBJCE_P(client), soap_class_entry) || + Z_TYPE_P(Z_CLIENT_EXCEPTIONS_P(client)) == IS_FALSE) { + return SOAP_ENCODING_ERROR_FATAL; + } + if (EG(exception) && instanceof_function(EG(exception)->ce, soap_fault_class_entry)) { + return SOAP_ENCODING_ERROR_IGNORE; + } + return SOAP_ENCODING_ERROR_THROW; +} +/* }}} */ + +/* 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 +2419,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 +2443,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(); } /* }}} */ @@ -2523,6 +2605,7 @@ static void do_soap_call(zend_execute_data *execute_data, SOAP_GLOBAL(features) = 0; } + ZVAL_NULL(&response); zend_try { if (sdl != NULL) { fn = get_function(sdl, ZSTR_VAL(function), ZSTR_LEN(function)); @@ -2546,10 +2629,14 @@ static void do_soap_call(zend_execute_data *execute_data, if (binding->bindingType == BINDING_SOAP) { sdlSoapBindingFunctionPtr fnb = (sdlSoapBindingFunctionPtr)fn->bindingAttributes; request = serialize_function_call(this_ptr, fn, NULL, fnb->input.ns, real_args, arg_count, soap_version, soap_headers); - ret = do_request(this_ptr, request, location_c_str, fnb->soapAction, soap_version, one_way, &response); + if (!SOAP_ENCODING_ERROR_THROWN()) { + ret = do_request(this_ptr, request, location_c_str, fnb->soapAction, soap_version, one_way, &response); + } } else { request = serialize_function_call(this_ptr, fn, NULL, sdl->target_ns, real_args, arg_count, soap_version, soap_headers); - ret = do_request(this_ptr, request, location_c_str, NULL, soap_version, one_way, &response); + if (!SOAP_ENCODING_ERROR_THROWN()) { + ret = do_request(this_ptr, request, location_c_str, NULL, soap_version, one_way, &response); + } } xmlFreeDoc(request); @@ -2603,7 +2690,9 @@ static void do_soap_call(zend_execute_data *execute_data, } smart_str_0(&action); - ret = do_request(this_ptr, request, ZSTR_VAL(location), ZSTR_VAL(action.s), soap_version, 0, &response); + if (!SOAP_ENCODING_ERROR_THROWN()) { + ret = do_request(this_ptr, request, ZSTR_VAL(location), ZSTR_VAL(action.s), soap_version, 0, &response); + } smart_str_free(&action); xmlFreeDoc(request); 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/bugs/gh10026_encoding.phpt b/ext/soap/tests/bugs/gh10026_encoding.phpt new file mode 100644 index 000000000000..8df03808dd51 --- /dev/null +++ b/ext/soap/tests/bugs/gh10026_encoding.phpt @@ -0,0 +1,62 @@ +--TEST-- +GH-10026 (Garbage collection stops after exception in SoapClient): encoding errors +--EXTENSIONS-- +soap +--FILE-- +self = $node; + } +} + +class TestSoapClient extends SoapClient { + public function __doRequest($request, $location, $action, $version, $oneWay = false, ?string $uriParserClass = null): string { + return <<<'XML' + + + + + not a number + + + +XML; + } +} + +$client = new TestSoapClient(null, ['location' => 'test://', 'uri' => 'urn:test']); + +// Encoding the request fails +try { + $client->test("\xff"); +} catch (SoapFault $e) { + echo $e->faultcode, ": ", $e->getMessage(), "\n"; +} +var_dump(gc_status()['protected']); + +// Decoding the response fails +try { + $client->test("ok"); +} 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']); +?> +--EXPECT-- +Client: SOAP-ERROR: Encoding: string '\xff...' is not a valid utf-8 string +bool(false) +Client: SOAP-ERROR: Encoding: Invalid value for type 'int' +bool(false) +int(20000) diff --git a/ext/soap/tests/encoding_errors_nesting.phpt b/ext/soap/tests/encoding_errors_nesting.phpt new file mode 100644 index 000000000000..7c92cf31461f --- /dev/null +++ b/ext/soap/tests/encoding_errors_nesting.phpt @@ -0,0 +1,43 @@ +--TEST-- +SoapClient reports "Nesting level too deep" as SoapFault without a bailout +--EXTENSIONS-- +soap +--SKIPIF-- + +--FILE-- +response; + } +} + +$client = new TestSoapClient(null, ['location' => 'test://', 'uri' => 'urn:test']); + +// An array that contains itself 1 and 8 times: decoding stops at the first error +foreach ([1, 8] as $n) { + $client->response = '' + . '' + . '' + . str_repeat('', $n) + . ''; + try { + $client->test(); + } catch (SoapFault $e) { + echo "$n: $e->faultcode: $e->faultstring\n"; + } +} +var_dump(gc_status()['protected']); +?> +--EXPECT-- +1: Client: SOAP-ERROR: Encoding: Nesting level too deep +8: Client: SOAP-ERROR: Encoding: Nesting level too deep +bool(false) diff --git a/ext/soap/tests/encoding_errors_no_bailout.phpt b/ext/soap/tests/encoding_errors_no_bailout.phpt new file mode 100644 index 000000000000..f925e93e3c1d --- /dev/null +++ b/ext/soap/tests/encoding_errors_no_bailout.phpt @@ -0,0 +1,201 @@ +--TEST-- +SoapClient reports encoding and decoding errors as SoapFault without a bailout +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--FILE-- +newInstanceWithoutConstructor(); +} + +$big = ''; + +class Client extends SoapClient +{ + public $response; + public function __doRequest($request, $location, $action, $version, $oneWay = false, ?string $uriParserClass = null): ?string + { + return $this->response; + } +} + +function envelope(string $body, string $header = ''): string +{ + return '' . $header . '' . $body . ''; +} + +$wsdl = __DIR__ . '/encoding_errors_no_bailout.wsdl'; +file_put_contents($wsdl, <<<'WSDL' + + + + + + + + + + + + + + + + + + + +WSDL); + +$cases = [ + + 'dec_string_elem' => ['value' => ''], + 'dec_normalized_elem' => ['value' => ''], + 'dec_token_elem' => ['value' => ''], + 'dec_base64_elem' => ['value' => ''], + 'dec_hexbin_elem' => ['value' => ''], + 'dec_double_elem' => ['value' => ''], + 'dec_long_elem' => ['value' => ''], + 'dec_bool_elem' => ['value' => ''], + 'dec_hexbin_odd' => ['value' => 'ABC'], + 'dec_hexbin_high' => ['value' => 'ZA'], + 'dec_hexbin_low' => ['value' => 'AZ'], + 'dec_double_invalid' => ['value' => 'abc'], + 'dec_long_invalid' => ['value' => 'abc'], + 'dec_two_values_first_wins' => ['value' => 'abcABC'], + 'dec_map_no_key' => ['value' => '1'], + 'dec_map_no_value' => ['value' => 'k'], + 'dec_map_bad_key' => ['value' => '11'], + 'dec_array_star' => ['value' => 'a'], + 'dec_array_position_digits' => ['value' => 'a'], + 'dec_array_position_increment' => ['value' => 'ab'], + 'dec_href_unresolved' => ['value' => ''], + 'dec_href_external' => ['value' => ''], + 'dec_ref12_unresolved' => ['value' => ''], + 'dec_ref12_self' => ['value' => ''], + 'dec_fault_string' => ['body' => 'SOAP-ENV:Server'], + 'dec_header' => ['header' => '

1

abc

', 'value' => 'abc'], + 'dec_classmap_typed' => ['wsdl' => 'Need', 'value' => 'abc', 'opts' => ['classmap' => ['Need' => 'TypedNeed']]], + 'enc_enum_after_error' => ['args' => fn() => [no_ctor(SoapVar::class), Plain::A]], + 'dec_fixed_element' => ['wsdl' => 'Fixed1', 'value' => 'zzz'], + 'dec_fixed_element_repeated' => ['wsdl' => 'FixedN', 'value' => 'abczzz'], + 'dec_fixed_attribute' => ['wsdl' => 'FixedAttr', 'value' => ''], + + 'enc_soapvar_no_enc_type' => ['args' => fn() => [no_ctor(SoapVar::class)]], + 'enc_soapvar_no_enc_type_in_array' => ['args' => fn() => [[no_ctor(SoapVar::class), no_ctor(SoapVar::class)]]], + 'enc_soapvar_unknown_type' => ['args' => function () { $v = no_ctor(SoapVar::class); $v->enc_type = 99999; $v->enc_value = 1; return [$v]; }], + 'enc_resource' => ['args' => fn() => [fopen('php://memory', 'r')]], + 'enc_resource_in_object' => ['args' => fn() => [(object) ['a' => 1, 'r' => fopen('php://memory', 'r'), 'b' => 2]]], + 'enc_invalid_utf8' => ['args' => fn() => ["ab\xffcd"]], + 'enc_invalid_utf8_twice' => ['args' => fn() => ["ab\xffcd", "\xfe"]], + 'enc_invalid_datetime' => ['args' => fn() => [new SoapVar(no_ctor(DateTime::class), XSD_DATETIME)]], + 'enc_fixed_element' => ['wsdl' => 'Fixed1', 'args' => fn() => [(object) ['f' => 'zzz']]], + 'enc_fixed_element_array' => ['wsdl' => 'FixedN', 'args' => fn() => [(object) ['f' => ['abc', 'zzz']]]], + 'enc_fixed_attribute' => ['wsdl' => 'FixedAttr', 'args' => fn() => [(object) ['a' => 'zzz']]], + 'enc_missing_property' => ['wsdl' => 'Need', 'args' => fn() => [(object) ['y' => 1]]], + 'enc_missing_any' => ['wsdl' => 'NeedAny', 'args' => fn() => [(object) []]], + 'enc_list_item' => ['wsdl' => 'IntList', 'args' => fn() => [[1, null, 3]]], +]; + +foreach ($cases as $name => $case) { + $fn = isset($case['wsdl']) ? 'op' . $case['wsdl'] : 'test'; + $opts = ($case['opts'] ?? []) + (isset($case['wsdl']) ? [] : ['location' => 'test://', 'uri' => 'urn:t']); + $client = new Client(isset($case['wsdl']) ? $wsdl : null, $opts + ['trace' => true]); + $value = $case['value'] ?? null; + $body = $case['body'] ?? ($value ? "$value" : ""); + $client->response = envelope($body, $case['header'] ?? ''); + $headers = []; + try { + $client->__soapCall($fn, isset($case['args']) ? ($case['args'])() : [], null, null, $headers); + echo "$name: no error\n"; + } catch (SoapFault $e) { + echo "$name: $e->faultcode: $e->faultstring\n"; + $fault = (new ReflectionProperty(SoapClient::class, '__soap_fault'))->getValue($client); + if ($fault !== $e) { + echo " __soap_fault is not the thrown SoapFault\n"; + } + if ($e->getPrevious()) { + echo " previous: ", get_class($e->getPrevious()), "\n"; + } + if (isset($case['args']) && $client->__getLastRequest() !== null) { + echo " the request was sent\n"; + } + if ($headers) { + echo " output headers: ", implode(', ', array_keys($headers)), "\n"; + } + } +} +var_dump(gc_status()['protected']); +?> +--CLEAN-- + +--EXPECT-- +dec_string_elem: Client: SOAP-ERROR: Encoding: Type 'string' value must contain a single text or CDATA node +dec_normalized_elem: Client: SOAP-ERROR: Encoding: Type 'normalizedString' value must contain a single text or CDATA node +dec_token_elem: Client: SOAP-ERROR: Encoding: Type 'token' value must contain a single text or CDATA node +dec_base64_elem: Client: SOAP-ERROR: Encoding: Type 'base64Binary' value must contain a single text or CDATA node +dec_hexbin_elem: Client: SOAP-ERROR: Encoding: Type 'hexBinary' value must contain a single text or CDATA node +dec_double_elem: Client: SOAP-ERROR: Encoding: Type 'double' value must contain a single text or CDATA node +dec_long_elem: Client: SOAP-ERROR: Encoding: Type 'int' value must contain a single text or CDATA node +dec_bool_elem: Client: SOAP-ERROR: Encoding: Type 'boolean' value must contain a single text or CDATA node +dec_hexbin_odd: Client: SOAP-ERROR: Encoding: Type 'hexBinary' value must contain an even number of hexadecimal digits +dec_hexbin_high: Client: SOAP-ERROR: Encoding: Invalid value for type 'hexBinary' +dec_hexbin_low: Client: SOAP-ERROR: Encoding: Invalid value for type 'hexBinary' +dec_double_invalid: Client: SOAP-ERROR: Encoding: Invalid value for type 'double' +dec_long_invalid: Client: SOAP-ERROR: Encoding: Invalid value for type 'int' +dec_two_values_first_wins: Client: SOAP-ERROR: Encoding: Invalid value for type 'int' +dec_map_no_key: Client: SOAP-ERROR: Encoding: Can't decode apache map, missing key +dec_map_no_value: Client: SOAP-ERROR: Encoding: Can't decode apache map, missing value +dec_map_bad_key: Client: SOAP-ERROR: Encoding: Can't decode apache map, only Strings or Longs are allowed as keys +dec_array_star: Client: SOAP-ERROR: Encoding: '*' may only be first arraySize value in list +dec_array_position_digits: Client: SOAP-ERROR: Encoding: array index out of range +dec_array_position_increment: Client: SOAP-ERROR: Encoding: array index out of range +dec_href_unresolved: Client: SOAP-ERROR: Encoding: Unresolved reference '#nope' +dec_href_external: Client: SOAP-ERROR: Encoding: External reference 'http://example.org/x' +dec_ref12_unresolved: Client: SOAP-ERROR: Encoding: Unresolved reference 'nope' +dec_ref12_self: Client: SOAP-ERROR: Encoding: Violation of id and ref information items 'me' +dec_fault_string: Client: SOAP-ERROR: Encoding: Type 'string' value must contain a single text or CDATA node +dec_header: Client: SOAP-ERROR: Encoding: Invalid value for type 'int' + output headers: h1 +dec_classmap_typed: Client: SOAP-ERROR: Encoding: Invalid value for type 'int' +enc_enum_after_error: Client: SOAP-ERROR: Encoding: SoapVar has no 'enc_type' property +dec_fixed_element: Client: SOAP-ERROR: Encoding: Element 'f' has fixed value 'abc' (value 'zzz' is not allowed) +dec_fixed_element_repeated: Client: SOAP-ERROR: Encoding: Element 'f' has fixed value 'abc' (value 'zzz' is not allowed) +dec_fixed_attribute: Client: SOAP-ERROR: Encoding: Attribute 'a' has fixed value 'x' (value 'zzz' is not allowed) +enc_soapvar_no_enc_type: Client: SOAP-ERROR: Encoding: SoapVar has no 'enc_type' property +enc_soapvar_no_enc_type_in_array: Client: SOAP-ERROR: Encoding: SoapVar has no 'enc_type' property +enc_soapvar_unknown_type: Client: SOAP-ERROR: Encoding: Cannot find encoding +enc_resource: Client: SOAP-ERROR: Encoding: Cannot find encoding +enc_resource_in_object: Client: SOAP-ERROR: Encoding: Cannot find encoding +enc_invalid_utf8: Client: SOAP-ERROR: Encoding: string 'ab\xff...' is not a valid utf-8 string +enc_invalid_utf8_twice: Client: SOAP-ERROR: Encoding: string 'ab\xff...' is not a valid utf-8 string +enc_invalid_datetime: Client: SOAP-ERROR: Encoding: Invalid DateTimeInterface +enc_fixed_element: Client: SOAP-ERROR: Encoding: Element 'f' has fixed value 'abc' (value 'zzz' is not allowed) +enc_fixed_element_array: Client: SOAP-ERROR: Encoding: Element 'f' has fixed value 'abc' (value 'zzz' is not allowed) +enc_fixed_attribute: Client: SOAP-ERROR: Encoding: Attribute 'a' has fixed value 'x' (value 'zzz' is not allowed) +enc_missing_property: Client: SOAP-ERROR: Encoding: object has no 'x' property +enc_missing_any: Client: SOAP-ERROR: Encoding: object has no 'any' property +enc_list_item: Client: SOAP-ERROR: Encoding: Failed to encode list item of type 'int' for list type 'IntList' +bool(false) 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)