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