Home | History | Annotate | Download | only in xmlwf
      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 }
    396