xref: /reactos/dll/win32/msxml3/schema.c (revision b4a8afe5)
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