xref: /reactos/dll/win32/msxml3/schema.c (revision 845faec4)
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
1649