wow64: In wow64_NtSetInformationToken forward TokenIntegrityLevel.
[wine.git] / dlls / qmgr / file.c
blobc82344a51a8937cb9c647650aff06cb1581ff4aa
1 /*
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
21 #include <stdarg.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "winreg.h"
27 #include "winhttp.h"
28 #define COBJMACROS
29 #include "qmgr.h"
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,
42 REFIID riid,
43 void **obj)
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))
53 *obj = iface;
55 else
57 *obj = NULL;
58 return E_NOINTERFACE;
61 IBackgroundCopyFile2_AddRef(iface);
62 return S_OK;
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);
71 return 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);
82 if (ref == 0)
84 IBackgroundCopyJob4_Release(&file->owner->IBackgroundCopyJob4_iface);
85 free(file->info.LocalName);
86 free(file->info.RemoteName);
87 free(file);
90 return ref;
93 /* Get the remote name of a background copy file */
94 static HRESULT WINAPI BackgroundCopyFile_GetRemoteName(
95 IBackgroundCopyFile2 *iface,
96 LPWSTR *pVal)
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,
107 LPWSTR *pVal)
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);
128 return S_OK;
131 static HRESULT WINAPI BackgroundCopyFile_GetFileRanges(
132 IBackgroundCopyFile2 *iface,
133 DWORD *RangeCount,
134 BG_FILE_RANGE **Ranges)
136 BackgroundCopyFileImpl *file = impl_from_IBackgroundCopyFile2(iface);
137 FIXME("(%p)->(%p %p)\n", file, RangeCount, Ranges);
138 return E_NOTIMPL;
141 static HRESULT WINAPI BackgroundCopyFile_SetRemoteName(
142 IBackgroundCopyFile2 *iface,
143 LPCWSTR Val)
145 BackgroundCopyFileImpl *file = impl_from_IBackgroundCopyFile2(iface);
146 FIXME("(%p)->(%s)\n", file, debugstr_w(Val));
147 return E_NOTIMPL;
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));
171 if (!This)
172 return E_OUTOFMEMORY;
174 This->info.RemoteName = wcsdup(remoteName);
175 if (!This->info.RemoteName)
177 free(This);
178 return E_OUTOFMEMORY;
181 This->info.LocalName = wcsdup(localName);
182 if (!This->info.LocalName)
184 free(This->info.RemoteName);
185 free(This);
186 return E_OUTOFMEMORY;
189 This->IBackgroundCopyFile2_iface.lpVtbl = &BackgroundCopyFile2Vtbl;
190 This->ref = 1;
191 This->fileProgress.BytesTotal = BG_SIZE_UNKNOWN;
192 This->owner = owner;
193 IBackgroundCopyJob4_AddRef(&owner->IBackgroundCopyJob4_iface);
195 *file = This;
196 return S_OK;
199 static HRESULT hresult_from_http_response(DWORD code)
201 switch (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;
213 default:
214 FIXME("unhandled response code %lu\n", code);
215 return S_OK;
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);
227 switch (status)
229 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
231 DWORD code, len, size;
233 size = sizeof(code);
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);
249 else
251 EnterCriticalSection(&job->cs);
253 job->error.context = 0;
254 if (job->error.file)
256 IBackgroundCopyFile2_Release(job->error.file);
257 job->error.file = NULL;
260 LeaveCriticalSection(&job->cs);
263 size = sizeof(len);
264 if (WinHttpQueryHeaders(handle, WINHTTP_QUERY_CONTENT_LENGTH|WINHTTP_QUERY_FLAG_NUMBER,
265 NULL, &len, &size, NULL))
267 file->fileProgress.BytesTotal = len;
269 break;
271 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
273 file->read_size = buflen;
274 break;
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);
281 break;
283 default: break;
286 SetEvent(job->wait);
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))
296 case WAIT_OBJECT_0:
297 break;
299 case WAIT_OBJECT_0 + 1:
300 error = ERROR_CANCELLED;
301 transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_CANCELLED);
302 break;
304 default:
305 error = GetLastError();
306 transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_ERROR);
307 break;
310 return error;
313 static UINT target_from_index(UINT index)
315 switch (index)
317 case 0: return WINHTTP_AUTH_TARGET_SERVER;
318 case 1: return WINHTTP_AUTH_TARGET_PROXY;
319 default:
320 ERR("unhandled index %u\n", index);
321 break;
323 return 0;
326 static UINT scheme_from_index(UINT index)
328 switch (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;
335 default:
336 ERR("unhandled index %u\n", index);
337 break;
339 return 0;
342 static BOOL set_request_credentials(HINTERNET req, BackgroundCopyJobImpl *job)
344 UINT i, j;
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;
359 return TRUE;
362 static BOOL transfer_file_http(BackgroundCopyFileImpl *file, URL_COMPONENTSW *uc,
363 const WCHAR *tmpfile)
365 BackgroundCopyJobImpl *job = file->owner;
366 HANDLE handle;
367 HINTERNET ses, con = NULL, req = NULL;
368 DWORD flags = (uc->nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;
369 char buf[4096];
370 BOOL ret = FALSE;
371 DWORD written;
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;
394 for (;;)
396 file->read_size = 0;
397 if (!(ret = WinHttpReadData(req, buf, sizeof(buf), NULL))) break;
398 if (wait_for_completion(job) || FAILED(job->error.code))
400 ret = FALSE;
401 break;
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);
412 CloseHandle(handle);
414 done:
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);
421 SetEvent(job->done);
422 return ret;
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;
432 ULONG64 diff;
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
444 ? PROGRESS_CONTINUE
445 : PROGRESS_CANCEL);
448 static BOOL transfer_file_local(BackgroundCopyFileImpl *file, const WCHAR *tmpname)
450 BackgroundCopyJobImpl *job = file->owner;
451 const WCHAR *ptr;
452 BOOL ret;
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;
458 else
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);
467 SetEvent(job->done);
468 return ret;
471 BOOL processFile(BackgroundCopyFileImpl *file, BackgroundCopyJobImpl *job)
473 WCHAR tmpDir[MAX_PATH], tmpName[MAX_PATH];
474 WCHAR host[MAX_PATH];
475 URL_COMPONENTSW uc;
476 BOOL ret;
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);
483 return FALSE;
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);
491 return FALSE;
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),
502 debugstr_w(tmpName),
503 debugstr_w(file->info.LocalName));
505 uc.dwStructSize = sizeof(uc);
506 uc.nScheme = 0;
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);
515 uc.nPort = 0;
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);
521 if (!ret)
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);
538 return TRUE;
540 else
542 DeleteFileW(tmpName);
543 return FALSE;