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