Home | History | Annotate | Download | only in xmlwf
      1 /*
      2                             __  __            _
      3                          ___\ \/ /_ __   __ _| |_
      4                         / _ \\  /| '_ \ / _` | __|
      5                        |  __//  \| |_) | (_| | |_
      6                         \___/_/\_\ .__/ \__,_|\__|
      7                                  |_| XML parser
      8 
      9    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
     10    Copyright (c) 2000-2017 Expat development team
     11    Licensed under the MIT license:
     12 
     13    Permission is  hereby granted,  free of charge,  to any  person obtaining
     14    a  copy  of  this  software   and  associated  documentation  files  (the
     15    "Software"),  to  deal in  the  Software  without restriction,  including
     16    without  limitation the  rights  to use,  copy,  modify, merge,  publish,
     17    distribute, sublicense, and/or sell copies of the Software, and to permit
     18    persons  to whom  the Software  is  furnished to  do so,  subject to  the
     19    following conditions:
     20 
     21    The above copyright  notice and this permission notice  shall be included
     22    in all copies or substantial portions of the Software.
     23 
     24    THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
     25    EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
     26    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
     27    NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
     28    DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
     29    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
     30    USE OR OTHER DEALINGS IN THE SOFTWARE.
     31 */
     32 
     33 #include "expat.h"
     34 #ifdef XML_UNICODE
     35 #define UNICODE
     36 #endif
     37 #include <windows.h>
     38 #include <urlmon.h>
     39 #include <wininet.h>
     40 #include <stdio.h>
     41 #include <tchar.h>
     42 #include "xmlurl.h"
     43 #include "xmlmime.h"
     44 
     45 static int
     46 processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url);
     47 
     48 typedef void (*StopHandler)(void *, HRESULT);
     49 
     50 class Callback : public IBindStatusCallback {
     51 public:
     52   // IUnknown methods
     53   STDMETHODIMP QueryInterface(REFIID,void **);
     54   STDMETHODIMP_(ULONG) AddRef();
     55   STDMETHODIMP_(ULONG) Release();
     56   // IBindStatusCallback methods
     57   STDMETHODIMP OnStartBinding(DWORD, IBinding *);
     58   STDMETHODIMP GetPriority(LONG *);
     59   STDMETHODIMP OnLowResource(DWORD);
     60   STDMETHODIMP OnProgress(ULONG, ULONG, ULONG, LPCWSTR);
     61   STDMETHODIMP OnStopBinding(HRESULT, LPCWSTR);
     62   STDMETHODIMP GetBindInfo(DWORD *, BINDINFO *);
     63   STDMETHODIMP OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *);
     64   STDMETHODIMP OnObjectAvailable(REFIID, IUnknown *);
     65   Callback(XML_Parser, IMoniker *, StopHandler, void *);
     66   ~Callback();
     67   int externalEntityRef(const XML_Char *context,
     68                         const XML_Char *systemId, const XML_Char *publicId);
     69 private:
     70   XML_Parser parser_;
     71   IMoniker *baseMoniker_;
     72   DWORD totalRead_;
     73   ULONG ref_;
     74   IBinding *pBinding_;
     75   StopHandler stopHandler_;
     76   void *stopArg_;
     77 };
     78 
     79 STDMETHODIMP_(ULONG)
     80 Callback::AddRef()
     81 {
     82   return ref_++;
     83 }
     84 
     85 STDMETHODIMP_(ULONG)
     86 Callback::Release()
     87 {
     88   if (--ref_ == 0) {
     89     delete this;
     90     return 0;
     91   }
     92   return ref_;
     93 }
     94 
     95 STDMETHODIMP
     96 Callback::QueryInterface(REFIID riid, void** ppv)
     97 {
     98   if (IsEqualGUID(riid, IID_IUnknown))
     99     *ppv = (IUnknown *)this;
    100   else if (IsEqualGUID(riid, IID_IBindStatusCallback))
    101     *ppv = (IBindStatusCallback *)this;
    102   else
    103     return E_NOINTERFACE;
    104   ((LPUNKNOWN)*ppv)->AddRef();
    105   return S_OK;
    106 }
    107 
    108 STDMETHODIMP
    109 Callback::OnStartBinding(DWORD, IBinding* pBinding)
    110 {
    111   pBinding_ = pBinding;
    112   pBinding->AddRef();
    113   return S_OK;
    114 }
    115 
    116 STDMETHODIMP
    117 Callback::GetPriority(LONG *)
    118 {
    119   return E_NOTIMPL;
    120 }
    121 
    122 STDMETHODIMP
    123 Callback::OnLowResource(DWORD)
    124 {
    125   return E_NOTIMPL;
    126 }
    127 
    128 STDMETHODIMP
    129 Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR)
    130 {
    131   return S_OK;
    132 }
    133 
    134 STDMETHODIMP
    135 Callback::OnStopBinding(HRESULT hr, LPCWSTR szError)
    136 {
    137   if (pBinding_) {
    138     pBinding_->Release();
    139     pBinding_ = 0;
    140   }
    141   if (baseMoniker_) {
    142     baseMoniker_->Release();
    143     baseMoniker_ = 0;
    144   }
    145   stopHandler_(stopArg_, hr);
    146   return S_OK;
    147 }
    148 
    149 STDMETHODIMP
    150 Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo)
    151 {
    152   *pgrfBINDF = BINDF_ASYNCHRONOUS;
    153   return S_OK;
    154 }
    155 
    156 static void
    157 reportError(XML_Parser parser)
    158 {
    159   int code = XML_GetErrorCode(parser);
    160   const XML_Char *message = XML_ErrorString(code);
    161   if (message)
    162     _ftprintf(stderr, _T("%s:%d:%ld: %s\n"),
    163 	     XML_GetBase(parser),
    164 	     XML_GetErrorLineNumber(parser),
    165 	     XML_GetErrorColumnNumber(parser),
    166 	     message);
    167   else
    168     _ftprintf(stderr, _T("%s: (unknown message %d)\n"),
    169               XML_GetBase(parser), code);
    170 }
    171 
    172 STDMETHODIMP
    173 Callback::OnDataAvailable(DWORD grfBSCF,
    174                           DWORD dwSize,
    175                           FORMATETC *pfmtetc,
    176                           STGMEDIUM* pstgmed)
    177 {
    178   if (grfBSCF & BSCF_FIRSTDATANOTIFICATION) {
    179     IWinInetHttpInfo *hp;
    180     HRESULT hr = pBinding_->QueryInterface(IID_IWinInetHttpInfo,
    181                                            (void **)&hp);
    182     if (SUCCEEDED(hr)) {
    183       char contentType[1024];
    184       DWORD bufSize = sizeof(contentType);
    185       DWORD flags = 0;
    186       contentType[0] = 0;
    187       hr = hp->QueryInfo(HTTP_QUERY_CONTENT_TYPE, contentType,
    188                          &bufSize, 0, NULL);
    189       if (SUCCEEDED(hr)) {
    190 	char charset[CHARSET_MAX];
    191 	getXMLCharset(contentType, charset);
    192 	if (charset[0]) {
    193 #ifdef XML_UNICODE
    194 	  XML_Char wcharset[CHARSET_MAX];
    195 	  XML_Char *p1 = wcharset;
    196 	  const char *p2 = charset;
    197 	  while ((*p1++ = (unsigned char)*p2++) != 0)
    198 	    ;
    199 	  XML_SetEncoding(parser_, wcharset);
    200 #else
    201 	  XML_SetEncoding(parser_, charset);
    202 #endif
    203 	}
    204       }
    205       hp->Release();
    206     }
    207   }
    208   if (!parser_)
    209     return E_ABORT;
    210   if (pstgmed->tymed == TYMED_ISTREAM) {
    211     while (totalRead_ < dwSize) {
    212 #define READ_MAX (64*1024)
    213       DWORD nToRead = dwSize - totalRead_;
    214       if (nToRead > READ_MAX)
    215 	nToRead = READ_MAX;
    216       void *buf = XML_GetBuffer(parser_, nToRead);
    217       if (!buf) {
    218 	_ftprintf(stderr, _T("out of memory\n"));
    219 	return E_ABORT;
    220       }
    221       DWORD nRead;
    222       HRESULT hr = pstgmed->pstm->Read(buf, nToRead, &nRead);
    223       if (SUCCEEDED(hr)) {
    224 	totalRead_ += nRead;
    225 	if (!XML_ParseBuffer(parser_,
    226 			     nRead,
    227 			     (grfBSCF & BSCF_LASTDATANOTIFICATION) != 0
    228 			     && totalRead_ == dwSize)) {
    229 	  reportError(parser_);
    230 	  return E_ABORT;
    231 	}
    232       }
    233     }
    234   }
    235   return S_OK;
    236 }
    237 
    238 STDMETHODIMP
    239 Callback::OnObjectAvailable(REFIID, IUnknown *)
    240 {
    241   return S_OK;
    242 }
    243 
    244 int
    245 Callback::externalEntityRef(const XML_Char *context,
    246                             const XML_Char *systemId,
    247                             const XML_Char *publicId)
    248 {
    249   XML_Parser entParser = XML_ExternalEntityParserCreate(parser_, context, 0);
    250   XML_SetBase(entParser, systemId);
    251   int ret = processURL(entParser, baseMoniker_, systemId);
    252   XML_ParserFree(entParser);
    253   return ret;
    254 }
    255 
    256 Callback::Callback(XML_Parser parser, IMoniker *baseMoniker,
    257                    StopHandler stopHandler, void *stopArg)
    258 : parser_(parser),
    259   baseMoniker_(baseMoniker),
    260   ref_(0),
    261   pBinding_(0),
    262   totalRead_(0),
    263   stopHandler_(stopHandler),
    264   stopArg_(stopArg)
    265 {
    266   if (baseMoniker_)
    267     baseMoniker_->AddRef();
    268 }
    269 
    270 Callback::~Callback()
    271 {
    272   if (pBinding_)
    273     pBinding_->Release();
    274   if (baseMoniker_)
    275     baseMoniker_->Release();
    276 }
    277 
    278 static int
    279 externalEntityRef(void *arg,
    280                   const XML_Char *context,
    281                   const XML_Char *base,
    282                   const XML_Char *systemId,
    283                   const XML_Char *publicId)
    284 {
    285   return ((Callback *)arg)->externalEntityRef(context, systemId, publicId);
    286 }
    287 
    288 
    289 static HRESULT
    290 openStream(XML_Parser parser,
    291            IMoniker *baseMoniker,
    292            const XML_Char *uri,
    293            StopHandler stopHandler, void *stopArg)
    294 {
    295   if (!XML_SetBase(parser, uri))
    296     return E_OUTOFMEMORY;
    297   HRESULT hr;
    298   IMoniker *m;
    299 #ifdef XML_UNICODE
    300   hr = CreateURLMoniker(0, uri, &m);
    301 #else
    302   LPWSTR uriw = new wchar_t[strlen(uri) + 1];
    303   for (int i = 0;; i++) {
    304     uriw[i] = uri[i];
    305     if (uriw[i] == 0)
    306       break;
    307   }
    308   hr = CreateURLMoniker(baseMoniker, uriw, &m);
    309   delete [] uriw;
    310 #endif
    311   if (FAILED(hr))
    312     return hr;
    313   IBindStatusCallback *cb = new Callback(parser, m, stopHandler, stopArg);
    314   XML_SetExternalEntityRefHandler(parser, externalEntityRef);
    315   XML_SetExternalEntityRefHandlerArg(parser, cb);
    316   cb->AddRef();
    317   IBindCtx *b;
    318   if (FAILED(hr = CreateAsyncBindCtx(0, cb, 0, &b))) {
    319     cb->Release();
    320     m->Release();
    321     return hr;
    322   }
    323   cb->Release();
    324   IStream *pStream;
    325   hr = m->BindToStorage(b, 0, IID_IStream, (void **)&pStream);
    326   if (SUCCEEDED(hr)) {
    327     if (pStream)
    328       pStream->Release();
    329   }
    330   if (hr == MK_S_ASYNCHRONOUS)
    331     hr = S_OK;
    332   m->Release();
    333   b->Release();
    334   return hr;
    335 }
    336 
    337 struct QuitInfo {
    338   const XML_Char *url;
    339   HRESULT hr;
    340   int stop;
    341 };
    342 
    343 static void
    344 winPerror(const XML_Char *url, HRESULT hr)
    345 {
    346   LPVOID buf;
    347   if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
    348 		    | FORMAT_MESSAGE_FROM_HMODULE,
    349 		    GetModuleHandleA("urlmon.dll"),
    350 		    hr,
    351 		    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    352 		    (LPTSTR) &buf,
    353 		    0,
    354 		    NULL)
    355       || FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
    356 		      | FORMAT_MESSAGE_FROM_SYSTEM,
    357 		      0,
    358 		      hr,
    359 		      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    360 		      (LPTSTR) &buf,
    361 		      0,
    362 		      NULL)) {
    363     /* The system error messages seem to end with a newline. */
    364     _ftprintf(stderr, _T("%s: %s"), url, buf);
    365     fflush(stderr);
    366     LocalFree(buf);
    367   }
    368   else
    369     _ftprintf(stderr, _T("%s: error %x\n"), url, hr);
    370 }
    371 
    372 static void
    373 threadQuit(void *p, HRESULT hr)
    374 {
    375   QuitInfo *qi = (QuitInfo *)p;
    376   qi->hr = hr;
    377   qi->stop = 1;
    378 }
    379 
    380 extern "C"
    381 int
    382 XML_URLInit(void)
    383 {
    384   return SUCCEEDED(CoInitialize(0));
    385 }
    386 
    387 extern "C"
    388 void
    389 XML_URLUninit(void)
    390 {
    391   CoUninitialize();
    392 }
    393 
    394 static int
    395 processURL(XML_Parser parser, IMoniker *baseMoniker,
    396            const XML_Char *url)
    397 {
    398   QuitInfo qi;
    399   qi.stop = 0;
    400   qi.url = url;
    401 
    402   XML_SetBase(parser, url);
    403   HRESULT hr = openStream(parser, baseMoniker, url, threadQuit, &qi);
    404   if (FAILED(hr)) {
    405     winPerror(url, hr);
    406     return 0;
    407   }
    408   else if (FAILED(qi.hr)) {
    409     winPerror(url, qi.hr);
    410     return 0;
    411   }
    412   MSG msg;
    413   while (!qi.stop && GetMessage (&msg, NULL, 0, 0)) {
    414     TranslateMessage (&msg);
    415     DispatchMessage (&msg);
    416   }
    417   return 1;
    418 }
    419 
    420 extern "C"
    421 int
    422 XML_ProcessURL(XML_Parser parser,
    423                const XML_Char *url,
    424                unsigned flags)
    425 {
    426   return processURL(parser, 0, url);
    427 }
    428