6 /* FIXME: add correct definitions to urlmon.idl */
8 #define URLDownloadToFile URLDownloadToFileW
10 #define URLDownloadToFile URLDownloadToFileA
13 #define DWNL_E_LASTERROR 0
14 #define DWNL_E_NEEDTARGETFILENAME -1
15 #define DWNL_E_UNSUPPORTEDSCHEME -2
19 const IBindStatusCallbackVtbl
* lpIBindStatusCallbackVtbl
;
21 TCHAR szHostName
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
22 TCHAR szMimeType
[128];
28 UINT bBeginTransfer
: 1;
29 } CBindStatusCallback
;
31 #define impl_to_interface(impl,iface) (struct iface *)(&(impl)->lp##iface##Vtbl)
32 #define interface_to_impl(instance,iface) ((CBindStatusCallback*)((ULONG_PTR)instance - FIELD_OFFSET(CBindStatusCallback,lp##iface##Vtbl)))
35 CBindStatusCallback_Destroy(CBindStatusCallback
*This
)
41 write_status(LPCTSTR lpFmt
, ...)
45 /* FIXME: Determine line length! */
49 va_start(args
, lpFmt
);
50 _vstprintf(szTxt
, lpFmt
, args
);
54 while (c
< (sizeof(szTxt
) / sizeof(szTxt
[0])) - 1)
58 _tprintf(_T("\r%.79s"), szTxt
);
62 CBindStatusCallback_UpdateProgress(CBindStatusCallback
*This
)
64 /* FIXME: better output */
69 Percentage
= (UINT
)((This
->Progress
* 100) / This
->Size
);
73 write_status(_T("%2d%% (%I64u bytes downloaded)"), Percentage
, This
->Progress
);
78 write_status(_T("%I64u bytes downloaded"), This
->Progress
);
82 static ULONG STDMETHODCALLTYPE
83 CBindStatusCallback_AddRef(IBindStatusCallback
*iface
)
85 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
88 ret
= InterlockedIncrement((PLONG
)&This
->ref
);
92 static ULONG STDMETHODCALLTYPE
93 CBindStatusCallback_Release(IBindStatusCallback
*iface
)
95 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
98 ret
= InterlockedDecrement((PLONG
)&This
->ref
);
101 CBindStatusCallback_Destroy(This
);
103 HeapFree(GetProcessHeap(),
111 static HRESULT STDMETHODCALLTYPE
112 CBindStatusCallback_QueryInterface(IBindStatusCallback
*iface
,
116 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
121 &IID_IBindStatusCallback
) ||
125 *pvObject
= impl_to_interface(This
, IBindStatusCallback
);
128 return E_NOINTERFACE
;
130 CBindStatusCallback_AddRef(iface
);
134 static HRESULT STDMETHODCALLTYPE
135 CBindStatusCallback_OnStartBinding(IBindStatusCallback
*iface
,
142 static HRESULT STDMETHODCALLTYPE
143 CBindStatusCallback_GetPriority(IBindStatusCallback
*iface
,
149 static HRESULT STDMETHODCALLTYPE
150 CBindStatusCallback_OnLowResource(IBindStatusCallback
*iface
,
156 static HRESULT STDMETHODCALLTYPE
157 CBindStatusCallback_OnProgress(IBindStatusCallback
*iface
,
161 LPCWSTR szStatusText
)
163 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
165 switch (ulStatusCode
)
167 case BINDSTATUS_FINDINGRESOURCE
:
168 if (!This
->bResolving
)
170 _tcscpy(This
->szHostName
, szStatusText
);
171 This
->bResolving
= TRUE
;
173 _tprintf(_T("Resolving %s... "), This
->szHostName
);
177 case BINDSTATUS_CONNECTING
:
178 This
->bConnecting
= TRUE
;
179 This
->bSendingReq
= FALSE
;
180 This
->bBeginTransfer
= FALSE
;
181 This
->szMimeType
[0] = _T('\0');
182 if (This
->bResolving
)
184 _tprintf(_T("done.\n"));
185 _tprintf(_T("Connecting to %s[%s]... "), This
->szHostName
, szStatusText
);
188 _tprintf(_T("Connecting to %s... "), szStatusText
);
191 case BINDSTATUS_REDIRECTING
:
192 This
->bResolving
= FALSE
;
193 This
->bConnecting
= FALSE
;
194 This
->bSendingReq
= FALSE
;
195 This
->bBeginTransfer
= FALSE
;
196 This
->szMimeType
[0] = _T('\0');
197 _tprintf(_T("Redirecting to %s... "), szStatusText
);
200 case BINDSTATUS_SENDINGREQUEST
:
201 This
->bBeginTransfer
= FALSE
;
202 This
->szMimeType
[0] = _T('\0');
203 if (This
->bResolving
|| This
->bConnecting
)
204 _tprintf(_T("done.\n"));
206 if (!This
->bSendingReq
)
207 _tprintf(_T("Sending request... "));
209 This
->bSendingReq
= TRUE
;
212 case BINDSTATUS_MIMETYPEAVAILABLE
:
213 _tcscpy(This
->szMimeType
, szStatusText
);
216 case BINDSTATUS_BEGINDOWNLOADDATA
:
217 This
->Progress
= (UINT64
)ulProgress
;
218 This
->Size
= (UINT64
)ulProgressMax
;
220 if (This
->bSendingReq
)
221 _tprintf(_T("done.\n"));
223 if (!This
->bBeginTransfer
&& This
->Size
!= 0)
225 if (This
->szMimeType
[0] != _T('\0'))
226 _tprintf(_T("Length: %I64u [%s]\n"), This
->Size
, This
->szMimeType
);
228 _tprintf(_T("Length: %ull\n"), This
->Size
);
233 This
->bBeginTransfer
= TRUE
;
236 case BINDSTATUS_ENDDOWNLOADDATA
:
237 write_status(_T("File saved."));
241 case BINDSTATUS_DOWNLOADINGDATA
:
242 This
->Progress
= (UINT64
)ulProgress
;
243 This
->Size
= (UINT64
)ulProgressMax
;
245 CBindStatusCallback_UpdateProgress(This
);
252 static HRESULT STDMETHODCALLTYPE
253 CBindStatusCallback_OnStopBinding(IBindStatusCallback
*iface
,
260 static HRESULT STDMETHODCALLTYPE
261 CBindStatusCallback_GetBindInfo(IBindStatusCallback
*iface
,
268 static HRESULT STDMETHODCALLTYPE
269 CBindStatusCallback_OnDataAvailable(IBindStatusCallback
*iface
,
272 FORMATETC
* pformatetc
,
278 static HRESULT STDMETHODCALLTYPE
279 CBindStatusCallback_OnObjectAvailable(IBindStatusCallback
*iface
,
286 static const struct IBindStatusCallbackVtbl vtblIBindStatusCallback
=
288 CBindStatusCallback_QueryInterface
,
289 CBindStatusCallback_AddRef
,
290 CBindStatusCallback_Release
,
291 CBindStatusCallback_OnStartBinding
,
292 CBindStatusCallback_GetPriority
,
293 CBindStatusCallback_OnLowResource
,
294 CBindStatusCallback_OnProgress
,
295 CBindStatusCallback_OnStopBinding
,
296 CBindStatusCallback_GetBindInfo
,
297 CBindStatusCallback_OnDataAvailable
,
298 CBindStatusCallback_OnObjectAvailable
,
301 static IBindStatusCallback
*
302 CreateBindStatusCallback(void)
304 CBindStatusCallback
*This
;
306 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
310 This
->lpIBindStatusCallbackVtbl
= &vtblIBindStatusCallback
;
313 return impl_to_interface(This
, IBindStatusCallback
);
317 // ToDo: Show status, get file name from webserver, better error reporting
320 get_display_url(IN LPURL_COMPONENTS purl
,
322 IN PDWORD pdwBufferSize
)
326 /* Hide the password */
328 urlc
.lpszPassword
= NULL
;
329 urlc
.dwPasswordLength
= 0;
331 if (!InternetCreateUrl(&urlc
, ICU_ESCAPE
, szBuffer
, pdwBufferSize
))
332 return DWNL_E_LASTERROR
;
338 download_file(IN LPCTSTR pszUrl
,
339 IN LPCTSTR pszFile OPTIONAL
)
341 TCHAR szScheme
[INTERNET_MAX_SCHEME_LENGTH
+ 1];
342 TCHAR szHostName
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
343 TCHAR szUserName
[INTERNET_MAX_USER_NAME_LENGTH
+ 1];
344 TCHAR szPassWord
[INTERNET_MAX_PASSWORD_LENGTH
+ 1];
345 TCHAR szUrlPath
[INTERNET_MAX_PATH_LENGTH
+ 1];
346 TCHAR szExtraInfo
[INTERNET_MAX_PATH_LENGTH
+ 1];
347 TCHAR szUrl
[INTERNET_MAX_URL_LENGTH
+ 1];
351 IBindStatusCallback
*pbsc
;
354 if (pszFile
!= NULL
&& pszFile
[0] == _T('\0'))
357 urlc
.dwStructSize
= sizeof(urlc
);
358 urlc
.lpszScheme
= szScheme
;
359 urlc
.dwSchemeLength
= sizeof(szScheme
) / sizeof(szScheme
[0]);
360 urlc
.lpszHostName
= szHostName
;
361 urlc
.dwHostNameLength
= sizeof(szHostName
) / sizeof(szHostName
[0]);
362 urlc
.lpszUserName
= szUserName
;
363 urlc
.dwUserNameLength
= sizeof(szUserName
) / sizeof(szUserName
[0]);
364 urlc
.lpszPassword
= szPassWord
;
365 urlc
.dwPasswordLength
= sizeof(szPassWord
) / sizeof(szPassWord
[0]);
366 urlc
.lpszUrlPath
= szUrlPath
;
367 urlc
.dwUrlPathLength
= sizeof(szUrlPath
) / sizeof(szUrlPath
[0]);
368 urlc
.lpszExtraInfo
= szExtraInfo
;
369 urlc
.dwExtraInfoLength
= sizeof(szExtraInfo
) / sizeof(szExtraInfo
[0]);
370 if (!InternetCrackUrl(pszUrl
, _tcslen(pszUrl
), ICU_ESCAPE
, &urlc
))
371 return DWNL_E_LASTERROR
;
373 if (urlc
.nScheme
!= INTERNET_SCHEME_FTP
&&
374 urlc
.nScheme
!= INTERNET_SCHEME_GOPHER
&&
375 urlc
.nScheme
!= INTERNET_SCHEME_HTTP
&&
376 urlc
.nScheme
!= INTERNET_SCHEME_HTTPS
)
378 return DWNL_E_UNSUPPORTEDSCHEME
;
381 if (urlc
.nScheme
== INTERNET_SCHEME_FTP
&& urlc
.dwUserNameLength
== 0 && urlc
.dwPasswordLength
== 0)
383 _tcscpy(szUserName
, _T("anonymous"));
384 urlc
.dwUserNameLength
= _tcslen(szUserName
);
387 /* FIXME: Get file name from server */
388 if (urlc
.dwUrlPathLength
== 0 && pszFile
== NULL
)
389 return DWNL_E_NEEDTARGETFILENAME
;
391 pszFilePart
= _tcsrchr(szUrlPath
, _T('/'));
392 if (pszFilePart
!= NULL
)
395 if (pszFilePart
== NULL
&& pszFile
== NULL
)
396 return DWNL_E_NEEDTARGETFILENAME
;
399 pszFile
= pszFilePart
;
401 if (urlc
.dwUserNameLength
== 0)
402 urlc
.lpszUserName
= NULL
;
404 if (urlc
.dwPasswordLength
== 0)
405 urlc
.lpszPassword
= NULL
;
407 /* Generate the URL to be displayed (without a password) */
408 dwUrlLen
= sizeof(szUrl
) / sizeof(szUrl
[0]);
409 iRet
= get_display_url(&urlc
, szUrl
, &dwUrlLen
);
413 _tprintf(_T("Download `%s\'\n\t=> `%s\'\n"), szUrl
, pszFile
);
415 /* Generate the URL to download */
416 dwUrlLen
= sizeof(szUrl
) / sizeof(szUrl
[0]);
417 if (!InternetCreateUrl(&urlc
, ICU_ESCAPE
, szUrl
, &dwUrlLen
))
418 return DWNL_E_LASTERROR
;
420 pbsc
= CreateBindStatusCallback();
422 return DWNL_E_LASTERROR
;
424 if(!SUCCEEDED(URLDownloadToFile(NULL
, szUrl
, pszFile
, 0, pbsc
)))
426 IBindStatusCallback_Release(pbsc
);
427 return DWNL_E_LASTERROR
; /* FIXME */
430 IBindStatusCallback_Release(pbsc
);
437 write_status(_T(""));
439 if (iErr
== DWNL_E_LASTERROR
)
441 if (GetLastError() == ERROR_SUCCESS
)
444 _ftprintf(stderr
, _T("\nERROR: Download failed.\n"));
448 /* Display last error code */
449 _ftprintf(stderr
, _T("\nERROR: %u\n"), GetLastError());
456 case DWNL_E_NEEDTARGETFILENAME
:
457 _ftprintf(stderr
, _T("\nERROR: Cannot determine filename, please specify a destination file name.\n"));
460 case DWNL_E_UNSUPPORTEDSCHEME
:
461 _ftprintf(stderr
, _T("\nERROR: Unsupported protocol.\n"));
469 int _tmain(int argc
, TCHAR
**argv
)
473 if(argc
!= 2 && argc
!= 3)
475 _tprintf(_T("Usage: dwnl URL [DESTINATION]"));
479 iErr
= download_file(argv
[1], argc
== 3 ? argv
[2] : NULL
);
481 iRet
= print_err(iErr
);