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)->(%d)\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)->(%d)\n", file
, ref
);
84 IBackgroundCopyJob3_Release(&file
->owner
->IBackgroundCopyJob3_iface
);
85 HeapFree(GetProcessHeap(), 0, file
->info
.LocalName
);
86 HeapFree(GetProcessHeap(), 0, file
->info
.RemoteName
);
87 HeapFree(GetProcessHeap(), 0, file
);
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
= HeapAlloc(GetProcessHeap(), 0, sizeof *This
);
172 return E_OUTOFMEMORY
;
174 This
->info
.RemoteName
= strdupW(remoteName
);
175 if (!This
->info
.RemoteName
)
177 HeapFree(GetProcessHeap(), 0, This
);
178 return E_OUTOFMEMORY
;
181 This
->info
.LocalName
= strdupW(localName
);
182 if (!This
->info
.LocalName
)
184 HeapFree(GetProcessHeap(), 0, This
->info
.RemoteName
);
185 HeapFree(GetProcessHeap(), 0, This
);
186 return E_OUTOFMEMORY
;
189 This
->IBackgroundCopyFile2_iface
.lpVtbl
= &BackgroundCopyFile2Vtbl
;
192 This
->fileProgress
.BytesTotal
= BG_SIZE_UNKNOWN
;
193 This
->fileProgress
.BytesTransferred
= 0;
194 This
->fileProgress
.Completed
= FALSE
;
197 This
->tempFileName
[0] = 0;
198 IBackgroundCopyJob3_AddRef(&owner
->IBackgroundCopyJob3_iface
);
204 static HRESULT
hresult_from_http_response(DWORD code
)
208 case 200: return S_OK
;
209 case 400: return BG_E_HTTP_ERROR_400
;
210 case 401: return BG_E_HTTP_ERROR_401
;
211 case 404: return BG_E_HTTP_ERROR_404
;
212 case 407: return BG_E_HTTP_ERROR_407
;
213 case 414: return BG_E_HTTP_ERROR_414
;
214 case 501: return BG_E_HTTP_ERROR_501
;
215 case 503: return BG_E_HTTP_ERROR_503
;
216 case 504: return BG_E_HTTP_ERROR_504
;
217 case 505: return BG_E_HTTP_ERROR_505
;
219 FIXME("unhandled response code %u\n", code
);
224 static void CALLBACK
progress_callback_http(HINTERNET handle
, DWORD_PTR context
, DWORD status
,
225 LPVOID buf
, DWORD buflen
)
227 BackgroundCopyFileImpl
*file
= (BackgroundCopyFileImpl
*)context
;
228 BackgroundCopyJobImpl
*job
= file
->owner
;
230 TRACE("%p, %p, %x, %p, %u\n", handle
, file
, status
, buf
, buflen
);
234 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE
:
236 DWORD code
, len
, size
;
239 if (WinHttpQueryHeaders(handle
, WINHTTP_QUERY_STATUS_CODE
|WINHTTP_QUERY_FLAG_NUMBER
,
240 NULL
, &code
, &size
, NULL
))
242 if ((job
->error
.code
= hresult_from_http_response(code
)))
244 EnterCriticalSection(&job
->cs
);
246 job
->error
.context
= BG_ERROR_CONTEXT_REMOTE_FILE
;
247 if (job
->error
.file
) IBackgroundCopyFile2_Release(job
->error
.file
);
248 job
->error
.file
= &file
->IBackgroundCopyFile2_iface
;
249 IBackgroundCopyFile2_AddRef(job
->error
.file
);
251 LeaveCriticalSection(&job
->cs
);
252 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
256 EnterCriticalSection(&job
->cs
);
258 job
->error
.context
= 0;
261 IBackgroundCopyFile2_Release(job
->error
.file
);
262 job
->error
.file
= NULL
;
265 LeaveCriticalSection(&job
->cs
);
269 if (WinHttpQueryHeaders(handle
, WINHTTP_QUERY_CONTENT_LENGTH
|WINHTTP_QUERY_FLAG_NUMBER
,
270 NULL
, &len
, &size
, NULL
))
272 file
->fileProgress
.BytesTotal
= len
;
276 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE
:
278 file
->read_size
= buflen
;
281 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
:
283 WINHTTP_ASYNC_RESULT
*result
= (WINHTTP_ASYNC_RESULT
*)buf
;
284 job
->error
.code
= HRESULT_FROM_WIN32(result
->dwError
);
285 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
294 static DWORD
wait_for_completion(BackgroundCopyJobImpl
*job
)
296 HANDLE handles
[2] = {job
->wait
, job
->cancel
};
297 DWORD error
= ERROR_SUCCESS
;
299 switch (WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
))
304 case WAIT_OBJECT_0
+ 1:
305 error
= ERROR_CANCELLED
;
306 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_CANCELLED
);
310 error
= GetLastError();
311 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
318 static UINT
target_from_index(UINT index
)
322 case 0: return WINHTTP_AUTH_TARGET_SERVER
;
323 case 1: return WINHTTP_AUTH_TARGET_PROXY
;
325 ERR("unhandled index %u\n", index
);
331 static UINT
scheme_from_index(UINT index
)
335 case 0: return WINHTTP_AUTH_SCHEME_BASIC
;
336 case 1: return WINHTTP_AUTH_SCHEME_NTLM
;
337 case 2: return WINHTTP_AUTH_SCHEME_PASSPORT
;
338 case 3: return WINHTTP_AUTH_SCHEME_DIGEST
;
339 case 4: return WINHTTP_AUTH_SCHEME_NEGOTIATE
;
341 ERR("unhandled index %u\n", index
);
347 static BOOL
set_request_credentials(HINTERNET req
, BackgroundCopyJobImpl
*job
)
351 for (i
= 0; i
< BG_AUTH_TARGET_PROXY
; i
++)
353 UINT target
= target_from_index(i
);
354 for (j
= 0; j
< BG_AUTH_SCHEME_PASSPORT
; j
++)
356 UINT scheme
= scheme_from_index(j
);
357 const WCHAR
*username
= job
->http_options
.creds
[i
][j
].Credentials
.Basic
.UserName
;
358 const WCHAR
*password
= job
->http_options
.creds
[i
][j
].Credentials
.Basic
.Password
;
360 if (!username
) continue;
361 if (!WinHttpSetCredentials(req
, target
, scheme
, username
, password
, NULL
)) return FALSE
;
367 static BOOL
transfer_file_http(BackgroundCopyFileImpl
*file
, URL_COMPONENTSW
*uc
,
368 const WCHAR
*tmpfile
)
370 BackgroundCopyJobImpl
*job
= file
->owner
;
372 HINTERNET ses
, con
= NULL
, req
= NULL
;
373 DWORD flags
= (uc
->nScheme
== INTERNET_SCHEME_HTTPS
) ? WINHTTP_FLAG_SECURE
: 0;
378 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_CONNECTING
);
380 if (!(ses
= WinHttpOpen(NULL
, 0, NULL
, NULL
, WINHTTP_FLAG_ASYNC
))) return FALSE
;
381 WinHttpSetStatusCallback(ses
, progress_callback_http
, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS
, 0);
382 if (!WinHttpSetOption(ses
, WINHTTP_OPTION_CONTEXT_VALUE
, &file
, sizeof(file
))) goto done
;
384 if (!(con
= WinHttpConnect(ses
, uc
->lpszHostName
, uc
->nPort
, 0))) goto done
;
385 if (!(req
= WinHttpOpenRequest(con
, NULL
, uc
->lpszUrlPath
, NULL
, NULL
, NULL
, flags
))) goto done
;
386 if (!set_request_credentials(req
, job
)) goto done
;
388 if (!(WinHttpSendRequest(req
, job
->http_options
.headers
, ~0u, NULL
, 0, 0, (DWORD_PTR
)file
))) goto done
;
389 if (wait_for_completion(job
) || FAILED(job
->error
.code
)) goto done
;
391 if (!(WinHttpReceiveResponse(req
, NULL
))) goto done
;
392 if (wait_for_completion(job
) || FAILED(job
->error
.code
)) goto done
;
394 transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_TRANSFERRING
);
396 handle
= CreateFileW(tmpfile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
397 if (handle
== INVALID_HANDLE_VALUE
) goto done
;
402 if (!(ret
= WinHttpReadData(req
, buf
, sizeof(buf
), NULL
))) break;
403 if (wait_for_completion(job
) || FAILED(job
->error
.code
))
408 if (!file
->read_size
) break;
409 if (!(ret
= WriteFile(handle
, buf
, file
->read_size
, &written
, NULL
))) break;
411 EnterCriticalSection(&job
->cs
);
412 file
->fileProgress
.BytesTransferred
+= file
->read_size
;
413 job
->jobProgress
.BytesTransferred
+= file
->read_size
;
414 LeaveCriticalSection(&job
->cs
);
420 WinHttpCloseHandle(req
);
421 WinHttpCloseHandle(con
);
422 WinHttpCloseHandle(ses
);
423 if (!ret
&& !transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_ERROR
))
424 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
430 static DWORD CALLBACK
progress_callback_local(LARGE_INTEGER totalSize
, LARGE_INTEGER totalTransferred
,
431 LARGE_INTEGER streamSize
, LARGE_INTEGER streamTransferred
,
432 DWORD streamNum
, DWORD reason
, HANDLE srcFile
,
433 HANDLE dstFile
, LPVOID obj
)
435 BackgroundCopyFileImpl
*file
= obj
;
436 BackgroundCopyJobImpl
*job
= file
->owner
;
439 EnterCriticalSection(&job
->cs
);
440 diff
= (file
->fileProgress
.BytesTotal
== BG_SIZE_UNKNOWN
441 ? totalTransferred
.QuadPart
442 : totalTransferred
.QuadPart
- file
->fileProgress
.BytesTransferred
);
443 file
->fileProgress
.BytesTotal
= totalSize
.QuadPart
;
444 file
->fileProgress
.BytesTransferred
= totalTransferred
.QuadPart
;
445 job
->jobProgress
.BytesTransferred
+= diff
;
446 LeaveCriticalSection(&job
->cs
);
448 return (job
->state
== BG_JOB_STATE_TRANSFERRING
453 static BOOL
transfer_file_local(BackgroundCopyFileImpl
*file
, const WCHAR
*tmpname
)
455 static const WCHAR fileW
[] = {'f','i','l','e',':','/','/',0};
456 BackgroundCopyJobImpl
*job
= file
->owner
;
460 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSFERRING
);
462 if (strlenW(file
->info
.RemoteName
) > 7 && !memicmpW(file
->info
.RemoteName
, fileW
, 7))
463 ptr
= file
->info
.RemoteName
+ 7;
465 ptr
= file
->info
.RemoteName
;
467 if (!(ret
= CopyFileExW(ptr
, tmpname
, progress_callback_local
, file
, NULL
, 0)))
469 WARN("Local file copy failed: error %u\n", GetLastError());
470 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_ERROR
);
477 BOOL
processFile(BackgroundCopyFileImpl
*file
, BackgroundCopyJobImpl
*job
)
479 static const WCHAR prefix
[] = {'B','I','T', 0};
480 WCHAR tmpDir
[MAX_PATH
], tmpName
[MAX_PATH
];
481 WCHAR host
[MAX_PATH
];
485 if (!GetTempPathW(MAX_PATH
, tmpDir
))
487 ERR("Couldn't create temp file name: %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 if (!GetTempFileNameW(tmpDir
, prefix
, 0, tmpName
))
495 ERR("Couldn't create temp file: %d\n", GetLastError());
496 /* Guessing on what state this should give us */
497 transitionJobState(job
, BG_JOB_STATE_QUEUED
, BG_JOB_STATE_TRANSIENT_ERROR
);
501 EnterCriticalSection(&job
->cs
);
502 file
->fileProgress
.BytesTotal
= BG_SIZE_UNKNOWN
;
503 file
->fileProgress
.BytesTransferred
= 0;
504 file
->fileProgress
.Completed
= FALSE
;
505 LeaveCriticalSection(&job
->cs
);
507 TRACE("Transferring: %s -> %s -> %s\n",
508 debugstr_w(file
->info
.RemoteName
),
510 debugstr_w(file
->info
.LocalName
));
512 uc
.dwStructSize
= sizeof(uc
);
514 uc
.lpszScheme
= NULL
;
515 uc
.dwSchemeLength
= 0;
516 uc
.lpszUserName
= NULL
;
517 uc
.dwUserNameLength
= 0;
518 uc
.lpszPassword
= NULL
;
519 uc
.dwPasswordLength
= 0;
520 uc
.lpszHostName
= host
;
521 uc
.dwHostNameLength
= ARRAY_SIZE(host
);
523 uc
.lpszUrlPath
= NULL
;
524 uc
.dwUrlPathLength
= ~0u;
525 uc
.lpszExtraInfo
= NULL
;
526 uc
.dwExtraInfoLength
= 0;
527 ret
= WinHttpCrackUrl(file
->info
.RemoteName
, 0, 0, &uc
);
530 TRACE("WinHttpCrackUrl failed, trying local file copy\n");
531 if (!transfer_file_local(file
, tmpName
)) WARN("local transfer failed\n");
533 else if (!transfer_file_http(file
, &uc
, tmpName
)) WARN("HTTP transfer failed\n");
535 if (transitionJobState(job
, BG_JOB_STATE_CONNECTING
, BG_JOB_STATE_QUEUED
) ||
536 transitionJobState(job
, BG_JOB_STATE_TRANSFERRING
, BG_JOB_STATE_QUEUED
))
538 lstrcpyW(file
->tempFileName
, tmpName
);
540 EnterCriticalSection(&job
->cs
);
541 file
->fileProgress
.Completed
= TRUE
;
542 job
->jobProgress
.FilesTransferred
++;
543 LeaveCriticalSection(&job
->cs
);
549 DeleteFileW(tmpName
);