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
30 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(qmgr
);
34 static inline BackgroundCopyFileImpl
*impl_from_IBackgroundCopyFile2(
35 IBackgroundCopyFile2
*iface
)
37 return CONTAINING_RECORD(iface
, BackgroundCopyFileImpl
, IBackgroundCopyFile2_iface
);
40 static HRESULT WINAPI
BackgroundCopyFile_QueryInterface(
41 IBackgroundCopyFile2
*iface
,
45 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
47 TRACE("(%p)->(%s %p)\n", file
, debugstr_guid(riid
), obj
);
49 if (IsEqualGUID(riid
, &IID_IUnknown
) ||
50 IsEqualGUID(riid
, &IID_IBackgroundCopyFile
) ||
51 IsEqualGUID(riid
, &IID_IBackgroundCopyFile2
))
61 IBackgroundCopyFile2_AddRef(iface
);
65 static ULONG WINAPI
BackgroundCopyFile_AddRef(
66 IBackgroundCopyFile2
*iface
)
68 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
69 ULONG ref
= InterlockedIncrement(&file
->ref
);
70 TRACE("(%p)->(%ld)\n", file
, ref
);
74 static ULONG WINAPI
BackgroundCopyFile_Release(
75 IBackgroundCopyFile2
*iface
)
77 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
78 ULONG ref
= InterlockedDecrement(&file
->ref
);
80 TRACE("(%p)->(%ld)\n", file
, ref
);
84 IBackgroundCopyJob4_Release(&file
->owner
->IBackgroundCopyJob4_iface
);
85 free(file
->info
.LocalName
);
86 free(file
->info
.RemoteName
);
93 /* Get the remote name of a background copy file */
94 static HRESULT WINAPI
BackgroundCopyFile_GetRemoteName(
95 IBackgroundCopyFile2
*iface
,
98 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
100 TRACE("(%p)->(%p)\n", file
, pVal
);
102 return return_strval(file
->info
.RemoteName
, pVal
);
105 static HRESULT WINAPI
BackgroundCopyFile_GetLocalName(
106 IBackgroundCopyFile2
*iface
,
109 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
111 TRACE("(%p)->(%p)\n", file
, pVal
);
113 return return_strval(file
->info
.LocalName
, pVal
);
116 static HRESULT WINAPI
BackgroundCopyFile_GetProgress(
117 IBackgroundCopyFile2
*iface
,
118 BG_FILE_PROGRESS
*pVal
)
120 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
122 TRACE("(%p)->(%p)\n", file
, pVal
);
124 EnterCriticalSection(&file
->owner
->cs
);
125 *pVal
= file
->fileProgress
;
126 LeaveCriticalSection(&file
->owner
->cs
);
131 static HRESULT WINAPI
BackgroundCopyFile_GetFileRanges(
132 IBackgroundCopyFile2
*iface
,
134 BG_FILE_RANGE
**Ranges
)
136 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
137 FIXME("(%p)->(%p %p)\n", file
, RangeCount
, Ranges
);
141 static HRESULT WINAPI
BackgroundCopyFile_SetRemoteName(
142 IBackgroundCopyFile2
*iface
,
145 BackgroundCopyFileImpl
*file
= impl_from_IBackgroundCopyFile2(iface
);
146 FIXME("(%p)->(%s)\n", file
, debugstr_w(Val
));
150 static const IBackgroundCopyFile2Vtbl BackgroundCopyFile2Vtbl
=
152 BackgroundCopyFile_QueryInterface
,
153 BackgroundCopyFile_AddRef
,
154 BackgroundCopyFile_Release
,
155 BackgroundCopyFile_GetRemoteName
,
156 BackgroundCopyFile_GetLocalName
,
157 BackgroundCopyFile_GetProgress
,
158 BackgroundCopyFile_GetFileRanges
,
159 BackgroundCopyFile_SetRemoteName
162 HRESULT
BackgroundCopyFileConstructor(BackgroundCopyJobImpl
*owner
,
163 LPCWSTR remoteName
, LPCWSTR localName
,
164 BackgroundCopyFileImpl
**file
)
166 BackgroundCopyFileImpl
*This
;
168 TRACE("(%s, %s, %p)\n", debugstr_w(remoteName
), debugstr_w(localName
), file
);
170 This
= calloc(1, sizeof(*This
));
172 return E_OUTOFMEMORY
;
174 This
->info
.RemoteName
= wcsdup(remoteName
);
175 if (!This
->info
.RemoteName
)
178 return E_OUTOFMEMORY
;
181 This
->info
.LocalName
= wcsdup(localName
);
182 if (!This
->info
.LocalName
)
184 free(This
->info
.RemoteName
);
186 return E_OUTOFMEMORY
;
189 This
->IBackgroundCopyFile2_iface
.lpVtbl
= &BackgroundCopyFile2Vtbl
;
191 This
->fileProgress
.BytesTotal
= BG_SIZE_UNKNOWN
;
193 IBackgroundCopyJob4_AddRef(&owner
->IBackgroundCopyJob4_iface
);
199 static HRESULT
hresult_from_http_response(DWORD code
)
203 case 200: return S_OK
;
204 case 400: return BG_E_HTTP_ERROR_400
;
205 case 401: return BG_E_HTTP_ERROR_401
;
206 case 404: return BG_E_HTTP_ERROR_404
;
207 case 407: return BG_E_HTTP_ERROR_407
;
208 case 414: return BG_E_HTTP_ERROR_414
;
209 case 501: return BG_E_HTTP_ERROR_501
;
210 case 503: return BG_E_HTTP_ERROR_503
;
211 case 504: return BG_E_HTTP_ERROR_504
;
212 case 505: return BG_E_HTTP_ERROR_505
;
214 FIXME("unhandled response code %lu\n", code
);
219 static void CALLBACK
progress_callback_http(HINTERNET handle
, DWORD_PTR context
, DWORD status
,
220 LPVOID buf
, DWORD buflen
)
222 BackgroundCopyFileImpl
*file
= (BackgroundCopyFileImpl
*)context
;
223 BackgroundCopyJobImpl
*job
= file
->owner
;
225 TRACE("%p, %p, %lx, %p, %lu\n", handle
, file
, status
, buf
, buflen
);
229 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE
:
231 DWORD code
, len
, size
;
234 if (WinHttpQueryHeaders(handle
, WINHTTP_QUERY_STATUS_CODE
|WINHTTP_QUERY_FLAG_NUMBER
,
235 NULL
, &code
, &size
, NULL
))
237 if ((job
->error
.code
= hresult_from_http_response(code
)))
239 EnterCriticalSection(&job
->cs
);
241 job
->error
.context
= BG_ERROR_CONTEXT_REMOTE_FILE
;
242 if (job
->error
.file
) IBackgroundCopyFile2_Release(job
->error
.file
);
243 job
->error
.file
= &file
->IBackgroundCopyFile2_iface
;
244 IBackgroundCopyFile2_AddRef(job
->error
.file
);
246 LeaveCriticalSection(&job
->cs
);
247 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
251 EnterCriticalSection(&job
->cs
);
253 job
->error
.context
= 0;
256 IBackgroundCopyFile2_Release(job
->error
.file
);
257 job
->error
.file
= NULL
;
260 LeaveCriticalSection(&job
->cs
);
264 if (WinHttpQueryHeaders(handle
, WINHTTP_QUERY_CONTENT_LENGTH
|WINHTTP_QUERY_FLAG_NUMBER
,
265 NULL
, &len
, &size
, NULL
))
267 file
->fileProgress
.BytesTotal
= len
;
271 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE
:
273 file
->read_size
= buflen
;
276 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
:
278 WINHTTP_ASYNC_RESULT
*result
= (WINHTTP_ASYNC_RESULT
*)buf
;
279 job
->error
.code
= HRESULT_FROM_WIN32(result
->dwError
);
280 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
289 static DWORD
wait_for_completion(BackgroundCopyJobImpl
*job
)
291 HANDLE handles
[2] = {job
->wait
, job
->cancel
};
292 DWORD error
= ERROR_SUCCESS
;
294 switch (WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
))
299 case WAIT_OBJECT_0
+ 1:
300 error
= ERROR_CANCELLED
;
301 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_CANCELLED
);
305 error
= GetLastError();
306 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
313 static UINT
target_from_index(UINT index
)
317 case 0: return WINHTTP_AUTH_TARGET_SERVER
;
318 case 1: return WINHTTP_AUTH_TARGET_PROXY
;
320 ERR("unhandled index %u\n", index
);
326 static UINT
scheme_from_index(UINT index
)
330 case 0: return WINHTTP_AUTH_SCHEME_BASIC
;
331 case 1: return WINHTTP_AUTH_SCHEME_NTLM
;
332 case 2: return WINHTTP_AUTH_SCHEME_PASSPORT
;
333 case 3: return WINHTTP_AUTH_SCHEME_DIGEST
;
334 case 4: return WINHTTP_AUTH_SCHEME_NEGOTIATE
;
336 ERR("unhandled index %u\n", index
);
342 static BOOL
set_request_credentials(HINTERNET req
, BackgroundCopyJobImpl
*job
)
346 for (i
= 0; i
< BG_AUTH_TARGET_PROXY
; i
++)
348 UINT target
= target_from_index(i
);
349 for (j
= 0; j
< BG_AUTH_SCHEME_PASSPORT
; j
++)
351 UINT scheme
= scheme_from_index(j
);
352 const WCHAR
*username
= job
->http_options
.creds
[i
][j
].Credentials
.Basic
.UserName
;
353 const WCHAR
*password
= job
->http_options
.creds
[i
][j
].Credentials
.Basic
.Password
;
355 if (!username
) continue;
356 if (!WinHttpSetCredentials(req
, target
, scheme
, username
, password
, NULL
)) return FALSE
;
362 static BOOL
transfer_file_http(BackgroundCopyFileImpl
*file
, URL_COMPONENTSW
*uc
,
363 const WCHAR
*tmpfile
)
365 BackgroundCopyJobImpl
*job
= file
->owner
;
367 HINTERNET ses
, con
= NULL
, req
= NULL
;
368 DWORD flags
= (uc
->nScheme
== INTERNET_SCHEME_HTTPS
) ? WINHTTP_FLAG_SECURE
: 0;
373 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_CONNECTING
);
375 if (!(ses
= WinHttpOpen(NULL
, 0, NULL
, NULL
, WINHTTP_FLAG_ASYNC
))) return FALSE
;
376 WinHttpSetStatusCallback(ses
, progress_callback_http
, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS
, 0);
377 if (!WinHttpSetOption(ses
, WINHTTP_OPTION_CONTEXT_VALUE
, &file
, sizeof(file
))) goto done
;
379 if (!(con
= WinHttpConnect(ses
, uc
->lpszHostName
, uc
->nPort
, 0))) goto done
;
380 if (!(req
= WinHttpOpenRequest(con
, NULL
, uc
->lpszUrlPath
, NULL
, NULL
, NULL
, flags
))) goto done
;
381 if (!set_request_credentials(req
, job
)) goto done
;
383 if (!(WinHttpSendRequest(req
, job
->http_options
.headers
, ~0u, NULL
, 0, 0, (DWORD_PTR
)file
))) goto done
;
384 if (wait_for_completion(job
) || FAILED(job
->error
.code
)) goto done
;
386 if (!(WinHttpReceiveResponse(req
, NULL
))) goto done
;
387 if (wait_for_completion(job
) || FAILED(job
->error
.code
)) goto done
;
389 transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_TRANSFERRING
);
391 handle
= CreateFileW(tmpfile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
392 if (handle
== INVALID_HANDLE_VALUE
) goto done
;
397 if (!(ret
= WinHttpReadData(req
, buf
, sizeof(buf
), NULL
))) break;
398 if (wait_for_completion(job
) || FAILED(job
->error
.code
))
403 if (!file
->read_size
) break;
404 if (!(ret
= WriteFile(handle
, buf
, file
->read_size
, &written
, NULL
))) break;
406 EnterCriticalSection(&job
->cs
);
407 file
->fileProgress
.BytesTransferred
+= file
->read_size
;
408 job
->jobProgress
.BytesTransferred
+= file
->read_size
;
409 LeaveCriticalSection(&job
->cs
);
415 WinHttpCloseHandle(req
);
416 WinHttpCloseHandle(con
);
417 WinHttpCloseHandle(ses
);
418 if (!ret
&& !transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_ERROR
))
419 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
425 static DWORD CALLBACK
progress_callback_local(LARGE_INTEGER totalSize
, LARGE_INTEGER totalTransferred
,
426 LARGE_INTEGER streamSize
, LARGE_INTEGER streamTransferred
,
427 DWORD streamNum
, DWORD reason
, HANDLE srcFile
,
428 HANDLE dstFile
, LPVOID obj
)
430 BackgroundCopyFileImpl
*file
= obj
;
431 BackgroundCopyJobImpl
*job
= file
->owner
;
434 EnterCriticalSection(&job
->cs
);
435 diff
= (file
->fileProgress
.BytesTotal
== BG_SIZE_UNKNOWN
436 ? totalTransferred
.QuadPart
437 : totalTransferred
.QuadPart
- file
->fileProgress
.BytesTransferred
);
438 file
->fileProgress
.BytesTotal
= totalSize
.QuadPart
;
439 file
->fileProgress
.BytesTransferred
= totalTransferred
.QuadPart
;
440 job
->jobProgress
.BytesTransferred
+= diff
;
441 LeaveCriticalSection(&job
->cs
);
443 return (job
->state
== BG_JOB_STATE_TRANSFERRING
448 static BOOL
transfer_file_local(BackgroundCopyFileImpl
*file
, const WCHAR
*tmpname
)
450 BackgroundCopyJobImpl
*job
= file
->owner
;
454 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSFERRING
);
456 if (lstrlenW(file
->info
.RemoteName
) > 7 && !wcsnicmp(file
->info
.RemoteName
, L
"file://", 7))
457 ptr
= file
->info
.RemoteName
+ 7;
459 ptr
= file
->info
.RemoteName
;
461 if (!(ret
= CopyFileExW(ptr
, tmpname
, progress_callback_local
, file
, NULL
, 0)))
463 WARN("Local file copy failed: error %lu\n", GetLastError());
464 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
471 BOOL
processFile(BackgroundCopyFileImpl
*file
, BackgroundCopyJobImpl
*job
)
473 WCHAR tmpDir
[MAX_PATH
], tmpName
[MAX_PATH
];
474 WCHAR host
[MAX_PATH
];
478 if (!GetTempPathW(MAX_PATH
, tmpDir
))
480 ERR("Couldn't create temp file name: %ld\n", GetLastError());
481 /* Guessing on what state this should give us */
482 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSIENT_ERROR
);
486 if (!GetTempFileNameW(tmpDir
, L
"BIT", 0, tmpName
))
488 ERR("Couldn't create temp file: %ld\n", GetLastError());
489 /* Guessing on what state this should give us */
490 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSIENT_ERROR
);
494 EnterCriticalSection(&job
->cs
);
495 file
->fileProgress
.BytesTotal
= BG_SIZE_UNKNOWN
;
496 file
->fileProgress
.BytesTransferred
= 0;
497 file
->fileProgress
.Completed
= FALSE
;
498 LeaveCriticalSection(&job
->cs
);
500 TRACE("Transferring: %s -> %s -> %s\n",
501 debugstr_w(file
->info
.RemoteName
),
503 debugstr_w(file
->info
.LocalName
));
505 uc
.dwStructSize
= sizeof(uc
);
507 uc
.lpszScheme
= NULL
;
508 uc
.dwSchemeLength
= 0;
509 uc
.lpszUserName
= NULL
;
510 uc
.dwUserNameLength
= 0;
511 uc
.lpszPassword
= NULL
;
512 uc
.dwPasswordLength
= 0;
513 uc
.lpszHostName
= host
;
514 uc
.dwHostNameLength
= ARRAY_SIZE(host
);
516 uc
.lpszUrlPath
= NULL
;
517 uc
.dwUrlPathLength
= ~0u;
518 uc
.lpszExtraInfo
= NULL
;
519 uc
.dwExtraInfoLength
= 0;
520 ret
= WinHttpCrackUrl(file
->info
.RemoteName
, 0, 0, &uc
);
523 TRACE("WinHttpCrackUrl failed, trying local file copy\n");
524 if (!transfer_file_local(file
, tmpName
)) WARN("local transfer failed\n");
526 else if (!transfer_file_http(file
, &uc
, tmpName
)) WARN("HTTP transfer failed\n");
528 if (transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_QUEUED
) ||
529 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_QUEUED
))
531 lstrcpyW(file
->tempFileName
, tmpName
);
533 EnterCriticalSection(&job
->cs
);
534 file
->fileProgress
.Completed
= TRUE
;
535 job
->jobProgress
.FilesTransferred
++;
536 LeaveCriticalSection(&job
->cs
);
542 DeleteFileW(tmpName
);