job.c 36.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Background Copy Job Interface for BITS
 *
 * Copyright 2007 Google (Roy Shea)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

21 22 23 24
#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
25 26 27 28 29
#include "qmgr.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(qmgr);

30 31 32 33 34 35 36 37 38 39 40 41 42 43
BOOL transitionJobState(BackgroundCopyJobImpl *job, BG_JOB_STATE from, BG_JOB_STATE to)
{
    BOOL ret = FALSE;

    EnterCriticalSection(&globalMgr.cs);
    if (job->state == from)
    {
        job->state = to;
        ret = TRUE;
    }
    LeaveCriticalSection(&globalMgr.cs);
    return ret;
}

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
struct copy_error
{
    IBackgroundCopyError  IBackgroundCopyError_iface;
    LONG                  refs;
    BG_ERROR_CONTEXT      context;
    HRESULT               code;
    IBackgroundCopyFile2 *file;
};

static inline struct copy_error *impl_from_IBackgroundCopyError(IBackgroundCopyError *iface)
{
    return CONTAINING_RECORD(iface, struct copy_error, IBackgroundCopyError_iface);
}

static HRESULT WINAPI copy_error_QueryInterface(
    IBackgroundCopyError *iface,
    REFIID riid,
    void **obj)
{
    struct copy_error *error = impl_from_IBackgroundCopyError(iface);

    TRACE("(%p)->(%s %p)\n", error, debugstr_guid(riid), obj);

    if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IBackgroundCopyError))
    {
        *obj = &error->IBackgroundCopyError_iface;
    }
    else
    {
        *obj = NULL;
        WARN("interface %s not supported\n", debugstr_guid(riid));
        return E_NOINTERFACE;
    }

    IBackgroundCopyError_AddRef(iface);
    return S_OK;
}

static ULONG WINAPI copy_error_AddRef(
    IBackgroundCopyError *iface)
{
    struct copy_error *error = impl_from_IBackgroundCopyError(iface);
    LONG refs = InterlockedIncrement(&error->refs);
    TRACE("(%p)->(%d)\n", error, refs);
    return refs;
}

static ULONG WINAPI copy_error_Release(
    IBackgroundCopyError *iface)
{
    struct copy_error *error = impl_from_IBackgroundCopyError(iface);
    LONG refs = InterlockedDecrement(&error->refs);

    TRACE("(%p)->(%d)\n", error, refs);

    if (!refs)
    {
        if (error->file) IBackgroundCopyFile2_Release(error->file);
        HeapFree(GetProcessHeap(), 0, error);
    }
    return refs;
}

static HRESULT WINAPI copy_error_GetError(
    IBackgroundCopyError *iface,
    BG_ERROR_CONTEXT *pContext,
    HRESULT *pCode)
{
    struct copy_error *error = impl_from_IBackgroundCopyError(iface);

    TRACE("(%p)->(%p %p)\n", error, pContext, pCode);

    *pContext = error->context;
    *pCode = error->code;

    TRACE("returning context %u error code 0x%08x\n", error->context, error->code);
    return S_OK;
}

static HRESULT WINAPI copy_error_GetFile(
    IBackgroundCopyError *iface,
    IBackgroundCopyFile **pVal)
{
    struct copy_error *error = impl_from_IBackgroundCopyError(iface);

    TRACE("(%p)->(%p)\n", error, pVal);

    if (error->file)
    {
        IBackgroundCopyFile2_AddRef(error->file);
        *pVal = (IBackgroundCopyFile *)error->file;
        return S_OK;
    }
    *pVal = NULL;
    return BG_E_FILE_NOT_AVAILABLE;
}

static HRESULT WINAPI copy_error_GetErrorDescription(
    IBackgroundCopyError *iface,
    DWORD LanguageId,
    LPWSTR *pErrorDescription)
{
    struct copy_error *error = impl_from_IBackgroundCopyError(iface);
    FIXME("(%p)->(%p)\n", error, pErrorDescription);
    return E_NOTIMPL;
}

static HRESULT WINAPI copy_error_GetErrorContextDescription(
    IBackgroundCopyError *iface,
    DWORD LanguageId,
    LPWSTR *pContextDescription)
{
    struct copy_error *error = impl_from_IBackgroundCopyError(iface);
    FIXME("(%p)->(%p)\n", error, pContextDescription);
    return E_NOTIMPL;
}

static HRESULT WINAPI copy_error_GetProtocol(
    IBackgroundCopyError *iface,
    LPWSTR *pProtocol)
{
    struct copy_error *error = impl_from_IBackgroundCopyError(iface);
    FIXME("(%p)->(%p)\n", error, pProtocol);
    return E_NOTIMPL;
}

static const IBackgroundCopyErrorVtbl copy_error_vtbl =
{
    copy_error_QueryInterface,
    copy_error_AddRef,
    copy_error_Release,
    copy_error_GetError,
    copy_error_GetFile,
    copy_error_GetErrorDescription,
    copy_error_GetErrorContextDescription,
    copy_error_GetProtocol
};

static HRESULT create_copy_error(
    BG_ERROR_CONTEXT context,
    HRESULT code,
    IBackgroundCopyFile2 *file,
    IBackgroundCopyError **obj)
{
    struct copy_error *error;

    TRACE("context %u code %08x file %p\n", context, code, file);

    if (!(error = HeapAlloc(GetProcessHeap(), 0, sizeof(*error) ))) return E_OUTOFMEMORY;
    error->IBackgroundCopyError_iface.lpVtbl = &copy_error_vtbl;
    error->refs    = 1;
    error->context = context;
    error->code    = code;
    error->file    = file;
    if (error->file) IBackgroundCopyFile2_AddRef(error->file);

    *obj = &error->IBackgroundCopyError_iface;
    TRACE("returning iface %p\n", *obj);
    return S_OK;
}

205 206 207 208 209
static inline BOOL is_job_done(const BackgroundCopyJobImpl *job)
{
    return job->state == BG_JOB_STATE_CANCELLED || job->state == BG_JOB_STATE_ACKNOWLEDGED;
}

210
static inline BackgroundCopyJobImpl *impl_from_IBackgroundCopyJob3(IBackgroundCopyJob3 *iface)
211
{
212
    return CONTAINING_RECORD(iface, BackgroundCopyJobImpl, IBackgroundCopyJob3_iface);
213 214
}

215
static HRESULT WINAPI BackgroundCopyJob_QueryInterface(
216
    IBackgroundCopyJob3 *iface, REFIID riid, void **obj)
217
{
218
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
219 220

    TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
221

222 223 224 225
    if (IsEqualGUID(riid, &IID_IUnknown) ||
        IsEqualGUID(riid, &IID_IBackgroundCopyJob) ||
        IsEqualGUID(riid, &IID_IBackgroundCopyJob2) ||
        IsEqualGUID(riid, &IID_IBackgroundCopyJob3))
226
    {
227 228
        *obj = &This->IBackgroundCopyJob3_iface;
    }
229 230 231 232
    else if (IsEqualGUID(riid, &IID_IBackgroundCopyJobHttpOptions))
    {
        *obj = &This->IBackgroundCopyJobHttpOptions_iface;
    }
233 234 235 236
    else
    {
        *obj = NULL;
        return E_NOINTERFACE;
237 238
    }

239 240
    IBackgroundCopyJob3_AddRef(iface);
    return S_OK;
241 242
}

243
static ULONG WINAPI BackgroundCopyJob_AddRef(IBackgroundCopyJob3 *iface)
244
{
245
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
246 247 248 249 250
    ULONG ref = InterlockedIncrement(&This->ref);
    TRACE("(%p)->(%d)\n", This, ref);
    return ref;
}

251
static ULONG WINAPI BackgroundCopyJob_Release(IBackgroundCopyJob3 *iface)
252
{
253
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
254
    ULONG i, j, ref = InterlockedDecrement(&This->ref);
255

256 257
    TRACE("(%p)->(%d)\n", This, ref);

258
    if (ref == 0)
259 260 261
    {
        This->cs.DebugInfo->Spare[0] = 0;
        DeleteCriticalSection(&This->cs);
262 263
        if (This->callback)
            IBackgroundCopyCallback2_Release(This->callback);
264
        HeapFree(GetProcessHeap(), 0, This->displayName);
265
        HeapFree(GetProcessHeap(), 0, This->description);
266
        HeapFree(GetProcessHeap(), 0, This->http_options.headers);
267 268 269 270 271 272 273 274 275
        for (i = 0; i < BG_AUTH_TARGET_PROXY; i++)
        {
            for (j = 0; j < BG_AUTH_SCHEME_PASSPORT; j++)
            {
                BG_AUTH_CREDENTIALS *cred = &This->http_options.creds[i][j];
                HeapFree(GetProcessHeap(), 0, cred->Credentials.Basic.UserName);
                HeapFree(GetProcessHeap(), 0, cred->Credentials.Basic.Password);
            }
        }
276 277 278
        CloseHandle(This->wait);
        CloseHandle(This->cancel);
        CloseHandle(This->done);
279 280
        HeapFree(GetProcessHeap(), 0, This);
    }
281 282 283 284 285 286

    return ref;
}

/*** IBackgroundCopyJob methods ***/

287
static HRESULT WINAPI BackgroundCopyJob_AddFileSet(
288
    IBackgroundCopyJob3 *iface,
289 290 291
    ULONG cFileCount,
    BG_FILE_INFO *pFileSet)
{
292
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
293
    HRESULT hr = S_OK;
294
    ULONG i;
295 296 297

    TRACE("(%p)->(%d %p)\n", This, cFileCount, pFileSet);

298 299
    EnterCriticalSection(&This->cs);

300 301
    for (i = 0; i < cFileCount; ++i)
    {
302 303 304 305 306 307 308 309 310 311 312 313
        BackgroundCopyFileImpl *file;

        /* We should return E_INVALIDARG in these cases. */
        FIXME("Check for valid filenames and supported protocols\n");

        hr = BackgroundCopyFileConstructor(This, pFileSet[i].RemoteName, pFileSet[i].LocalName, &file);
        if (hr != S_OK) break;

        /* Add a reference to the file to file list */
        list_add_head(&This->files, &file->entryFromJob);
        This->jobProgress.BytesTotal = BG_SIZE_UNKNOWN;
        ++This->jobProgress.FilesTotal;
314
    }
315 316 317 318

    LeaveCriticalSection(&This->cs);

    return hr;
319 320
}

321
static HRESULT WINAPI BackgroundCopyJob_AddFile(
322
    IBackgroundCopyJob3 *iface,
323 324 325
    LPCWSTR RemoteUrl,
    LPCWSTR LocalName)
{
326
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
327
    BG_FILE_INFO file;
328

329
    TRACE("(%p)->(%s %s)\n", This, debugstr_w(RemoteUrl), debugstr_w(LocalName));
330

331 332
    file.RemoteName = (LPWSTR)RemoteUrl;
    file.LocalName = (LPWSTR)LocalName;
333
    return IBackgroundCopyJob3_AddFileSet(iface, 1, &file);
334 335
}

336
static HRESULT WINAPI BackgroundCopyJob_EnumFiles(
337
    IBackgroundCopyJob3 *iface,
338
    IEnumBackgroundCopyFiles **enum_files)
339
{
340
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
341 342
    TRACE("(%p)->(%p)\n", This, enum_files);
    return EnumBackgroundCopyFilesConstructor(This, enum_files);
343 344
}

345
static HRESULT WINAPI BackgroundCopyJob_Suspend(
346
    IBackgroundCopyJob3 *iface)
347
{
348
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
349
    FIXME("(%p): stub\n", This);
350 351 352
    return E_NOTIMPL;
}

353
static HRESULT WINAPI BackgroundCopyJob_Resume(
354
    IBackgroundCopyJob3 *iface)
355
{
356
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
357
    HRESULT rv = S_OK;
358

359 360
    TRACE("(%p)\n", This);

361
    EnterCriticalSection(&globalMgr.cs);
362
    if (is_job_done(This))
363
    {
364
        rv = BG_E_INVALID_STATE;
365
    }
366
    else if (This->jobProgress.FilesTransferred == This->jobProgress.FilesTotal)
367
    {
368
        rv = BG_E_EMPTY;
369
    }
370 371 372 373
    else if (This->state != BG_JOB_STATE_CONNECTING
             && This->state != BG_JOB_STATE_TRANSFERRING)
    {
        This->state = BG_JOB_STATE_QUEUED;
374 375
        This->error.context = 0;
        This->error.code = S_OK;
376 377 378 379 380
        if (This->error.file)
        {
            IBackgroundCopyFile2_Release(This->error.file);
            This->error.file = NULL;
        }
381
        SetEvent(globalMgr.jobEvent);
382 383
    }
    LeaveCriticalSection(&globalMgr.cs);
384

385
    return rv;
386 387
}

388
static HRESULT WINAPI BackgroundCopyJob_Cancel(
389
    IBackgroundCopyJob3 *iface)
390
{
391
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
    HRESULT rv = S_OK;

    TRACE("(%p)\n", This);

    EnterCriticalSection(&This->cs);

    if (is_job_done(This))
    {
        rv = BG_E_INVALID_STATE;
    }
    else
    {
        BackgroundCopyFileImpl *file;

        if (This->state == BG_JOB_STATE_CONNECTING || This->state == BG_JOB_STATE_TRANSFERRING)
        {
            This->state = BG_JOB_STATE_CANCELLED;
            SetEvent(This->cancel);

            LeaveCriticalSection(&This->cs);
            WaitForSingleObject(This->done, INFINITE);
            EnterCriticalSection(&This->cs);
        }

        LIST_FOR_EACH_ENTRY(file, &This->files, BackgroundCopyFileImpl, entryFromJob)
        {
            if (file->tempFileName[0] && !DeleteFileW(file->tempFileName))
            {
                WARN("Couldn't delete %s (%u)\n", debugstr_w(file->tempFileName), GetLastError());
                rv = BG_S_UNABLE_TO_DELETE_FILES;
            }
            if (file->info.LocalName && !DeleteFileW(file->info.LocalName))
            {
                WARN("Couldn't delete %s (%u)\n", debugstr_w(file->info.LocalName), GetLastError());
                rv = BG_S_UNABLE_TO_DELETE_FILES;
            }
        }
        This->state = BG_JOB_STATE_CANCELLED;
    }

    LeaveCriticalSection(&This->cs);
    return rv;
434 435
}

436
static HRESULT WINAPI BackgroundCopyJob_Complete(
437
    IBackgroundCopyJob3 *iface)
438
{
439
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
440 441
    HRESULT rv = S_OK;

442 443
    TRACE("(%p)\n", This);

444 445
    EnterCriticalSection(&This->cs);

446
    if (is_job_done(This))
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
    {
        rv = BG_E_INVALID_STATE;
    }
    else
    {
        BackgroundCopyFileImpl *file;
        LIST_FOR_EACH_ENTRY(file, &This->files, BackgroundCopyFileImpl, entryFromJob)
        {
            if (file->fileProgress.Completed)
            {
                if (!MoveFileExW(file->tempFileName, file->info.LocalName,
                                 (MOVEFILE_COPY_ALLOWED
                                  | MOVEFILE_REPLACE_EXISTING
                                  | MOVEFILE_WRITE_THROUGH)))
                {
                    ERR("Couldn't rename file %s -> %s\n",
                        debugstr_w(file->tempFileName),
                        debugstr_w(file->info.LocalName));
                    rv = BG_S_PARTIAL_COMPLETE;
                }
            }
            else
                rv = BG_S_PARTIAL_COMPLETE;
        }
    }

    This->state = BG_JOB_STATE_ACKNOWLEDGED;
    LeaveCriticalSection(&This->cs);

    return rv;
477 478
}

479
static HRESULT WINAPI BackgroundCopyJob_GetId(
480
    IBackgroundCopyJob3 *iface,
481 482
    GUID *pVal)
{
483
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
484
    TRACE("(%p)->(%p)\n", This, pVal);
485
    *pVal = This->jobId;
486
    return S_OK;
487 488
}

489
static HRESULT WINAPI BackgroundCopyJob_GetType(
490
    IBackgroundCopyJob3 *iface,
491 492
    BG_JOB_TYPE *pVal)
{
493
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
494

495 496
    TRACE("(%p)->(%p)\n", This, pVal);

497 498 499 500 501
    if (!pVal)
        return E_INVALIDARG;

    *pVal = This->type;
    return S_OK;
502 503
}

504
static HRESULT WINAPI BackgroundCopyJob_GetProgress(
505
    IBackgroundCopyJob3 *iface,
506 507
    BG_JOB_PROGRESS *pVal)
{
508
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
509

510 511
    TRACE("(%p)->(%p)\n", This, pVal);

512 513 514
    if (!pVal)
        return E_INVALIDARG;

515
    EnterCriticalSection(&This->cs);
516
    *pVal = This->jobProgress;
517
    LeaveCriticalSection(&This->cs);
518 519

    return S_OK;
520 521
}

522
static HRESULT WINAPI BackgroundCopyJob_GetTimes(
523
    IBackgroundCopyJob3 *iface,
524 525
    BG_JOB_TIMES *pVal)
{
526
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
527
    FIXME("(%p)->(%p): stub\n", This, pVal);
528 529 530
    return E_NOTIMPL;
}

531
static HRESULT WINAPI BackgroundCopyJob_GetState(
532
    IBackgroundCopyJob3 *iface,
533 534
    BG_JOB_STATE *pVal)
{
535
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
536

537 538
    TRACE("(%p)->(%p)\n", This, pVal);

539 540 541
    if (!pVal)
        return E_INVALIDARG;

542
    /* Don't think we need a critical section for this */
543 544
    *pVal = This->state;
    return S_OK;
545 546
}

547
static HRESULT WINAPI BackgroundCopyJob_GetError(
548
    IBackgroundCopyJob3 *iface,
549 550
    IBackgroundCopyError **ppError)
{
551 552 553 554 555 556 557
    BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJob3(iface);

    TRACE("(%p)->(%p)\n", job, ppError);

    if (!job->error.context) return BG_E_ERROR_INFORMATION_UNAVAILABLE;

    return create_copy_error(job->error.context, job->error.code, job->error.file, ppError);
558 559
}

560
static HRESULT WINAPI BackgroundCopyJob_GetOwner(
561
    IBackgroundCopyJob3 *iface,
562 563
    LPWSTR *pVal)
{
564
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
565
    FIXME("(%p)->(%p): stub\n", This, pVal);
566 567 568
    return E_NOTIMPL;
}

569
static HRESULT WINAPI BackgroundCopyJob_SetDisplayName(
570
    IBackgroundCopyJob3 *iface,
571 572
    LPCWSTR Val)
{
573
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
574
    FIXME("(%p)->(%s): stub\n", This, debugstr_w(Val));
575 576 577
    return E_NOTIMPL;
}

578
static HRESULT WINAPI BackgroundCopyJob_GetDisplayName(
579
    IBackgroundCopyJob3 *iface,
580 581
    LPWSTR *pVal)
{
582
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
583

584
    TRACE("(%p)->(%p)\n", This, pVal);
585

586
    return return_strval(This->displayName, pVal);
587 588
}

589
static HRESULT WINAPI BackgroundCopyJob_SetDescription(
590
    IBackgroundCopyJob3 *iface,
591 592
    LPCWSTR Val)
{
593
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
594 595 596 597 598 599 600 601
    static const int max_description_len = 1024;
    HRESULT hr = S_OK;
    int len;

    TRACE("(%p)->(%s)\n", This, debugstr_w(Val));

    if (!Val) return E_INVALIDARG;

602
    len = lstrlenW(Val);
603 604 605 606 607 608 609 610 611 612 613 614
    if (len > max_description_len) return BG_E_STRING_TOO_LONG;

    EnterCriticalSection(&This->cs);

    if (is_job_done(This))
    {
        hr = BG_E_INVALID_STATE;
    }
    else
    {
        HeapFree(GetProcessHeap(), 0, This->description);
        if ((This->description = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR))))
615
            lstrcpyW(This->description, Val);
616 617 618 619 620 621 622
        else
            hr = E_OUTOFMEMORY;
    }

    LeaveCriticalSection(&This->cs);

    return hr;
623 624
}

625
static HRESULT WINAPI BackgroundCopyJob_GetDescription(
626
    IBackgroundCopyJob3 *iface,
627 628
    LPWSTR *pVal)
{
629
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
630 631 632 633

    TRACE("(%p)->(%p)\n", This, pVal);

    return return_strval(This->description, pVal);
634 635
}

636
static HRESULT WINAPI BackgroundCopyJob_SetPriority(
637
    IBackgroundCopyJob3 *iface,
638 639
    BG_JOB_PRIORITY Val)
{
640
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
641
    FIXME("(%p)->(%d): stub\n", This, Val);
642
    return S_OK;
643 644
}

645
static HRESULT WINAPI BackgroundCopyJob_GetPriority(
646
    IBackgroundCopyJob3 *iface,
647 648
    BG_JOB_PRIORITY *pVal)
{
649
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
650
    FIXME("(%p)->(%p): stub\n", This, pVal);
651 652 653
    return E_NOTIMPL;
}

654
static HRESULT WINAPI BackgroundCopyJob_SetNotifyFlags(
655
    IBackgroundCopyJob3 *iface,
656 657
    ULONG Val)
{
658
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
659 660 661 662 663 664 665 666
    static const ULONG valid_flags = BG_NOTIFY_JOB_TRANSFERRED |
                                     BG_NOTIFY_JOB_ERROR |
                                     BG_NOTIFY_DISABLE |
                                     BG_NOTIFY_JOB_MODIFICATION |
                                     BG_NOTIFY_FILE_TRANSFERRED;

    TRACE("(%p)->(0x%x)\n", This, Val);

667
    if (is_job_done(This)) return BG_E_INVALID_STATE;
668 669 670
    if (Val & ~valid_flags) return E_NOTIMPL;
    This->notify_flags = Val;
    return S_OK;
671 672
}

673
static HRESULT WINAPI BackgroundCopyJob_GetNotifyFlags(
674
    IBackgroundCopyJob3 *iface,
675 676
    ULONG *pVal)
{
677
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
678 679 680 681 682 683 684 685

    TRACE("(%p)->(%p)\n", This, pVal);

    if (!pVal) return E_INVALIDARG;

    *pVal = This->notify_flags;

    return S_OK;
686 687
}

688
static HRESULT WINAPI BackgroundCopyJob_SetNotifyInterface(
689
    IBackgroundCopyJob3 *iface,
690 691
    IUnknown *Val)
{
692
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
    HRESULT hr = S_OK;

    TRACE("(%p)->(%p)\n", This, Val);

    if (is_job_done(This)) return BG_E_INVALID_STATE;

    if (This->callback)
    {
        IBackgroundCopyCallback2_Release(This->callback);
        This->callback = NULL;
        This->callback2 = FALSE;
    }

    if (Val)
    {
        hr = IUnknown_QueryInterface(Val, &IID_IBackgroundCopyCallback2, (void**)&This->callback);
        if (FAILED(hr))
            hr = IUnknown_QueryInterface(Val, &IID_IBackgroundCopyCallback, (void**)&This->callback);
        else
            This->callback2 = TRUE;
    }

    return hr;
716 717
}

718
static HRESULT WINAPI BackgroundCopyJob_GetNotifyInterface(
719
    IBackgroundCopyJob3 *iface,
720 721
    IUnknown **pVal)
{
722
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
723 724 725 726 727 728 729 730 731 732

    TRACE("(%p)->(%p)\n", This, pVal);

    if (!pVal) return E_INVALIDARG;

    *pVal = (IUnknown*)This->callback;
    if (*pVal)
        IUnknown_AddRef(*pVal);

    return S_OK;
733 734
}

735
static HRESULT WINAPI BackgroundCopyJob_SetMinimumRetryDelay(
736
    IBackgroundCopyJob3 *iface,
737 738
    ULONG Seconds)
{
739 740
    FIXME("%u\n", Seconds);
    return S_OK;
741 742
}

743
static HRESULT WINAPI BackgroundCopyJob_GetMinimumRetryDelay(
744
    IBackgroundCopyJob3 *iface,
745 746
    ULONG *Seconds)
{
747
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
748
    FIXME("(%p)->(%p): stub\n", This, Seconds);
749 750
    *Seconds = 30;
    return S_OK;
751 752
}

753
static HRESULT WINAPI BackgroundCopyJob_SetNoProgressTimeout(
754
    IBackgroundCopyJob3 *iface,
755 756
    ULONG Seconds)
{
757
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
758
    FIXME("(%p)->(%d): stub\n", This, Seconds);
759
    return S_OK;
760 761
}

762
static HRESULT WINAPI BackgroundCopyJob_GetNoProgressTimeout(
763
    IBackgroundCopyJob3 *iface,
764 765
    ULONG *Seconds)
{
766
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
767
    FIXME("(%p)->(%p): stub\n", This, Seconds);
768 769
    *Seconds = 900;
    return S_OK;
770 771
}

772
static HRESULT WINAPI BackgroundCopyJob_GetErrorCount(
773
    IBackgroundCopyJob3 *iface,
774 775
    ULONG *Errors)
{
776
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
777
    FIXME("(%p)->(%p): stub\n", This, Errors);
778 779 780
    return E_NOTIMPL;
}

781
static HRESULT WINAPI BackgroundCopyJob_SetProxySettings(
782
    IBackgroundCopyJob3 *iface,
783 784 785 786
    BG_JOB_PROXY_USAGE ProxyUsage,
    const WCHAR *ProxyList,
    const WCHAR *ProxyBypassList)
{
787
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
788
    FIXME("(%p)->(%d %s %s): stub\n", This, ProxyUsage, debugstr_w(ProxyList), debugstr_w(ProxyBypassList));
789 790 791
    return E_NOTIMPL;
}

792
static HRESULT WINAPI BackgroundCopyJob_GetProxySettings(
793
    IBackgroundCopyJob3 *iface,
794 795 796 797
    BG_JOB_PROXY_USAGE *pProxyUsage,
    LPWSTR *pProxyList,
    LPWSTR *pProxyBypassList)
{
798
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
799
    FIXME("(%p)->(%p %p %p): stub\n", This, pProxyUsage, pProxyList, pProxyBypassList);
800 801 802
    return E_NOTIMPL;
}

803
static HRESULT WINAPI BackgroundCopyJob_TakeOwnership(
804
    IBackgroundCopyJob3 *iface)
805
{
806
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
807
    FIXME("(%p): stub\n", This);
808 809 810
    return E_NOTIMPL;
}

811
static HRESULT WINAPI BackgroundCopyJob_SetNotifyCmdLine(
812
    IBackgroundCopyJob3 *iface,
813 814 815
    LPCWSTR prog,
    LPCWSTR params)
{
816
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
817
    FIXME("(%p)->(%s %s): stub\n", This, debugstr_w(prog), debugstr_w(params));
818 819 820
    return E_NOTIMPL;
}

821
static HRESULT WINAPI BackgroundCopyJob_GetNotifyCmdLine(
822
    IBackgroundCopyJob3 *iface,
823 824
    LPWSTR *prog,
    LPWSTR *params)
825
{
826
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
827
    FIXME("(%p)->(%p %p): stub\n", This, prog, params);
828 829 830
    return E_NOTIMPL;
}

831
static HRESULT WINAPI BackgroundCopyJob_GetReplyProgress(
832
    IBackgroundCopyJob3 *iface,
833 834
    BG_JOB_REPLY_PROGRESS *progress)
{
835
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
836
    FIXME("(%p)->(%p): stub\n", This, progress);
837 838 839
    return E_NOTIMPL;
}

840
static HRESULT WINAPI BackgroundCopyJob_GetReplyData(
841
    IBackgroundCopyJob3 *iface,
842 843 844
    byte **pBuffer,
    UINT64 *pLength)
{
845
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
846
    FIXME("(%p)->(%p %p): stub\n", This, pBuffer, pLength);
847 848 849
    return E_NOTIMPL;
}

850
static HRESULT WINAPI BackgroundCopyJob_SetReplyFileName(
851
    IBackgroundCopyJob3 *iface,
852 853
    LPCWSTR filename)
{
854
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
855
    FIXME("(%p)->(%s): stub\n", This, debugstr_w(filename));
856 857 858
    return E_NOTIMPL;
}

859
static HRESULT WINAPI BackgroundCopyJob_GetReplyFileName(
860
    IBackgroundCopyJob3 *iface,
861 862
    LPWSTR *pFilename)
{
863
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
864
    FIXME("(%p)->(%p): stub\n", This, pFilename);
865 866 867
    return E_NOTIMPL;
}

868 869 870 871 872 873 874 875 876 877 878 879
static int index_from_target(BG_AUTH_TARGET target)
{
    if (!target || target > BG_AUTH_TARGET_PROXY) return -1;
    return target - 1;
}

static int index_from_scheme(BG_AUTH_SCHEME scheme)
{
    if (!scheme || scheme > BG_AUTH_SCHEME_PASSPORT) return -1;
    return scheme - 1;
}

880
static HRESULT WINAPI BackgroundCopyJob_SetCredentials(
881
    IBackgroundCopyJob3 *iface,
882 883
    BG_AUTH_CREDENTIALS *cred)
{
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
    BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJob3(iface);
    BG_AUTH_CREDENTIALS *new_cred;
    int idx_target, idx_scheme;

    TRACE("(%p)->(%p)\n", job, cred);

    if ((idx_target = index_from_target(cred->Target)) < 0) return BG_E_INVALID_AUTH_TARGET;
    if ((idx_scheme = index_from_scheme(cred->Scheme)) < 0) return BG_E_INVALID_AUTH_SCHEME;
    new_cred = &job->http_options.creds[idx_target][idx_scheme];

    EnterCriticalSection(&job->cs);

    new_cred->Target = cred->Target;
    new_cred->Scheme = cred->Scheme;

    if (cred->Credentials.Basic.UserName)
    {
        HeapFree(GetProcessHeap(), 0, new_cred->Credentials.Basic.UserName);
        new_cred->Credentials.Basic.UserName = strdupW(cred->Credentials.Basic.UserName);
    }
    if (cred->Credentials.Basic.Password)
    {
        HeapFree(GetProcessHeap(), 0, new_cred->Credentials.Basic.Password);
        new_cred->Credentials.Basic.Password = strdupW(cred->Credentials.Basic.Password);
    }

    LeaveCriticalSection(&job->cs);
911
    return S_OK;
912 913
}

914
static HRESULT WINAPI BackgroundCopyJob_RemoveCredentials(
915
    IBackgroundCopyJob3 *iface,
916 917 918
    BG_AUTH_TARGET target,
    BG_AUTH_SCHEME scheme)
{
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
    BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJob3(iface);
    BG_AUTH_CREDENTIALS *new_cred;
    int idx_target, idx_scheme;

    TRACE("(%p)->(%u %u)\n", job, target, scheme);

    if ((idx_target = index_from_target(target)) < 0) return BG_E_INVALID_AUTH_TARGET;
    if ((idx_scheme = index_from_scheme(scheme)) < 0) return BG_E_INVALID_AUTH_SCHEME;
    new_cred = &job->http_options.creds[idx_target][idx_scheme];

    EnterCriticalSection(&job->cs);

    new_cred->Target = new_cred->Scheme = 0;
    HeapFree(GetProcessHeap(), 0, new_cred->Credentials.Basic.UserName);
    new_cred->Credentials.Basic.UserName = NULL;
    HeapFree(GetProcessHeap(), 0, new_cred->Credentials.Basic.Password);
    new_cred->Credentials.Basic.Password = NULL;

    LeaveCriticalSection(&job->cs);
938
    return S_OK;
939
}
940

941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
static HRESULT WINAPI BackgroundCopyJob_ReplaceRemotePrefix(
    IBackgroundCopyJob3 *iface,
    LPCWSTR OldPrefix,
    LPCWSTR NewPrefix)
{
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
    FIXME("(%p)->(%s %s): stub\n", This, debugstr_w(OldPrefix), debugstr_w(NewPrefix));
    return S_OK;
}

static HRESULT WINAPI BackgroundCopyJob_AddFileWithRanges(
    IBackgroundCopyJob3 *iface,
    LPCWSTR RemoteUrl,
    LPCWSTR LocalName,
    DWORD RangeCount,
    BG_FILE_RANGE Ranges[])
{
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
    FIXME("(%p)->(%s %s %u %p): stub\n", This, debugstr_w(RemoteUrl), debugstr_w(LocalName), RangeCount, Ranges);
    return S_OK;
}

static HRESULT WINAPI BackgroundCopyJob_SetFileACLFlags(
    IBackgroundCopyJob3 *iface,
    DWORD Flags)
{
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
    FIXME("(%p)->(%x): stub\n", This, Flags);
    return S_OK;
}

static HRESULT WINAPI BackgroundCopyJob_GetFileACLFlags(
    IBackgroundCopyJob3 *iface,
    DWORD *Flags)
{
    BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface);
    FIXME("(%p)->(%p): stub\n", This, Flags);
    return S_OK;
}

static const IBackgroundCopyJob3Vtbl BackgroundCopyJob3Vtbl =
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
{
    BackgroundCopyJob_QueryInterface,
    BackgroundCopyJob_AddRef,
    BackgroundCopyJob_Release,
    BackgroundCopyJob_AddFileSet,
    BackgroundCopyJob_AddFile,
    BackgroundCopyJob_EnumFiles,
    BackgroundCopyJob_Suspend,
    BackgroundCopyJob_Resume,
    BackgroundCopyJob_Cancel,
    BackgroundCopyJob_Complete,
    BackgroundCopyJob_GetId,
    BackgroundCopyJob_GetType,
    BackgroundCopyJob_GetProgress,
    BackgroundCopyJob_GetTimes,
    BackgroundCopyJob_GetState,
    BackgroundCopyJob_GetError,
    BackgroundCopyJob_GetOwner,
    BackgroundCopyJob_SetDisplayName,
    BackgroundCopyJob_GetDisplayName,
    BackgroundCopyJob_SetDescription,
    BackgroundCopyJob_GetDescription,
    BackgroundCopyJob_SetPriority,
    BackgroundCopyJob_GetPriority,
    BackgroundCopyJob_SetNotifyFlags,
    BackgroundCopyJob_GetNotifyFlags,
    BackgroundCopyJob_SetNotifyInterface,
    BackgroundCopyJob_GetNotifyInterface,
    BackgroundCopyJob_SetMinimumRetryDelay,
    BackgroundCopyJob_GetMinimumRetryDelay,
    BackgroundCopyJob_SetNoProgressTimeout,
    BackgroundCopyJob_GetNoProgressTimeout,
    BackgroundCopyJob_GetErrorCount,
    BackgroundCopyJob_SetProxySettings,
    BackgroundCopyJob_GetProxySettings,
    BackgroundCopyJob_TakeOwnership,
    BackgroundCopyJob_SetNotifyCmdLine,
    BackgroundCopyJob_GetNotifyCmdLine,
    BackgroundCopyJob_GetReplyProgress,
    BackgroundCopyJob_GetReplyData,
    BackgroundCopyJob_SetReplyFileName,
    BackgroundCopyJob_GetReplyFileName,
    BackgroundCopyJob_SetCredentials,
1025 1026 1027 1028 1029
    BackgroundCopyJob_RemoveCredentials,
    BackgroundCopyJob_ReplaceRemotePrefix,
    BackgroundCopyJob_AddFileWithRanges,
    BackgroundCopyJob_SetFileACLFlags,
    BackgroundCopyJob_GetFileACLFlags
1030
};
1031

1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
static inline BackgroundCopyJobImpl *impl_from_IBackgroundCopyJobHttpOptions(
    IBackgroundCopyJobHttpOptions *iface)
{
    return CONTAINING_RECORD(iface, BackgroundCopyJobImpl, IBackgroundCopyJobHttpOptions_iface);
}

static HRESULT WINAPI http_options_QueryInterface(
    IBackgroundCopyJobHttpOptions *iface,
    REFIID riid,
    void **ppvObject)
{
    BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJobHttpOptions(iface);
    return IBackgroundCopyJob3_QueryInterface(&job->IBackgroundCopyJob3_iface, riid, ppvObject);
}

static ULONG WINAPI http_options_AddRef(
    IBackgroundCopyJobHttpOptions *iface)
{
    BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJobHttpOptions(iface);
    return IBackgroundCopyJob3_AddRef(&job->IBackgroundCopyJob3_iface);
}

static ULONG WINAPI http_options_Release(
    IBackgroundCopyJobHttpOptions *iface)
{
    BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJobHttpOptions(iface);
    return IBackgroundCopyJob3_Release(&job->IBackgroundCopyJob3_iface);
}

static HRESULT WINAPI http_options_SetClientCertificateByID(
    IBackgroundCopyJobHttpOptions *iface,
    BG_CERT_STORE_LOCATION StoreLocation,
    LPCWSTR StoreName,
    BYTE *pCertHashBlob)
{
    FIXME("\n");
    return E_NOTIMPL;
}

static HRESULT WINAPI http_options_SetClientCertificateByName(
    IBackgroundCopyJobHttpOptions *iface,
    BG_CERT_STORE_LOCATION StoreLocation,
    LPCWSTR StoreName,
    LPCWSTR SubjectName)
{
    FIXME("\n");
    return E_NOTIMPL;
}

static HRESULT WINAPI http_options_RemoveClientCertificate(
    IBackgroundCopyJobHttpOptions *iface)
{
    FIXME("\n");
    return E_NOTIMPL;
}

static HRESULT WINAPI http_options_GetClientCertificate(
    IBackgroundCopyJobHttpOptions *iface,
    BG_CERT_STORE_LOCATION *pStoreLocation,
    LPWSTR *pStoreName,
    BYTE **ppCertHashBlob,
    LPWSTR *pSubjectName)
{
    FIXME("\n");
    return E_NOTIMPL;
}

static HRESULT WINAPI http_options_SetCustomHeaders(
    IBackgroundCopyJobHttpOptions *iface,
    LPCWSTR RequestHeaders)
{
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
    BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJobHttpOptions(iface);

    TRACE("(%p)->(%s)\n", iface, debugstr_w(RequestHeaders));

    EnterCriticalSection(&job->cs);

    if (RequestHeaders)
    {
        WCHAR *headers = strdupW(RequestHeaders);
        if (!headers)
        {
            LeaveCriticalSection(&job->cs);
            return E_OUTOFMEMORY;
        }
        HeapFree(GetProcessHeap(), 0, job->http_options.headers);
        job->http_options.headers = headers;
    }
    else
    {
        HeapFree(GetProcessHeap(), 0, job->http_options.headers);
        job->http_options.headers = NULL;
    }

    LeaveCriticalSection(&job->cs);
    return S_OK;
1128 1129 1130 1131 1132 1133
}

static HRESULT WINAPI http_options_GetCustomHeaders(
    IBackgroundCopyJobHttpOptions *iface,
    LPWSTR *pRequestHeaders)
{
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
    BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJobHttpOptions(iface);

    TRACE("(%p)->(%p)\n", iface, pRequestHeaders);

    EnterCriticalSection(&job->cs);

    if (job->http_options.headers)
    {
        WCHAR *headers = co_strdupW(job->http_options.headers);
        if (!headers)
        {
            LeaveCriticalSection(&job->cs);
            return E_OUTOFMEMORY;
        }
        *pRequestHeaders = headers;
        LeaveCriticalSection(&job->cs);
        return S_OK;
    }

    *pRequestHeaders = NULL;
    LeaveCriticalSection(&job->cs);
    return S_FALSE;
1156 1157 1158 1159 1160 1161
}

static HRESULT WINAPI http_options_SetSecurityFlags(
    IBackgroundCopyJobHttpOptions *iface,
    ULONG Flags)
{
1162 1163 1164 1165 1166 1167
    BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJobHttpOptions(iface);

    TRACE("(%p)->(0x%08x)\n", iface, Flags);

    job->http_options.flags = Flags;
    return S_OK;
1168 1169 1170 1171 1172 1173
}

static HRESULT WINAPI http_options_GetSecurityFlags(
    IBackgroundCopyJobHttpOptions *iface,
    ULONG *pFlags)
{
1174 1175 1176 1177 1178 1179
    BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJobHttpOptions(iface);

    TRACE("(%p)->(%p)\n", iface, pFlags);

    *pFlags = job->http_options.flags;
    return S_OK;
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
}

static const IBackgroundCopyJobHttpOptionsVtbl http_options_vtbl =
{
    http_options_QueryInterface,
    http_options_AddRef,
    http_options_Release,
    http_options_SetClientCertificateByID,
    http_options_SetClientCertificateByName,
    http_options_RemoveClientCertificate,
    http_options_GetClientCertificate,
    http_options_SetCustomHeaders,
    http_options_GetCustomHeaders,
    http_options_SetSecurityFlags,
    http_options_GetSecurityFlags
};

1197
HRESULT BackgroundCopyJobConstructor(LPCWSTR displayName, BG_JOB_TYPE type, GUID *job_id, BackgroundCopyJobImpl **job)
1198 1199 1200 1201
{
    HRESULT hr;
    BackgroundCopyJobImpl *This;

1202
    TRACE("(%s,%d,%p)\n", debugstr_w(displayName), type, job);
1203 1204 1205 1206 1207

    This = HeapAlloc(GetProcessHeap(), 0, sizeof *This);
    if (!This)
        return E_OUTOFMEMORY;

1208
    This->IBackgroundCopyJob3_iface.lpVtbl = &BackgroundCopyJob3Vtbl;
1209
    This->IBackgroundCopyJobHttpOptions_iface.lpVtbl = &http_options_vtbl;
1210
    InitializeCriticalSection(&This->cs);
1211 1212
    This->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": BackgroundCopyJobImpl.cs");

1213 1214 1215
    This->ref = 1;
    This->type = type;

1216
    This->displayName = strdupW(displayName);
1217 1218
    if (!This->displayName)
    {
1219
        This->cs.DebugInfo->Spare[0] = 0;
1220
        DeleteCriticalSection(&This->cs);
1221 1222 1223 1224 1225 1226 1227
        HeapFree(GetProcessHeap(), 0, This);
        return E_OUTOFMEMORY;
    }

    hr = CoCreateGuid(&This->jobId);
    if (FAILED(hr))
    {
1228
        This->cs.DebugInfo->Spare[0] = 0;
1229
        DeleteCriticalSection(&This->cs);
1230 1231 1232 1233
        HeapFree(GetProcessHeap(), 0, This->displayName);
        HeapFree(GetProcessHeap(), 0, This);
        return hr;
    }
1234
    *job_id = This->jobId;
1235

1236
    list_init(&This->files);
1237
    This->jobProgress.BytesTotal = 0;
1238 1239 1240 1241
    This->jobProgress.BytesTransferred = 0;
    This->jobProgress.FilesTotal = 0;
    This->jobProgress.FilesTransferred = 0;

1242
    This->state = BG_JOB_STATE_SUSPENDED;
1243
    This->description = NULL;
1244
    This->notify_flags = BG_NOTIFY_JOB_ERROR | BG_NOTIFY_JOB_TRANSFERRED;
1245 1246
    This->callback = NULL;
    This->callback2 = FALSE;
1247

1248
    This->error.context = 0;
1249
    This->error.code = S_OK;
1250 1251
    This->error.file = NULL;

1252 1253
    memset(&This->http_options, 0, sizeof(This->http_options));

1254 1255 1256 1257
    This->wait   = CreateEventW(NULL, FALSE, FALSE, NULL);
    This->cancel = CreateEventW(NULL, FALSE, FALSE, NULL);
    This->done   = CreateEventW(NULL, FALSE, FALSE, NULL);

1258
    *job = This;
1259 1260 1261

    TRACE("created job %s:%p\n", debugstr_guid(&This->jobId), This);

1262 1263
    return S_OK;
}
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282

void processJob(BackgroundCopyJobImpl *job)
{
    for (;;)
    {
        BackgroundCopyFileImpl *file;
        BOOL done = TRUE;

        EnterCriticalSection(&job->cs);
        LIST_FOR_EACH_ENTRY(file, &job->files, BackgroundCopyFileImpl, entryFromJob)
            if (!file->fileProgress.Completed)
            {
                done = FALSE;
                break;
            }
        LeaveCriticalSection(&job->cs);
        if (done)
        {
            transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSFERRED);
1283 1284
            if (job->callback && (job->notify_flags & BG_NOTIFY_JOB_TRANSFERRED))
                IBackgroundCopyCallback2_JobTransferred(job->callback, (IBackgroundCopyJob*)&job->IBackgroundCopyJob3_iface);
1285 1286 1287 1288 1289 1290 1291
            return;
        }

        if (!processFile(file, job))
          return;
    }
}