| #include "expat.h" |
| #ifdef XML_UNICODE |
| #define UNICODE |
| #endif |
| #include <windows.h> |
| #include <urlmon.h> |
| #include <wininet.h> |
| #include <stdio.h> |
| #include <tchar.h> |
| #include "xmlurl.h" |
| #include "xmlmime.h" |
| |
| static int |
| processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url); |
| |
| typedef void (*StopHandler)(void *, HRESULT); |
| |
| class Callback : public IBindStatusCallback { |
| public: |
| // IUnknown methods |
| STDMETHODIMP QueryInterface(REFIID,void **); |
| STDMETHODIMP_(ULONG) AddRef(); |
| STDMETHODIMP_(ULONG) Release(); |
| // IBindStatusCallback methods |
| STDMETHODIMP OnStartBinding(DWORD, IBinding *); |
| STDMETHODIMP GetPriority(LONG *); |
| STDMETHODIMP OnLowResource(DWORD); |
| STDMETHODIMP OnProgress(ULONG, ULONG, ULONG, LPCWSTR); |
| STDMETHODIMP OnStopBinding(HRESULT, LPCWSTR); |
| STDMETHODIMP GetBindInfo(DWORD *, BINDINFO *); |
| STDMETHODIMP OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *); |
| STDMETHODIMP OnObjectAvailable(REFIID, IUnknown *); |
| Callback(XML_Parser, IMoniker *, StopHandler, void *); |
| ~Callback(); |
| int externalEntityRef(const XML_Char *context, |
| const XML_Char *systemId, const XML_Char *publicId); |
| private: |
| XML_Parser parser_; |
| IMoniker *baseMoniker_; |
| DWORD totalRead_; |
| ULONG ref_; |
| IBinding *pBinding_; |
| StopHandler stopHandler_; |
| void *stopArg_; |
| }; |
| |
| STDMETHODIMP_(ULONG) |
| Callback::AddRef() |
| { |
| return ref_++; |
| } |
| |
| STDMETHODIMP_(ULONG) |
| Callback::Release() |
| { |
| if (--ref_ == 0) { |
| delete this; |
| return 0; |
| } |
| return ref_; |
| } |
| |
| STDMETHODIMP |
| Callback::QueryInterface(REFIID riid, void** ppv) |
| { |
| if (IsEqualGUID(riid, IID_IUnknown)) |
| *ppv = (IUnknown *)this; |
| else if (IsEqualGUID(riid, IID_IBindStatusCallback)) |
| *ppv = (IBindStatusCallback *)this; |
| else |
| return E_NOINTERFACE; |
| ((LPUNKNOWN)*ppv)->AddRef(); |
| return S_OK; |
| } |
| |
| STDMETHODIMP |
| Callback::OnStartBinding(DWORD, IBinding* pBinding) |
| { |
| pBinding_ = pBinding; |
| pBinding->AddRef(); |
| return S_OK; |
| } |
| |
| STDMETHODIMP |
| Callback::GetPriority(LONG *) |
| { |
| return E_NOTIMPL; |
| } |
| |
| STDMETHODIMP |
| Callback::OnLowResource(DWORD) |
| { |
| return E_NOTIMPL; |
| } |
| |
| STDMETHODIMP |
| Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR) |
| { |
| return S_OK; |
| } |
| |
| STDMETHODIMP |
| Callback::OnStopBinding(HRESULT hr, LPCWSTR szError) |
| { |
| if (pBinding_) { |
| pBinding_->Release(); |
| pBinding_ = 0; |
| } |
| if (baseMoniker_) { |
| baseMoniker_->Release(); |
| baseMoniker_ = 0; |
| } |
| stopHandler_(stopArg_, hr); |
| return S_OK; |
| } |
| |
| STDMETHODIMP |
| Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo) |
| { |
| *pgrfBINDF = BINDF_ASYNCHRONOUS; |
| return S_OK; |
| } |
| |
| static void |
| reportError(XML_Parser parser) |
| { |
| int code = XML_GetErrorCode(parser); |
| const XML_Char *message = XML_ErrorString(code); |
| if (message) |
| _ftprintf(stderr, _T("%s:%d:%ld: %s\n"), |
| XML_GetBase(parser), |
| XML_GetErrorLineNumber(parser), |
| XML_GetErrorColumnNumber(parser), |
| message); |
| else |
| _ftprintf(stderr, _T("%s: (unknown message %d)\n"), |
| XML_GetBase(parser), code); |
| } |
| |
| STDMETHODIMP |
| Callback::OnDataAvailable(DWORD grfBSCF, |
| DWORD dwSize, |
| FORMATETC *pfmtetc, |
| STGMEDIUM* pstgmed) |
| { |
| if (grfBSCF & BSCF_FIRSTDATANOTIFICATION) { |
| IWinInetHttpInfo *hp; |
| HRESULT hr = pBinding_->QueryInterface(IID_IWinInetHttpInfo, |
| (void **)&hp); |
| if (SUCCEEDED(hr)) { |
| char contentType[1024]; |
| DWORD bufSize = sizeof(contentType); |
| DWORD flags = 0; |
| contentType[0] = 0; |
| hr = hp->QueryInfo(HTTP_QUERY_CONTENT_TYPE, contentType, |
| &bufSize, 0, NULL); |
| if (SUCCEEDED(hr)) { |
| char charset[CHARSET_MAX]; |
| getXMLCharset(contentType, charset); |
| if (charset[0]) { |
| #ifdef XML_UNICODE |
| XML_Char wcharset[CHARSET_MAX]; |
| XML_Char *p1 = wcharset; |
| const char *p2 = charset; |
| while ((*p1++ = (unsigned char)*p2++) != 0) |
| ; |
| XML_SetEncoding(parser_, wcharset); |
| #else |
| XML_SetEncoding(parser_, charset); |
| #endif |
| } |
| } |
| hp->Release(); |
| } |
| } |
| if (!parser_) |
| return E_ABORT; |
| if (pstgmed->tymed == TYMED_ISTREAM) { |
| while (totalRead_ < dwSize) { |
| #define READ_MAX (64*1024) |
| DWORD nToRead = dwSize - totalRead_; |
| if (nToRead > READ_MAX) |
| nToRead = READ_MAX; |
| void *buf = XML_GetBuffer(parser_, nToRead); |
| if (!buf) { |
| _ftprintf(stderr, _T("out of memory\n")); |
| return E_ABORT; |
| } |
| DWORD nRead; |
| HRESULT hr = pstgmed->pstm->Read(buf, nToRead, &nRead); |
| if (SUCCEEDED(hr)) { |
| totalRead_ += nRead; |
| if (!XML_ParseBuffer(parser_, |
| nRead, |
| (grfBSCF & BSCF_LASTDATANOTIFICATION) != 0 |
| && totalRead_ == dwSize)) { |
| reportError(parser_); |
| return E_ABORT; |
| } |
| } |
| } |
| } |
| return S_OK; |
| } |
| |
| STDMETHODIMP |
| Callback::OnObjectAvailable(REFIID, IUnknown *) |
| { |
| return S_OK; |
| } |
| |
| int |
| Callback::externalEntityRef(const XML_Char *context, |
| const XML_Char *systemId, |
| const XML_Char *publicId) |
| { |
| XML_Parser entParser = XML_ExternalEntityParserCreate(parser_, context, 0); |
| XML_SetBase(entParser, systemId); |
| int ret = processURL(entParser, baseMoniker_, systemId); |
| XML_ParserFree(entParser); |
| return ret; |
| } |
| |
| Callback::Callback(XML_Parser parser, IMoniker *baseMoniker, |
| StopHandler stopHandler, void *stopArg) |
| : parser_(parser), |
| baseMoniker_(baseMoniker), |
| ref_(0), |
| pBinding_(0), |
| totalRead_(0), |
| stopHandler_(stopHandler), |
| stopArg_(stopArg) |
| { |
| if (baseMoniker_) |
| baseMoniker_->AddRef(); |
| } |
| |
| Callback::~Callback() |
| { |
| if (pBinding_) |
| pBinding_->Release(); |
| if (baseMoniker_) |
| baseMoniker_->Release(); |
| } |
| |
| static int |
| externalEntityRef(void *arg, |
| const XML_Char *context, |
| const XML_Char *base, |
| const XML_Char *systemId, |
| const XML_Char *publicId) |
| { |
| return ((Callback *)arg)->externalEntityRef(context, systemId, publicId); |
| } |
| |
| |
| static HRESULT |
| openStream(XML_Parser parser, |
| IMoniker *baseMoniker, |
| const XML_Char *uri, |
| StopHandler stopHandler, void *stopArg) |
| { |
| if (!XML_SetBase(parser, uri)) |
| return E_OUTOFMEMORY; |
| HRESULT hr; |
| IMoniker *m; |
| #ifdef XML_UNICODE |
| hr = CreateURLMoniker(0, uri, &m); |
| #else |
| LPWSTR uriw = new wchar_t[strlen(uri) + 1]; |
| for (int i = 0;; i++) { |
| uriw[i] = uri[i]; |
| if (uriw[i] == 0) |
| break; |
| } |
| hr = CreateURLMoniker(baseMoniker, uriw, &m); |
| delete [] uriw; |
| #endif |
| if (FAILED(hr)) |
| return hr; |
| IBindStatusCallback *cb = new Callback(parser, m, stopHandler, stopArg); |
| XML_SetExternalEntityRefHandler(parser, externalEntityRef); |
| XML_SetExternalEntityRefHandlerArg(parser, cb); |
| cb->AddRef(); |
| IBindCtx *b; |
| if (FAILED(hr = CreateAsyncBindCtx(0, cb, 0, &b))) { |
| cb->Release(); |
| m->Release(); |
| return hr; |
| } |
| cb->Release(); |
| IStream *pStream; |
| hr = m->BindToStorage(b, 0, IID_IStream, (void **)&pStream); |
| if (SUCCEEDED(hr)) { |
| if (pStream) |
| pStream->Release(); |
| } |
| if (hr == MK_S_ASYNCHRONOUS) |
| hr = S_OK; |
| m->Release(); |
| b->Release(); |
| return hr; |
| } |
| |
| struct QuitInfo { |
| const XML_Char *url; |
| HRESULT hr; |
| int stop; |
| }; |
| |
| static void |
| winPerror(const XML_Char *url, HRESULT hr) |
| { |
| LPVOID buf; |
| if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
| | FORMAT_MESSAGE_FROM_HMODULE, |
| GetModuleHandleA("urlmon.dll"), |
| hr, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| (LPTSTR) &buf, |
| 0, |
| NULL) |
| || FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
| | FORMAT_MESSAGE_FROM_SYSTEM, |
| 0, |
| hr, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| (LPTSTR) &buf, |
| 0, |
| NULL)) { |
| /* The system error messages seem to end with a newline. */ |
| _ftprintf(stderr, _T("%s: %s"), url, buf); |
| fflush(stderr); |
| LocalFree(buf); |
| } |
| else |
| _ftprintf(stderr, _T("%s: error %x\n"), url, hr); |
| } |
| |
| static void |
| threadQuit(void *p, HRESULT hr) |
| { |
| QuitInfo *qi = (QuitInfo *)p; |
| qi->hr = hr; |
| qi->stop = 1; |
| } |
| |
| extern "C" |
| int |
| XML_URLInit(void) |
| { |
| return SUCCEEDED(CoInitialize(0)); |
| } |
| |
| extern "C" |
| void |
| XML_URLUninit(void) |
| { |
| CoUninitialize(); |
| } |
| |
| static int |
| processURL(XML_Parser parser, IMoniker *baseMoniker, |
| const XML_Char *url) |
| { |
| QuitInfo qi; |
| qi.stop = 0; |
| qi.url = url; |
| |
| XML_SetBase(parser, url); |
| HRESULT hr = openStream(parser, baseMoniker, url, threadQuit, &qi); |
| if (FAILED(hr)) { |
| winPerror(url, hr); |
| return 0; |
| } |
| else if (FAILED(qi.hr)) { |
| winPerror(url, qi.hr); |
| return 0; |
| } |
| MSG msg; |
| while (!qi.stop && GetMessage (&msg, NULL, 0, 0)) { |
| TranslateMessage (&msg); |
| DispatchMessage (&msg); |
| } |
| return 1; |
| } |
| |
| extern "C" |
| int |
| XML_ProcessURL(XML_Parser parser, |
| const XML_Char *url, |
| unsigned flags) |
| { |
| return processURL(parser, 0, url); |
| } |