]>
Commit | Line | Data |
---|---|---|
5e9f2524 VS |
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 | } |