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 
STDMETHODIMP_(ULONG)47 STDMETHODIMP_(ULONG)
48 Callback::AddRef()
49 {
50   return ref_++;
51 }
52 
STDMETHODIMP_(ULONG)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
QueryInterface(REFIID riid,void ** ppv)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
OnStartBinding(DWORD,IBinding * pBinding)77 Callback::OnStartBinding(DWORD, IBinding* pBinding)
78 {
79   pBinding_ = pBinding;
80   pBinding->AddRef();
81   return S_OK;
82 }
83 
84 STDMETHODIMP
GetPriority(LONG *)85 Callback::GetPriority(LONG *)
86 {
87   return E_NOTIMPL;
88 }
89 
90 STDMETHODIMP
OnLowResource(DWORD)91 Callback::OnLowResource(DWORD)
92 {
93   return E_NOTIMPL;
94 }
95 
96 STDMETHODIMP
OnProgress(ULONG,ULONG,ULONG,LPCWSTR)97 Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR)
98 {
99   return S_OK;
100 }
101 
102 STDMETHODIMP
OnStopBinding(HRESULT hr,LPCWSTR szError)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
GetBindInfo(DWORD * pgrfBINDF,BINDINFO * pbindinfo)118 Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo)
119 {
120   *pgrfBINDF = BINDF_ASYNCHRONOUS;
121   return S_OK;
122 }
123 
124 static void
reportError(XML_Parser parser)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
OnDataAvailable(DWORD grfBSCF,DWORD dwSize,FORMATETC * pfmtetc,STGMEDIUM * pstgmed)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
OnObjectAvailable(REFIID,IUnknown *)207 Callback::OnObjectAvailable(REFIID, IUnknown *)
208 {
209   return S_OK;
210 }
211 
212 int
externalEntityRef(const XML_Char * context,const XML_Char * systemId,const XML_Char * publicId)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 
Callback(XML_Parser parser,IMoniker * baseMoniker,StopHandler stopHandler,void * stopArg)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 
~Callback()238 Callback::~Callback()
239 {
240   if (pBinding_)
241     pBinding_->Release();
242   if (baseMoniker_)
243     baseMoniker_->Release();
244 }
245 
246 static int
externalEntityRef(void * arg,const XML_Char * context,const XML_Char * base,const XML_Char * systemId,const XML_Char * publicId)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
openStream(XML_Parser parser,IMoniker * baseMoniker,const XML_Char * uri,StopHandler stopHandler,void * stopArg)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
winPerror(const XML_Char * url,HRESULT hr)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
threadQuit(void * p,HRESULT hr)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
XML_URLInit(void)350 XML_URLInit(void)
351 {
352   return SUCCEEDED(CoInitialize(0));
353 }
354 
355 extern "C"
356 void
XML_URLUninit(void)357 XML_URLUninit(void)
358 {
359   CoUninitialize();
360 }
361 
362 static int
processURL(XML_Parser parser,IMoniker * baseMoniker,const XML_Char * url)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
XML_ProcessURL(XML_Parser parser,const XML_Char * url,unsigned flags)390 XML_ProcessURL(XML_Parser parser,
391                const XML_Char *url,
392                unsigned flags)
393 {
394   return processURL(parser, 0, url);
395 }
396