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