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