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