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