2 * Queue Manager (BITS) File
4 * Copyright 2007, 2008 Google (Roy Shea, Dan Hipschman)
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 static inline BackgroundCopyFileImpl
*impl_from_IBackgroundCopyFile2(
27 IBackgroundCopyFile2
*iface
)
29 return CONTAINING_RECORD(iface
, BackgroundCopyFileImpl
, IBackgroundCopyFile2_iface
);
32 static HRESULT WINAPI
BackgroundCopyFile_QueryInterface(
33 IBackgroundCopyFile2
*iface
,
37 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
39 TRACE("(%p)->(%s %p)\n", file
, debugstr_guid(riid
), obj
);
41 if (IsEqualGUID(riid
, &IID_IUnknown
) ||
42 IsEqualGUID(riid
, &IID_IBackgroundCopyFile
) ||
43 IsEqualGUID(riid
, &IID_IBackgroundCopyFile2
))
53 IBackgroundCopyFile2_AddRef(iface
);
57 static ULONG WINAPI
BackgroundCopyFile_AddRef(
58 IBackgroundCopyFile2
*iface
)
60 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
61 ULONG ref
= InterlockedIncrement(&file
->ref
);
62 TRACE("(%p)->(%d)\n", file
, ref
);
66 static ULONG WINAPI
BackgroundCopyFile_Release(
67 IBackgroundCopyFile2
*iface
)
69 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
70 ULONG ref
= InterlockedDecrement(&file
->ref
);
72 TRACE("(%p)->(%d)\n", file
, ref
);
76 IBackgroundCopyJob3_Release(&file
->owner
->IBackgroundCopyJob3_iface
);
77 HeapFree(GetProcessHeap(), 0, file
->info
.LocalName
);
78 HeapFree(GetProcessHeap(), 0, file
->info
.RemoteName
);
79 HeapFree(GetProcessHeap(), 0, file
);
85 /* Get the remote name of a background copy file */
86 static HRESULT WINAPI
BackgroundCopyFile_GetRemoteName(
87 IBackgroundCopyFile2
*iface
,
90 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
92 TRACE("(%p)->(%p)\n", file
, pVal
);
94 return return_strval(file
->info
.RemoteName
, pVal
);
97 static HRESULT WINAPI
BackgroundCopyFile_GetLocalName(
98 IBackgroundCopyFile2
*iface
,
101 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
103 TRACE("(%p)->(%p)\n", file
, pVal
);
105 return return_strval(file
->info
.LocalName
, pVal
);
108 static HRESULT WINAPI
BackgroundCopyFile_GetProgress(
109 IBackgroundCopyFile2
*iface
,
110 BG_FILE_PROGRESS
*pVal
)
112 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
114 TRACE("(%p)->(%p)\n", file
, pVal
);
116 EnterCriticalSection(&file
->owner
->cs
);
117 *pVal
= file
->fileProgress
;
118 LeaveCriticalSection(&file
->owner
->cs
);
123 static HRESULT WINAPI
BackgroundCopyFile_GetFileRanges(
124 IBackgroundCopyFile2
*iface
,
126 BG_FILE_RANGE
**Ranges
)
128 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
129 FIXME("(%p)->(%p %p)\n", file
, RangeCount
, Ranges
);
133 static HRESULT WINAPI
BackgroundCopyFile_SetRemoteName(
134 IBackgroundCopyFile2
*iface
,
137 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
138 FIXME("(%p)->(%s)\n", file
, debugstr_w(Val
));
142 static const IBackgroundCopyFile2Vtbl BackgroundCopyFile2Vtbl
=
144 BackgroundCopyFile_QueryInterface
,
145 BackgroundCopyFile_AddRef
,
146 BackgroundCopyFile_Release
,
147 BackgroundCopyFile_GetRemoteName
,
148 BackgroundCopyFile_GetLocalName
,
149 BackgroundCopyFile_GetProgress
,
150 BackgroundCopyFile_GetFileRanges
,
151 BackgroundCopyFile_SetRemoteName
154 HRESULT
BackgroundCopyFileConstructor(BackgroundCopyJobImpl
*owner
,
155 LPCWSTR remoteName
, LPCWSTR localName
,
156 BackgroundCopyFileImpl
**file
)
158 BackgroundCopyFileImpl
*This
;
160 TRACE("(%s, %s, %p)\n", debugstr_w(remoteName
), debugstr_w(localName
), file
);
162 This
= HeapAlloc(GetProcessHeap(), 0, sizeof *This
);
164 return E_OUTOFMEMORY
;
166 This
->info
.RemoteName
= strdupW(remoteName
);
167 if (!This
->info
.RemoteName
)
169 HeapFree(GetProcessHeap(), 0, This
);
170 return E_OUTOFMEMORY
;
173 This
->info
.LocalName
= strdupW(localName
);
174 if (!This
->info
.LocalName
)
176 HeapFree(GetProcessHeap(), 0, This
->info
.RemoteName
);
177 HeapFree(GetProcessHeap(), 0, This
);
178 return E_OUTOFMEMORY
;
181 This
->IBackgroundCopyFile2_iface
.lpVtbl
= &BackgroundCopyFile2Vtbl
;
184 This
->fileProgress
.BytesTotal
= BG_SIZE_UNKNOWN
;
185 This
->fileProgress
.BytesTransferred
= 0;
186 This
->fileProgress
.Completed
= FALSE
;
189 This
->tempFileName
[0] = 0;
190 IBackgroundCopyJob3_AddRef(&owner
->IBackgroundCopyJob3_iface
);
196 static HRESULT
hresult_from_http_response(DWORD code
)
200 case 200: return S_OK
;
201 case 400: return BG_E_HTTP_ERROR_400
;
202 case 401: return BG_E_HTTP_ERROR_401
;
203 case 404: return BG_E_HTTP_ERROR_404
;
204 case 407: return BG_E_HTTP_ERROR_407
;
205 case 414: return BG_E_HTTP_ERROR_414
;
206 case 501: return BG_E_HTTP_ERROR_501
;
207 case 503: return BG_E_HTTP_ERROR_503
;
208 case 504: return BG_E_HTTP_ERROR_504
;
209 case 505: return BG_E_HTTP_ERROR_505
;
211 FIXME("unhandled response code %u\n", code
);
216 static void CALLBACK
progress_callback_http(HINTERNET handle
, DWORD_PTR context
, DWORD status
,
217 LPVOID buf
, DWORD buflen
)
219 BackgroundCopyFileImpl
*file
= (BackgroundCopyFileImpl
*)context
;
220 BackgroundCopyJobImpl
*job
= file
->owner
;
222 TRACE("%p, %p, %x, %p, %u\n", handle
, file
, status
, buf
, buflen
);
226 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE
:
228 DWORD code
, len
, size
;
231 if (WinHttpQueryHeaders(handle
, WINHTTP_QUERY_STATUS_CODE
|WINHTTP_QUERY_FLAG_NUMBER
,
232 NULL
, &code
, &size
, NULL
))
234 if ((job
->error
.code
= hresult_from_http_response(code
)))
236 EnterCriticalSection(&job
->cs
);
238 job
->error
.context
= BG_ERROR_CONTEXT_REMOTE_FILE
;
239 if (job
->error
.file
) IBackgroundCopyFile2_Release(job
->error
.file
);
240 job
->error
.file
= &file
->IBackgroundCopyFile2_iface
;
241 IBackgroundCopyFile2_AddRef(job
->error
.file
);
243 LeaveCriticalSection(&job
->cs
);
244 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
248 EnterCriticalSection(&job
->cs
);
250 job
->error
.context
= 0;
253 IBackgroundCopyFile2_Release(job
->error
.file
);
254 job
->error
.file
= NULL
;
257 LeaveCriticalSection(&job
->cs
);
261 if (WinHttpQueryHeaders(handle
, WINHTTP_QUERY_CONTENT_LENGTH
|WINHTTP_QUERY_FLAG_NUMBER
,
262 NULL
, &len
, &size
, NULL
))
264 file
->fileProgress
.BytesTotal
= len
;
268 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE
:
270 file
->read_size
= buflen
;
273 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
:
275 WINHTTP_ASYNC_RESULT
*result
= (WINHTTP_ASYNC_RESULT
*)buf
;
276 job
->error
.code
= HRESULT_FROM_WIN32(result
->dwError
);
277 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
286 static DWORD
wait_for_completion(BackgroundCopyJobImpl
*job
)
288 HANDLE handles
[2] = {job
->wait
, job
->cancel
};
289 DWORD error
= ERROR_SUCCESS
;
291 switch (WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
))
296 case WAIT_OBJECT_0
+ 1:
297 error
= ERROR_CANCELLED
;
298 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_CANCELLED
);
302 error
= GetLastError();
303 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
310 static UINT
target_from_index(UINT index
)
314 case 0: return WINHTTP_AUTH_TARGET_SERVER
;
315 case 1: return WINHTTP_AUTH_TARGET_PROXY
;
317 ERR("unhandled index %u\n", index
);
323 static UINT
scheme_from_index(UINT index
)
327 case 0: return WINHTTP_AUTH_SCHEME_BASIC
;
328 case 1: return WINHTTP_AUTH_SCHEME_NTLM
;
329 case 2: return WINHTTP_AUTH_SCHEME_PASSPORT
;
330 case 3: return WINHTTP_AUTH_SCHEME_DIGEST
;
331 case 4: return WINHTTP_AUTH_SCHEME_NEGOTIATE
;
333 ERR("unhandled index %u\n", index
);
339 static BOOL
set_request_credentials(HINTERNET req
, BackgroundCopyJobImpl
*job
)
343 for (i
= 0; i
< BG_AUTH_TARGET_PROXY
; i
++)
345 UINT target
= target_from_index(i
);
346 for (j
= 0; j
< BG_AUTH_SCHEME_PASSPORT
; j
++)
348 UINT scheme
= scheme_from_index(j
);
349 const WCHAR
*username
= job
->http_options
.creds
[i
][j
].Credentials
.Basic
.UserName
;
350 const WCHAR
*password
= job
->http_options
.creds
[i
][j
].Credentials
.Basic
.Password
;
352 if (!username
) continue;
353 if (!WinHttpSetCredentials(req
, target
, scheme
, username
, password
, NULL
)) return FALSE
;
359 static BOOL
transfer_file_http(BackgroundCopyFileImpl
*file
, URL_COMPONENTSW
*uc
,
360 const WCHAR
*tmpfile
)
362 BackgroundCopyJobImpl
*job
= file
->owner
;
364 HINTERNET ses
, con
= NULL
, req
= NULL
;
365 DWORD flags
= (uc
->nScheme
== INTERNET_SCHEME_HTTPS
) ? WINHTTP_FLAG_SECURE
: 0;
370 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_CONNECTING
);
372 if (!(ses
= WinHttpOpen(NULL
, 0, NULL
, NULL
, WINHTTP_FLAG_ASYNC
))) return FALSE
;
373 WinHttpSetStatusCallback(ses
, progress_callback_http
, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS
, 0);
374 if (!WinHttpSetOption(ses
, WINHTTP_OPTION_CONTEXT_VALUE
, &file
, sizeof(file
))) goto done
;
376 if (!(con
= WinHttpConnect(ses
, uc
->lpszHostName
, uc
->nPort
, 0))) goto done
;
377 if (!(req
= WinHttpOpenRequest(con
, NULL
, uc
->lpszUrlPath
, NULL
, NULL
, NULL
, flags
))) goto done
;
378 if (!set_request_credentials(req
, job
)) goto done
;
380 if (!(WinHttpSendRequest(req
, job
->http_options
.headers
, ~0u, NULL
, 0, 0, (DWORD_PTR
)file
))) goto done
;
381 if (wait_for_completion(job
) || FAILED(job
->error
.code
)) goto done
;
383 if (!(WinHttpReceiveResponse(req
, NULL
))) goto done
;
384 if (wait_for_completion(job
) || FAILED(job
->error
.code
)) goto done
;
386 transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_TRANSFERRING
);
388 handle
= CreateFileW(tmpfile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
389 if (handle
== INVALID_HANDLE_VALUE
) goto done
;
394 if (!(ret
= WinHttpReadData(req
, buf
, sizeof(buf
), NULL
))) break;
395 if (wait_for_completion(job
) || FAILED(job
->error
.code
))
400 if (!file
->read_size
) break;
401 if (!(ret
= WriteFile(handle
, buf
, file
->read_size
, &written
, NULL
))) break;
403 EnterCriticalSection(&job
->cs
);
404 file
->fileProgress
.BytesTransferred
+= file
->read_size
;
405 job
->jobProgress
.BytesTransferred
+= file
->read_size
;
406 LeaveCriticalSection(&job
->cs
);
412 WinHttpCloseHandle(req
);
413 WinHttpCloseHandle(con
);
414 WinHttpCloseHandle(ses
);
415 if (!ret
&& !transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_ERROR
))
416 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
422 static DWORD CALLBACK
progress_callback_local(LARGE_INTEGER totalSize
, LARGE_INTEGER totalTransferred
,
423 LARGE_INTEGER streamSize
, LARGE_INTEGER streamTransferred
,
424 DWORD streamNum
, DWORD reason
, HANDLE srcFile
,
425 HANDLE dstFile
, LPVOID obj
)
427 BackgroundCopyFileImpl
*file
= obj
;
428 BackgroundCopyJobImpl
*job
= file
->owner
;
431 EnterCriticalSection(&job
->cs
);
432 diff
= (file
->fileProgress
.BytesTotal
== BG_SIZE_UNKNOWN
433 ? totalTransferred
.QuadPart
434 : totalTransferred
.QuadPart
- file
->fileProgress
.BytesTransferred
);
435 file
->fileProgress
.BytesTotal
= totalSize
.QuadPart
;
436 file
->fileProgress
.BytesTransferred
= totalTransferred
.QuadPart
;
437 job
->jobProgress
.BytesTransferred
+= diff
;
438 LeaveCriticalSection(&job
->cs
);
440 return (job
->state
== BG_JOB_STATE_TRANSFERRING
445 static BOOL
transfer_file_local(BackgroundCopyFileImpl
*file
, const WCHAR
*tmpname
)
447 static const WCHAR fileW
[] = {'f','i','l','e',':','/','/',0};
448 BackgroundCopyJobImpl
*job
= file
->owner
;
452 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSFERRING
);
454 if (strlenW(file
->info
.RemoteName
) > 7 && !memicmpW(file
->info
.RemoteName
, fileW
, 7))
455 ptr
= file
->info
.RemoteName
+ 7;
457 ptr
= file
->info
.RemoteName
;
459 if (!(ret
= CopyFileExW(ptr
, tmpname
, progress_callback_local
, file
, NULL
, 0)))
461 WARN("Local file copy failed: error %u\n", GetLastError());
462 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
469 BOOL
processFile(BackgroundCopyFileImpl
*file
, BackgroundCopyJobImpl
*job
)
471 static const WCHAR prefix
[] = {'B','I','T', 0};
472 WCHAR tmpDir
[MAX_PATH
], tmpName
[MAX_PATH
];
473 WCHAR host
[MAX_PATH
];
477 if (!GetTempPathW(MAX_PATH
, tmpDir
))
479 ERR("Couldn't create temp file name: %d\n", GetLastError());
480 /* Guessing on what state this should give us */
481 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSIENT_ERROR
);
485 if (!GetTempFileNameW(tmpDir
, prefix
, 0, tmpName
))
487 ERR("Couldn't create temp file: %d\n", GetLastError());
488 /* Guessing on what state this should give us */
489 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSIENT_ERROR
);
493 EnterCriticalSection(&job
->cs
);
494 file
->fileProgress
.BytesTotal
= BG_SIZE_UNKNOWN
;
495 file
->fileProgress
.BytesTransferred
= 0;
496 file
->fileProgress
.Completed
= FALSE
;
497 LeaveCriticalSection(&job
->cs
);
499 TRACE("Transferring: %s -> %s -> %s\n",
500 debugstr_w(file
->info
.RemoteName
),
502 debugstr_w(file
->info
.LocalName
));
504 uc
.dwStructSize
= sizeof(uc
);
506 uc
.lpszScheme
= NULL
;
507 uc
.dwSchemeLength
= 0;
508 uc
.lpszUserName
= NULL
;
509 uc
.dwUserNameLength
= 0;
510 uc
.lpszPassword
= NULL
;
511 uc
.dwPasswordLength
= 0;
512 uc
.lpszHostName
= host
;
513 uc
.dwHostNameLength
= sizeof(host
)/sizeof(host
[0]);
515 uc
.lpszUrlPath
= NULL
;
516 uc
.dwUrlPathLength
= ~0u;
517 uc
.lpszExtraInfo
= NULL
;
518 uc
.dwExtraInfoLength
= 0;
519 ret
= WinHttpCrackUrl(file
->info
.RemoteName
, 0, 0, &uc
);
522 TRACE("WinHttpCrackUrl failed, trying local file copy\n");
523 if (!transfer_file_local(file
, tmpName
)) WARN("local transfer failed\n");
525 else if (!transfer_file_http(file
, &uc
, tmpName
)) WARN("HTTP transfer failed\n");
527 if (transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_QUEUED
) ||
528 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_QUEUED
))
530 lstrcpyW(file
->tempFileName
, tmpName
);
532 EnterCriticalSection(&job
->cs
);
533 file
->fileProgress
.Completed
= TRUE
;
534 job
->jobProgress
.FilesTransferred
++;
535 LeaveCriticalSection(&job
->cs
);
541 DeleteFileW(tmpName
);