Import expat-2.0.1
[dragonfly.git] / contrib / expat / xmlwf / xmlwin32url.cxx
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 }