1 /* 2 * Schema cache implementation 3 * 4 * Copyright 2007 Huw Davies 5 * Copyright 2010 Adam Martinson for CodeWeavers 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include "precomp.h" 23 24 #include <assert.h> 25 26 #ifdef HAVE_LIBXML2 27 # include <libxml/xmlschemas.h> 28 # include <libxml/schemasInternals.h> 29 # include <libxml/parserInternals.h> 30 # include <libxml/xpath.h> 31 #endif 32 33 /* We use a chained hashtable, which can hold any number of schemas 34 * TODO: grow/shrink hashtable depending on load factor 35 * TODO: implement read-only where appropriate 36 */ 37 38 /* This is just the number of buckets, should be prime */ 39 #define DEFAULT_HASHTABLE_SIZE 17 40 41 #ifdef HAVE_LIBXML2 42 43 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI); 44 45 static const xmlChar XSD_schema[] = "schema"; 46 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema"; 47 static const xmlChar XDR_schema[] = "Schema"; 48 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data"; 49 static const xmlChar DT_nsURI[] = "urn:schemas-microsoft-com:datatypes"; 50 51 static xmlChar * datatypes_src; 52 static int datatypes_len; 53 static HGLOBAL datatypes_handle; 54 static HRSRC datatypes_rsrc; 55 static xmlSchemaPtr datatypes_schema; 56 57 static const WCHAR emptyW[] = {0}; 58 59 /* Supported types: 60 * msxml3 - XDR only 61 * msxml4 - XDR & XSD 62 * msxml5 - XDR & XSD 63 * mxsml6 - XSD only 64 * 65 * CacheType_NS is a special type used for read-only collection build with 66 * IXMLDOMDocument2::namespaces() 67 */ 68 typedef enum { 69 CacheEntryType_Invalid, 70 CacheEntryType_XDR, 71 CacheEntryType_XSD, 72 CacheEntryType_NS 73 } CacheEntryType; 74 75 typedef struct 76 { 77 DispatchEx dispex; 78 IXMLDOMSchemaCollection2 IXMLDOMSchemaCollection2_iface; 79 LONG ref; 80 81 MSXML_VERSION version; 82 xmlHashTablePtr cache; 83 xmlChar **uris; 84 int allocated; 85 int count; 86 87 VARIANT_BOOL validateOnLoad; 88 int read_only; 89 } schema_cache; 90 91 typedef struct 92 { 93 CacheEntryType type; 94 xmlSchemaPtr schema; 95 xmlDocPtr doc; 96 LONG ref; 97 } cache_entry; 98 99 static const tid_t schema_cache_se_tids[] = { 100 IXMLDOMSchemaCollection_tid, 101 IXMLDOMSchemaCollection2_tid, 102 NULL_tid 103 }; 104 105 /* datatypes lookup stuff 106 * generated with help from gperf */ 107 #define DT_MIN_STR_LEN 2 108 #define DT_MAX_STR_LEN 11 109 #define DT_MIN_HASH_VALUE 2 110 #define DT_MAX_HASH_VALUE 115 111 112 static const xmlChar DT_bin_base64[] = "bin.base64"; 113 static const xmlChar DT_bin_hex[] = "bin.hex"; 114 static const xmlChar DT_boolean[] = "boolean"; 115 static const xmlChar DT_char[] = "char"; 116 static const xmlChar DT_date[] = "date"; 117 static const xmlChar DT_date_tz[] = "date.tz"; 118 static const xmlChar DT_dateTime[] = "dateTime"; 119 static const xmlChar DT_dateTime_tz[] = "dateTime.tz"; 120 static const xmlChar DT_entity[] = "entity"; 121 static const xmlChar DT_entities[] = "entities"; 122 static const xmlChar DT_enumeration[] = "enumeration"; 123 static const xmlChar DT_fixed_14_4[] = "fixed.14.4"; 124 static const xmlChar DT_float[] = "float"; 125 static const xmlChar DT_i1[] = "i1"; 126 static const xmlChar DT_i2[] = "i2"; 127 static const xmlChar DT_i4[] = "i4"; 128 static const xmlChar DT_i8[] = "i8"; 129 static const xmlChar DT_id[] = "id"; 130 static const xmlChar DT_idref[] = "idref"; 131 static const xmlChar DT_idrefs[] = "idrefs"; 132 static const xmlChar DT_int[] = "int"; 133 static const xmlChar DT_nmtoken[] = "nmtoken"; 134 static const xmlChar DT_nmtokens[] = "nmtokens"; 135 static const xmlChar DT_notation[] = "notation"; 136 static const xmlChar DT_number[] = "number"; 137 static const xmlChar DT_r4[] = "r4"; 138 static const xmlChar DT_r8[] = "r8"; 139 static const xmlChar DT_string[] = "string"; 140 static const xmlChar DT_time[] = "time"; 141 static const xmlChar DT_time_tz[] = "time.tz"; 142 static const xmlChar DT_ui1[] = "ui1"; 143 static const xmlChar DT_ui2[] = "ui2"; 144 static const xmlChar DT_ui4[] = "ui4"; 145 static const xmlChar DT_ui8[] = "ui8"; 146 static const xmlChar DT_uri[] = "uri"; 147 static const xmlChar DT_uuid[] = "uuid"; 148 149 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0}; 150 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0}; 151 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0}; 152 static const OLECHAR wDT_char[] = {'c','h','a','r',0}; 153 static const OLECHAR wDT_date[] = {'d','a','t','e',0}; 154 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0}; 155 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0}; 156 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0}; 157 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0}; 158 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0}; 159 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0}; 160 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0}; 161 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0}; 162 static const OLECHAR wDT_i1[] = {'i','1',0}; 163 static const OLECHAR wDT_i2[] = {'i','2',0}; 164 static const OLECHAR wDT_i4[] = {'i','4',0}; 165 static const OLECHAR wDT_i8[] = {'i','8',0}; 166 static const OLECHAR wDT_id[] = {'i','d',0}; 167 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0}; 168 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0}; 169 static const OLECHAR wDT_int[] = {'i','n','t',0}; 170 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0}; 171 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0}; 172 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0}; 173 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0}; 174 static const OLECHAR wDT_r4[] = {'r','4',0}; 175 static const OLECHAR wDT_r8[] = {'r','8',0}; 176 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0}; 177 static const OLECHAR wDT_time[] = {'t','i','m','e',0}; 178 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0}; 179 static const OLECHAR wDT_ui1[] = {'u','i','1',0}; 180 static const OLECHAR wDT_ui2[] = {'u','i','2',0}; 181 static const OLECHAR wDT_ui4[] = {'u','i','4',0}; 182 static const OLECHAR wDT_ui8[] = {'u','i','8',0}; 183 static const OLECHAR wDT_uri[] = {'u','r','i',0}; 184 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0}; 185 186 static const BYTE hash_assoc_values[] = 187 { 188 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 189 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 190 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 191 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 192 116, 116, 116, 116, 116, 116, 10, 116, 116, 55, 193 45, 116, 5, 116, 0, 116, 0, 116, 116, 116, 194 116, 116, 116, 116, 116, 5, 0, 0, 20, 0, 195 0, 10, 0, 0, 116, 0, 0, 0, 15, 5, 196 116, 116, 10, 0, 0, 0, 116, 116, 0, 0, 197 10, 116, 116, 116, 116, 116, 116, 5, 0, 0, 198 20, 0, 0, 10, 0, 0, 116, 0, 0, 0, 199 15, 5, 116, 116, 10, 0, 0, 0, 116, 116, 200 0, 0, 10, 116, 116, 116, 116, 116, 116, 116, 201 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 202 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 203 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 204 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 205 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 206 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 207 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 208 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 209 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 210 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 211 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 212 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 213 116, 116, 116, 116, 116, 116 214 }; 215 216 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...) 217 { 218 va_list ap; 219 va_start(ap, msg); 220 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap); 221 va_end(ap); 222 } 223 224 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...) 225 { 226 va_list ap; 227 va_start(ap, msg); 228 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap); 229 va_end(ap); 230 } 231 232 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS 233 static void parser_serror(void* ctx, xmlErrorPtr err) 234 { 235 LIBXML2_CALLBACK_SERROR(Schema_parse, err); 236 } 237 #endif 238 239 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx) 240 { 241 TRACE("(%p)\n", spctx); 242 243 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL); 244 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS 245 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL); 246 #endif 247 248 return xmlSchemaParse(spctx); 249 } 250 251 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...) 252 { 253 va_list ap; 254 va_start(ap, msg); 255 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap); 256 va_end(ap); 257 } 258 259 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...) 260 { 261 va_list ap; 262 va_start(ap, msg); 263 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap); 264 va_end(ap); 265 } 266 267 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS 268 static void validate_serror(void* ctx, xmlErrorPtr err) 269 { 270 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err); 271 } 272 #endif 273 274 static HRESULT schema_cache_get_item(IUnknown *iface, LONG index, VARIANT *item) 275 { 276 V_VT(item) = VT_BSTR; 277 return IXMLDOMSchemaCollection2_get_namespaceURI((IXMLDOMSchemaCollection2*)iface, index, &V_BSTR(item)); 278 } 279 280 static const struct enumvariant_funcs schemacache_enumvariant = { 281 schema_cache_get_item, 282 NULL 283 }; 284 285 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree) 286 { 287 xmlSchemaValidCtxtPtr svctx; 288 int err; 289 290 TRACE("(%p, %p)\n", schema, tree); 291 /* TODO: if validateOnLoad property is false, 292 * we probably need to validate the schema here. */ 293 svctx = xmlSchemaNewValidCtxt(schema); 294 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL); 295 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS 296 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL); 297 #endif 298 299 if (tree->type == XML_DOCUMENT_NODE) 300 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree); 301 else 302 err = xmlSchemaValidateOneElement(svctx, tree); 303 304 xmlSchemaFreeValidCtxt(svctx); 305 return err? S_FALSE : S_OK; 306 } 307 308 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */) 309 { 310 DWORD hval = (len == -1)? xmlStrlen(str) : len; 311 312 switch (hval) 313 { 314 default: 315 hval += hash_assoc_values[str[10]]; 316 /*FALLTHROUGH*/ 317 case 10: 318 hval += hash_assoc_values[str[9]]; 319 /*FALLTHROUGH*/ 320 case 9: 321 hval += hash_assoc_values[str[8]]; 322 /*FALLTHROUGH*/ 323 case 8: 324 hval += hash_assoc_values[str[7]]; 325 /*FALLTHROUGH*/ 326 case 7: 327 hval += hash_assoc_values[str[6]]; 328 /*FALLTHROUGH*/ 329 case 6: 330 hval += hash_assoc_values[str[5]]; 331 /*FALLTHROUGH*/ 332 case 5: 333 hval += hash_assoc_values[str[4]]; 334 /*FALLTHROUGH*/ 335 case 4: 336 hval += hash_assoc_values[str[3]]; 337 /*FALLTHROUGH*/ 338 case 3: 339 hval += hash_assoc_values[str[2]]; 340 /*FALLTHROUGH*/ 341 case 2: 342 hval += hash_assoc_values[str[1]]; 343 /*FALLTHROUGH*/ 344 case 1: 345 hval += hash_assoc_values[str[0]]; 346 break; 347 } 348 return hval; 349 } 350 351 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */) 352 { 353 DWORD hval = (len == -1)? lstrlenW(bstr) : len; 354 355 switch (hval) 356 { 357 default: 358 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]]; 359 /*FALLTHROUGH*/ 360 case 10: 361 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]]; 362 /*FALLTHROUGH*/ 363 case 9: 364 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]]; 365 /*FALLTHROUGH*/ 366 case 8: 367 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]]; 368 /*FALLTHROUGH*/ 369 case 7: 370 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]]; 371 /*FALLTHROUGH*/ 372 case 6: 373 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]]; 374 /*FALLTHROUGH*/ 375 case 5: 376 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]]; 377 /*FALLTHROUGH*/ 378 case 4: 379 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]]; 380 /*FALLTHROUGH*/ 381 case 3: 382 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]]; 383 /*FALLTHROUGH*/ 384 case 2: 385 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]]; 386 /*FALLTHROUGH*/ 387 case 1: 388 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]]; 389 break; 390 } 391 return hval; 392 } 393 394 static const xmlChar *const DT_string_table[LAST_DT] = 395 { 396 DT_bin_base64, 397 DT_bin_hex, 398 DT_boolean, 399 DT_char, 400 DT_date, 401 DT_date_tz, 402 DT_dateTime, 403 DT_dateTime_tz, 404 DT_entity, 405 DT_entities, 406 DT_enumeration, 407 DT_fixed_14_4, 408 DT_float, 409 DT_i1, 410 DT_i2, 411 DT_i4, 412 DT_i8, 413 DT_id, 414 DT_idref, 415 DT_idrefs, 416 DT_int, 417 DT_nmtoken, 418 DT_nmtokens, 419 DT_notation, 420 DT_number, 421 DT_r4, 422 DT_r8, 423 DT_string, 424 DT_time, 425 DT_time_tz, 426 DT_ui1, 427 DT_ui2, 428 DT_ui4, 429 DT_ui8, 430 DT_uri, 431 DT_uuid 432 }; 433 434 static const WCHAR *const DT_wstring_table[LAST_DT] = 435 { 436 wDT_bin_base64, 437 wDT_bin_hex, 438 wDT_boolean, 439 wDT_char, 440 wDT_date, 441 wDT_date_tz, 442 wDT_dateTime, 443 wDT_dateTime_tz, 444 wDT_entity, 445 wDT_entities, 446 wDT_enumeration, 447 wDT_fixed_14_4, 448 wDT_float, 449 wDT_i1, 450 wDT_i2, 451 wDT_i4, 452 wDT_i8, 453 wDT_id, 454 wDT_idref, 455 wDT_idrefs, 456 wDT_int, 457 wDT_nmtoken, 458 wDT_nmtokens, 459 wDT_notation, 460 wDT_number, 461 wDT_r4, 462 wDT_r8, 463 wDT_string, 464 wDT_time, 465 wDT_time_tz, 466 wDT_ui1, 467 wDT_ui2, 468 wDT_ui4, 469 wDT_ui8, 470 wDT_uri, 471 wDT_uuid 472 }; 473 474 static const XDR_DT DT_lookup_table[] = 475 { 476 -1, -1, 477 DT_I8, 478 DT_UI8, 479 DT_TIME, 480 -1, -1, 481 DT_I4, 482 DT_UI4, 483 -1, -1, -1, 484 DT_R8, 485 DT_URI, 486 -1, 487 DT_FLOAT, 488 -1, 489 DT_R4, 490 DT_INT, 491 DT_CHAR, 492 -1, 493 DT_ENTITY, 494 DT_ID, 495 DT_ENTITIES, 496 DT_UUID, 497 -1, -1, 498 DT_TIME_TZ, 499 -1, 500 DT_DATE, 501 -1, 502 DT_NUMBER, 503 DT_BIN_HEX, 504 DT_DATETIME, 505 -1, 506 DT_IDREF, 507 DT_IDREFS, 508 DT_BOOLEAN, 509 -1, -1, -1, 510 DT_STRING, 511 DT_NMTOKEN, 512 DT_NMTOKENS, 513 -1, 514 DT_BIN_BASE64, 515 -1, 516 DT_I2, 517 DT_UI2, 518 -1, -1, -1, 519 DT_DATE_TZ, 520 DT_NOTATION, 521 -1, -1, 522 DT_DATETIME_TZ, 523 DT_I1, 524 DT_UI1, 525 -1, -1, 526 DT_ENUMERATION, 527 -1, -1, -1, -1, -1, -1, -1, -1, -1, 528 -1, -1, -1, -1, -1, -1, -1, -1, -1, 529 -1, -1, -1, -1, -1, -1, -1, -1, -1, 530 -1, -1, -1, -1, -1, -1, -1, -1, -1, 531 -1, -1, -1, -1, -1, -1, -1, -1, -1, 532 -1, -1, -1, -1, -1, -1, -1, -1, 533 DT_FIXED_14_4 534 }; 535 536 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */) 537 { 538 DWORD hash = dt_hash(str, len); 539 XDR_DT dt = DT_INVALID; 540 541 if (hash <= DT_MAX_HASH_VALUE) 542 dt = DT_lookup_table[hash]; 543 544 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0) 545 return dt; 546 547 return DT_INVALID; 548 } 549 550 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */) 551 { 552 DWORD hash = dt_hash_bstr(bstr, len); 553 XDR_DT dt = DT_INVALID; 554 555 if (hash <= DT_MAX_HASH_VALUE) 556 dt = DT_lookup_table[hash]; 557 558 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0) 559 return dt; 560 561 return DT_INVALID; 562 } 563 564 xmlChar const* dt_to_str(XDR_DT dt) 565 { 566 if (dt == DT_INVALID) 567 return NULL; 568 569 return DT_string_table[dt]; 570 } 571 572 OLECHAR const* dt_to_bstr(XDR_DT dt) 573 { 574 if (dt == DT_INVALID) 575 return NULL; 576 577 return DT_wstring_table[dt]; 578 } 579 580 const char* debugstr_dt(XDR_DT dt) 581 { 582 return debugstr_a(dt != DT_INVALID ? (const char*)DT_string_table[dt] : NULL); 583 } 584 585 HRESULT dt_validate(XDR_DT dt, xmlChar const* content) 586 { 587 xmlDocPtr tmp_doc; 588 xmlNodePtr node; 589 xmlNsPtr ns; 590 HRESULT hr; 591 592 TRACE("(dt:%s, %s)\n", debugstr_dt(dt), debugstr_a((char const*)content)); 593 594 if (!datatypes_schema) 595 { 596 xmlSchemaParserCtxtPtr spctx; 597 assert(datatypes_src != NULL); 598 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len); 599 datatypes_schema = Schema_parse(spctx); 600 xmlSchemaFreeParserCtxt(spctx); 601 } 602 603 switch (dt) 604 { 605 case DT_INVALID: 606 return E_FAIL; 607 case DT_BIN_BASE64: 608 case DT_BIN_HEX: 609 case DT_BOOLEAN: 610 case DT_CHAR: 611 case DT_DATE: 612 case DT_DATE_TZ: 613 case DT_DATETIME: 614 case DT_DATETIME_TZ: 615 case DT_FIXED_14_4: 616 case DT_FLOAT: 617 case DT_I1: 618 case DT_I2: 619 case DT_I4: 620 case DT_I8: 621 case DT_INT: 622 case DT_NMTOKEN: 623 case DT_NMTOKENS: 624 case DT_NUMBER: 625 case DT_R4: 626 case DT_R8: 627 case DT_STRING: 628 case DT_TIME: 629 case DT_TIME_TZ: 630 case DT_UI1: 631 case DT_UI2: 632 case DT_UI4: 633 case DT_UI8: 634 case DT_URI: 635 case DT_UUID: 636 if (!datatypes_schema) 637 { 638 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, " 639 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION "\n"); 640 641 /* Hopefully they don't need much in the way of XDR datatypes support... */ 642 return S_OK; 643 } 644 645 if (content && xmlStrlen(content)) 646 { 647 tmp_doc = xmlNewDoc(NULL); 648 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content); 649 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt"); 650 xmlSetNs(node, ns); 651 xmlDocSetRootElement(tmp_doc, node); 652 653 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc); 654 xmlFreeDoc(tmp_doc); 655 } 656 else 657 { /* probably the node is being created manually and has no content yet */ 658 hr = S_OK; 659 } 660 return hr; 661 default: 662 FIXME("need to handle dt:%s\n", debugstr_dt(dt)); 663 return S_OK; 664 } 665 } 666 667 static inline xmlChar const* get_node_nsURI(xmlNodePtr node) 668 { 669 return (node->ns != NULL)? node->ns->href : NULL; 670 } 671 672 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI) 673 { 674 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") : 675 xmlHashLookup(This->cache, nsURI); 676 } 677 678 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node) 679 { 680 cache_entry* entry = get_entry(This, get_node_nsURI(node)); 681 return (!entry)? NULL : entry->schema; 682 } 683 684 static xmlExternalEntityLoader _external_entity_loader; 685 686 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID, 687 xmlParserCtxtPtr ctxt) 688 { 689 xmlParserInputPtr input; 690 691 TRACE("(%s %s %p)\n", debugstr_a(URL), debugstr_a(ID), ctxt); 692 693 assert(MSXML_hInstance != NULL); 694 assert(datatypes_rsrc != NULL); 695 assert(datatypes_handle != NULL); 696 assert(datatypes_src != NULL); 697 698 /* TODO: if the desired schema is in the cache, load it from there */ 699 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0) 700 { 701 TRACE("loading built-in schema for %s\n", URL); 702 input = xmlNewStringInputStream(ctxt, datatypes_src); 703 } 704 else 705 { 706 input = _external_entity_loader(URL, ID, ctxt); 707 } 708 709 return input; 710 } 711 712 void schemasInit(void) 713 { 714 xmlChar* buf; 715 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML"))) 716 { 717 FIXME("failed to find resource for %s\n", DT_nsURI); 718 return; 719 } 720 721 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc))) 722 { 723 FIXME("failed to load resource for %s\n", DT_nsURI); 724 return; 725 } 726 buf = LockResource(datatypes_handle); 727 datatypes_len = SizeofResource(MSXML_hInstance, datatypes_rsrc); 728 729 /* Resource is loaded as raw data, 730 * need a null-terminated string */ 731 while (buf[datatypes_len - 1] != '>') datatypes_len--; 732 datatypes_src = HeapAlloc(GetProcessHeap(), 0, datatypes_len + 1); 733 memcpy(datatypes_src, buf, datatypes_len); 734 datatypes_src[datatypes_len] = 0; 735 736 if (xmlGetExternalEntityLoader() != external_entity_loader) 737 { 738 _external_entity_loader = xmlGetExternalEntityLoader(); 739 xmlSetExternalEntityLoader(external_entity_loader); 740 } 741 } 742 743 void schemasCleanup(void) 744 { 745 xmlSchemaFree(datatypes_schema); 746 HeapFree(GetProcessHeap(), 0, datatypes_src); 747 xmlSetExternalEntityLoader(_external_entity_loader); 748 } 749 750 static LONG cache_entry_add_ref(cache_entry* entry) 751 { 752 LONG ref = InterlockedIncrement(&entry->ref); 753 TRACE("(%p)->(%d)\n", entry, ref); 754 return ref; 755 } 756 757 static LONG cache_entry_release(cache_entry* entry) 758 { 759 LONG ref = InterlockedDecrement(&entry->ref); 760 TRACE("(%p)->(%d)\n", entry, ref); 761 762 if (ref == 0) 763 { 764 if (entry->type == CacheEntryType_XSD) 765 { 766 xmldoc_release(entry->doc); 767 entry->schema->doc = NULL; 768 xmlSchemaFree(entry->schema); 769 } 770 else if (entry->type == CacheEntryType_XDR) 771 { 772 xmldoc_release(entry->doc); 773 xmldoc_release(entry->schema->doc); 774 entry->schema->doc = NULL; 775 xmlSchemaFree(entry->schema); 776 } 777 778 heap_free(entry); 779 } 780 return ref; 781 } 782 783 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl; 784 785 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface) 786 { 787 return CONTAINING_RECORD(iface, schema_cache, IXMLDOMSchemaCollection2_iface); 788 } 789 790 static inline schema_cache* impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection* iface) 791 { 792 return CONTAINING_RECORD(iface, schema_cache, IXMLDOMSchemaCollection2_iface); 793 } 794 795 static inline schema_cache* unsafe_impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection *iface) 796 { 797 return iface->lpVtbl == (void*)&XMLDOMSchemaCollection2Vtbl ? impl_from_IXMLDOMSchemaCollection(iface) : NULL; 798 } 799 800 static inline CacheEntryType cache_type_from_xmlDocPtr(xmlDocPtr schema) 801 { 802 xmlNodePtr root = NULL; 803 if (schema) 804 root = xmlDocGetRootElement(schema); 805 if (root && root->ns) 806 { 807 808 if (xmlStrEqual(root->name, XDR_schema) && 809 xmlStrEqual(root->ns->href, XDR_nsURI)) 810 { 811 return CacheEntryType_XDR; 812 } 813 else if (xmlStrEqual(root->name, XSD_schema) && 814 xmlStrEqual(root->ns->href, XSD_nsURI)) 815 { 816 return CacheEntryType_XSD; 817 } 818 } 819 return CacheEntryType_Invalid; 820 } 821 822 static BOOL link_datatypes(xmlDocPtr schema) 823 { 824 xmlNodePtr root, next, child; 825 xmlNsPtr ns; 826 827 assert(xmlGetExternalEntityLoader() == external_entity_loader); 828 root = xmlDocGetRootElement(schema); 829 if (!root) 830 return FALSE; 831 832 for (ns = root->nsDef; ns != NULL; ns = ns->next) 833 { 834 if (xmlStrEqual(ns->href, DT_nsURI)) 835 break; 836 } 837 838 if (!ns) 839 return FALSE; 840 841 next = xmlFirstElementChild(root); 842 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL); 843 if (next) child = xmlAddPrevSibling(next, child); 844 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI); 845 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI); 846 847 return TRUE; 848 } 849 850 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v) 851 { 852 cache_entry* entry = heap_alloc(sizeof(cache_entry)); 853 xmlSchemaParserCtxtPtr spctx; 854 xmlDocPtr new_doc = xmlCopyDoc(doc, 1); 855 856 link_datatypes(new_doc); 857 858 /* TODO: if the nsURI is different from the default xmlns or targetNamespace, 859 * do we need to do something special here? */ 860 entry->type = CacheEntryType_XSD; 861 entry->ref = 0; 862 spctx = xmlSchemaNewDocParserCtxt(new_doc); 863 864 if ((entry->schema = Schema_parse(spctx))) 865 { 866 xmldoc_init(entry->schema->doc, v); 867 entry->doc = entry->schema->doc; 868 xmldoc_add_ref(entry->doc); 869 } 870 else 871 { 872 FIXME("failed to parse doc\n"); 873 xmlFreeDoc(new_doc); 874 heap_free(entry); 875 entry = NULL; 876 } 877 xmlSchemaFreeParserCtxt(spctx); 878 return entry; 879 } 880 881 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION version) 882 { 883 cache_entry* entry = heap_alloc(sizeof(cache_entry)); 884 xmlSchemaParserCtxtPtr spctx; 885 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI); 886 887 link_datatypes(xsd_doc); 888 889 entry->type = CacheEntryType_XDR; 890 entry->ref = 0; 891 spctx = xmlSchemaNewDocParserCtxt(xsd_doc); 892 893 if ((entry->schema = Schema_parse(spctx))) 894 { 895 entry->doc = new_doc; 896 xmldoc_init(entry->schema->doc, version); 897 xmldoc_init(entry->doc, version); 898 xmldoc_add_ref(entry->doc); 899 xmldoc_add_ref(entry->schema->doc); 900 } 901 else 902 { 903 FIXME("failed to parse doc\n"); 904 xmlFreeDoc(new_doc); 905 xmlFreeDoc(xsd_doc); 906 heap_free(entry); 907 entry = NULL; 908 } 909 xmlSchemaFreeParserCtxt(spctx); 910 911 return entry; 912 } 913 914 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION version) 915 { 916 cache_entry* entry; 917 IXMLDOMDocument3* domdoc = NULL; 918 xmlDocPtr doc = NULL; 919 HRESULT hr = DOMDocument_create(version, (void**)&domdoc); 920 VARIANT_BOOL b = VARIANT_FALSE; 921 CacheEntryType type = CacheEntryType_Invalid; 922 923 if (hr != S_OK) 924 { 925 FIXME("failed to create domdoc\n"); 926 return NULL; 927 } 928 assert(domdoc != NULL); 929 assert(V_VT(&url) == VT_BSTR); 930 931 hr = IXMLDOMDocument3_load(domdoc, url, &b); 932 if (hr != S_OK) 933 { 934 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr); 935 if (b != VARIANT_TRUE) 936 { 937 FIXME("Failed to load doc at %s\n", debugstr_w(V_BSTR(&url))); 938 IXMLDOMDocument3_Release(domdoc); 939 return NULL; 940 } 941 } 942 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc; 943 type = cache_type_from_xmlDocPtr(doc); 944 945 switch (type) 946 { 947 case CacheEntryType_XSD: 948 entry = cache_entry_from_xsd_doc(doc, nsURI, version); 949 break; 950 case CacheEntryType_XDR: 951 entry = cache_entry_from_xdr_doc(doc, nsURI, version); 952 break; 953 default: 954 entry = NULL; 955 FIXME("invalid schema\n"); 956 break; 957 } 958 IXMLDOMDocument3_Release(domdoc); 959 960 return entry; 961 } 962 963 static void cache_free(void* data, xmlChar* name /* ignored */) 964 { 965 cache_entry_release((cache_entry*)data); 966 } 967 968 /* returns index or -1 if not found */ 969 static int cache_free_uri(schema_cache *cache, const xmlChar *uri) 970 { 971 int i; 972 973 for (i = 0; i < cache->count; i++) 974 if (xmlStrEqual(cache->uris[i], uri)) 975 { 976 heap_free(cache->uris[i]); 977 return i; 978 } 979 980 return -1; 981 } 982 983 static void cache_add_entry(schema_cache *cache, const xmlChar *uri, cache_entry *entry) 984 { 985 int i; 986 987 /* meaning no entry found with this name */ 988 if (xmlHashRemoveEntry(cache->cache, uri, cache_free)) 989 { 990 if (cache->count == cache->allocated) 991 { 992 cache->allocated *= 2; 993 cache->uris = heap_realloc(cache->uris, cache->allocated*sizeof(xmlChar*)); 994 } 995 i = cache->count++; 996 } 997 else 998 i = cache_free_uri(cache, uri); 999 1000 cache->uris[i] = heap_strdupxmlChar(uri); 1001 xmlHashAddEntry(cache->cache, uri, entry); 1002 } 1003 1004 static void cache_remove_entry(schema_cache *cache, const xmlChar *uri) 1005 { 1006 /* adjust index if entry was really removed */ 1007 if (xmlHashRemoveEntry(cache->cache, uri, cache_free) == 0) 1008 { 1009 int i = cache_free_uri(cache, uri); 1010 if (i == -1) return; 1011 /* shift array */ 1012 if (i != --cache->count) 1013 memmove(&cache->uris[i], &cache->uris[i+1], (cache->count-i)*sizeof(xmlChar*)); 1014 } 1015 } 1016 1017 /* This one adds all namespaces defined in document to a cache, without anything 1018 associated with uri obviously. 1019 Unfortunately namespace:: axis implementation in libxml2 differs from what we need, 1020 it uses additional node type to describe namespace definition attribute while 1021 in msxml it's expected to be a normal attribute - as a workaround document is 1022 queried at libxml2 level here. */ 1023 HRESULT cache_from_doc_ns(IXMLDOMSchemaCollection2 *iface, xmlnode *node) 1024 { 1025 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1026 static const xmlChar query[] = "//*/namespace::*"; 1027 xmlXPathObjectPtr nodeset; 1028 xmlXPathContextPtr ctxt; 1029 1030 This->read_only = 1; 1031 1032 ctxt = xmlXPathNewContext(node->node->doc); 1033 1034 nodeset = xmlXPathEvalExpression(query, ctxt); 1035 xmlXPathFreeContext(ctxt); 1036 1037 if (nodeset) 1038 { 1039 int pos = 0, len = xmlXPathNodeSetGetLength(nodeset->nodesetval); 1040 1041 while (pos < len) 1042 { 1043 xmlNodePtr node = xmlXPathNodeSetItem(nodeset->nodesetval, pos); 1044 if (node->type == XML_NAMESPACE_DECL) 1045 { 1046 static const xmlChar defns[] = "http://www.w3.org/XML/1998/namespace"; 1047 xmlNsPtr ns = (xmlNsPtr)node; 1048 cache_entry *entry; 1049 1050 /* filter out default uri */ 1051 if (xmlStrEqual(ns->href, defns)) 1052 { 1053 pos++; 1054 continue; 1055 } 1056 1057 entry = heap_alloc(sizeof(cache_entry)); 1058 entry->type = CacheEntryType_NS; 1059 entry->ref = 1; 1060 entry->schema = NULL; 1061 entry->doc = NULL; 1062 1063 cache_add_entry(This, ns->href, entry); 1064 } 1065 pos++; 1066 } 1067 1068 xmlXPathFreeObject(nodeset); 1069 } 1070 1071 return S_OK; 1072 } 1073 1074 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface, 1075 REFIID riid, void** ppvObject) 1076 { 1077 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1078 1079 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject); 1080 1081 if ( IsEqualIID(riid, &IID_IUnknown) || 1082 IsEqualIID(riid, &IID_IDispatch) || 1083 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) || 1084 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) ) 1085 { 1086 *ppvObject = iface; 1087 } 1088 else if(This->version == MSXML6 && IsEqualIID(riid, &CLSID_XMLSchemaCache60)) 1089 { 1090 /* 1091 * Version 6 can be queried for an interface with IID equal to CLSID. 1092 * There is no public interface with that IID and returned pointer 1093 * is equal to returned IXMLDOMSchemaCollection2 iface. We assume 1094 * that it's just another way for querying IXMLDOMSchemaCollection2 1095 * interface. Office 2013 ClickToRun installer uses this. 1096 */ 1097 WARN("riid CLSID_XMLSchemaCache60, returning IXMLDOMSchemaCollection2 interface.\n"); 1098 *ppvObject = iface; 1099 } 1100 else if (dispex_query_interface(&This->dispex, riid, ppvObject)) 1101 { 1102 return *ppvObject ? S_OK : E_NOINTERFACE; 1103 } 1104 else if(IsEqualGUID( riid, &IID_ISupportErrorInfo )) 1105 { 1106 return node_create_supporterrorinfo(schema_cache_se_tids, ppvObject); 1107 } 1108 else 1109 { 1110 FIXME("interface %s not implemented\n", debugstr_guid(riid)); 1111 *ppvObject = NULL; 1112 return E_NOINTERFACE; 1113 } 1114 1115 IXMLDOMSchemaCollection2_AddRef(iface); 1116 1117 return S_OK; 1118 } 1119 1120 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface) 1121 { 1122 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1123 LONG ref = InterlockedIncrement(&This->ref); 1124 TRACE("(%p)->(%d)\n", This, ref); 1125 return ref; 1126 } 1127 1128 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface) 1129 { 1130 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1131 LONG ref = InterlockedDecrement(&This->ref); 1132 TRACE("(%p)->(%d)\n", This, ref); 1133 1134 if (ref == 0) 1135 { 1136 int i; 1137 1138 for (i = 0; i < This->count; i++) 1139 heap_free(This->uris[i]); 1140 heap_free(This->uris); 1141 xmlHashFree(This->cache, cache_free); 1142 heap_free(This); 1143 } 1144 1145 return ref; 1146 } 1147 1148 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface, 1149 UINT* pctinfo) 1150 { 1151 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1152 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo); 1153 } 1154 1155 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface, 1156 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) 1157 { 1158 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1159 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, 1160 iTInfo, lcid, ppTInfo); 1161 } 1162 1163 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface, 1164 REFIID riid, LPOLESTR* rgszNames, 1165 UINT cNames, LCID lcid, DISPID* rgDispId) 1166 { 1167 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1168 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, 1169 riid, rgszNames, cNames, lcid, rgDispId); 1170 } 1171 1172 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface, 1173 DISPID dispIdMember, REFIID riid, LCID lcid, 1174 WORD wFlags, DISPPARAMS* pDispParams, 1175 VARIANT* pVarResult, EXCEPINFO* pExcepInfo, 1176 UINT* puArgErr) 1177 { 1178 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1179 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, 1180 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); 1181 } 1182 1183 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var) 1184 { 1185 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1186 xmlChar* name; 1187 1188 TRACE("(%p)->(%s %s)\n", This, debugstr_w(uri), debugstr_variant(&var)); 1189 1190 if (This->read_only) return E_FAIL; 1191 1192 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW); 1193 1194 switch (V_VT(&var)) 1195 { 1196 case VT_NULL: 1197 { 1198 cache_remove_entry(This, name); 1199 } 1200 break; 1201 1202 case VT_BSTR: 1203 { 1204 cache_entry* entry = cache_entry_from_url(var, name, This->version); 1205 1206 if (entry) 1207 { 1208 cache_entry_add_ref(entry); 1209 } 1210 else 1211 { 1212 heap_free(name); 1213 return E_FAIL; 1214 } 1215 1216 cache_add_entry(This, name, entry); 1217 } 1218 break; 1219 1220 case VT_DISPATCH: 1221 case VT_UNKNOWN: 1222 { 1223 xmlDocPtr doc = NULL; 1224 cache_entry* entry; 1225 CacheEntryType type; 1226 IXMLDOMNode* domnode = NULL; 1227 IUnknown_QueryInterface(V_UNKNOWN(&var), &IID_IXMLDOMNode, (void**)&domnode); 1228 1229 if (domnode) 1230 { 1231 DOMNodeType type; 1232 1233 IXMLDOMNode_get_nodeType(domnode, &type); 1234 switch (type) 1235 { 1236 case NODE_ELEMENT: 1237 { 1238 IXMLDOMDocument *domdoc; 1239 VARIANT_BOOL b; 1240 BSTR xml; 1241 1242 IXMLDOMNode_get_xml(domnode, &xml); 1243 DOMDocument_create(This->version, (void**)&domdoc); 1244 IXMLDOMDocument_loadXML(domdoc, xml, &b); 1245 SysFreeString(xml); 1246 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc; 1247 break; 1248 } 1249 default: 1250 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc; 1251 break; 1252 } 1253 } 1254 1255 if (!doc) 1256 { 1257 IXMLDOMNode_Release(domnode); 1258 heap_free(name); 1259 return E_INVALIDARG; 1260 } 1261 type = cache_type_from_xmlDocPtr(doc); 1262 1263 if (type == CacheEntryType_XSD) 1264 { 1265 entry = cache_entry_from_xsd_doc(doc, name, This->version); 1266 } 1267 else if (type == CacheEntryType_XDR) 1268 { 1269 entry = cache_entry_from_xdr_doc(doc, name, This->version); 1270 } 1271 else 1272 { 1273 WARN("invalid schema!\n"); 1274 entry = NULL; 1275 } 1276 1277 IXMLDOMNode_Release(domnode); 1278 1279 if (entry) 1280 { 1281 cache_entry_add_ref(entry); 1282 } 1283 else 1284 { 1285 heap_free(name); 1286 return E_FAIL; 1287 } 1288 1289 cache_add_entry(This, name, entry); 1290 } 1291 break; 1292 1293 default: 1294 FIXME("arg type is not supported, %s\n", debugstr_variant(&var)); 1295 heap_free(name); 1296 return E_INVALIDARG; 1297 } 1298 heap_free(name); 1299 return S_OK; 1300 } 1301 1302 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri, 1303 IXMLDOMNode** node) 1304 { 1305 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1306 cache_entry* entry; 1307 xmlChar* name; 1308 1309 TRACE("(%p)->(%s %p)\n", This, debugstr_w(uri), node); 1310 1311 if (This->version == MSXML6) 1312 { 1313 if (node) *node = NULL; 1314 return E_NOTIMPL; 1315 } 1316 1317 if (!node) 1318 return E_POINTER; 1319 1320 *node = NULL; 1321 1322 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW); 1323 entry = (cache_entry*) xmlHashLookup(This->cache, name); 1324 heap_free(name); 1325 1326 /* TODO: this should be read-only */ 1327 if (entry && entry->doc) 1328 return get_domdoc_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node); 1329 1330 return S_OK; 1331 } 1332 1333 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri) 1334 { 1335 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1336 xmlChar* name; 1337 1338 TRACE("(%p)->(%s)\n", This, debugstr_w(uri)); 1339 1340 if (This->version == MSXML6) return E_NOTIMPL; 1341 1342 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW); 1343 cache_remove_entry(This, name); 1344 heap_free(name); 1345 return S_OK; 1346 } 1347 1348 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length) 1349 { 1350 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1351 TRACE("(%p)->(%p)\n", This, length); 1352 1353 if (!length) 1354 return E_POINTER; 1355 1356 *length = This->count; 1357 return S_OK; 1358 } 1359 1360 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface, 1361 LONG index, BSTR* uri) 1362 { 1363 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1364 1365 TRACE("(%p)->(%i %p)\n", This, index, uri); 1366 1367 if (!uri) 1368 return E_POINTER; 1369 1370 if (This->version == MSXML6) 1371 *uri = NULL; 1372 1373 if (index >= This->count) 1374 return E_FAIL; 1375 1376 *uri = bstr_from_xmlChar(This->uris[index]); 1377 return S_OK; 1378 } 1379 1380 static void cache_copy(void* data, void* dest, xmlChar* name) 1381 { 1382 schema_cache* This = (schema_cache*) dest; 1383 cache_entry* entry = (cache_entry*) data; 1384 1385 if (xmlHashLookup(This->cache, name) == NULL) 1386 { 1387 cache_entry_add_ref(entry); 1388 cache_add_entry(This, name, entry); 1389 } 1390 } 1391 1392 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface, 1393 IXMLDOMSchemaCollection* collection) 1394 { 1395 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1396 schema_cache* That; 1397 1398 TRACE("(%p)->(%p)\n", This, collection); 1399 1400 if (!collection) 1401 return E_POINTER; 1402 1403 That = unsafe_impl_from_IXMLDOMSchemaCollection(collection); 1404 if (!That) 1405 { 1406 ERR("external collection implementation\n"); 1407 return E_FAIL; 1408 } 1409 1410 /* TODO: detect errors while copying & return E_FAIL */ 1411 xmlHashScan(That->cache, cache_copy, This); 1412 1413 return S_OK; 1414 } 1415 1416 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface, IUnknown** enumv) 1417 { 1418 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1419 TRACE("(%p)->(%p)\n", This, enumv); 1420 return create_enumvariant((IUnknown*)iface, TRUE, &schemacache_enumvariant, (IEnumVARIANT**)enumv); 1421 } 1422 1423 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface) 1424 { 1425 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1426 FIXME("(%p): stub\n", This); 1427 return E_NOTIMPL; 1428 } 1429 1430 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface, 1431 VARIANT_BOOL value) 1432 { 1433 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1434 FIXME("(%p)->(%d): stub\n", This, value); 1435 1436 This->validateOnLoad = value; 1437 /* it's ok to disable it, cause we don't validate on load anyway */ 1438 if (value == VARIANT_FALSE) return S_OK; 1439 1440 return E_NOTIMPL; 1441 } 1442 1443 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface, 1444 VARIANT_BOOL* value) 1445 { 1446 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1447 TRACE("(%p)->(%p)\n", This, value); 1448 1449 if (!value) return E_POINTER; 1450 *value = This->validateOnLoad; 1451 1452 return S_OK; 1453 } 1454 1455 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface, 1456 BSTR namespaceURI, ISchema** schema) 1457 { 1458 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1459 FIXME("(%p)->(%s %p): stub\n", This, debugstr_w(namespaceURI), schema); 1460 if (schema) 1461 *schema = NULL; 1462 return E_NOTIMPL; 1463 } 1464 1465 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface, 1466 IXMLDOMNode* node, ISchemaItem** item) 1467 { 1468 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1469 FIXME("(%p)->(%p %p): stub\n", This, node, item); 1470 if (item) 1471 *item = NULL; 1472 return E_NOTIMPL; 1473 } 1474 1475 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl = 1476 { 1477 schema_cache_QueryInterface, 1478 schema_cache_AddRef, 1479 schema_cache_Release, 1480 schema_cache_GetTypeInfoCount, 1481 schema_cache_GetTypeInfo, 1482 schema_cache_GetIDsOfNames, 1483 schema_cache_Invoke, 1484 schema_cache_add, 1485 schema_cache_get, 1486 schema_cache_remove, 1487 schema_cache_get_length, 1488 schema_cache_get_namespaceURI, 1489 schema_cache_addCollection, 1490 schema_cache_get__newEnum, 1491 schema_cache_validate, 1492 schema_cache_put_validateOnLoad, 1493 schema_cache_get_validateOnLoad, 1494 schema_cache_getSchema, 1495 schema_cache_getDeclaration 1496 }; 1497 1498 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node) 1499 { 1500 xmlSchemaElementPtr decl = NULL; 1501 xmlChar const* nsURI = get_node_nsURI(node); 1502 1503 TRACE("(%p, %p)\n", schema, node); 1504 1505 if (xmlStrEqual(nsURI, schema->targetNamespace)) 1506 decl = xmlHashLookup(schema->elemDecl, node->name); 1507 1508 if (!decl && xmlHashSize(schema->schemasImports) > 1) 1509 { 1510 FIXME("declaration not found in main schema - need to check schema imports!\n"); 1511 /*xmlSchemaImportPtr import; 1512 if (nsURI == NULL) 1513 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE); 1514 else 1515 import = xmlHashLookup(schema->schemasImports, node->ns->href); 1516 1517 if (import != NULL) 1518 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/ 1519 } 1520 1521 return decl; 1522 } 1523 1524 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node) 1525 { 1526 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node); 1527 while (decl != NULL && decl->refDecl != NULL) 1528 decl = decl->refDecl; 1529 return (decl != NULL)? decl->node : NULL; 1530 } 1531 1532 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree) 1533 { 1534 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1535 xmlSchemaPtr schema; 1536 1537 TRACE("(%p, %p)\n", This, tree); 1538 1539 if (!tree) 1540 return E_POINTER; 1541 1542 if (tree->type == XML_DOCUMENT_NODE) 1543 tree = xmlDocGetRootElement(tree->doc); 1544 1545 schema = get_node_schema(This, tree); 1546 /* TODO: if the ns is not in the cache, and it's a URL, 1547 * do we try to load from that? */ 1548 if (schema) 1549 return Schema_validate_tree(schema, tree); 1550 else 1551 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree)); 1552 1553 return E_FAIL; 1554 } 1555 1556 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node) 1557 { 1558 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface); 1559 xmlSchemaPtr schema = get_node_schema(This, node); 1560 XDR_DT dt = DT_INVALID; 1561 1562 TRACE("(%p, %p)\n", This, node); 1563 1564 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI)) 1565 { 1566 dt = str_to_dt(node->name, -1); 1567 } 1568 else if (schema) 1569 { 1570 xmlChar* str; 1571 xmlNodePtr schema_node = lookup_schema_element(schema, node); 1572 1573 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI); 1574 if (str) 1575 { 1576 dt = str_to_dt(str, -1); 1577 xmlFree(str); 1578 } 1579 } 1580 1581 return dt; 1582 } 1583 1584 static const tid_t schemacache_iface_tids[] = { 1585 IXMLDOMSchemaCollection2_tid, 1586 0 1587 }; 1588 1589 static dispex_static_data_t schemacache_dispex = { 1590 NULL, 1591 IXMLDOMSchemaCollection2_tid, 1592 NULL, 1593 schemacache_iface_tids 1594 }; 1595 1596 HRESULT SchemaCache_create(MSXML_VERSION version, void** obj) 1597 { 1598 schema_cache* This = heap_alloc(sizeof(schema_cache)); 1599 if (!This) 1600 return E_OUTOFMEMORY; 1601 1602 TRACE("(%d %p)\n", version, obj); 1603 1604 This->IXMLDOMSchemaCollection2_iface.lpVtbl = &XMLDOMSchemaCollection2Vtbl; 1605 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE); 1606 This->allocated = 10; 1607 This->count = 0; 1608 This->uris = heap_alloc(This->allocated*sizeof(xmlChar*)); 1609 This->ref = 1; 1610 This->version = version; 1611 This->validateOnLoad = VARIANT_TRUE; 1612 This->read_only = 0; 1613 init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMSchemaCollection2_iface, &schemacache_dispex); 1614 1615 *obj = &This->IXMLDOMSchemaCollection2_iface; 1616 return S_OK; 1617 } 1618 1619 #else 1620 1621 HRESULT SchemaCache_create(MSXML_VERSION version, void** obj) 1622 { 1623 MESSAGE("This program tried to use a SchemaCache object, but\n" 1624 "libxml2 support was not present at compile time.\n"); 1625 return E_NOTIMPL; 1626 } 1627 1628 #endif 1629