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