Casting away some integer conversion warnings that should not pose a problem.
[mirror-ossqm-expat.git] / xmlwf / xmlwin32url.cxx
blobbbfcce22c964357b7991d3e1dc74b739ed7ca320
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"
13 static int
14 processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url);
16 typedef void (*StopHandler)(void *, HRESULT);
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_;
47 STDMETHODIMP_(ULONG)
48 Callback::AddRef()
50 return ref_++;
53 STDMETHODIMP_(ULONG)
54 Callback::Release()
56 if (--ref_ == 0) {
57 delete this;
58 return 0;
60 return ref_;
63 STDMETHODIMP
64 Callback::QueryInterface(REFIID riid, void** ppv)
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;
76 STDMETHODIMP
77 Callback::OnStartBinding(DWORD, IBinding* pBinding)
79 pBinding_ = pBinding;
80 pBinding->AddRef();
81 return S_OK;
84 STDMETHODIMP
85 Callback::GetPriority(LONG *)
87 return E_NOTIMPL;
90 STDMETHODIMP
91 Callback::OnLowResource(DWORD)
93 return E_NOTIMPL;
96 STDMETHODIMP
97 Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR)
99 return S_OK;
102 STDMETHODIMP
103 Callback::OnStopBinding(HRESULT hr, LPCWSTR szError)
105 if (pBinding_) {
106 pBinding_->Release();
107 pBinding_ = 0;
109 if (baseMoniker_) {
110 baseMoniker_->Release();
111 baseMoniker_ = 0;
113 stopHandler_(stopArg_, hr);
114 return S_OK;
117 STDMETHODIMP
118 Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo)
120 *pgrfBINDF = BINDF_ASYNCHRONOUS;
121 return S_OK;
124 static void
125 reportError(XML_Parser parser)
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);
140 STDMETHODIMP
141 Callback::OnDataAvailable(DWORD grfBSCF,
142 DWORD dwSize,
143 FORMATETC *pfmtetc,
144 STGMEDIUM* pstgmed)
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)
167 XML_SetEncoding(parser_, wcharset);
168 #else
169 XML_SetEncoding(parser_, charset);
170 #endif
173 hp->Release();
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;
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;
203 return S_OK;
206 STDMETHODIMP
207 Callback::OnObjectAvailable(REFIID, IUnknown *)
209 return S_OK;
213 Callback::externalEntityRef(const XML_Char *context,
214 const XML_Char *systemId,
215 const XML_Char *publicId)
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;
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)
234 if (baseMoniker_)
235 baseMoniker_->AddRef();
238 Callback::~Callback()
240 if (pBinding_)
241 pBinding_->Release();
242 if (baseMoniker_)
243 baseMoniker_->Release();
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)
253 return ((Callback *)arg)->externalEntityRef(context, systemId, publicId);
257 static HRESULT
258 openStream(XML_Parser parser,
259 IMoniker *baseMoniker,
260 const XML_Char *uri,
261 StopHandler stopHandler, void *stopArg)
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;
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;
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();
298 if (hr == MK_S_ASYNCHRONOUS)
299 hr = S_OK;
300 m->Release();
301 b->Release();
302 return hr;
305 struct QuitInfo {
306 const XML_Char *url;
307 HRESULT hr;
308 int stop;
311 static void
312 winPerror(const XML_Char *url, HRESULT hr)
314 LPVOID buf;
315 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
316 | FORMAT_MESSAGE_FROM_HMODULE,
317 GetModuleHandleA("urlmon.dll"),
319 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
320 (LPTSTR) &buf,
322 NULL)
323 || FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
324 | FORMAT_MESSAGE_FROM_SYSTEM,
327 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
328 (LPTSTR) &buf,
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);
336 else
337 _ftprintf(stderr, _T("%s: error %x\n"), url, hr);
340 static void
341 threadQuit(void *p, HRESULT hr)
343 QuitInfo *qi = (QuitInfo *)p;
344 qi->hr = hr;
345 qi->stop = 1;
348 extern "C"
350 XML_URLInit(void)
352 return SUCCEEDED(CoInitialize(0));
355 extern "C"
356 void
357 XML_URLUninit(void)
359 CoUninitialize();
362 static int
363 processURL(XML_Parser parser, IMoniker *baseMoniker,
364 const XML_Char *url)
366 QuitInfo qi;
367 qi.stop = 0;
368 qi.url = url;
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;
376 else if (FAILED(qi.hr)) {
377 winPerror(url, qi.hr);
378 return 0;
380 MSG msg;
381 while (!qi.stop && GetMessage (&msg, NULL, 0, 0)) {
382 TranslateMessage (&msg);
383 DispatchMessage (&msg);
385 return 1;
388 extern "C"
390 XML_ProcessURL(XML_Parser parser,
391 const XML_Char *url,
392 unsigned flags)
394 return processURL(parser, 0, url);