1 #include "expat.h" 2 #ifdef XML_UNICODE 3 #define UNICODE 4 #endif 5 #include <windows.h> 6 #include <urlmon.h> 7 #include <wininet.h> 8 #include <stdio.h> 9 #include <tchar.h> 10 #include "xmlurl.h" 11 #include "xmlmime.h" 12 13 static int 14 processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url); 15 16 typedef void (*StopHandler)(void *, HRESULT); 17 18 class Callback : public IBindStatusCallback { 19 public: 20 // IUnknown methods 21 STDMETHODIMP QueryInterface(REFIID,void **); 22 STDMETHODIMP_(ULONG) AddRef(); 23 STDMETHODIMP_(ULONG) Release(); 24 // IBindStatusCallback methods 25 STDMETHODIMP OnStartBinding(DWORD, IBinding *); 26 STDMETHODIMP GetPriority(LONG *); 27 STDMETHODIMP OnLowResource(DWORD); 28 STDMETHODIMP OnProgress(ULONG, ULONG, ULONG, LPCWSTR); 29 STDMETHODIMP OnStopBinding(HRESULT, LPCWSTR); 30 STDMETHODIMP GetBindInfo(DWORD *, BINDINFO *); 31 STDMETHODIMP OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *); 32 STDMETHODIMP OnObjectAvailable(REFIID, IUnknown *); 33 Callback(XML_Parser, IMoniker *, StopHandler, void *); 34 ~Callback(); 35 int externalEntityRef(const XML_Char *context, 36 const XML_Char *systemId, const XML_Char *publicId); 37 private: 38 XML_Parser parser_; 39 IMoniker *baseMoniker_; 40 DWORD totalRead_; 41 ULONG ref_; 42 IBinding *pBinding_; 43 StopHandler stopHandler_; 44 void *stopArg_; 45 }; 46 47 STDMETHODIMP_(ULONG) 48 Callback::AddRef() 49 { 50 return ref_++; 51 } 52 53 STDMETHODIMP_(ULONG) 54 Callback::Release() 55 { 56 if (--ref_ == 0) { 57 delete this; 58 return 0; 59 } 60 return ref_; 61 } 62 63 STDMETHODIMP 64 Callback::QueryInterface(REFIID riid, void** ppv) 65 { 66 if (IsEqualGUID(riid, IID_IUnknown)) 67 *ppv = (IUnknown *)this; 68 else if (IsEqualGUID(riid, IID_IBindStatusCallback)) 69 *ppv = (IBindStatusCallback *)this; 70 else 71 return E_NOINTERFACE; 72 ((LPUNKNOWN)*ppv)->AddRef(); 73 return S_OK; 74 } 75 76 STDMETHODIMP 77 Callback::OnStartBinding(DWORD, IBinding* pBinding) 78 { 79 pBinding_ = pBinding; 80 pBinding->AddRef(); 81 return S_OK; 82 } 83 84 STDMETHODIMP 85 Callback::GetPriority(LONG *) 86 { 87 return E_NOTIMPL; 88 } 89 90 STDMETHODIMP 91 Callback::OnLowResource(DWORD) 92 { 93 return E_NOTIMPL; 94 } 95 96 STDMETHODIMP 97 Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR) 98 { 99 return S_OK; 100 } 101 102 STDMETHODIMP 103 Callback::OnStopBinding(HRESULT hr, LPCWSTR szError) 104 { 105 if (pBinding_) { 106 pBinding_->Release(); 107 pBinding_ = 0; 108 } 109 if (baseMoniker_) { 110 baseMoniker_->Release(); 111 baseMoniker_ = 0; 112 } 113 stopHandler_(stopArg_, hr); 114 return S_OK; 115 } 116 117 STDMETHODIMP 118 Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo) 119 { 120 *pgrfBINDF = BINDF_ASYNCHRONOUS; 121 return S_OK; 122 } 123 124 static void 125 reportError(XML_Parser parser) 126 { 127 int code = XML_GetErrorCode(parser); 128 const XML_Char *message = XML_ErrorString(code); 129 if (message) 130 _ftprintf(stderr, _T("%s:%d:%ld: %s\n"), 131 XML_GetBase(parser), 132 XML_GetErrorLineNumber(parser), 133 XML_GetErrorColumnNumber(parser), 134 message); 135 else 136 _ftprintf(stderr, _T("%s: (unknown message %d)\n"), 137 XML_GetBase(parser), code); 138 } 139 140 STDMETHODIMP 141 Callback::OnDataAvailable(DWORD grfBSCF, 142 DWORD dwSize, 143 FORMATETC *pfmtetc, 144 STGMEDIUM* pstgmed) 145 { 146 if (grfBSCF & BSCF_FIRSTDATANOTIFICATION) { 147 IWinInetHttpInfo *hp; 148 HRESULT hr = pBinding_->QueryInterface(IID_IWinInetHttpInfo, 149 (void **)&hp); 150 if (SUCCEEDED(hr)) { 151 char contentType[1024]; 152 DWORD bufSize = sizeof(contentType); 153 DWORD flags = 0; 154 contentType[0] = 0; 155 hr = hp->QueryInfo(HTTP_QUERY_CONTENT_TYPE, contentType, 156 &bufSize, 0, NULL); 157 if (SUCCEEDED(hr)) { 158 char charset[CHARSET_MAX]; 159 getXMLCharset(contentType, charset); 160 if (charset[0]) { 161 #ifdef XML_UNICODE 162 XML_Char wcharset[CHARSET_MAX]; 163 XML_Char *p1 = wcharset; 164 const char *p2 = charset; 165 while ((*p1++ = (unsigned char)*p2++) != 0) 166 ; 167 XML_SetEncoding(parser_, wcharset); 168 #else 169 XML_SetEncoding(parser_, charset); 170 #endif 171 } 172 } 173 hp->Release(); 174 } 175 } 176 if (!parser_) 177 return E_ABORT; 178 if (pstgmed->tymed == TYMED_ISTREAM) { 179 while (totalRead_ < dwSize) { 180 #define READ_MAX (64*1024) 181 DWORD nToRead = dwSize - totalRead_; 182 if (nToRead > READ_MAX) 183 nToRead = READ_MAX; 184 void *buf = XML_GetBuffer(parser_, nToRead); 185 if (!buf) { 186 _ftprintf(stderr, _T("out of memory\n")); 187 return E_ABORT; 188 } 189 DWORD nRead; 190 HRESULT hr = pstgmed->pstm->Read(buf, nToRead, &nRead); 191 if (SUCCEEDED(hr)) { 192 totalRead_ += nRead; 193 if (!XML_ParseBuffer(parser_, 194 nRead, 195 (grfBSCF & BSCF_LASTDATANOTIFICATION) != 0 196 && totalRead_ == dwSize)) { 197 reportError(parser_); 198 return E_ABORT; 199 } 200 } 201 } 202 } 203 return S_OK; 204 } 205 206 STDMETHODIMP 207 Callback::OnObjectAvailable(REFIID, IUnknown *) 208 { 209 return S_OK; 210 } 211 212 int 213 Callback::externalEntityRef(const XML_Char *context, 214 const XML_Char *systemId, 215 const XML_Char *publicId) 216 { 217 XML_Parser entParser = XML_ExternalEntityParserCreate(parser_, context, 0); 218 XML_SetBase(entParser, systemId); 219 int ret = processURL(entParser, baseMoniker_, systemId); 220 XML_ParserFree(entParser); 221 return ret; 222 } 223 224 Callback::Callback(XML_Parser parser, IMoniker *baseMoniker, 225 StopHandler stopHandler, void *stopArg) 226 : parser_(parser), 227 baseMoniker_(baseMoniker), 228 ref_(0), 229 pBinding_(0), 230 totalRead_(0), 231 stopHandler_(stopHandler), 232 stopArg_(stopArg) 233 { 234 if (baseMoniker_) 235 baseMoniker_->AddRef(); 236 } 237 238 Callback::~Callback() 239 { 240 if (pBinding_) 241 pBinding_->Release(); 242 if (baseMoniker_) 243 baseMoniker_->Release(); 244 } 245 246 static int 247 externalEntityRef(void *arg, 248 const XML_Char *context, 249 const XML_Char *base, 250 const XML_Char *systemId, 251 const XML_Char *publicId) 252 { 253 return ((Callback *)arg)->externalEntityRef(context, systemId, publicId); 254 } 255 256 257 static HRESULT 258 openStream(XML_Parser parser, 259 IMoniker *baseMoniker, 260 const XML_Char *uri, 261 StopHandler stopHandler, void *stopArg) 262 { 263 if (!XML_SetBase(parser, uri)) 264 return E_OUTOFMEMORY; 265 HRESULT hr; 266 IMoniker *m; 267 #ifdef XML_UNICODE 268 hr = CreateURLMoniker(0, uri, &m); 269 #else 270 LPWSTR uriw = new wchar_t[strlen(uri) + 1]; 271 for (int i = 0;; i++) { 272 uriw[i] = uri[i]; 273 if (uriw[i] == 0) 274 break; 275 } 276 hr = CreateURLMoniker(baseMoniker, uriw, &m); 277 delete [] uriw; 278 #endif 279 if (FAILED(hr)) 280 return hr; 281 IBindStatusCallback *cb = new Callback(parser, m, stopHandler, stopArg); 282 XML_SetExternalEntityRefHandler(parser, externalEntityRef); 283 XML_SetExternalEntityRefHandlerArg(parser, cb); 284 cb->AddRef(); 285 IBindCtx *b; 286 if (FAILED(hr = CreateAsyncBindCtx(0, cb, 0, &b))) { 287 cb->Release(); 288 m->Release(); 289 return hr; 290 } 291 cb->Release(); 292 IStream *pStream; 293 hr = m->BindToStorage(b, 0, IID_IStream, (void **)&pStream); 294 if (SUCCEEDED(hr)) { 295 if (pStream) 296 pStream->Release(); 297 } 298 if (hr == MK_S_ASYNCHRONOUS) 299 hr = S_OK; 300 m->Release(); 301 b->Release(); 302 return hr; 303 } 304 305 struct QuitInfo { 306 const XML_Char *url; 307 HRESULT hr; 308 int stop; 309 }; 310 311 static void 312 winPerror(const XML_Char *url, HRESULT hr) 313 { 314 LPVOID buf; 315 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 316 | FORMAT_MESSAGE_FROM_HMODULE, 317 GetModuleHandleA("urlmon.dll"), 318 hr, 319 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 320 (LPTSTR) &buf, 321 0, 322 NULL) 323 || FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 324 | FORMAT_MESSAGE_FROM_SYSTEM, 325 0, 326 hr, 327 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 328 (LPTSTR) &buf, 329 0, 330 NULL)) { 331 /* The system error messages seem to end with a newline. */ 332 _ftprintf(stderr, _T("%s: %s"), url, buf); 333 fflush(stderr); 334 LocalFree(buf); 335 } 336 else 337 _ftprintf(stderr, _T("%s: error %x\n"), url, hr); 338 } 339 340 static void 341 threadQuit(void *p, HRESULT hr) 342 { 343 QuitInfo *qi = (QuitInfo *)p; 344 qi->hr = hr; 345 qi->stop = 1; 346 } 347 348 extern "C" 349 int 350 XML_URLInit(void) 351 { 352 return SUCCEEDED(CoInitialize(0)); 353 } 354 355 extern "C" 356 void 357 XML_URLUninit(void) 358 { 359 CoUninitialize(); 360 } 361 362 static int 363 processURL(XML_Parser parser, IMoniker *baseMoniker, 364 const XML_Char *url) 365 { 366 QuitInfo qi; 367 qi.stop = 0; 368 qi.url = url; 369 370 XML_SetBase(parser, url); 371 HRESULT hr = openStream(parser, baseMoniker, url, threadQuit, &qi); 372 if (FAILED(hr)) { 373 winPerror(url, hr); 374 return 0; 375 } 376 else if (FAILED(qi.hr)) { 377 winPerror(url, qi.hr); 378 return 0; 379 } 380 MSG msg; 381 while (!qi.stop && GetMessage (&msg, NULL, 0, 0)) { 382 TranslateMessage (&msg); 383 DispatchMessage (&msg); 384 } 385 return 1; 386 } 387 388 extern "C" 389 int 390 XML_ProcessURL(XML_Parser parser, 391 const XML_Char *url, 392 unsigned flags) 393 { 394 return processURL(parser, 0, url); 395 } 396