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