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