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