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 } 428