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
error_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
= error_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
= 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;
369 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_CONNECTING
);
371 if (!(ses
= WinHttpOpen(NULL
, 0, NULL
, NULL
, WINHTTP_FLAG_ASYNC
))) return FALSE
;
372 WinHttpSetStatusCallback(ses
, progress_callback_http
, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS
, 0);
373 if (!WinHttpSetOption(ses
, WINHTTP_OPTION_CONTEXT_VALUE
, &file
, sizeof(file
))) goto done
;
375 if (!(con
= WinHttpConnect(ses
, uc
->lpszHostName
, uc
->nPort
, 0))) goto done
;
376 if (!(req
= WinHttpOpenRequest(con
, NULL
, uc
->lpszUrlPath
, NULL
, NULL
, NULL
, flags
))) goto done
;
377 if (!set_request_credentials(req
, job
)) goto done
;
379 if (!(WinHttpSendRequest(req
, job
->http_options
.headers
, ~0u, NULL
, 0, 0, (DWORD_PTR
)file
))) goto done
;
380 if (wait_for_completion(job
) || job
->error
.code
) goto done
;
382 if (!(WinHttpReceiveResponse(req
, NULL
))) goto done
;
383 if (wait_for_completion(job
) || job
->error
.code
) goto done
;
385 transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_TRANSFERRING
);
387 handle
= CreateFileW(tmpfile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
388 if (handle
== INVALID_HANDLE_VALUE
) goto done
;
393 if (!(ret
= WinHttpReadData(req
, buf
, sizeof(buf
), NULL
))) break;
394 if (wait_for_completion(job
) || job
->error
.code
)
399 if (!file
->read_size
) break;
400 if (!(ret
= WriteFile(handle
, buf
, file
->read_size
, NULL
, NULL
))) break;
402 EnterCriticalSection(&job
->cs
);
403 file
->fileProgress
.BytesTransferred
+= file
->read_size
;
404 job
->jobProgress
.BytesTransferred
+= file
->read_size
;
405 LeaveCriticalSection(&job
->cs
);
411 WinHttpCloseHandle(req
);
412 WinHttpCloseHandle(con
);
413 WinHttpCloseHandle(ses
);
414 if (!ret
) DeleteFileW(tmpfile
);
420 static DWORD CALLBACK
progress_callback_local(LARGE_INTEGER totalSize
, LARGE_INTEGER totalTransferred
,
421 LARGE_INTEGER streamSize
, LARGE_INTEGER streamTransferred
,
422 DWORD streamNum
, DWORD reason
, HANDLE srcFile
,
423 HANDLE dstFile
, LPVOID obj
)
425 BackgroundCopyFileImpl
*file
= obj
;
426 BackgroundCopyJobImpl
*job
= file
->owner
;
429 EnterCriticalSection(&job
->cs
);
430 diff
= (file
->fileProgress
.BytesTotal
== BG_SIZE_UNKNOWN
431 ? totalTransferred
.QuadPart
432 : totalTransferred
.QuadPart
- file
->fileProgress
.BytesTransferred
);
433 file
->fileProgress
.BytesTotal
= totalSize
.QuadPart
;
434 file
->fileProgress
.BytesTransferred
= totalTransferred
.QuadPart
;
435 job
->jobProgress
.BytesTransferred
+= diff
;
436 LeaveCriticalSection(&job
->cs
);
438 return (job
->state
== BG_JOB_STATE_TRANSFERRING
443 static BOOL
transfer_file_local(BackgroundCopyFileImpl
*file
, const WCHAR
*tmpname
)
445 static const WCHAR fileW
[] = {'f','i','l','e',':','/','/',0};
446 BackgroundCopyJobImpl
*job
= file
->owner
;
450 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSFERRING
);
452 if (strlenW(file
->info
.RemoteName
) > 7 && !memicmpW(file
->info
.RemoteName
, fileW
, 7))
453 ptr
= file
->info
.RemoteName
+ 7;
455 ptr
= file
->info
.RemoteName
;
457 if (!(ret
= CopyFileExW(ptr
, tmpname
, progress_callback_local
, file
, NULL
, 0)))
459 WARN("Local file copy failed: error %u\n", GetLastError());
460 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
467 BOOL
processFile(BackgroundCopyFileImpl
*file
, BackgroundCopyJobImpl
*job
)
469 static const WCHAR prefix
[] = {'B','I','T', 0};
470 WCHAR tmpDir
[MAX_PATH
], tmpName
[MAX_PATH
];
471 WCHAR host
[MAX_PATH
], path
[MAX_PATH
];
475 if (!GetTempPathW(MAX_PATH
, tmpDir
))
477 ERR("Couldn't create temp file name: %d\n", GetLastError());
478 /* Guessing on what state this should give us */
479 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSIENT_ERROR
);
483 if (!GetTempFileNameW(tmpDir
, prefix
, 0, tmpName
))
485 ERR("Couldn't create temp file: %d\n", GetLastError());
486 /* Guessing on what state this should give us */
487 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSIENT_ERROR
);
491 EnterCriticalSection(&job
->cs
);
492 file
->fileProgress
.BytesTotal
= BG_SIZE_UNKNOWN
;
493 file
->fileProgress
.BytesTransferred
= 0;
494 file
->fileProgress
.Completed
= FALSE
;
495 LeaveCriticalSection(&job
->cs
);
497 TRACE("Transferring: %s -> %s -> %s\n",
498 debugstr_w(file
->info
.RemoteName
),
500 debugstr_w(file
->info
.LocalName
));
502 uc
.dwStructSize
= sizeof(uc
);
504 uc
.lpszScheme
= NULL
;
505 uc
.dwSchemeLength
= 0;
506 uc
.lpszUserName
= NULL
;
507 uc
.dwUserNameLength
= 0;
508 uc
.lpszPassword
= NULL
;
509 uc
.dwPasswordLength
= 0;
510 uc
.lpszHostName
= host
;
511 uc
.dwHostNameLength
= sizeof(host
)/sizeof(host
[0]);
513 uc
.lpszUrlPath
= path
;
514 uc
.dwUrlPathLength
= sizeof(path
)/sizeof(path
[0]);
515 uc
.lpszExtraInfo
= NULL
;
516 uc
.dwExtraInfoLength
= 0;
517 ret
= WinHttpCrackUrl(file
->info
.RemoteName
, 0, 0, &uc
);
520 TRACE("WinHttpCrackUrl failed, trying local file copy\n");
521 if (!transfer_file_local(file
, tmpName
)) return FALSE
;
523 else if (!transfer_file_http(file
, &uc
, tmpName
))
525 WARN("HTTP transfer failed\n");
529 if (transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_QUEUED
) ||
530 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_QUEUED
))
532 lstrcpyW(file
->tempFileName
, tmpName
);
534 EnterCriticalSection(&job
->cs
);
535 file
->fileProgress
.Completed
= TRUE
;
536 job
->jobProgress
.FilesTransferred
++;
537 LeaveCriticalSection(&job
->cs
);
543 DeleteFileW(tmpName
);