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