7 /* FIXME: add correct definitions to urlmon.idl */
9 #define URLDownloadToFile URLDownloadToFileW
11 #define URLDownloadToFile URLDownloadToFileA
14 #define DWNL_E_LASTERROR 0
15 #define DWNL_E_NEEDTARGETFILENAME -1
16 #define DWNL_E_UNSUPPORTEDSCHEME -2
20 const IBindStatusCallbackVtbl
* lpIBindStatusCallbackVtbl
;
22 TCHAR szHostName
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
23 TCHAR szMimeType
[128];
29 UINT bBeginTransfer
: 1;
30 } CBindStatusCallback
;
32 #define impl_to_interface(impl,iface) (struct iface *)(&(impl)->lp##iface##Vtbl)
33 #define interface_to_impl(instance,iface) ((CBindStatusCallback*)((ULONG_PTR)instance - FIELD_OFFSET(CBindStatusCallback,lp##iface##Vtbl)))
36 CBindStatusCallback_Destroy(CBindStatusCallback
*This
)
42 write_status(LPCTSTR lpFmt
, ...)
46 /* FIXME: Determine line length! */
50 va_start(args
, lpFmt
);
51 _vstprintf(szTxt
, lpFmt
, args
);
55 while (c
< (sizeof(szTxt
) / sizeof(szTxt
[0])) - 1)
59 _tprintf(_T("\r%.79s"), szTxt
);
63 CBindStatusCallback_UpdateProgress(CBindStatusCallback
*This
)
65 /* FIXME: better output */
70 Percentage
= (UINT
)((This
->Progress
* 100) / This
->Size
);
74 write_status(_T("%2d%% (%I64u bytes downloaded)"), Percentage
, This
->Progress
);
79 write_status(_T("%I64u bytes downloaded"), This
->Progress
);
83 static ULONG STDMETHODCALLTYPE
84 CBindStatusCallback_AddRef(IBindStatusCallback
*iface
)
86 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
89 ret
= InterlockedIncrement((PLONG
)&This
->ref
);
93 static ULONG STDMETHODCALLTYPE
94 CBindStatusCallback_Release(IBindStatusCallback
*iface
)
96 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
99 ret
= InterlockedDecrement((PLONG
)&This
->ref
);
102 CBindStatusCallback_Destroy(This
);
104 HeapFree(GetProcessHeap(),
112 static HRESULT STDMETHODCALLTYPE
113 CBindStatusCallback_QueryInterface(IBindStatusCallback
*iface
,
117 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
122 &IID_IBindStatusCallback
) ||
126 *pvObject
= impl_to_interface(This
, IBindStatusCallback
);
129 return E_NOINTERFACE
;
131 CBindStatusCallback_AddRef(iface
);
135 static HRESULT STDMETHODCALLTYPE
136 CBindStatusCallback_OnStartBinding(IBindStatusCallback
*iface
,
143 static HRESULT STDMETHODCALLTYPE
144 CBindStatusCallback_GetPriority(IBindStatusCallback
*iface
,
150 static HRESULT STDMETHODCALLTYPE
151 CBindStatusCallback_OnLowResource(IBindStatusCallback
*iface
,
157 static HRESULT STDMETHODCALLTYPE
158 CBindStatusCallback_OnProgress(IBindStatusCallback
*iface
,
162 LPCWSTR szStatusText
)
164 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
166 switch (ulStatusCode
)
168 case BINDSTATUS_FINDINGRESOURCE
:
169 if (!This
->bResolving
)
171 _tcscpy(This
->szHostName
, szStatusText
);
172 This
->bResolving
= TRUE
;
174 _tprintf(_T("Resolving %s... "), This
->szHostName
);
178 case BINDSTATUS_CONNECTING
:
179 This
->bConnecting
= TRUE
;
180 This
->bSendingReq
= FALSE
;
181 This
->bBeginTransfer
= FALSE
;
182 This
->szMimeType
[0] = _T('\0');
183 if (This
->bResolving
)
185 _tprintf(_T("done.\n"));
186 _tprintf(_T("Connecting to %s[%s]... "), This
->szHostName
, szStatusText
);
189 _tprintf(_T("Connecting to %s... "), szStatusText
);
192 case BINDSTATUS_REDIRECTING
:
193 This
->bResolving
= FALSE
;
194 This
->bConnecting
= FALSE
;
195 This
->bSendingReq
= FALSE
;
196 This
->bBeginTransfer
= FALSE
;
197 This
->szMimeType
[0] = _T('\0');
198 _tprintf(_T("Redirecting to %s... "), szStatusText
);
201 case BINDSTATUS_SENDINGREQUEST
:
202 This
->bBeginTransfer
= FALSE
;
203 This
->szMimeType
[0] = _T('\0');
204 if (This
->bResolving
|| This
->bConnecting
)
205 _tprintf(_T("done.\n"));
207 if (!This
->bSendingReq
)
208 _tprintf(_T("Sending request... "));
210 This
->bSendingReq
= TRUE
;
213 case BINDSTATUS_MIMETYPEAVAILABLE
:
214 _tcscpy(This
->szMimeType
, szStatusText
);
217 case BINDSTATUS_BEGINDOWNLOADDATA
:
218 This
->Progress
= (UINT64
)ulProgress
;
219 This
->Size
= (UINT64
)ulProgressMax
;
221 if (This
->bSendingReq
)
222 _tprintf(_T("done.\n"));
224 if (!This
->bBeginTransfer
&& This
->Size
!= 0)
226 if (This
->szMimeType
[0] != _T('\0'))
227 _tprintf(_T("Length: %I64u [%s]\n"), This
->Size
, This
->szMimeType
);
229 _tprintf(_T("Length: %ull\n"), This
->Size
);
234 This
->bBeginTransfer
= TRUE
;
237 case BINDSTATUS_ENDDOWNLOADDATA
:
238 write_status(_T("File saved."));
242 case BINDSTATUS_DOWNLOADINGDATA
:
243 This
->Progress
= (UINT64
)ulProgress
;
244 This
->Size
= (UINT64
)ulProgressMax
;
246 CBindStatusCallback_UpdateProgress(This
);
253 static HRESULT STDMETHODCALLTYPE
254 CBindStatusCallback_OnStopBinding(IBindStatusCallback
*iface
,
261 static HRESULT STDMETHODCALLTYPE
262 CBindStatusCallback_GetBindInfo(IBindStatusCallback
*iface
,
269 static HRESULT STDMETHODCALLTYPE
270 CBindStatusCallback_OnDataAvailable(IBindStatusCallback
*iface
,
273 FORMATETC
* pformatetc
,
279 static HRESULT STDMETHODCALLTYPE
280 CBindStatusCallback_OnObjectAvailable(IBindStatusCallback
*iface
,
287 static const struct IBindStatusCallbackVtbl vtblIBindStatusCallback
=
289 CBindStatusCallback_QueryInterface
,
290 CBindStatusCallback_AddRef
,
291 CBindStatusCallback_Release
,
292 CBindStatusCallback_OnStartBinding
,
293 CBindStatusCallback_GetPriority
,
294 CBindStatusCallback_OnLowResource
,
295 CBindStatusCallback_OnProgress
,
296 CBindStatusCallback_OnStopBinding
,
297 CBindStatusCallback_GetBindInfo
,
298 CBindStatusCallback_OnDataAvailable
,
299 CBindStatusCallback_OnObjectAvailable
,
302 static IBindStatusCallback
*
303 CreateBindStatusCallback(void)
305 CBindStatusCallback
*This
;
307 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
311 This
->lpIBindStatusCallbackVtbl
= &vtblIBindStatusCallback
;
314 return impl_to_interface(This
, IBindStatusCallback
);
318 // ToDo: Show status, get file name from webserver, better error reporting
321 get_display_url(IN LPURL_COMPONENTS purl
,
323 IN PDWORD pdwBufferSize
)
327 /* Hide the password */
329 urlc
.lpszPassword
= NULL
;
330 urlc
.dwPasswordLength
= 0;
332 if (!InternetCreateUrl(&urlc
, ICU_ESCAPE
, szBuffer
, pdwBufferSize
))
333 return DWNL_E_LASTERROR
;
339 download_file(IN LPCTSTR pszUrl
,
340 IN LPCTSTR pszFile OPTIONAL
)
342 TCHAR szScheme
[INTERNET_MAX_SCHEME_LENGTH
+ 1];
343 TCHAR szHostName
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
344 TCHAR szUserName
[INTERNET_MAX_USER_NAME_LENGTH
+ 1];
345 TCHAR szPassWord
[INTERNET_MAX_PASSWORD_LENGTH
+ 1];
346 TCHAR szUrlPath
[INTERNET_MAX_PATH_LENGTH
+ 1];
347 TCHAR szExtraInfo
[INTERNET_MAX_PATH_LENGTH
+ 1];
348 TCHAR szUrl
[INTERNET_MAX_URL_LENGTH
+ 1];
352 IBindStatusCallback
*pbsc
;
355 if (pszFile
!= NULL
&& pszFile
[0] == _T('\0'))
358 urlc
.dwStructSize
= sizeof(urlc
);
359 urlc
.lpszScheme
= szScheme
;
360 urlc
.dwSchemeLength
= sizeof(szScheme
) / sizeof(szScheme
[0]);
361 urlc
.lpszHostName
= szHostName
;
362 urlc
.dwHostNameLength
= sizeof(szHostName
) / sizeof(szHostName
[0]);
363 urlc
.lpszUserName
= szUserName
;
364 urlc
.dwUserNameLength
= sizeof(szUserName
) / sizeof(szUserName
[0]);
365 urlc
.lpszPassword
= szPassWord
;
366 urlc
.dwPasswordLength
= sizeof(szPassWord
) / sizeof(szPassWord
[0]);
367 urlc
.lpszUrlPath
= szUrlPath
;
368 urlc
.dwUrlPathLength
= sizeof(szUrlPath
) / sizeof(szUrlPath
[0]);
369 urlc
.lpszExtraInfo
= szExtraInfo
;
370 urlc
.dwExtraInfoLength
= sizeof(szExtraInfo
) / sizeof(szExtraInfo
[0]);
371 if (!InternetCrackUrl(pszUrl
, _tcslen(pszUrl
), ICU_ESCAPE
, &urlc
))
372 return DWNL_E_LASTERROR
;
374 if (urlc
.nScheme
!= INTERNET_SCHEME_FTP
&&
375 urlc
.nScheme
!= INTERNET_SCHEME_GOPHER
&&
376 urlc
.nScheme
!= INTERNET_SCHEME_HTTP
&&
377 urlc
.nScheme
!= INTERNET_SCHEME_HTTPS
)
379 return DWNL_E_UNSUPPORTEDSCHEME
;
382 if (urlc
.nScheme
== INTERNET_SCHEME_FTP
&& urlc
.dwUserNameLength
== 0 && urlc
.dwPasswordLength
== 0)
384 _tcscpy(szUserName
, _T("anonymous"));
385 urlc
.dwUserNameLength
= _tcslen(szUserName
);
388 /* FIXME: Get file name from server */
389 if (urlc
.dwUrlPathLength
== 0 && pszFile
== NULL
)
390 return DWNL_E_NEEDTARGETFILENAME
;
392 pszFilePart
= _tcsrchr(szUrlPath
, _T('/'));
393 if (pszFilePart
!= NULL
)
396 if (pszFilePart
== NULL
&& pszFile
== NULL
)
397 return DWNL_E_NEEDTARGETFILENAME
;
400 pszFile
= pszFilePart
;
402 if (urlc
.dwUserNameLength
== 0)
403 urlc
.lpszUserName
= NULL
;
405 if (urlc
.dwPasswordLength
== 0)
406 urlc
.lpszPassword
= NULL
;
408 /* Generate the URL to be displayed (without a password) */
409 dwUrlLen
= sizeof(szUrl
) / sizeof(szUrl
[0]);
410 iRet
= get_display_url(&urlc
, szUrl
, &dwUrlLen
);
414 _tprintf(_T("Download `%s\'\n\t=> `%s\'\n"), szUrl
, pszFile
);
416 /* Generate the URL to download */
417 dwUrlLen
= sizeof(szUrl
) / sizeof(szUrl
[0]);
418 if (!InternetCreateUrl(&urlc
, ICU_ESCAPE
, szUrl
, &dwUrlLen
))
419 return DWNL_E_LASTERROR
;
421 pbsc
= CreateBindStatusCallback();
423 return DWNL_E_LASTERROR
;
425 if(!SUCCEEDED(URLDownloadToFile(NULL
, szUrl
, pszFile
, 0, pbsc
)))
427 IBindStatusCallback_Release(pbsc
);
428 return DWNL_E_LASTERROR
; /* FIXME */
431 IBindStatusCallback_Release(pbsc
);
438 write_status(_T(""));
440 if (iErr
== DWNL_E_LASTERROR
)
442 if (GetLastError() == ERROR_SUCCESS
)
445 _ftprintf(stderr
, _T("\nERROR: Download failed.\n"));
449 /* Display last error code */
450 _ftprintf(stderr
, _T("\nERROR: %u\n"), GetLastError());
457 case DWNL_E_NEEDTARGETFILENAME
:
458 _ftprintf(stderr
, _T("\nERROR: Cannot determine filename, please specify a destination file name.\n"));
461 case DWNL_E_UNSUPPORTEDSCHEME
:
462 _ftprintf(stderr
, _T("\nERROR: Unsupported protocol.\n"));
470 int _tmain(int argc
, TCHAR
**argv
)
474 if(argc
!= 2 && argc
!= 3)
476 _tprintf(_T("Usage: dwnl URL [DESTINATION]"));
480 iErr
= download_file(argv
[1], argc
== 3 ? argv
[2] : NULL
);
482 iRet
= print_err(iErr
);