Merge branch 'tizen_base' into tizen
[platform/upstream/expat.git] / xmlwf / xmlwin32url.cxx
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 }