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