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
13static int
14processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url);
15
16typedef void (*StopHandler)(void *, HRESULT);
17
18class Callback : public IBindStatusCallback {
19public:
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);
37private:
38  XML_Parser parser_;
39  IMoniker *baseMoniker_;
40  DWORD totalRead_;
41  ULONG ref_;
42  IBinding *pBinding_;
43  StopHandler stopHandler_;
44  void *stopArg_;
45};
46
47STDMETHODIMP_(ULONG)
48Callback::AddRef()
49{
50  return ref_++;
51}
52
53STDMETHODIMP_(ULONG)
54Callback::Release()
55{
56  if (--ref_ == 0) {
57    delete this;
58    return 0;
59  }
60  return ref_;
61}
62
63STDMETHODIMP
64Callback::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
76STDMETHODIMP
77Callback::OnStartBinding(DWORD, IBinding* pBinding)
78{
79  pBinding_ = pBinding;
80  pBinding->AddRef();
81  return S_OK;
82}
83
84STDMETHODIMP
85Callback::GetPriority(LONG *)
86{
87  return E_NOTIMPL;
88}
89
90STDMETHODIMP
91Callback::OnLowResource(DWORD)
92{
93  return E_NOTIMPL;
94}
95
96STDMETHODIMP
97Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR)
98{
99  return S_OK;
100}
101
102STDMETHODIMP
103Callback::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
117STDMETHODIMP
118Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo)
119{
120  *pgrfBINDF = BINDF_ASYNCHRONOUS;
121  return S_OK;
122}
123
124static void
125reportError(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
140STDMETHODIMP
141Callback::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
206STDMETHODIMP
207Callback::OnObjectAvailable(REFIID, IUnknown *)
208{
209  return S_OK;
210}
211
212int
213Callback::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
224Callback::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
238Callback::~Callback()
239{
240  if (pBinding_)
241    pBinding_->Release();
242  if (baseMoniker_)
243    baseMoniker_->Release();
244}
245
246static int
247externalEntityRef(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
257static HRESULT
258openStream(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
305struct QuitInfo {
306  const XML_Char *url;
307  HRESULT hr;
308  int stop;
309};
310
311static void
312winPerror(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
340static void
341threadQuit(void *p, HRESULT hr)
342{
343  QuitInfo *qi = (QuitInfo *)p;
344  qi->hr = hr;
345  qi->stop = 1;
346}
347
348extern "C"
349int
350XML_URLInit(void)
351{
352  return SUCCEEDED(CoInitialize(0));
353}
354
355extern "C"
356void
357XML_URLUninit(void)
358{
359  CoUninitialize();
360}
361
362static int
363processURL(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
388extern "C"
389int
390XML_ProcessURL(XML_Parser parser,
391               const XML_Char *url,
392               unsigned flags)
393{
394  return processURL(parser, 0, url);
395}
396