xref: /reactos/dll/win32/qmgr/job.c (revision b819608e)
1 /*
2  * Background Copy Job Interface for BITS
3  *
4  * Copyright 2007 Google (Roy Shea)
5  *
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.
10  *
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.
15  *
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
19  */
20 
21 #include "qmgr.h"
22 
23 static inline BOOL is_job_done(const BackgroundCopyJobImpl *job)
24 {
25     return job->state == BG_JOB_STATE_CANCELLED || job->state == BG_JOB_STATE_ACKNOWLEDGED;
26 }
27 
28 static inline BackgroundCopyJobImpl *impl_from_IBackgroundCopyJob2(IBackgroundCopyJob2 *iface)
29 {
30     return CONTAINING_RECORD(iface, BackgroundCopyJobImpl, IBackgroundCopyJob2_iface);
31 }
32 
33 static HRESULT WINAPI BackgroundCopyJob_QueryInterface(
34     IBackgroundCopyJob2 *iface, REFIID riid, void **obj)
35 {
36     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
37 
38     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
39 
40     if (IsEqualGUID(riid, &IID_IUnknown)
41         || IsEqualGUID(riid, &IID_IBackgroundCopyJob)
42         || IsEqualGUID(riid, &IID_IBackgroundCopyJob2))
43     {
44         *obj = iface;
45         IBackgroundCopyJob2_AddRef(iface);
46         return S_OK;
47     }
48 
49     *obj = NULL;
50     return E_NOINTERFACE;
51 }
52 
53 static ULONG WINAPI BackgroundCopyJob_AddRef(IBackgroundCopyJob2 *iface)
54 {
55     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
56     ULONG ref = InterlockedIncrement(&This->ref);
57     TRACE("(%p)->(%d)\n", This, ref);
58     return ref;
59 }
60 
61 static ULONG WINAPI BackgroundCopyJob_Release(IBackgroundCopyJob2 *iface)
62 {
63     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
64     ULONG ref = InterlockedDecrement(&This->ref);
65 
66     TRACE("(%p)->(%d)\n", This, ref);
67 
68     if (ref == 0)
69     {
70         This->cs.DebugInfo->Spare[0] = 0;
71         DeleteCriticalSection(&This->cs);
72         if (This->callback)
73             IBackgroundCopyCallback2_Release(This->callback);
74         HeapFree(GetProcessHeap(), 0, This->displayName);
75         HeapFree(GetProcessHeap(), 0, This->description);
76         HeapFree(GetProcessHeap(), 0, This);
77     }
78 
79     return ref;
80 }
81 
82 /*** IBackgroundCopyJob methods ***/
83 
84 static HRESULT WINAPI BackgroundCopyJob_AddFileSet(
85     IBackgroundCopyJob2 *iface,
86     ULONG cFileCount,
87     BG_FILE_INFO *pFileSet)
88 {
89     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
90     HRESULT hr = S_OK;
91     ULONG i;
92 
93     TRACE("(%p)->(%d %p)\n", This, cFileCount, pFileSet);
94 
95     EnterCriticalSection(&This->cs);
96 
97     for (i = 0; i < cFileCount; ++i)
98     {
99         BackgroundCopyFileImpl *file;
100 
101         /* We should return E_INVALIDARG in these cases. */
102         FIXME("Check for valid filenames and supported protocols\n");
103 
104         hr = BackgroundCopyFileConstructor(This, pFileSet[i].RemoteName, pFileSet[i].LocalName, &file);
105         if (hr != S_OK) break;
106 
107         /* Add a reference to the file to file list */
108         list_add_head(&This->files, &file->entryFromJob);
109         This->jobProgress.BytesTotal = BG_SIZE_UNKNOWN;
110         ++This->jobProgress.FilesTotal;
111     }
112 
113     LeaveCriticalSection(&This->cs);
114 
115     return hr;
116 }
117 
118 static HRESULT WINAPI BackgroundCopyJob_AddFile(
119     IBackgroundCopyJob2 *iface,
120     LPCWSTR RemoteUrl,
121     LPCWSTR LocalName)
122 {
123     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
124     BG_FILE_INFO file;
125 
126     TRACE("(%p)->(%s %s)\n", This, debugstr_w(RemoteUrl), debugstr_w(LocalName));
127 
128     file.RemoteName = (LPWSTR)RemoteUrl;
129     file.LocalName = (LPWSTR)LocalName;
130     return IBackgroundCopyJob2_AddFileSet(iface, 1, &file);
131 }
132 
133 static HRESULT WINAPI BackgroundCopyJob_EnumFiles(
134     IBackgroundCopyJob2 *iface,
135     IEnumBackgroundCopyFiles **enum_files)
136 {
137     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
138     TRACE("(%p)->(%p)\n", This, enum_files);
139     return EnumBackgroundCopyFilesConstructor(This, enum_files);
140 }
141 
142 static HRESULT WINAPI BackgroundCopyJob_Suspend(
143     IBackgroundCopyJob2 *iface)
144 {
145     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
146     FIXME("(%p): stub\n", This);
147     return E_NOTIMPL;
148 }
149 
150 static HRESULT WINAPI BackgroundCopyJob_Resume(
151     IBackgroundCopyJob2 *iface)
152 {
153     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
154     HRESULT rv = S_OK;
155 
156     TRACE("(%p)\n", This);
157 
158     EnterCriticalSection(&globalMgr.cs);
159     if (is_job_done(This))
160     {
161         rv = BG_E_INVALID_STATE;
162     }
163     else if (This->jobProgress.FilesTransferred == This->jobProgress.FilesTotal)
164     {
165         rv = BG_E_EMPTY;
166     }
167     else if (This->state != BG_JOB_STATE_CONNECTING
168              && This->state != BG_JOB_STATE_TRANSFERRING)
169     {
170         This->state = BG_JOB_STATE_QUEUED;
171         SetEvent(globalMgr.jobEvent);
172     }
173     LeaveCriticalSection(&globalMgr.cs);
174 
175     return rv;
176 }
177 
178 static HRESULT WINAPI BackgroundCopyJob_Cancel(
179     IBackgroundCopyJob2 *iface)
180 {
181     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
182     FIXME("(%p): stub\n", This);
183     return E_NOTIMPL;
184 }
185 
186 static HRESULT WINAPI BackgroundCopyJob_Complete(
187     IBackgroundCopyJob2 *iface)
188 {
189     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
190     HRESULT rv = S_OK;
191 
192     TRACE("(%p)\n", This);
193 
194     EnterCriticalSection(&This->cs);
195 
196     if (is_job_done(This))
197     {
198         rv = BG_E_INVALID_STATE;
199     }
200     else
201     {
202         BackgroundCopyFileImpl *file;
203         LIST_FOR_EACH_ENTRY(file, &This->files, BackgroundCopyFileImpl, entryFromJob)
204         {
205             if (file->fileProgress.Completed)
206             {
207                 if (!MoveFileExW(file->tempFileName, file->info.LocalName,
208                                  (MOVEFILE_COPY_ALLOWED
209                                   | MOVEFILE_REPLACE_EXISTING
210                                   | MOVEFILE_WRITE_THROUGH)))
211                 {
212                     ERR("Couldn't rename file %s -> %s\n",
213                         debugstr_w(file->tempFileName),
214                         debugstr_w(file->info.LocalName));
215                     rv = BG_S_PARTIAL_COMPLETE;
216                 }
217             }
218             else
219                 rv = BG_S_PARTIAL_COMPLETE;
220         }
221     }
222 
223     This->state = BG_JOB_STATE_ACKNOWLEDGED;
224     LeaveCriticalSection(&This->cs);
225 
226     return rv;
227 }
228 
229 static HRESULT WINAPI BackgroundCopyJob_GetId(
230     IBackgroundCopyJob2 *iface,
231     GUID *pVal)
232 {
233     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
234     TRACE("(%p)->(%p)\n", This, pVal);
235     *pVal = This->jobId;
236     return S_OK;
237 }
238 
239 static HRESULT WINAPI BackgroundCopyJob_GetType(
240     IBackgroundCopyJob2 *iface,
241     BG_JOB_TYPE *pVal)
242 {
243     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
244 
245     TRACE("(%p)->(%p)\n", This, pVal);
246 
247     if (!pVal)
248         return E_INVALIDARG;
249 
250     *pVal = This->type;
251     return S_OK;
252 }
253 
254 static HRESULT WINAPI BackgroundCopyJob_GetProgress(
255     IBackgroundCopyJob2 *iface,
256     BG_JOB_PROGRESS *pVal)
257 {
258     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
259 
260     TRACE("(%p)->(%p)\n", This, pVal);
261 
262     if (!pVal)
263         return E_INVALIDARG;
264 
265     EnterCriticalSection(&This->cs);
266     pVal->BytesTotal = This->jobProgress.BytesTotal;
267     pVal->BytesTransferred = This->jobProgress.BytesTransferred;
268     pVal->FilesTotal = This->jobProgress.FilesTotal;
269     pVal->FilesTransferred = This->jobProgress.FilesTransferred;
270     LeaveCriticalSection(&This->cs);
271 
272     return S_OK;
273 }
274 
275 static HRESULT WINAPI BackgroundCopyJob_GetTimes(
276     IBackgroundCopyJob2 *iface,
277     BG_JOB_TIMES *pVal)
278 {
279     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
280     FIXME("(%p)->(%p): stub\n", This, pVal);
281     return E_NOTIMPL;
282 }
283 
284 static HRESULT WINAPI BackgroundCopyJob_GetState(
285     IBackgroundCopyJob2 *iface,
286     BG_JOB_STATE *pVal)
287 {
288     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
289 
290     TRACE("(%p)->(%p)\n", This, pVal);
291 
292     if (!pVal)
293         return E_INVALIDARG;
294 
295     /* Don't think we need a critical section for this */
296     *pVal = This->state;
297     return S_OK;
298 }
299 
300 static HRESULT WINAPI BackgroundCopyJob_GetError(
301     IBackgroundCopyJob2 *iface,
302     IBackgroundCopyError **ppError)
303 {
304     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
305     FIXME("(%p)->(%p): stub\n", This, ppError);
306     return E_NOTIMPL;
307 }
308 
309 static HRESULT WINAPI BackgroundCopyJob_GetOwner(
310     IBackgroundCopyJob2 *iface,
311     LPWSTR *pVal)
312 {
313     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
314     FIXME("(%p)->(%p): stub\n", This, pVal);
315     return E_NOTIMPL;
316 }
317 
318 static HRESULT WINAPI BackgroundCopyJob_SetDisplayName(
319     IBackgroundCopyJob2 *iface,
320     LPCWSTR Val)
321 {
322     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
323     FIXME("(%p)->(%s): stub\n", This, debugstr_w(Val));
324     return E_NOTIMPL;
325 }
326 
327 static HRESULT WINAPI BackgroundCopyJob_GetDisplayName(
328     IBackgroundCopyJob2 *iface,
329     LPWSTR *pVal)
330 {
331     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
332 
333     TRACE("(%p)->(%p)\n", This, pVal);
334 
335     return return_strval(This->displayName, pVal);
336 }
337 
338 static HRESULT WINAPI BackgroundCopyJob_SetDescription(
339     IBackgroundCopyJob2 *iface,
340     LPCWSTR Val)
341 {
342     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
343     static const int max_description_len = 1024;
344     HRESULT hr = S_OK;
345     int len;
346 
347     TRACE("(%p)->(%s)\n", This, debugstr_w(Val));
348 
349     if (!Val) return E_INVALIDARG;
350 
351     len = strlenW(Val);
352     if (len > max_description_len) return BG_E_STRING_TOO_LONG;
353 
354     EnterCriticalSection(&This->cs);
355 
356     if (is_job_done(This))
357     {
358         hr = BG_E_INVALID_STATE;
359     }
360     else
361     {
362         HeapFree(GetProcessHeap(), 0, This->description);
363         if ((This->description = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR))))
364             strcpyW(This->description, Val);
365         else
366             hr = E_OUTOFMEMORY;
367     }
368 
369     LeaveCriticalSection(&This->cs);
370 
371     return hr;
372 }
373 
374 static HRESULT WINAPI BackgroundCopyJob_GetDescription(
375     IBackgroundCopyJob2 *iface,
376     LPWSTR *pVal)
377 {
378     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
379 
380     TRACE("(%p)->(%p)\n", This, pVal);
381 
382     return return_strval(This->description, pVal);
383 }
384 
385 static HRESULT WINAPI BackgroundCopyJob_SetPriority(
386     IBackgroundCopyJob2 *iface,
387     BG_JOB_PRIORITY Val)
388 {
389     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
390     FIXME("(%p)->(%d): stub\n", This, Val);
391     return S_OK;
392 }
393 
394 static HRESULT WINAPI BackgroundCopyJob_GetPriority(
395     IBackgroundCopyJob2 *iface,
396     BG_JOB_PRIORITY *pVal)
397 {
398     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
399     FIXME("(%p)->(%p): stub\n", This, pVal);
400     return E_NOTIMPL;
401 }
402 
403 static HRESULT WINAPI BackgroundCopyJob_SetNotifyFlags(
404     IBackgroundCopyJob2 *iface,
405     ULONG Val)
406 {
407     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
408     static const ULONG valid_flags = BG_NOTIFY_JOB_TRANSFERRED |
409                                      BG_NOTIFY_JOB_ERROR |
410                                      BG_NOTIFY_DISABLE |
411                                      BG_NOTIFY_JOB_MODIFICATION |
412                                      BG_NOTIFY_FILE_TRANSFERRED;
413 
414     TRACE("(%p)->(0x%x)\n", This, Val);
415 
416     if (is_job_done(This)) return BG_E_INVALID_STATE;
417     if (Val & ~valid_flags) return E_NOTIMPL;
418     This->notify_flags = Val;
419     return S_OK;
420 }
421 
422 static HRESULT WINAPI BackgroundCopyJob_GetNotifyFlags(
423     IBackgroundCopyJob2 *iface,
424     ULONG *pVal)
425 {
426     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
427 
428     TRACE("(%p)->(%p)\n", This, pVal);
429 
430     if (!pVal) return E_INVALIDARG;
431 
432     *pVal = This->notify_flags;
433 
434     return S_OK;
435 }
436 
437 static HRESULT WINAPI BackgroundCopyJob_SetNotifyInterface(
438     IBackgroundCopyJob2 *iface,
439     IUnknown *Val)
440 {
441     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
442     HRESULT hr = S_OK;
443 
444     TRACE("(%p)->(%p)\n", This, Val);
445 
446     if (is_job_done(This)) return BG_E_INVALID_STATE;
447 
448     if (This->callback)
449     {
450         IBackgroundCopyCallback2_Release(This->callback);
451         This->callback = NULL;
452         This->callback2 = FALSE;
453     }
454 
455     if (Val)
456     {
457         hr = IUnknown_QueryInterface(Val, &IID_IBackgroundCopyCallback2, (void**)&This->callback);
458         if (FAILED(hr))
459             hr = IUnknown_QueryInterface(Val, &IID_IBackgroundCopyCallback, (void**)&This->callback);
460         else
461             This->callback2 = TRUE;
462     }
463 
464     return hr;
465 }
466 
467 static HRESULT WINAPI BackgroundCopyJob_GetNotifyInterface(
468     IBackgroundCopyJob2 *iface,
469     IUnknown **pVal)
470 {
471     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
472 
473     TRACE("(%p)->(%p)\n", This, pVal);
474 
475     if (!pVal) return E_INVALIDARG;
476 
477     *pVal = (IUnknown*)This->callback;
478     if (*pVal)
479         IUnknown_AddRef(*pVal);
480 
481     return S_OK;
482 }
483 
484 static HRESULT WINAPI BackgroundCopyJob_SetMinimumRetryDelay(
485     IBackgroundCopyJob2 *iface,
486     ULONG Seconds)
487 {
488     FIXME("%u\n", Seconds);
489     return S_OK;
490 }
491 
492 static HRESULT WINAPI BackgroundCopyJob_GetMinimumRetryDelay(
493     IBackgroundCopyJob2 *iface,
494     ULONG *Seconds)
495 {
496     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
497     FIXME("(%p)->(%p): stub\n", This, Seconds);
498     *Seconds = 30;
499     return S_OK;
500 }
501 
502 static HRESULT WINAPI BackgroundCopyJob_SetNoProgressTimeout(
503     IBackgroundCopyJob2 *iface,
504     ULONG Seconds)
505 {
506     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
507     FIXME("(%p)->(%d): stub\n", This, Seconds);
508     return S_OK;
509 }
510 
511 static HRESULT WINAPI BackgroundCopyJob_GetNoProgressTimeout(
512     IBackgroundCopyJob2 *iface,
513     ULONG *Seconds)
514 {
515     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
516     FIXME("(%p)->(%p): stub\n", This, Seconds);
517     *Seconds = 900;
518     return S_OK;
519 }
520 
521 static HRESULT WINAPI BackgroundCopyJob_GetErrorCount(
522     IBackgroundCopyJob2 *iface,
523     ULONG *Errors)
524 {
525     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
526     FIXME("(%p)->(%p): stub\n", This, Errors);
527     return E_NOTIMPL;
528 }
529 
530 static HRESULT WINAPI BackgroundCopyJob_SetProxySettings(
531     IBackgroundCopyJob2 *iface,
532     BG_JOB_PROXY_USAGE ProxyUsage,
533     const WCHAR *ProxyList,
534     const WCHAR *ProxyBypassList)
535 {
536     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
537     FIXME("(%p)->(%d %s %s): stub\n", This, ProxyUsage, debugstr_w(ProxyList), debugstr_w(ProxyBypassList));
538     return E_NOTIMPL;
539 }
540 
541 static HRESULT WINAPI BackgroundCopyJob_GetProxySettings(
542     IBackgroundCopyJob2 *iface,
543     BG_JOB_PROXY_USAGE *pProxyUsage,
544     LPWSTR *pProxyList,
545     LPWSTR *pProxyBypassList)
546 {
547     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
548     FIXME("(%p)->(%p %p %p): stub\n", This, pProxyUsage, pProxyList, pProxyBypassList);
549     return E_NOTIMPL;
550 }
551 
552 static HRESULT WINAPI BackgroundCopyJob_TakeOwnership(
553     IBackgroundCopyJob2 *iface)
554 {
555     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
556     FIXME("(%p): stub\n", This);
557     return E_NOTIMPL;
558 }
559 
560 static HRESULT WINAPI BackgroundCopyJob_SetNotifyCmdLine(
561     IBackgroundCopyJob2 *iface,
562     LPCWSTR prog,
563     LPCWSTR params)
564 {
565     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
566     FIXME("(%p)->(%s %s): stub\n", This, debugstr_w(prog), debugstr_w(params));
567     return E_NOTIMPL;
568 }
569 
570 static HRESULT WINAPI BackgroundCopyJob_GetNotifyCmdLine(
571     IBackgroundCopyJob2 *iface,
572     LPWSTR *prog,
573     LPWSTR *params)
574 {
575     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
576     FIXME("(%p)->(%p %p): stub\n", This, prog, params);
577     return E_NOTIMPL;
578 }
579 
580 static HRESULT WINAPI BackgroundCopyJob_GetReplyProgress(
581     IBackgroundCopyJob2 *iface,
582     BG_JOB_REPLY_PROGRESS *progress)
583 {
584     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
585     FIXME("(%p)->(%p): stub\n", This, progress);
586     return E_NOTIMPL;
587 }
588 
589 static HRESULT WINAPI BackgroundCopyJob_GetReplyData(
590     IBackgroundCopyJob2 *iface,
591     byte **pBuffer,
592     UINT64 *pLength)
593 {
594     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
595     FIXME("(%p)->(%p %p): stub\n", This, pBuffer, pLength);
596     return E_NOTIMPL;
597 }
598 
599 static HRESULT WINAPI BackgroundCopyJob_SetReplyFileName(
600     IBackgroundCopyJob2 *iface,
601     LPCWSTR filename)
602 {
603     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
604     FIXME("(%p)->(%s): stub\n", This, debugstr_w(filename));
605     return E_NOTIMPL;
606 }
607 
608 static HRESULT WINAPI BackgroundCopyJob_GetReplyFileName(
609     IBackgroundCopyJob2 *iface,
610     LPWSTR *pFilename)
611 {
612     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
613     FIXME("(%p)->(%p): stub\n", This, pFilename);
614     return E_NOTIMPL;
615 }
616 
617 static HRESULT WINAPI BackgroundCopyJob_SetCredentials(
618     IBackgroundCopyJob2 *iface,
619     BG_AUTH_CREDENTIALS *cred)
620 {
621     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
622     FIXME("(%p)->(%p): stub\n", This, cred);
623     return S_OK;
624 }
625 
626 static HRESULT WINAPI BackgroundCopyJob_RemoveCredentials(
627     IBackgroundCopyJob2 *iface,
628     BG_AUTH_TARGET target,
629     BG_AUTH_SCHEME scheme)
630 {
631     BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob2(iface);
632     FIXME("(%p)->(%d %d): stub\n", This, target, scheme);
633     return S_OK;
634 }
635 
636 static const IBackgroundCopyJob2Vtbl BackgroundCopyJobVtbl =
637 {
638     BackgroundCopyJob_QueryInterface,
639     BackgroundCopyJob_AddRef,
640     BackgroundCopyJob_Release,
641     BackgroundCopyJob_AddFileSet,
642     BackgroundCopyJob_AddFile,
643     BackgroundCopyJob_EnumFiles,
644     BackgroundCopyJob_Suspend,
645     BackgroundCopyJob_Resume,
646     BackgroundCopyJob_Cancel,
647     BackgroundCopyJob_Complete,
648     BackgroundCopyJob_GetId,
649     BackgroundCopyJob_GetType,
650     BackgroundCopyJob_GetProgress,
651     BackgroundCopyJob_GetTimes,
652     BackgroundCopyJob_GetState,
653     BackgroundCopyJob_GetError,
654     BackgroundCopyJob_GetOwner,
655     BackgroundCopyJob_SetDisplayName,
656     BackgroundCopyJob_GetDisplayName,
657     BackgroundCopyJob_SetDescription,
658     BackgroundCopyJob_GetDescription,
659     BackgroundCopyJob_SetPriority,
660     BackgroundCopyJob_GetPriority,
661     BackgroundCopyJob_SetNotifyFlags,
662     BackgroundCopyJob_GetNotifyFlags,
663     BackgroundCopyJob_SetNotifyInterface,
664     BackgroundCopyJob_GetNotifyInterface,
665     BackgroundCopyJob_SetMinimumRetryDelay,
666     BackgroundCopyJob_GetMinimumRetryDelay,
667     BackgroundCopyJob_SetNoProgressTimeout,
668     BackgroundCopyJob_GetNoProgressTimeout,
669     BackgroundCopyJob_GetErrorCount,
670     BackgroundCopyJob_SetProxySettings,
671     BackgroundCopyJob_GetProxySettings,
672     BackgroundCopyJob_TakeOwnership,
673     BackgroundCopyJob_SetNotifyCmdLine,
674     BackgroundCopyJob_GetNotifyCmdLine,
675     BackgroundCopyJob_GetReplyProgress,
676     BackgroundCopyJob_GetReplyData,
677     BackgroundCopyJob_SetReplyFileName,
678     BackgroundCopyJob_GetReplyFileName,
679     BackgroundCopyJob_SetCredentials,
680     BackgroundCopyJob_RemoveCredentials
681 };
682 
683 HRESULT BackgroundCopyJobConstructor(LPCWSTR displayName, BG_JOB_TYPE type, GUID *job_id, BackgroundCopyJobImpl **job)
684 {
685     HRESULT hr;
686     BackgroundCopyJobImpl *This;
687     int n;
688 
689     TRACE("(%s,%d,%p)\n", debugstr_w(displayName), type, job);
690 
691     This = HeapAlloc(GetProcessHeap(), 0, sizeof *This);
692     if (!This)
693         return E_OUTOFMEMORY;
694 
695     This->IBackgroundCopyJob2_iface.lpVtbl = &BackgroundCopyJobVtbl;
696     InitializeCriticalSection(&This->cs);
697     This->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": BackgroundCopyJobImpl.cs");
698 
699     This->ref = 1;
700     This->type = type;
701 
702     n = (strlenW(displayName) + 1) *  sizeof *displayName;
703     This->displayName = HeapAlloc(GetProcessHeap(), 0, n);
704     if (!This->displayName)
705     {
706         This->cs.DebugInfo->Spare[0] = 0;
707         DeleteCriticalSection(&This->cs);
708         HeapFree(GetProcessHeap(), 0, This);
709         return E_OUTOFMEMORY;
710     }
711     memcpy(This->displayName, displayName, n);
712 
713     hr = CoCreateGuid(&This->jobId);
714     if (FAILED(hr))
715     {
716         This->cs.DebugInfo->Spare[0] = 0;
717         DeleteCriticalSection(&This->cs);
718         HeapFree(GetProcessHeap(), 0, This->displayName);
719         HeapFree(GetProcessHeap(), 0, This);
720         return hr;
721     }
722     *job_id = This->jobId;
723 
724     list_init(&This->files);
725     This->jobProgress.BytesTotal = 0;
726     This->jobProgress.BytesTransferred = 0;
727     This->jobProgress.FilesTotal = 0;
728     This->jobProgress.FilesTransferred = 0;
729 
730     This->state = BG_JOB_STATE_SUSPENDED;
731     This->description = NULL;
732     This->notify_flags = BG_NOTIFY_JOB_ERROR | BG_NOTIFY_JOB_TRANSFERRED;
733     This->callback = NULL;
734     This->callback2 = FALSE;
735 
736     *job = This;
737 
738     TRACE("created job %s:%p\n", debugstr_guid(&This->jobId), This);
739 
740     return S_OK;
741 }
742 
743 void processJob(BackgroundCopyJobImpl *job)
744 {
745     for (;;)
746     {
747         BackgroundCopyFileImpl *file;
748         BOOL done = TRUE;
749 
750         EnterCriticalSection(&job->cs);
751         LIST_FOR_EACH_ENTRY(file, &job->files, BackgroundCopyFileImpl, entryFromJob)
752             if (!file->fileProgress.Completed)
753             {
754                 done = FALSE;
755                 break;
756             }
757         LeaveCriticalSection(&job->cs);
758         if (done)
759         {
760             transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSFERRED);
761             return;
762         }
763 
764         if (!processFile(file, job))
765           return;
766     }
767 }
768