This is needed by Anarchy Online's web downloader tool. Thanks to Mike to have given me some Win32 code of his which did about what I needed, I just had to tidy it up, add the QueryInfo call and the callbacks. Lionel Changelog: Mike McCormack <mike@codeweavers.com> Lionel Ulmer <lionel.ulmer@free.fr> - implement URLDownloadToFileA/W -- Lionel Ulmer - http://www.bbrox.org/
Index: include/urlmon.h =================================================================== RCS file: /home/wine/wine/include/urlmon.h,v retrieving revision 1.17 diff -u -r1.17 urlmon.h --- include/urlmon.h 13 Jun 2003 16:29:36 -0000 1.17 +++ include/urlmon.h 13 Jul 2003 21:27:07 -0000 @@ -312,6 +312,10 @@ HRESULT WINAPI RegisterBindStatusCallback(IBindCtx *pbc, IBindStatusCallback *pbsc, IBindStatusCallback **ppbsc, DWORD dwReserved); HRESULT WINAPI CompareSecurityIds(BYTE*,DWORD,BYTE*,DWORD,DWORD); +HRESULT WINAPI URLDownloadToFileA(LPUNKNOWN pCaller, LPCSTR szURL, LPCSTR szFileName, DWORD dwReserved, LPBINDSTATUSCALLBACK lpfnCB); +HRESULT WINAPI URLDownloadToFileW(LPUNKNOWN pCaller, LPCWSTR szURL, LPCWSTR szFileName, DWORD dwReserved, LPBINDSTATUSCALLBACK lpfnCB); + + #ifdef __cplusplus } /* extern "C" */ #endif /* defined(__cplusplus) */ Index: dlls/urlmon/umon.c =================================================================== RCS file: /home/wine/wine/dlls/urlmon/umon.c,v retrieving revision 1.22 diff -u -r1.22 umon.c --- dlls/urlmon/umon.c 31 Mar 2003 23:58:27 -0000 1.22 +++ dlls/urlmon/umon.c 13 Jul 2003 21:27:08 -0000 @@ -26,6 +26,7 @@ #define NONAMELESSSTRUCT #include "windef.h" #include "winbase.h" +#include "winternl.h" #include "winuser.h" #include "objbase.h" #include "wine/debug.h" @@ -1013,4 +1014,218 @@ void WINAPI ReleaseBindInfo(BINDINFO* pbindinfo) { FIXME("(%p)stub!\n", pbindinfo); +} + +/*********************************************************************** + * URLDownloadToFileA (URLMON.@) + * + * Downloads URL szURL to rile szFileName and call lpfnCB callback to + * report progress. + * + * PARAMS + * pCaller [I] controlling IUnknown interface. + * szURL [I] URL of the file to download + * szFileName [I] file name to store the content of the URL + * dwReserved [I] reserved - set to 0 + * lpfnCB [I] callback for progress report + * + * RETURNS + * S_OK on success + * E_OUTOFMEMORY when going out of memory + */ +HRESULT WINAPI URLDownloadToFileA(LPUNKNOWN pCaller, + LPCSTR szURL, + LPCSTR szFileName, + DWORD dwReserved, + LPBINDSTATUSCALLBACK lpfnCB) +{ + UNICODE_STRING szURL_w, szFileName_w; + + if ((szURL == NULL) || (szFileName == NULL)) { + FIXME("(%p,%s,%s,%08lx,%p) cannot accept NULL strings !\n", pCaller, debugstr_a(szURL), debugstr_a(szFileName), dwReserved, lpfnCB); + return E_INVALIDARG; /* The error code is not specified in this case... */ + } + + if (RtlCreateUnicodeStringFromAsciiz(&szURL_w, szURL)) { + if (RtlCreateUnicodeStringFromAsciiz(&szFileName_w, szFileName)) { + HRESULT ret = URLDownloadToFileW(pCaller, szURL_w.Buffer, szFileName_w.Buffer, dwReserved, lpfnCB); + + RtlFreeUnicodeString(&szURL_w); + RtlFreeUnicodeString(&szFileName_w); + + return ret; + } else { + RtlFreeUnicodeString(&szURL_w); + } + } + + FIXME("(%p,%s,%s,%08lx,%p) could not allocate W strings !\n", pCaller, szURL, szFileName, dwReserved, lpfnCB); + return E_OUTOFMEMORY; +} + +/*********************************************************************** + * URLDownloadToFileW (URLMON.@) + * + * Downloads URL szURL to rile szFileName and call lpfnCB callback to + * report progress. + * + * PARAMS + * pCaller [I] controlling IUnknown interface. + * szURL [I] URL of the file to download + * szFileName [I] file name to store the content of the URL + * dwReserved [I] reserved - set to 0 + * lpfnCB [I] callback for progress report + * + * RETURNS + * S_OK on success + * E_OUTOFMEMORY when going out of memory + */ +HRESULT WINAPI URLDownloadToFileW(LPUNKNOWN pCaller, + LPCWSTR szURL, + LPCWSTR szFileName, + DWORD dwReserved, + LPBINDSTATUSCALLBACK lpfnCB) +{ + HINTERNET hinternet, hcon, hreq; + BOOL r; + CHAR buffer[0x1000]; + DWORD sz, total, written; + DWORD total_size = 0xFFFFFFFF, arg_size = sizeof(total_size); + URL_COMPONENTSW url; + WCHAR host[0x80], path[0x100]; + HANDLE hfile; + WCHAR wszAppName[]={'u','r','l','m','o','n','.','d','l','l',0}; + + /* Note: all error codes would need to be checked agains real Windows behaviour... */ + TRACE("(%p,%s,%s,%08lx,%p) stub!\n", pCaller, debugstr_w(szURL), debugstr_w(szFileName), dwReserved, lpfnCB); + + if ((szURL == NULL) || (szFileName == NULL)) { + FIXME(" cannot accept NULL strings !\n"); + return E_INVALIDARG; + } + + /* Would be better to use the application name here rather than 'urlmon' :-/ */ + hinternet = InternetOpenW(wszAppName, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + if (hinternet == NULL) { + return E_OUTOFMEMORY; + } + + memset(&url, 0, sizeof(url)); + url.dwStructSize = sizeof(url); + url.lpszHostName = host; + url.dwHostNameLength = sizeof(host); + url.lpszUrlPath = path; + url.dwUrlPathLength = sizeof(path); + + if (!InternetCrackUrlW(szURL, 0, 0, &url)) { + InternetCloseHandle(hinternet); + return E_OUTOFMEMORY; + } + + if (lpfnCB) { + if (IBindStatusCallback_OnProgress(lpfnCB, 0, 0, BINDSTATUS_CONNECTING, url.lpszHostName) == E_ABORT) { + InternetCloseHandle(hinternet); + return S_OK; + } + } + + hcon = InternetConnectW(hinternet, url.lpszHostName, url.nPort, + url.lpszUserName, url.lpszPassword, + INTERNET_SERVICE_HTTP, 0, 0); + if (!hcon) { + InternetCloseHandle(hinternet); + return E_OUTOFMEMORY; + } + + hreq = HttpOpenRequestW(hcon, NULL, url.lpszUrlPath, NULL, NULL, NULL, 0, 0); + if (!hreq) { + InternetCloseHandle(hinternet); + InternetCloseHandle(hcon); + return E_OUTOFMEMORY; + } + + if (!HttpSendRequestW(hreq, NULL, 0, NULL, 0)) { + InternetCloseHandle(hinternet); + InternetCloseHandle(hcon); + InternetCloseHandle(hreq); + return E_OUTOFMEMORY; + } + + if (HttpQueryInfoW(hreq, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, + &total_size, &arg_size, NULL)) { + TRACE(" total size : %ld\n", total_size); + } + + hfile = CreateFileW(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL ); + if (hfile == INVALID_HANDLE_VALUE) { + return E_ACCESSDENIED; + } + + if (lpfnCB) { + if (IBindStatusCallback_OnProgress(lpfnCB, 0, total_size != 0xFFFFFFFF ? total_size : 0, + BINDSTATUS_BEGINDOWNLOADDATA, szURL) == E_ABORT) { + InternetCloseHandle(hreq); + InternetCloseHandle(hcon); + InternetCloseHandle(hinternet); + CloseHandle(hfile); + return S_OK; + } + } + + total = 0; + while (1) { + r = InternetReadFile(hreq, buffer, sizeof(buffer), &sz); + if (!r) { + InternetCloseHandle(hreq); + InternetCloseHandle(hcon); + InternetCloseHandle(hinternet); + + CloseHandle(hfile); + return E_OUTOFMEMORY; + } + if (!sz) + break; + + total += sz; + + if (lpfnCB) { + if (IBindStatusCallback_OnProgress(lpfnCB, total, total_size != 0xFFFFFFFF ? total_size : 0, + BINDSTATUS_DOWNLOADINGDATA, szURL) == E_ABORT) { + InternetCloseHandle(hreq); + InternetCloseHandle(hcon); + InternetCloseHandle(hinternet); + CloseHandle(hfile); + return S_OK; + } + } + + if (!WriteFile(hfile, buffer, sz, &written, NULL)) { + InternetCloseHandle(hreq); + InternetCloseHandle(hcon); + InternetCloseHandle(hinternet); + + CloseHandle(hfile); + return E_OUTOFMEMORY; + } + } + + if (lpfnCB) { + if (IBindStatusCallback_OnProgress(lpfnCB, total, total_size != 0xFFFFFFFF ? total_size : 0, + BINDSTATUS_ENDDOWNLOADDATA, szURL) == E_ABORT) { + InternetCloseHandle(hreq); + InternetCloseHandle(hcon); + InternetCloseHandle(hinternet); + CloseHandle(hfile); + return S_OK; + } + } + + InternetCloseHandle(hreq); + InternetCloseHandle(hcon); + InternetCloseHandle(hinternet); + + CloseHandle(hfile); + + return S_OK; } Index: dlls/urlmon/urlmon.spec =================================================================== RCS file: /home/wine/wine/dlls/urlmon/urlmon.spec,v retrieving revision 1.24 diff -u -r1.24 urlmon.spec --- dlls/urlmon/urlmon.spec 13 Jun 2003 16:29:36 -0000 1.24 +++ dlls/urlmon/urlmon.spec 13 Jul 2003 21:27:08 -0000 @@ -61,8 +61,8 @@ @ stub URLDownloadA @ stub URLDownloadToCacheFileA @ stub URLDownloadToCacheFileW -@ stub URLDownloadToFileA -@ stub URLDownloadToFileW +@ stdcall URLDownloadToFileA(ptr str str long ptr) +@ stdcall URLDownloadToFileW(ptr wstr wstr long ptr) @ stub URLDownloadW @ stub URLOpenBlockingStreamA @ stub URLOpenBlockingStreamW