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