10 /* FIXME: add correct definitions to urlmon.idl */
12 #define URLDownloadToFile URLDownloadToFileW
14 #define URLDownloadToFile URLDownloadToFileA
17 #define DWNL_E_LASTERROR 0
18 #define DWNL_E_NEEDTARGETFILENAME -1
19 #define DWNL_E_UNSUPPORTEDSCHEME -2
23 const IBindStatusCallbackVtbl
* lpIBindStatusCallbackVtbl
;
25 WCHAR szHostName
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
26 WCHAR szMimeType
[128];
32 UINT bBeginTransfer
: 1;
33 } CBindStatusCallback
;
35 #define impl_to_interface(impl,iface) (struct iface *)(&(impl)->lp##iface##Vtbl)
36 #define interface_to_impl(instance,iface) ((CBindStatusCallback*)((ULONG_PTR)instance - FIELD_OFFSET(CBindStatusCallback,lp##iface##Vtbl)))
39 CBindStatusCallback_Destroy(CBindStatusCallback
*This
)
45 write_status(LPCWSTR lpFmt
, ...)
49 CONSOLE_SCREEN_BUFFER_INFO csbi
;
51 va_start(args
, lpFmt
);
52 StringCbVPrintf(szTxt
, sizeof(szTxt
), lpFmt
, args
);
55 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &csbi
))
57 ConPrintf(StdOut
, L
"\r%*.*s", -(csbi
.dwSize
.X
- 1), csbi
.dwSize
.X
- 1, szTxt
);
61 ConPuts(StdOut
, szTxt
);
66 CBindStatusCallback_UpdateProgress(CBindStatusCallback
*This
)
68 WCHAR szMessage
[MAX_PATH
];
70 /* FIXME: better output */
75 Percentage
= (UINT
)((This
->Progress
* 100) / This
->Size
);
79 LoadStringW(NULL
, IDS_BYTES_DOWNLOADED_FULL
, szMessage
, ARRAYSIZE(szMessage
));
81 write_status(szMessage
, Percentage
, This
->Progress
);
85 LoadStringW(NULL
, IDS_BYTES_DOWNLOADED
, szMessage
, ARRAYSIZE(szMessage
));
88 write_status(szMessage
, This
->Progress
);
92 static ULONG STDMETHODCALLTYPE
93 CBindStatusCallback_AddRef(IBindStatusCallback
*iface
)
95 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
98 ret
= InterlockedIncrement((PLONG
)&This
->ref
);
102 static ULONG STDMETHODCALLTYPE
103 CBindStatusCallback_Release(IBindStatusCallback
*iface
)
105 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
108 ret
= InterlockedDecrement((PLONG
)&This
->ref
);
111 CBindStatusCallback_Destroy(This
);
113 HeapFree(GetProcessHeap(),
121 static HRESULT STDMETHODCALLTYPE
122 CBindStatusCallback_QueryInterface(IBindStatusCallback
*iface
,
126 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
131 &IID_IBindStatusCallback
) ||
135 *pvObject
= impl_to_interface(This
, IBindStatusCallback
);
138 return E_NOINTERFACE
;
140 CBindStatusCallback_AddRef(iface
);
144 static HRESULT STDMETHODCALLTYPE
145 CBindStatusCallback_OnStartBinding(IBindStatusCallback
*iface
,
152 static HRESULT STDMETHODCALLTYPE
153 CBindStatusCallback_GetPriority(IBindStatusCallback
*iface
,
159 static HRESULT STDMETHODCALLTYPE
160 CBindStatusCallback_OnLowResource(IBindStatusCallback
*iface
,
166 static HRESULT STDMETHODCALLTYPE
167 CBindStatusCallback_OnProgress(IBindStatusCallback
*iface
,
171 LPCWSTR szStatusText
)
173 CBindStatusCallback
*This
= interface_to_impl(iface
, IBindStatusCallback
);
175 switch (ulStatusCode
)
177 case BINDSTATUS_FINDINGRESOURCE
:
178 if (!This
->bResolving
)
180 wcscpy(This
->szHostName
, szStatusText
);
181 This
->bResolving
= TRUE
;
183 ConResPrintf(StdOut
, IDS_RESOLVING
, This
->szHostName
);
187 case BINDSTATUS_CONNECTING
:
188 This
->bConnecting
= TRUE
;
189 This
->bSendingReq
= FALSE
;
190 This
->bBeginTransfer
= FALSE
;
191 This
->szMimeType
[0] = L
'\0';
192 if (This
->bResolving
)
194 ConResPrintf(StdOut
, IDS_DONE
);
195 ConResPrintf(StdOut
, IDS_CONNECTING_TO_FULL
, This
->szHostName
, szStatusText
);
199 ConResPrintf(StdOut
, IDS_CONNECTING_TO
, szStatusText
);
203 case BINDSTATUS_REDIRECTING
:
204 This
->bResolving
= FALSE
;
205 This
->bConnecting
= FALSE
;
206 This
->bSendingReq
= FALSE
;
207 This
->bBeginTransfer
= FALSE
;
208 This
->szMimeType
[0] = L
'\0';
209 ConResPrintf(StdOut
, IDS_REDIRECTING_TO
, szStatusText
);
212 case BINDSTATUS_SENDINGREQUEST
:
213 This
->bBeginTransfer
= FALSE
;
214 This
->szMimeType
[0] = L
'\0';
215 if (This
->bResolving
|| This
->bConnecting
)
216 ConResPrintf(StdOut
, IDS_DONE
);
218 if (!This
->bSendingReq
)
219 ConResPrintf(StdOut
, IDS_SEND_REQUEST
);
221 This
->bSendingReq
= TRUE
;
224 case BINDSTATUS_MIMETYPEAVAILABLE
:
225 wcscpy(This
->szMimeType
, szStatusText
);
228 case BINDSTATUS_BEGINDOWNLOADDATA
:
229 This
->Progress
= (UINT64
)ulProgress
;
230 This
->Size
= (UINT64
)ulProgressMax
;
232 if (This
->bSendingReq
)
233 ConResPrintf(StdOut
, IDS_DONE
);
235 if (!This
->bBeginTransfer
&& This
->Size
!= 0)
237 if (This
->szMimeType
[0] != L
'\0')
238 ConResPrintf(StdOut
, IDS_LENGTH_FULL
, This
->Size
, This
->szMimeType
);
240 ConResPrintf(StdOut
, IDS_LENGTH
, This
->Size
);
243 ConPuts(StdOut
, L
"\n");
245 This
->bBeginTransfer
= TRUE
;
248 case BINDSTATUS_ENDDOWNLOADDATA
:
249 ConResPrintf(StdOut
, IDS_FILE_SAVED
);
252 case BINDSTATUS_DOWNLOADINGDATA
:
253 This
->Progress
= (UINT64
)ulProgress
;
254 This
->Size
= (UINT64
)ulProgressMax
;
256 CBindStatusCallback_UpdateProgress(This
);
263 static HRESULT STDMETHODCALLTYPE
264 CBindStatusCallback_OnStopBinding(IBindStatusCallback
*iface
,
271 static HRESULT STDMETHODCALLTYPE
272 CBindStatusCallback_GetBindInfo(IBindStatusCallback
*iface
,
279 static HRESULT STDMETHODCALLTYPE
280 CBindStatusCallback_OnDataAvailable(IBindStatusCallback
*iface
,
283 FORMATETC
* pformatetc
,
289 static HRESULT STDMETHODCALLTYPE
290 CBindStatusCallback_OnObjectAvailable(IBindStatusCallback
*iface
,
297 static const struct IBindStatusCallbackVtbl vtblIBindStatusCallback
=
299 CBindStatusCallback_QueryInterface
,
300 CBindStatusCallback_AddRef
,
301 CBindStatusCallback_Release
,
302 CBindStatusCallback_OnStartBinding
,
303 CBindStatusCallback_GetPriority
,
304 CBindStatusCallback_OnLowResource
,
305 CBindStatusCallback_OnProgress
,
306 CBindStatusCallback_OnStopBinding
,
307 CBindStatusCallback_GetBindInfo
,
308 CBindStatusCallback_OnDataAvailable
,
309 CBindStatusCallback_OnObjectAvailable
,
312 static IBindStatusCallback
*
313 CreateBindStatusCallback(void)
315 CBindStatusCallback
*This
;
317 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
321 This
->lpIBindStatusCallbackVtbl
= &vtblIBindStatusCallback
;
324 return impl_to_interface(This
, IBindStatusCallback
);
328 // ToDo: Show status, get file name from webserver, better error reporting
331 get_display_url(IN LPURL_COMPONENTS purl
,
333 IN PDWORD pdwBufferSize
)
337 /* Hide the password */
339 urlc
.lpszPassword
= NULL
;
340 urlc
.dwPasswordLength
= 0;
342 if (!InternetCreateUrl(&urlc
, ICU_ESCAPE
, szBuffer
, pdwBufferSize
))
343 return DWNL_E_LASTERROR
;
349 download_file(IN LPCWSTR pszUrl
,
350 IN LPCWSTR pszFile OPTIONAL
)
352 WCHAR szScheme
[INTERNET_MAX_SCHEME_LENGTH
+ 1];
353 WCHAR szHostName
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
354 WCHAR szUserName
[INTERNET_MAX_USER_NAME_LENGTH
+ 1];
355 WCHAR szPassWord
[INTERNET_MAX_PASSWORD_LENGTH
+ 1];
356 WCHAR szUrlPath
[INTERNET_MAX_PATH_LENGTH
+ 1];
357 WCHAR szExtraInfo
[INTERNET_MAX_PATH_LENGTH
+ 1];
358 WCHAR szUrl
[INTERNET_MAX_URL_LENGTH
+ 1];
362 IBindStatusCallback
*pbsc
;
366 if (pszFile
!= NULL
&& pszFile
[0] == L
'\0')
369 urlc
.dwStructSize
= sizeof(urlc
);
370 urlc
.lpszScheme
= szScheme
;
371 urlc
.dwSchemeLength
= sizeof(szScheme
) / sizeof(szScheme
[0]);
372 urlc
.lpszHostName
= szHostName
;
373 urlc
.dwHostNameLength
= sizeof(szHostName
) / sizeof(szHostName
[0]);
374 urlc
.lpszUserName
= szUserName
;
375 urlc
.dwUserNameLength
= sizeof(szUserName
) / sizeof(szUserName
[0]);
376 urlc
.lpszPassword
= szPassWord
;
377 urlc
.dwPasswordLength
= sizeof(szPassWord
) / sizeof(szPassWord
[0]);
378 urlc
.lpszUrlPath
= szUrlPath
;
379 urlc
.dwUrlPathLength
= sizeof(szUrlPath
) / sizeof(szUrlPath
[0]);
380 urlc
.lpszExtraInfo
= szExtraInfo
;
381 urlc
.dwExtraInfoLength
= sizeof(szExtraInfo
) / sizeof(szExtraInfo
[0]);
382 if (!InternetCrackUrl(pszUrl
, wcslen(pszUrl
), ICU_ESCAPE
, &urlc
))
383 return DWNL_E_LASTERROR
;
385 if (urlc
.nScheme
!= INTERNET_SCHEME_FTP
&&
386 urlc
.nScheme
!= INTERNET_SCHEME_GOPHER
&&
387 urlc
.nScheme
!= INTERNET_SCHEME_HTTP
&&
388 urlc
.nScheme
!= INTERNET_SCHEME_HTTPS
)
390 return DWNL_E_UNSUPPORTEDSCHEME
;
393 if (urlc
.nScheme
== INTERNET_SCHEME_FTP
&& urlc
.dwUserNameLength
== 0 && urlc
.dwPasswordLength
== 0)
395 wcscpy(szUserName
, L
"anonymous");
396 urlc
.dwUserNameLength
= wcslen(szUserName
);
399 /* FIXME: Get file name from server */
400 if (urlc
.dwUrlPathLength
== 0 && pszFile
== NULL
)
401 return DWNL_E_NEEDTARGETFILENAME
;
403 pszFilePart
= wcsrchr(szUrlPath
, L
'/');
404 if (pszFilePart
!= NULL
)
407 if (pszFilePart
== NULL
&& pszFile
== NULL
)
408 return DWNL_E_NEEDTARGETFILENAME
;
411 pszFile
= pszFilePart
;
413 if (urlc
.dwUserNameLength
== 0)
414 urlc
.lpszUserName
= NULL
;
416 if (urlc
.dwPasswordLength
== 0)
417 urlc
.lpszPassword
= NULL
;
419 /* Generate the URL to be displayed (without a password) */
420 dwUrlLen
= sizeof(szUrl
) / sizeof(szUrl
[0]);
421 iRet
= get_display_url(&urlc
, szUrl
, &dwUrlLen
);
425 GetLocalTime(&sysTime
);
427 ConPrintf(StdOut
, L
"--%d-%02d-%02d %02d:%02d:%02d-- %s\n\t=> %s\n",
428 sysTime
.wYear
, sysTime
.wMonth
, sysTime
.wDay
,
429 sysTime
.wHour
, sysTime
.wMinute
, sysTime
.wSecond
,
432 /* Generate the URL to download */
433 dwUrlLen
= sizeof(szUrl
) / sizeof(szUrl
[0]);
434 if (!InternetCreateUrl(&urlc
, ICU_ESCAPE
, szUrl
, &dwUrlLen
))
435 return DWNL_E_LASTERROR
;
437 pbsc
= CreateBindStatusCallback();
439 return DWNL_E_LASTERROR
;
441 if(!SUCCEEDED(URLDownloadToFile(NULL
, szUrl
, pszFile
, 0, pbsc
)))
443 IBindStatusCallback_Release(pbsc
);
444 return DWNL_E_LASTERROR
; /* FIXME */
447 IBindStatusCallback_Release(pbsc
);
456 if (iErr
== DWNL_E_LASTERROR
)
458 if (GetLastError() == ERROR_SUCCESS
)
461 ConResPrintf(StdErr
, IDS_ERROR_DOWNLOAD
);
465 /* Display last error code */
466 ConResPrintf(StdErr
, IDS_ERROR_CODE
, GetLastError());
473 case DWNL_E_NEEDTARGETFILENAME
:
474 ConResPrintf(StdErr
, IDS_ERROR_FILENAME
);
477 case DWNL_E_UNSUPPORTEDSCHEME
:
478 ConResPrintf(StdErr
, IDS_ERROR_PROTOCOL
);
486 int wmain(int argc
, WCHAR
**argv
)
490 /* Initialize the Console Standard Streams */
493 if(argc
!= 2 && argc
!= 3)
495 ConResPrintf(StdOut
, IDS_USAGE
);
499 iErr
= download_file(argv
[1], argc
== 3 ? argv
[2] : NULL
);
501 iRet
= print_err(iErr
);