/// <summary> /// Registers the task in the storage. /// </summary> /// <param name="task">The DownloadTask instance.</param> public void RegisterTask(DownloadTask task) { lock (registry) { if (!TasksDictionary.ContainsKey(task.DownloadItem.Enclosure.Url)) { TasksDictionary.Add(task.DownloadItem.Enclosure.Url, task); AddTask(task); } else //change state to downloading because we are reattempting { TasksDictionary[task.DownloadItem.Enclosure.Url].State = DownloadTaskState.Pending; } } SaveTask(task); }
/// <summary> /// Load the tasks stored in a specified path. /// </summary> /// <param name="taskFilePath">The base path for the registry storage.</param> /// <returns>An DownloadTask instance.</returns> private DownloadTask LoadTask(string taskFilePath) { DownloadTask task = null; var formatter = new BinaryFormatter(); using (var stream = new FileStream(taskFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { try { task = (DownloadTask)formatter.Deserialize(stream); task.Downloader = (task.SupportsBITS ? (IDownloader)bitsDownloader : (IDownloader)httpDownloader); lock (registry) { //TODO: Once we have a UI for managing enclosures we'll need to //always load all tasks. if (!registry.ContainsKey(task.DownloadItem.Enclosure.Url)) { // Revert to pending if it was in a downloading state if (task.State == DownloadTaskState.Downloading) { task.State = DownloadTaskState.Pending; } registry.Add(task.DownloadItem.Enclosure.Url, task); AddTask(task); } else { string fileName = Path.Combine(RootDir.FullName, task.TaskId + ".task"); // it might be currrently opened: if (String.Equals(taskFilePath, fileName, StringComparison.OrdinalIgnoreCase)) { stream.Close(); } FileHelper.DestroyFile(fileName); } } } catch (Exception e) { Logger.Error("Error in DownloadRegistryManager.LoadTask():", e); } } return(task); }
/// <summary> /// Removes the task from the storage. /// </summary> /// <param name="task">The DownloadTask.</param> public void UnRegisterTask(DownloadTask task) { lock (registry) { if (TasksDictionary.ContainsKey(task.DownloadItem.Enclosure.Url)) { TasksDictionary.Remove(task.DownloadItem.Enclosure.Url); RemoveTask(task); var disposable = task.Downloader as IDisposable; if (disposable != null) { disposable.Dispose(); } } } string fileName = Path.Combine(RootDir.FullName, task.TaskId + ".task"); FileHelper.DestroyFile(fileName); }
/// <summary> /// Synchronous download method implementation. /// </summary> /// <param name="task">The DownloadTask to process.</param> /// <param name="maxWaitTime">The maximum wait time.</param> public void Download(DownloadTask task, TimeSpan maxWaitTime) { currentTask = task; // If we resume way too often, just return if (CheckForResumeAndProceed(currentTask)) { return; } WebResponse response = SyncWebRequest.GetResponse(HttpMethod.Get, task.DownloadItem.Enclosure.Url, task.DownloadItem.Credentials, FeedSource.UserAgentString(String.Empty), task.DownloadItem.Proxy, DateTime.MinValue, null /* eTag */, Convert.ToInt32(maxWaitTime.TotalSeconds), null /* cookie */, null /* body */, null /* newsGatorAPIToken */); OnRequestComplete(new Uri(task.DownloadItem.Enclosure.Url), response.GetResponseStream(), response, null, null, DateTime.MinValue, RequestResult.OK, 0); }
/// <summary> /// Cancels an asynhronous download operation. /// </summary> /// <param name="task">The <see cref="DownloadTask"/> for the operation.</param> /// <returns>Indicates whether the operation was canceled.</returns> public bool CancelDownload(DownloadTask task) { IBackgroundCopyManager copyManager = null; IBackgroundCopyJob pJob = null; if (task.JobId != null) { try { Guid jobID = task.JobId.Value; copyManager = (IBackgroundCopyManager) new BackgroundCopyManager(); copyManager.GetJob(ref jobID, out pJob); if (pJob != null) { pJob.Cancel(); } } catch (COMException) { /* we may come up empty when trying to get the job */ } finally { if (copyManager != null) { Marshal.ReleaseComObject(copyManager); } if (pJob != null) { Marshal.ReleaseComObject(pJob); } } } return(true); }
/// <summary> /// Stores a task in the registry storage. /// </summary> /// <param name="task">The DownloadTask instance.</param> private void SaveTask(DownloadTask task) { string filename = Path.Combine(RootDir.FullName, String.Format(CultureInfo.InvariantCulture, "{0}.task", task.TaskId)); try { using (Stream stream = FileHelper.OpenForWrite(filename)) { var formatter = new BinaryFormatter(); formatter.Serialize(stream, task); } } catch (IOException ex) { Logger.Error(ex); } catch (Exception ex) { File.Delete(filename); Logger.Error(ex); throw; } }
/// <summary> /// Method called by BITS when the job is completed. /// </summary> /// <param name="task">The Updater task instance.</param> /// <param name="pJob">The BITS job reeference.</param> private void OnJobTransferred(DownloadTask task, IBackgroundCopyJob pJob) { pJob.Complete(); OnDownloadCompleted(new TaskEventArgs(task)); }
/// <summary> /// Method responsible for checking the authentication type and setting the /// appropriate credentials. If the NTLM authentication is used then /// if the username and password are not provided then we use null values. For /// all other authentication schemes we need a username and password. /// </summary> /// <param name="backgroundCopyJob">BackgroundJob on which we need to set the credentials.</param> /// <param name="task">DownloadTask. Provides the infos about download credentials</param> private static void VerifyAndSetBackgroundCopyJobCredentials(IBackgroundCopyJob backgroundCopyJob, DownloadTask task) { try { var copyJob = (IBackgroundCopyJob2)backgroundCopyJob; ICredentials creds = task.DownloadItem.Credentials; var uri = new Uri(task.DownloadItem.File.Source); if (creds != null) { //Specify HTTP Authentication (Basic) credentials var credentials = new BG_AUTH_CREDENTIALS(); credentials.Credentials.Basic.UserName = creds.GetCredential(uri, "Basic").UserName; credentials.Credentials.Basic.Password = creds.GetCredential(uri, "Basic").Password; credentials.Scheme = BG_AUTH_SCHEME.BG_AUTH_SCHEME_BASIC; credentials.Target = BG_AUTH_TARGET.BG_AUTH_TARGET_SERVER; copyJob.SetCredentials(ref credentials); //Specify HTTP Authentication (Digest) credentials credentials = new BG_AUTH_CREDENTIALS(); credentials.Credentials.Basic.UserName = creds.GetCredential(uri, "Digest").UserName; credentials.Credentials.Basic.Password = creds.GetCredential(uri, "Digest").Password; credentials.Scheme = BG_AUTH_SCHEME.BG_AUTH_SCHEME_DIGEST; credentials.Target = BG_AUTH_TARGET.BG_AUTH_TARGET_SERVER; copyJob.SetCredentials(ref credentials); //Specify NTLM credentials credentials = new BG_AUTH_CREDENTIALS(); credentials.Credentials.Basic.UserName = string.IsNullOrEmpty(creds.GetCredential(uri, "NTLM").Domain) ? creds.GetCredential(uri, "NTLM").UserName : creds.GetCredential(uri, "NTLM").Domain + "\\" + creds.GetCredential(uri, "NTLM").UserName; credentials.Credentials.Basic.Password = creds.GetCredential(uri, "NTLM").Password; credentials.Scheme = BG_AUTH_SCHEME.BG_AUTH_SCHEME_NTLM; credentials.Target = BG_AUTH_TARGET.BG_AUTH_TARGET_SERVER; copyJob.SetCredentials(ref credentials); } //if(creds != null) } catch (Exception e) { Logger.Error("Error in VerifyAndSetBackgroundCopyJobCredentials():", e); } }
/// <summary> /// Method responsible for checking the proxy and proxy authentication type and setting the /// appropriate credentials. If the NTLM authentication is used then /// if the username and password are not provided then we use null values. For /// all other authentication schemes we need a username and password. /// </summary> /// <param name="backgroundCopyJob">BackgroundJob on which we need to set the credentials.</param> /// <param name="task">DownloadTask. Provides the infos about download credentials</param> private static void VerifyAndSetBackgroundCopyJobProxy(IBackgroundCopyJob backgroundCopyJob, DownloadTask task) { // Specify the proxy URL // see also http://msdn.microsoft.com/library/en-us/bits/bits/ibackgroundcopyjob_setproxysettings.asp try { IWebProxy proxy = task.DownloadItem.Proxy; var sourceUri = new Uri(task.DownloadItem.File.Source); Uri proxyUri = proxy.GetProxy(sourceUri); //trim trailing '/' because it causes BITS to throw an exception string proxyUriStr = proxyUri.ToString().TrimEnd('/'); if (!proxy.IsBypassed(proxyUri)) { backgroundCopyJob.SetProxySettings(BG_JOB_PROXY_USAGE.BG_JOB_PROXY_USAGE_OVERRIDE, proxyUriStr, null); } //specify proxy credentials if (proxy.Credentials != null) { ICredentials creds = proxy.Credentials; var copyJob = (IBackgroundCopyJob2)backgroundCopyJob; var credentials = new BG_AUTH_CREDENTIALS(); credentials.Credentials.Basic.UserName = string.IsNullOrEmpty(creds.GetCredential(sourceUri, "NTLM").Domain) ? creds.GetCredential(sourceUri, "NTLM").UserName : creds.GetCredential(sourceUri, "NTLM").Domain + "\\" + creds.GetCredential(sourceUri, "NTLM").UserName; credentials.Credentials.Basic.Password = creds.GetCredential(sourceUri, "NTLM").Password; credentials.Scheme = BG_AUTH_SCHEME.BG_AUTH_SCHEME_NTLM; credentials.Target = BG_AUTH_TARGET.BG_AUTH_TARGET_PROXY; copyJob.SetCredentials(ref credentials); } } catch (Exception e) { Logger.Error("Error in VerifyAndSetBackgroundCopyJobProxy():", e); } }
/// <summary> /// Waits for the download to complete, for the synchronous usage of the downloader. /// </summary> /// <param name="backgroundCopyJob">The job instance reference.</param> /// <param name="maxWaitTime">The maximum wait time.</param> /// <param name="task">The updater task instance.</param> private void WaitForDownload(DownloadTask task, IBackgroundCopyJob backgroundCopyJob, TimeSpan maxWaitTime) { bool isCompleted = false; bool isSuccessful = false; try { Guid jobID; backgroundCopyJob.GetId(out jobID); // set endtime to current tickcount + allowable # milliseconds to wait for job double endTime = Environment.TickCount + maxWaitTime.TotalMilliseconds; while (!isCompleted) { BG_JOB_STATE state; backgroundCopyJob.GetState(out state); switch (state) { case BG_JOB_STATE.BG_JOB_STATE_SUSPENDED: { OnDownloadStarted(new TaskEventArgs(task)); backgroundCopyJob.Resume(); break; } case BG_JOB_STATE.BG_JOB_STATE_ERROR: { // use utility to: // a) get error info // b) report it // c) cancel and remove copy job OnJobError(task, backgroundCopyJob, null, null); // complete loop, but DON'T say it's successful isCompleted = true; break; } case BG_JOB_STATE.BG_JOB_STATE_TRANSIENT_ERROR: { // NOTE: during debugging + test, transient errors resulted in complete job failure about 90% // of the time. Therefore we treat transients just like full errors, and CANCEL the job // use utility to manage error etc. OnJobError(task, backgroundCopyJob, null, null); // stop the loop, set completed to true but not successful isCompleted = true; break; } case BG_JOB_STATE.BG_JOB_STATE_TRANSFERRING: { OnJobModification(task, backgroundCopyJob); break; } case BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED: { OnJobTransferred(task, backgroundCopyJob); isCompleted = true; isSuccessful = true; break; } default: break; } if (isCompleted) { break; } if (endTime < Environment.TickCount) { var ex = new DownloaderException("Download attempt timed out"); OnJobError(task, backgroundCopyJob, null, ex); break; } // Avoid 100% CPU utilisation with too tight a loop, let download happen. Thread.Sleep(TimeToWaitDuringSynchronousDownload); } if (!isSuccessful) { // create message + error, package it, publish var ex = new DownloaderException(String.Format("Download attempt for {0} failed", task.DownloadItem.ItemId)); OnJobError(task, backgroundCopyJob, null, ex); } } catch (ThreadInterruptedException tie) { // if interrupted, clean up job OnJobError(task, backgroundCopyJob, null, tie); } }
/// <summary> /// Asynchronous download method implementation. /// </summary> /// <param name="task">The DownloadTask to process.</param> public void BeginDownload(DownloadTask task) { IBackgroundCopyManager backGroundCopyManager = null; IBackgroundCopyJob backgroundCopyJob = null; Guid jobID = Guid.Empty; try { // create the manager backGroundCopyManager = (IBackgroundCopyManager) new BackgroundCopyManager(); // If the job is already finished, just return if (CheckForResumeAndProceed(backGroundCopyManager, task, out backgroundCopyJob)) { return; } if (backgroundCopyJob != null) { // if CheckForResumeAndProceed connected to an ongoing BITS job // wire up our notify interface to forward events to the client backgroundCopyJob.SetNotifyInterface(this); backgroundCopyJob.SetNotifyFlags((uint)( BG_JOB_NOTIFICATION_TYPE.BG_NOTIFY_JOB_ERROR | BG_JOB_NOTIFICATION_TYPE.BG_NOTIFY_JOB_MODIFICATION | BG_JOB_NOTIFICATION_TYPE.BG_NOTIFY_JOB_TRANSFERRED) ); } else { // use utility function to create the job. CreateCopyJob( backGroundCopyManager, out backgroundCopyJob, ref jobID, task.DownloadItem.OwnerItemId, task.DownloadItem.Enclosure.Description, task); // Save the jobId in the task task.JobId = jobID; // Prepare the job to download the manifest files PrepareJob(backgroundCopyJob, task); // Set the notify interface to get BITS events backgroundCopyJob.SetNotifyInterface(this); backgroundCopyJob.SetNotifyFlags((uint)( BG_JOB_NOTIFICATION_TYPE.BG_NOTIFY_JOB_ERROR | BG_JOB_NOTIFICATION_TYPE.BG_NOTIFY_JOB_MODIFICATION | BG_JOB_NOTIFICATION_TYPE.BG_NOTIFY_JOB_TRANSFERRED) ); // Fire our download start event OnDownloadStarted(new TaskEventArgs(task)); // Initiate the BITS Job backgroundCopyJob.Resume(); } } catch (Exception e) { // if exception, clean up job OnJobError(task, backgroundCopyJob, null, e); } finally { if (null != backgroundCopyJob) { Marshal.ReleaseComObject(backgroundCopyJob); } if (null != backGroundCopyManager) { Marshal.ReleaseComObject(backGroundCopyManager); } } }
private void RemoveTask(DownloadTask task) { _context.Post(o => _tasks.Remove(task), null); }
private void AddTask(DownloadTask task) { _context.Post(o => _tasks.Add(task), null); }
/// <summary> /// Updates the information of an existing stored task. /// </summary> /// <param name="task">The DownloadTask instance.</param> public void UpdateTask(DownloadTask task) { SaveTask(task); }
/// <summary> /// Constructor for a TaskEventArgs. /// </summary> /// <param name="task">The DownloadTask for initialization.</param> public TaskEventArgs(DownloadTask task) { currentDownloadTask = task; }
/// <summary> /// Constructor for the DownloadTaskErrorEventArgs. /// </summary> /// <param name="task">The <see cref="DownloadTask"/> instance.</param> /// <param name="exception">The exception information.</param> public DownloadTaskErrorEventArgs(DownloadTask task, Exception exception) : base(task) { this.exception = exception; }