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