Elliott Hughes | 7247294 | 2018-01-10 08:36:10 -0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 45 | static int |
| 46 | processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url); |
| 47 | |
| 48 | typedef void (*StopHandler)(void *, HRESULT); |
| 49 | |
| 50 | class Callback : public IBindStatusCallback { |
| 51 | public: |
| 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); |
| 69 | private: |
| 70 | XML_Parser parser_; |
| 71 | IMoniker *baseMoniker_; |
| 72 | DWORD totalRead_; |
| 73 | ULONG ref_; |
| 74 | IBinding *pBinding_; |
| 75 | StopHandler stopHandler_; |
| 76 | void *stopArg_; |
| 77 | }; |
| 78 | |
| 79 | STDMETHODIMP_(ULONG) |
| 80 | Callback::AddRef() |
| 81 | { |
| 82 | return ref_++; |
| 83 | } |
| 84 | |
| 85 | STDMETHODIMP_(ULONG) |
| 86 | Callback::Release() |
| 87 | { |
| 88 | if (--ref_ == 0) { |
| 89 | delete this; |
| 90 | return 0; |
| 91 | } |
| 92 | return ref_; |
| 93 | } |
| 94 | |
| 95 | STDMETHODIMP |
| 96 | Callback::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 | |
| 108 | STDMETHODIMP |
| 109 | Callback::OnStartBinding(DWORD, IBinding* pBinding) |
| 110 | { |
| 111 | pBinding_ = pBinding; |
| 112 | pBinding->AddRef(); |
| 113 | return S_OK; |
| 114 | } |
| 115 | |
| 116 | STDMETHODIMP |
| 117 | Callback::GetPriority(LONG *) |
| 118 | { |
| 119 | return E_NOTIMPL; |
| 120 | } |
| 121 | |
| 122 | STDMETHODIMP |
| 123 | Callback::OnLowResource(DWORD) |
| 124 | { |
| 125 | return E_NOTIMPL; |
| 126 | } |
| 127 | |
| 128 | STDMETHODIMP |
| 129 | Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR) |
| 130 | { |
| 131 | return S_OK; |
| 132 | } |
| 133 | |
| 134 | STDMETHODIMP |
| 135 | Callback::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 | |
| 149 | STDMETHODIMP |
| 150 | Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo) |
| 151 | { |
| 152 | *pgrfBINDF = BINDF_ASYNCHRONOUS; |
| 153 | return S_OK; |
| 154 | } |
| 155 | |
| 156 | static void |
| 157 | reportError(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 | |
| 172 | STDMETHODIMP |
| 173 | Callback::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 | |
| 238 | STDMETHODIMP |
| 239 | Callback::OnObjectAvailable(REFIID, IUnknown *) |
| 240 | { |
| 241 | return S_OK; |
| 242 | } |
| 243 | |
| 244 | int |
| 245 | Callback::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 | |
| 256 | Callback::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 | |
| 270 | Callback::~Callback() |
| 271 | { |
| 272 | if (pBinding_) |
| 273 | pBinding_->Release(); |
| 274 | if (baseMoniker_) |
| 275 | baseMoniker_->Release(); |
| 276 | } |
| 277 | |
| 278 | static int |
| 279 | externalEntityRef(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 | |
| 289 | static HRESULT |
| 290 | openStream(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 | |
| 337 | struct QuitInfo { |
| 338 | const XML_Char *url; |
| 339 | HRESULT hr; |
| 340 | int stop; |
| 341 | }; |
| 342 | |
| 343 | static void |
| 344 | winPerror(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 | |
| 372 | static void |
| 373 | threadQuit(void *p, HRESULT hr) |
| 374 | { |
| 375 | QuitInfo *qi = (QuitInfo *)p; |
| 376 | qi->hr = hr; |
| 377 | qi->stop = 1; |
| 378 | } |
| 379 | |
| 380 | extern "C" |
| 381 | int |
| 382 | XML_URLInit(void) |
| 383 | { |
| 384 | return SUCCEEDED(CoInitialize(0)); |
| 385 | } |
| 386 | |
| 387 | extern "C" |
| 388 | void |
| 389 | XML_URLUninit(void) |
| 390 | { |
| 391 | CoUninitialize(); |
| 392 | } |
| 393 | |
| 394 | static int |
| 395 | processURL(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 | |
| 420 | extern "C" |
| 421 | int |
| 422 | XML_ProcessURL(XML_Parser parser, |
| 423 | const XML_Char *url, |
| 424 | unsigned flags) |
| 425 | { |
| 426 | return processURL(parser, 0, url); |
| 427 | } |