xmlwin32url.cxx 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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. static int
  13. processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url);
  14. typedef void (*StopHandler)(void *, HRESULT);
  15. class Callback : public IBindStatusCallback {
  16. public:
  17. // IUnknown methods
  18. STDMETHODIMP QueryInterface(REFIID,void **);
  19. STDMETHODIMP_(ULONG) AddRef();
  20. STDMETHODIMP_(ULONG) Release();
  21. // IBindStatusCallback methods
  22. STDMETHODIMP OnStartBinding(DWORD, IBinding *);
  23. STDMETHODIMP GetPriority(LONG *);
  24. STDMETHODIMP OnLowResource(DWORD);
  25. STDMETHODIMP OnProgress(ULONG, ULONG, ULONG, LPCWSTR);
  26. STDMETHODIMP OnStopBinding(HRESULT, LPCWSTR);
  27. STDMETHODIMP GetBindInfo(DWORD *, BINDINFO *);
  28. STDMETHODIMP OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *);
  29. STDMETHODIMP OnObjectAvailable(REFIID, IUnknown *);
  30. Callback(XML_Parser, IMoniker *, StopHandler, void *);
  31. ~Callback();
  32. int externalEntityRef(const XML_Char *context,
  33. const XML_Char *systemId, const XML_Char *publicId);
  34. private:
  35. XML_Parser parser_;
  36. IMoniker *baseMoniker_;
  37. DWORD totalRead_;
  38. ULONG ref_;
  39. IBinding *pBinding_;
  40. StopHandler stopHandler_;
  41. void *stopArg_;
  42. };
  43. STDMETHODIMP_(ULONG)
  44. Callback::AddRef()
  45. {
  46. return ref_++;
  47. }
  48. STDMETHODIMP_(ULONG)
  49. Callback::Release()
  50. {
  51. if (--ref_ == 0) {
  52. delete this;
  53. return 0;
  54. }
  55. return ref_;
  56. }
  57. STDMETHODIMP
  58. Callback::QueryInterface(REFIID riid, void** ppv)
  59. {
  60. if (IsEqualGUID(riid, IID_IUnknown))
  61. *ppv = (IUnknown *)this;
  62. else if (IsEqualGUID(riid, IID_IBindStatusCallback))
  63. *ppv = (IBindStatusCallback *)this;
  64. else
  65. return E_NOINTERFACE;
  66. ((LPUNKNOWN)*ppv)->AddRef();
  67. return S_OK;
  68. }
  69. STDMETHODIMP
  70. Callback::OnStartBinding(DWORD, IBinding* pBinding)
  71. {
  72. pBinding_ = pBinding;
  73. pBinding->AddRef();
  74. return S_OK;
  75. }
  76. STDMETHODIMP
  77. Callback::GetPriority(LONG *)
  78. {
  79. return E_NOTIMPL;
  80. }
  81. STDMETHODIMP
  82. Callback::OnLowResource(DWORD)
  83. {
  84. return E_NOTIMPL;
  85. }
  86. STDMETHODIMP
  87. Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR)
  88. {
  89. return S_OK;
  90. }
  91. STDMETHODIMP
  92. Callback::OnStopBinding(HRESULT hr, LPCWSTR szError)
  93. {
  94. if (pBinding_) {
  95. pBinding_->Release();
  96. pBinding_ = 0;
  97. }
  98. if (baseMoniker_) {
  99. baseMoniker_->Release();
  100. baseMoniker_ = 0;
  101. }
  102. stopHandler_(stopArg_, hr);
  103. return S_OK;
  104. }
  105. STDMETHODIMP
  106. Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo)
  107. {
  108. *pgrfBINDF = BINDF_ASYNCHRONOUS;
  109. return S_OK;
  110. }
  111. static void
  112. reportError(XML_Parser parser)
  113. {
  114. int code = XML_GetErrorCode(parser);
  115. const XML_Char *message = XML_ErrorString(code);
  116. if (message)
  117. _ftprintf(stderr, _T("%s:%d:%ld: %s\n"),
  118. XML_GetBase(parser),
  119. XML_GetErrorLineNumber(parser),
  120. XML_GetErrorColumnNumber(parser),
  121. message);
  122. else
  123. _ftprintf(stderr, _T("%s: (unknown message %d)\n"),
  124. XML_GetBase(parser), code);
  125. }
  126. STDMETHODIMP
  127. Callback::OnDataAvailable(DWORD grfBSCF,
  128. DWORD dwSize,
  129. FORMATETC *pfmtetc,
  130. STGMEDIUM* pstgmed)
  131. {
  132. if (grfBSCF & BSCF_FIRSTDATANOTIFICATION) {
  133. IWinInetHttpInfo *hp;
  134. HRESULT hr = pBinding_->QueryInterface(IID_IWinInetHttpInfo,
  135. (void **)&hp);
  136. if (SUCCEEDED(hr)) {
  137. char contentType[1024];
  138. DWORD bufSize = sizeof(contentType);
  139. DWORD flags = 0;
  140. contentType[0] = 0;
  141. hr = hp->QueryInfo(HTTP_QUERY_CONTENT_TYPE, contentType,
  142. &bufSize, 0, NULL);
  143. if (SUCCEEDED(hr)) {
  144. char charset[CHARSET_MAX];
  145. getXMLCharset(contentType, charset);
  146. if (charset[0]) {
  147. #ifdef XML_UNICODE
  148. XML_Char wcharset[CHARSET_MAX];
  149. XML_Char *p1 = wcharset;
  150. const char *p2 = charset;
  151. while ((*p1++ = (unsigned char)*p2++) != 0)
  152. ;
  153. XML_SetEncoding(parser_, wcharset);
  154. #else
  155. XML_SetEncoding(parser_, charset);
  156. #endif
  157. }
  158. }
  159. hp->Release();
  160. }
  161. }
  162. if (!parser_)
  163. return E_ABORT;
  164. if (pstgmed->tymed == TYMED_ISTREAM) {
  165. while (totalRead_ < dwSize) {
  166. #define READ_MAX (64*1024)
  167. DWORD nToRead = dwSize - totalRead_;
  168. if (nToRead > READ_MAX)
  169. nToRead = READ_MAX;
  170. void *buf = XML_GetBuffer(parser_, nToRead);
  171. if (!buf) {
  172. _ftprintf(stderr, _T("out of memory\n"));
  173. return E_ABORT;
  174. }
  175. DWORD nRead;
  176. HRESULT hr = pstgmed->pstm->Read(buf, nToRead, &nRead);
  177. if (SUCCEEDED(hr)) {
  178. totalRead_ += nRead;
  179. if (!XML_ParseBuffer(parser_,
  180. nRead,
  181. (grfBSCF & BSCF_LASTDATANOTIFICATION) != 0
  182. && totalRead_ == dwSize)) {
  183. reportError(parser_);
  184. return E_ABORT;
  185. }
  186. }
  187. }
  188. }
  189. return S_OK;
  190. }
  191. STDMETHODIMP
  192. Callback::OnObjectAvailable(REFIID, IUnknown *)
  193. {
  194. return S_OK;
  195. }
  196. int
  197. Callback::externalEntityRef(const XML_Char *context,
  198. const XML_Char *systemId,
  199. const XML_Char *publicId)
  200. {
  201. XML_Parser entParser = XML_ExternalEntityParserCreate(parser_, context, 0);
  202. XML_SetBase(entParser, systemId);
  203. int ret = processURL(entParser, baseMoniker_, systemId);
  204. XML_ParserFree(entParser);
  205. return ret;
  206. }
  207. Callback::Callback(XML_Parser parser, IMoniker *baseMoniker,
  208. StopHandler stopHandler, void *stopArg)
  209. : parser_(parser),
  210. baseMoniker_(baseMoniker),
  211. ref_(0),
  212. pBinding_(0),
  213. totalRead_(0),
  214. stopHandler_(stopHandler),
  215. stopArg_(stopArg)
  216. {
  217. if (baseMoniker_)
  218. baseMoniker_->AddRef();
  219. }
  220. Callback::~Callback()
  221. {
  222. if (pBinding_)
  223. pBinding_->Release();
  224. if (baseMoniker_)
  225. baseMoniker_->Release();
  226. }
  227. static int
  228. externalEntityRef(void *arg,
  229. const XML_Char *context,
  230. const XML_Char *base,
  231. const XML_Char *systemId,
  232. const XML_Char *publicId)
  233. {
  234. return ((Callback *)arg)->externalEntityRef(context, systemId, publicId);
  235. }
  236. static HRESULT
  237. openStream(XML_Parser parser,
  238. IMoniker *baseMoniker,
  239. const XML_Char *uri,
  240. StopHandler stopHandler, void *stopArg)
  241. {
  242. if (!XML_SetBase(parser, uri))
  243. return E_OUTOFMEMORY;
  244. HRESULT hr;
  245. IMoniker *m;
  246. #ifdef XML_UNICODE
  247. hr = CreateURLMoniker(0, uri, &m);
  248. #else
  249. LPWSTR uriw = new wchar_t[strlen(uri) + 1];
  250. for (int i = 0;; i++) {
  251. uriw[i] = uri[i];
  252. if (uriw[i] == 0)
  253. break;
  254. }
  255. hr = CreateURLMoniker(baseMoniker, uriw, &m);
  256. delete [] uriw;
  257. #endif
  258. if (FAILED(hr))
  259. return hr;
  260. IBindStatusCallback *cb = new Callback(parser, m, stopHandler, stopArg);
  261. XML_SetExternalEntityRefHandler(parser, externalEntityRef);
  262. XML_SetExternalEntityRefHandlerArg(parser, cb);
  263. cb->AddRef();
  264. IBindCtx *b;
  265. if (FAILED(hr = CreateAsyncBindCtx(0, cb, 0, &b))) {
  266. cb->Release();
  267. m->Release();
  268. return hr;
  269. }
  270. cb->Release();
  271. IStream *pStream;
  272. hr = m->BindToStorage(b, 0, IID_IStream, (void **)&pStream);
  273. if (SUCCEEDED(hr)) {
  274. if (pStream)
  275. pStream->Release();
  276. }
  277. if (hr == MK_S_ASYNCHRONOUS)
  278. hr = S_OK;
  279. m->Release();
  280. b->Release();
  281. return hr;
  282. }
  283. struct QuitInfo {
  284. const XML_Char *url;
  285. HRESULT hr;
  286. int stop;
  287. };
  288. static void
  289. winPerror(const XML_Char *url, HRESULT hr)
  290. {
  291. LPVOID buf;
  292. if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
  293. | FORMAT_MESSAGE_FROM_HMODULE,
  294. GetModuleHandleA("urlmon.dll"),
  295. hr,
  296. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  297. (LPTSTR) &buf,
  298. 0,
  299. NULL)
  300. || FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
  301. | FORMAT_MESSAGE_FROM_SYSTEM,
  302. 0,
  303. hr,
  304. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  305. (LPTSTR) &buf,
  306. 0,
  307. NULL)) {
  308. /* The system error messages seem to end with a newline. */
  309. _ftprintf(stderr, _T("%s: %s"), url, buf);
  310. fflush(stderr);
  311. LocalFree(buf);
  312. }
  313. else
  314. _ftprintf(stderr, _T("%s: error %x\n"), url, hr);
  315. }
  316. static void
  317. threadQuit(void *p, HRESULT hr)
  318. {
  319. QuitInfo *qi = (QuitInfo *)p;
  320. qi->hr = hr;
  321. qi->stop = 1;
  322. }
  323. extern "C"
  324. int
  325. XML_URLInit(void)
  326. {
  327. return SUCCEEDED(CoInitialize(0));
  328. }
  329. extern "C"
  330. void
  331. XML_URLUninit(void)
  332. {
  333. CoUninitialize();
  334. }
  335. static int
  336. processURL(XML_Parser parser, IMoniker *baseMoniker,
  337. const XML_Char *url)
  338. {
  339. QuitInfo qi;
  340. qi.stop = 0;
  341. qi.url = url;
  342. XML_SetBase(parser, url);
  343. HRESULT hr = openStream(parser, baseMoniker, url, threadQuit, &qi);
  344. if (FAILED(hr)) {
  345. winPerror(url, hr);
  346. return 0;
  347. }
  348. else if (FAILED(qi.hr)) {
  349. winPerror(url, qi.hr);
  350. return 0;
  351. }
  352. MSG msg;
  353. while (!qi.stop && GetMessage (&msg, NULL, 0, 0)) {
  354. TranslateMessage (&msg);
  355. DispatchMessage (&msg);
  356. }
  357. return 1;
  358. }
  359. extern "C"
  360. int
  361. XML_ProcessURL(XML_Parser parser,
  362. const XML_Char *url,
  363. unsigned flags)
  364. {
  365. return processURL(parser, 0, url);
  366. }