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