blob: ef2a137452e7a690d020fb0bb588d33826057956 [file] [log] [blame]
Elliott Hughes72472942018-01-10 08:36:10 -08001/*
2 __ __ _
3 ___\ \/ /_ __ __ _| |_
4 / _ \\ /| '_ \ / _` | __|
5 | __// \| |_) | (_| | |_
6 \___/_/\_\ .__/ \__,_|\__|
7 |_| XML parser
8
9 Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
10 Copyright (c) 2000-2017 Expat development team
11 Licensed under the MIT license:
12
13 Permission is hereby granted, free of charge, to any person obtaining
14 a copy of this software and associated documentation files (the
15 "Software"), to deal in the Software without restriction, including
16 without limitation the rights to use, copy, modify, merge, publish,
17 distribute, sublicense, and/or sell copies of the Software, and to permit
18 persons to whom the Software is furnished to do so, subject to the
19 following conditions:
20
21 The above copyright notice and this permission notice shall be included
22 in all copies or substantial portions of the Software.
23
24 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
27 NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
28 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
29 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
30 USE OR OTHER DEALINGS IN THE SOFTWARE.
31*/
32
33#include "expat.h"
34#ifdef XML_UNICODE
35#define UNICODE
36#endif
37#include <windows.h>
38#include <urlmon.h>
39#include <wininet.h>
40#include <stdio.h>
41#include <tchar.h>
42#include "xmlurl.h"
43#include "xmlmime.h"
44
45static int
46processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url);
47
48typedef void (*StopHandler)(void *, HRESULT);
49
50class Callback : public IBindStatusCallback {
51public:
52 // IUnknown methods
53 STDMETHODIMP QueryInterface(REFIID,void **);
54 STDMETHODIMP_(ULONG) AddRef();
55 STDMETHODIMP_(ULONG) Release();
56 // IBindStatusCallback methods
57 STDMETHODIMP OnStartBinding(DWORD, IBinding *);
58 STDMETHODIMP GetPriority(LONG *);
59 STDMETHODIMP OnLowResource(DWORD);
60 STDMETHODIMP OnProgress(ULONG, ULONG, ULONG, LPCWSTR);
61 STDMETHODIMP OnStopBinding(HRESULT, LPCWSTR);
62 STDMETHODIMP GetBindInfo(DWORD *, BINDINFO *);
63 STDMETHODIMP OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *);
64 STDMETHODIMP OnObjectAvailable(REFIID, IUnknown *);
65 Callback(XML_Parser, IMoniker *, StopHandler, void *);
66 ~Callback();
67 int externalEntityRef(const XML_Char *context,
68 const XML_Char *systemId, const XML_Char *publicId);
69private:
70 XML_Parser parser_;
71 IMoniker *baseMoniker_;
72 DWORD totalRead_;
73 ULONG ref_;
74 IBinding *pBinding_;
75 StopHandler stopHandler_;
76 void *stopArg_;
77};
78
79STDMETHODIMP_(ULONG)
80Callback::AddRef()
81{
82 return ref_++;
83}
84
85STDMETHODIMP_(ULONG)
86Callback::Release()
87{
88 if (--ref_ == 0) {
89 delete this;
90 return 0;
91 }
92 return ref_;
93}
94
95STDMETHODIMP
96Callback::QueryInterface(REFIID riid, void** ppv)
97{
98 if (IsEqualGUID(riid, IID_IUnknown))
99 *ppv = (IUnknown *)this;
100 else if (IsEqualGUID(riid, IID_IBindStatusCallback))
101 *ppv = (IBindStatusCallback *)this;
102 else
103 return E_NOINTERFACE;
104 ((LPUNKNOWN)*ppv)->AddRef();
105 return S_OK;
106}
107
108STDMETHODIMP
109Callback::OnStartBinding(DWORD, IBinding* pBinding)
110{
111 pBinding_ = pBinding;
112 pBinding->AddRef();
113 return S_OK;
114}
115
116STDMETHODIMP
117Callback::GetPriority(LONG *)
118{
119 return E_NOTIMPL;
120}
121
122STDMETHODIMP
123Callback::OnLowResource(DWORD)
124{
125 return E_NOTIMPL;
126}
127
128STDMETHODIMP
129Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR)
130{
131 return S_OK;
132}
133
134STDMETHODIMP
135Callback::OnStopBinding(HRESULT hr, LPCWSTR szError)
136{
137 if (pBinding_) {
138 pBinding_->Release();
139 pBinding_ = 0;
140 }
141 if (baseMoniker_) {
142 baseMoniker_->Release();
143 baseMoniker_ = 0;
144 }
145 stopHandler_(stopArg_, hr);
146 return S_OK;
147}
148
149STDMETHODIMP
150Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo)
151{
152 *pgrfBINDF = BINDF_ASYNCHRONOUS;
153 return S_OK;
154}
155
156static void
157reportError(XML_Parser parser)
158{
159 int code = XML_GetErrorCode(parser);
160 const XML_Char *message = XML_ErrorString(code);
161 if (message)
162 _ftprintf(stderr, _T("%s:%d:%ld: %s\n"),
163 XML_GetBase(parser),
164 XML_GetErrorLineNumber(parser),
165 XML_GetErrorColumnNumber(parser),
166 message);
167 else
168 _ftprintf(stderr, _T("%s: (unknown message %d)\n"),
169 XML_GetBase(parser), code);
170}
171
172STDMETHODIMP
173Callback::OnDataAvailable(DWORD grfBSCF,
174 DWORD dwSize,
175 FORMATETC *pfmtetc,
176 STGMEDIUM* pstgmed)
177{
178 if (grfBSCF & BSCF_FIRSTDATANOTIFICATION) {
179 IWinInetHttpInfo *hp;
180 HRESULT hr = pBinding_->QueryInterface(IID_IWinInetHttpInfo,
181 (void **)&hp);
182 if (SUCCEEDED(hr)) {
183 char contentType[1024];
184 DWORD bufSize = sizeof(contentType);
185 DWORD flags = 0;
186 contentType[0] = 0;
187 hr = hp->QueryInfo(HTTP_QUERY_CONTENT_TYPE, contentType,
188 &bufSize, 0, NULL);
189 if (SUCCEEDED(hr)) {
190 char charset[CHARSET_MAX];
191 getXMLCharset(contentType, charset);
192 if (charset[0]) {
193#ifdef XML_UNICODE
194 XML_Char wcharset[CHARSET_MAX];
195 XML_Char *p1 = wcharset;
196 const char *p2 = charset;
197 while ((*p1++ = (unsigned char)*p2++) != 0)
198 ;
199 XML_SetEncoding(parser_, wcharset);
200#else
201 XML_SetEncoding(parser_, charset);
202#endif
203 }
204 }
205 hp->Release();
206 }
207 }
208 if (!parser_)
209 return E_ABORT;
210 if (pstgmed->tymed == TYMED_ISTREAM) {
211 while (totalRead_ < dwSize) {
212#define READ_MAX (64*1024)
213 DWORD nToRead = dwSize - totalRead_;
214 if (nToRead > READ_MAX)
215 nToRead = READ_MAX;
216 void *buf = XML_GetBuffer(parser_, nToRead);
217 if (!buf) {
218 _ftprintf(stderr, _T("out of memory\n"));
219 return E_ABORT;
220 }
221 DWORD nRead;
222 HRESULT hr = pstgmed->pstm->Read(buf, nToRead, &nRead);
223 if (SUCCEEDED(hr)) {
224 totalRead_ += nRead;
225 if (!XML_ParseBuffer(parser_,
226 nRead,
227 (grfBSCF & BSCF_LASTDATANOTIFICATION) != 0
228 && totalRead_ == dwSize)) {
229 reportError(parser_);
230 return E_ABORT;
231 }
232 }
233 }
234 }
235 return S_OK;
236}
237
238STDMETHODIMP
239Callback::OnObjectAvailable(REFIID, IUnknown *)
240{
241 return S_OK;
242}
243
244int
245Callback::externalEntityRef(const XML_Char *context,
246 const XML_Char *systemId,
247 const XML_Char *publicId)
248{
249 XML_Parser entParser = XML_ExternalEntityParserCreate(parser_, context, 0);
250 XML_SetBase(entParser, systemId);
251 int ret = processURL(entParser, baseMoniker_, systemId);
252 XML_ParserFree(entParser);
253 return ret;
254}
255
256Callback::Callback(XML_Parser parser, IMoniker *baseMoniker,
257 StopHandler stopHandler, void *stopArg)
258: parser_(parser),
259 baseMoniker_(baseMoniker),
260 ref_(0),
261 pBinding_(0),
262 totalRead_(0),
263 stopHandler_(stopHandler),
264 stopArg_(stopArg)
265{
266 if (baseMoniker_)
267 baseMoniker_->AddRef();
268}
269
270Callback::~Callback()
271{
272 if (pBinding_)
273 pBinding_->Release();
274 if (baseMoniker_)
275 baseMoniker_->Release();
276}
277
278static int
279externalEntityRef(void *arg,
280 const XML_Char *context,
281 const XML_Char *base,
282 const XML_Char *systemId,
283 const XML_Char *publicId)
284{
285 return ((Callback *)arg)->externalEntityRef(context, systemId, publicId);
286}
287
288
289static HRESULT
290openStream(XML_Parser parser,
291 IMoniker *baseMoniker,
292 const XML_Char *uri,
293 StopHandler stopHandler, void *stopArg)
294{
295 if (!XML_SetBase(parser, uri))
296 return E_OUTOFMEMORY;
297 HRESULT hr;
298 IMoniker *m;
299#ifdef XML_UNICODE
300 hr = CreateURLMoniker(0, uri, &m);
301#else
302 LPWSTR uriw = new wchar_t[strlen(uri) + 1];
303 for (int i = 0;; i++) {
304 uriw[i] = uri[i];
305 if (uriw[i] == 0)
306 break;
307 }
308 hr = CreateURLMoniker(baseMoniker, uriw, &m);
309 delete [] uriw;
310#endif
311 if (FAILED(hr))
312 return hr;
313 IBindStatusCallback *cb = new Callback(parser, m, stopHandler, stopArg);
314 XML_SetExternalEntityRefHandler(parser, externalEntityRef);
315 XML_SetExternalEntityRefHandlerArg(parser, cb);
316 cb->AddRef();
317 IBindCtx *b;
318 if (FAILED(hr = CreateAsyncBindCtx(0, cb, 0, &b))) {
319 cb->Release();
320 m->Release();
321 return hr;
322 }
323 cb->Release();
324 IStream *pStream;
325 hr = m->BindToStorage(b, 0, IID_IStream, (void **)&pStream);
326 if (SUCCEEDED(hr)) {
327 if (pStream)
328 pStream->Release();
329 }
330 if (hr == MK_S_ASYNCHRONOUS)
331 hr = S_OK;
332 m->Release();
333 b->Release();
334 return hr;
335}
336
337struct QuitInfo {
338 const XML_Char *url;
339 HRESULT hr;
340 int stop;
341};
342
343static void
344winPerror(const XML_Char *url, HRESULT hr)
345{
346 LPVOID buf;
347 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
348 | FORMAT_MESSAGE_FROM_HMODULE,
349 GetModuleHandleA("urlmon.dll"),
350 hr,
351 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
352 (LPTSTR) &buf,
353 0,
354 NULL)
355 || FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
356 | FORMAT_MESSAGE_FROM_SYSTEM,
357 0,
358 hr,
359 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
360 (LPTSTR) &buf,
361 0,
362 NULL)) {
363 /* The system error messages seem to end with a newline. */
364 _ftprintf(stderr, _T("%s: %s"), url, buf);
365 fflush(stderr);
366 LocalFree(buf);
367 }
368 else
369 _ftprintf(stderr, _T("%s: error %x\n"), url, hr);
370}
371
372static void
373threadQuit(void *p, HRESULT hr)
374{
375 QuitInfo *qi = (QuitInfo *)p;
376 qi->hr = hr;
377 qi->stop = 1;
378}
379
380extern "C"
381int
382XML_URLInit(void)
383{
384 return SUCCEEDED(CoInitialize(0));
385}
386
387extern "C"
388void
389XML_URLUninit(void)
390{
391 CoUninitialize();
392}
393
394static int
395processURL(XML_Parser parser, IMoniker *baseMoniker,
396 const XML_Char *url)
397{
398 QuitInfo qi;
399 qi.stop = 0;
400 qi.url = url;
401
402 XML_SetBase(parser, url);
403 HRESULT hr = openStream(parser, baseMoniker, url, threadQuit, &qi);
404 if (FAILED(hr)) {
405 winPerror(url, hr);
406 return 0;
407 }
408 else if (FAILED(qi.hr)) {
409 winPerror(url, qi.hr);
410 return 0;
411 }
412 MSG msg;
413 while (!qi.stop && GetMessage (&msg, NULL, 0, 0)) {
414 TranslateMessage (&msg);
415 DispatchMessage (&msg);
416 }
417 return 1;
418}
419
420extern "C"
421int
422XML_ProcessURL(XML_Parser parser,
423 const XML_Char *url,
424 unsigned flags)
425{
426 return processURL(parser, 0, url);
427}