/// <summary>
		/// Instructs the AutoUpdateDownloader to query for the latest version available 
		/// </summary>
		/// <param name="progressViewer">The progress viewer by which progress should be displayed</param>
		/// <param name="options">The options that affect this downloader</param>
		/// <param name="productToUpdate">The product descriptor for the product that should be updated</param>
		/// <param name="updateAvailable">The download descriptor that describes the download that could potentially occur</param>
		/// <returns></returns>
		public virtual bool QueryLatestVersion(
			IProgressViewer progressViewer,
			AutoUpdateOptions options,
			AutoUpdateProductDescriptor productToUpdate, 
			out AutoUpdateDownloadDescriptor updateAvailable)
		{			
			updateAvailable = null;

			return false;							
		}
        /// <summary>
        /// Instructs the AutoUpdateDownloader to query for the latest version available
        /// </summary>
        /// <param name="progressViewer">The progress viewer by which progress should be displayed</param>
        /// <param name="options">The options that affect this downloader</param>
        /// <param name="productToUpdate">The product descriptor for the product that should be updated</param>
        /// <param name="updateAvailable">The download descriptor that describes the download that could potentially occur</param>
        /// <returns></returns>
        public virtual bool QueryLatestVersion(
            IProgressViewer progressViewer,
            AutoUpdateOptions options,
            AutoUpdateProductDescriptor productToUpdate,
            out AutoUpdateDownloadDescriptor updateAvailable)
        {
            updateAvailable = null;

            return(false);
        }
		/// <summary>
		/// Instructs the AutoUpdateDownloader to query for the latest version available 
		/// </summary>
		/// <param name="progressViewer">The progress viewer by which progress should be displayed</param>
		/// <param name="options">The options that affect this downloader</param>
		/// <param name="productToUpdate">The product descriptor for the product that should be updated</param>
		/// <param name="updateAvailable">The download descriptor that describes the download that could potentially occur</param>
		/// <returns></returns>
		public override bool QueryLatestVersion(IProgressViewer progressViewer, AutoUpdateOptions options, AutoUpdateProductDescriptor productToUpdate, out AutoUpdateDownloadDescriptor updateAvailable)
		{
			updateAvailable = null;
			
			try
			{
				// create a manual web service proxy based on the url specified in the options
				Debug.WriteLine(string.Format("Creating a web service proxy to the following url.\n\tThe web service url is '{0}'.", options.WebServiceUrl), MY_TRACE_CATEGORY);			
				AutoUpdateWebServiceProxy service = new AutoUpdateWebServiceProxy(options.WebServiceUrl);

				// use the web service to query for updates
				Debug.WriteLine(string.Format("Querying the web service for the latest version of '{0}'.\n\tThe current product's version is '{1}'.\n\tThe current product's id is '{2}'.\n\tThe web service url is '{3}'.", productToUpdate.Name, productToUpdate.Version.ToString(), productToUpdate.Id, options.WebServiceUrl), MY_TRACE_CATEGORY);			
				XmlNode node = service.QueryLatestVersionEx(productToUpdate.Name, productToUpdate.Version.ToString(), productToUpdate.Id);
				
				// if the service returned no results, then there is no update availabe
				if (node == null)
				{
					// bail out 
					Debug.WriteLine(string.Format("No updates are available from the web service at '{0}' for this product.", options.WebServiceUrl), MY_TRACE_CATEGORY);
					return false;
				}

				// otherwise create a reader and try and read the xml from the xml node returned from the web service
				XmlAutoUpdateManifestReader reader = new XmlAutoUpdateManifestReader(node);

				// using the reader we can recreate the manifeset from the xml
				AutoUpdateManifest manifest = reader.Read();	

				/*
				* now create a download descriptor that says, yes we have found an update.
				* we are capable of downloading it, according to these options.
				* the autoupdate manager will decide which downloader to use to download the update
				* */
				updateAvailable = new AutoUpdateDownloadDescriptor(manifest, this, options);
				
				// just to let everyone know that there is a version available
				Debug.WriteLine(string.Format("Version '{0}' of '{1}' is available for download.\n\tThe download url is '{2}'.\n\tThe size of the download is {3}.", updateAvailable.Manifest.Product.Version.ToString(), updateAvailable.Manifest.Product.Name, updateAvailable.Manifest.UrlOfUpdate, this.FormatFileLengthForDisplay(updateAvailable.Manifest.SizeOfUpdate)), MY_TRACE_CATEGORY);

				// we've successfully queried for the latest version of the product to update
				return true;
			}
			catch(ThreadAbortException)
			{

			}
			catch(Exception ex)
			{
				throw new Exception(ex.Message, ex);
			}
			return false;
		}
示例#4
0
 /// <summary>
 /// Bubble sorts the elements in the descriptor array using their product verion (The newest version will be at element 0).
 /// </summary>
 /// <param name="updates"></param>
 /// <returns></returns>
 public static AutoUpdateDownloadDescriptor[] Sort(AutoUpdateDownloadDescriptor[] updates)
 {
     // front to back - 1
     for (int i = 0; i < updates.Length - 1; i++)
     {
         // front + 1 to back
         for (int j = i + 1; j < updates.Length; j++)
         {
             if (updates[i].Manifest.Product.Version < updates[j].Manifest.Product.Version)
             {
                 // swap i with j, where i=1 and j=2
                 AutoUpdateDownloadDescriptor update = updates[j];
                 updates[j] = updates[i];
                 updates[i] = update;
             }
         }
     }
     return(updates);
 }
		/// <summary>
		/// 
		/// </summary>
		/// <param name="manager"></param>
		/// <param name="progressViewer"></param>
		/// <param name="downloadDescriptor"></param>
		/// <param name="cancel"></param>
		public AutoUpdateManagerWithDownloadDescriptorCancelEventArgs(AutoUpdateManager manager, IProgressViewer progressViewer, AutoUpdateDownloadDescriptor downloadDescriptor, bool cancel) : base(manager, progressViewer, downloadDescriptor)
		{
			_cancel = cancel;
		}
		/// <summary>
		/// Creates a copy of the update file in the alternate path
		/// </summary>
		/// <param name="progressViewer"></param>
		/// <param name="updateFilename"></param>
		/// <returns></returns>
		public virtual void CreateCopyOfUpdateInAlternatePath(IProgressViewer progressViewer, AutoUpdateDownloadDescriptor downloadDescriptor)
		{				 													
			try
			{
				// if the alternate path is not set, then we don't have to do this
				if (downloadDescriptor.Options.AlternatePath == null ||	downloadDescriptor.Options.AlternatePath == string.Empty)
					return;

				// take the alternate path
				string altPath = Path.Combine(downloadDescriptor.Options.AlternatePath, downloadDescriptor.Manifest.Product.Name);

				// see if the folder exists
				bool folderExists = Directory.Exists(altPath);				
				Debug.WriteLine(string.Format("Confirming the product's 'Alternate Download Path' folder.\n\tThe folder '{0}' {1}.", altPath, (folderExists ? "already exists" : "does not exist")), MY_TRACE_CATEGORY);
				if (!folderExists)
				{
					Debug.WriteLine(string.Format("Creating the product's 'Alternate Download Path' folder at '{0}'.", altPath), MY_TRACE_CATEGORY);
					Directory.CreateDirectory(altPath);
				}
	
				// format the backup filename from the alternate path, and the url where the update
				string dstPath = Path.Combine(altPath, Path.GetFileName(downloadDescriptor.Manifest.UrlOfUpdate));

				// see if the file already exists
				bool fileExists = File.Exists(dstPath);
				Debug.WriteLine(string.Format("Preparing to copy the update to the product's 'Alternate Download Path' folder.\n\tThe file '{0}' {1}.", dstPath, (fileExists ? "already exists" : "does not exist")), MY_TRACE_CATEGORY);
				
				// copy the .update we downloaded to the backup location in the alternate path directory
				ProgressViewer.SetExtendedDescription(progressViewer, "Creating a backup copy of the update file.");
				Debug.WriteLine(string.Format("Copying the update to '{0}'.", dstPath), MY_TRACE_CATEGORY);				
				File.Copy(downloadDescriptor.DownloadedPath, dstPath, false);								
			}
			catch(ThreadAbortException)
			{

			}
			catch(Exception ex)
			{
				Debug.WriteLine(ex);
			}
		}		
 /// <summary>
 /// Instructs the AutoUpdateDownloader to cleanup after an install
 /// </summary>
 /// <param name="progressViewer">The progress viewer by which progress should be displayed</param>
 /// <param name="downloadDescriptor">The download descriptor that describes the download that occurred and was installed</param>
 public virtual bool FinalizeInstallation(
     IProgressViewer progressViewer,
     AutoUpdateDownloadDescriptor downloadDescriptor)
 {
     return(true);
 }
		/// <summary>
		/// Adjusts the url where the .update can be found, using the product descriptor and the alternate download path 
		/// </summary>
		/// <remarks>
		/// By default the url probably points to some web url. When the update is copied to the alternate download path
		/// we want the download to occur from that path, so the url in the manifest must be manipulated to point the download
		/// to the update in the alternate path.
		/// </remarks>
		/// <param name="options"></param>
		/// <param name="downloadDescriptor"></param>
		public virtual void AdjustUrlOfUpdateInManifest(AutoUpdateOptions options, AutoUpdateDownloadDescriptor downloadDescriptor)
		{
			/*
			 * we're supposed to be adjusting the the url where the update can be downloaded
			 * to point to the alternate path
			 * where ideally the manifest file will reside alongside the update
			 * 
			 * the problem is that the manifest file contains a url to where the update was originally downloaded
			 * most likely some web server somewhere, which after the manifest is copied to some network file share
			 * we won't want to use, more likely in an effort to keep the downloaders from going over the web
			 * we'll try and tweak the url to just be the unc path of the download as it resides next to the 
			 * manifest in the alternate download path
			 * 
			 * */

			try
			{

				// if there's no alternate path, don't worry about adjusting anything
				// as nothing is going to be copied anyways
				if (options.AlternatePath == null || options.AlternatePath == string.Empty)
					// if there
					return;

				// redirect the url of the update to the alternate location
				downloadDescriptor.Manifest.UrlOfUpdate = string.Format("{0}\\{1}\\{1}-{2}.Update", options.AlternatePath, downloadDescriptor.Manifest.Product.Name, downloadDescriptor.Manifest.Product.Version.ToString());			
			}
			catch(ThreadAbortException)
			{

			}
		}
		/// <summary>
		/// Creates a copy of the manifest file in the alternate path
		/// </summary>
		/// <param name="downloadDescriptor"></param>
		public virtual void CreateCopyOfManifestInAlternatePath(IProgressViewer progressViewer, AutoUpdateDownloadDescriptor downloadDescriptor)
		{
			try
			{
				// if the alternate path is not set, then we don't have to do this
				if (downloadDescriptor.Options.AlternatePath == null ||	downloadDescriptor.Options.AlternatePath == string.Empty)
					return;

				// format a path to the product's alternate path
				string altPath = Path.Combine(downloadDescriptor.Options.AlternatePath, downloadDescriptor.Manifest.Product.Name);

				// if the path doesn't exist, just bail, we don't create alternate paths
				bool folderExists = Directory.Exists(altPath);
				Debug.WriteLine(string.Format("Confirming the product's 'Alternate Download Path' folder.\n\tThe folder '{0}' {1}.", altPath, (folderExists ? "already exists" : "does not exist")), MY_TRACE_CATEGORY);
				if (!folderExists)
				{
					Debug.WriteLine(string.Format("Creating the product's 'Alternate Download Path' folder at '{0}'.", altPath), MY_TRACE_CATEGORY);
					Directory.CreateDirectory(altPath);
				}
				
				// format a path to the file in the alternate path
				string dstPath = Path.Combine(altPath, string.Format("{0}-{1}.manifest", downloadDescriptor.Manifest.Product.Name, downloadDescriptor.Manifest.Product.Version.ToString()));

				bool fileExists = File.Exists(dstPath);
				Debug.WriteLine(string.Format("Preparing to copy the manifest to the product's 'Alternate Download Path' folder.\n\tThe file '{0}' {1}.", dstPath, (fileExists ? "already exists" : "does not exist")), MY_TRACE_CATEGORY);
							
				// otherwise write the manifest to the alternate path
				ProgressViewer.SetExtendedDescription(progressViewer, "Creating a backup copy of the manifest file.");
				Debug.WriteLine(string.Format("Copying the manifest to '{0}'.", dstPath), MY_TRACE_CATEGORY);
				XmlAutoUpdateManifestWriter.Write(downloadDescriptor.Manifest, dstPath, System.Text.Encoding.UTF8);				
			}
			catch(ThreadAbortException)
			{

			}
			catch(Exception ex)
			{
				Debug.WriteLine(ex);
			}
		}
		/// <summary>
		/// Decrypts the .update file to a .zip file using the name of the .update file as a base in the same directory as the .update file
		/// </summary>
		/// <param name="progressViewer"></param>
		/// <param name="updateFilename"></param>
		/// <param name="zipFilename"></param>
		/// <returns></returns>
		protected virtual bool DecryptToZip(IProgressViewer progressViewer, AutoUpdateDownloadDescriptor downloadDescriptor, out string zipFilename)
		{
			zipFilename = null;
			try
			{						
				ProgressViewer.SetExtendedDescription(progressViewer, "Parsing update...");

				// save the path to the update
				string updateFilename = downloadDescriptor.DownloadedPath;

				// format the .zip file
				zipFilename = Path.Combine(Path.GetDirectoryName(updateFilename), Path.GetFileName(updateFilename).Replace(Path.GetExtension(updateFilename), null) + ".zip");
				
				// it is rijndael encrypted
				RijndaelEncryptionEngine ee = new RijndaelEncryptionEngine();
				
				// decrypt the .update file to a .zip file
				Debug.WriteLine(string.Format("Converting the update into an archive.\n\tThe archive will exist at '{0}'.", zipFilename), MY_TRACE_CATEGORY);
				ee.Decrypt(updateFilename, zipFilename);				
				
				return true;
			}
			catch(ThreadAbortException)
			{

			}
			return false;
		}
		/// <summary>
		/// Unzips the .zip file to a directory of it's own using the name of the .zip file as a base
		/// </summary>
		/// <param name="progressViewer"></param>
		/// <param name="zipFilename"></param>
		/// <returns></returns>
		protected virtual bool Unzip(IProgressViewer progressViewer, AutoUpdateDownloadDescriptor downloadDescriptor, string zipFilename)
		{
			ZipInputStream zipStream = null;
			FileStream fs = null;
			string newVersionPath = null;

			try
			{												
				// calculate the rootname 
				string rootName = Path.GetFileName(zipFilename).Replace(Path.GetExtension(zipFilename), null);

				// the text to remove includes the name of the product and a dash
				string prependedTextToRemove = string.Format("{0}-", downloadDescriptor.Manifest.Product.Name);
				
				// remove that and we're left with a version
				rootName = rootName.Replace(prependedTextToRemove, null);
				
				// extract here
				string rootPath = Path.GetDirectoryName(zipFilename);
				
				// the destination where the files will be unzipped for the new version
				newVersionPath = Path.Combine(rootPath, rootName);
				
				// make sure the directory where the new version will be extracted exists
				bool folderExists = Directory.Exists(newVersionPath);
				Debug.WriteLine(string.Format("Confirming the new version's path.\n\tThe folder '{0}' {1}.", newVersionPath, (folderExists ? "already exists" : "does not exist")), MY_TRACE_CATEGORY);
				if (!folderExists)
				{
					Debug.WriteLine(string.Format("Creating the new verion's folder '{0}'.", newVersionPath), MY_TRACE_CATEGORY);
					Directory.CreateDirectory(newVersionPath);
				}

				// try and find the postbuildevent.bat file
				string postBuildFile = Path.Combine(rootPath, "PostBuildEvent.bat");

				// open the zip file using a zip input stream
				Debug.WriteLine(string.Format("Opening the archive '{0}' for reading.", zipFilename), MY_TRACE_CATEGORY);
				zipStream = new ZipInputStream(File.OpenRead(zipFilename));								
				
				// ready each zip entry
				ZipEntry zipEntry;
				while((zipEntry = zipStream.GetNextEntry()) != null)
				{					
					try
					{
						string zipEntryFilename = Path.Combine(rootPath, zipEntry.Name);
						zipEntryFilename = zipEntryFilename.Replace("/", "\\");
	                    
						// trace the entry to where it is going
						Debug.WriteLine(string.Format("Extracting '{0}' to '{1}'.", zipEntry.Name, zipEntryFilename), MY_TRACE_CATEGORY);

						if (zipEntry.IsDirectory)
						{
							Debug.WriteLine(string.Format("Creating the folder '{0}'.", zipEntryFilename), MY_TRACE_CATEGORY);
							Directory.CreateDirectory(zipEntryFilename);
						}
						else
						{
							ProgressViewer.SetExtendedDescription(progressViewer, "Extracting " + zipEntry.Name + "...");
							
							// make sure the directory exists
							FileInfo fi = new FileInfo(zipEntryFilename);
							DirectoryInfo di = fi.Directory;
							if (!Directory.Exists(di.FullName))
								Directory.CreateDirectory(di.FullName);
							fi = null;
							di = null;

							// create each file
							fs = File.Create(zipEntryFilename);
							int size = 2048;
							byte[] data = new byte[size];
							while(true)
							{
								size = zipStream.Read(data, 0, data.Length);
								if (size > 0)
									fs.Write(data, 0, size);
								else
									break;
							}
							// close the extracted file
							fs.Close();
							fs = null;
						}
					}					
					catch(Exception ex)
					{
						Debug.WriteLine(ex);
					}
				}

				// close the zip stream
				zipStream.Close();
				
				RunFileIfFound(progressViewer, postBuildFile);

				return true;
			}
			catch(ThreadAbortException)
			{
				try
				{					
					// make sure the streams are closed
					if (zipStream != null)
						zipStream.Close();

					if (fs != null)
						fs.Close();

					// delete the root folder of the new install upon cancellation
					Directory.Delete(newVersionPath, true);
				}
				catch(Exception)
				{

				}
			}
			catch(Exception ex)
			{
				Debug.WriteLine(ex);
			}
			finally
			{
				// make sure the streams are closed
				if (zipStream != null)
					zipStream.Close();

				if (fs != null)
					fs.Close();
			}

			return false;
		}
		/// <summary>
		/// Initializes a new instance of the 
		/// </summary>
		/// <param name="cancel"></param>
		/// <param name="updateDescriptor"></param>
		public AutoUpdateDownloadDescriptorCancelEventArgs(bool cancel, AutoUpdateDownloadDescriptor downloadDescriptor) : base(downloadDescriptor)
		{
			_cancel = cancel;
		}
		/// <summary>
		/// Installs the .update file specified by decrypting it and then unziping the contents to a new versioned directory (ie. 1.0.0.1)
		/// </summary>
		/// <param name="progressViewer"></param>
		/// <param name="updateFilename"></param>
		/// <returns></returns>
		protected virtual bool InstallUpdate(IProgressViewer progressViewer, AutoUpdateDownloadDescriptor downloadDescriptor)
		{
			string zipFilename = null;
			try
			{
				Debug.WriteLine(string.Format("Preparing to install update from '{0}'.", downloadDescriptor.DownloadedPath), MY_TRACE_CATEGORY);

				// decrypt the .update file first				
				if (!this.DecryptToZip(progressViewer, downloadDescriptor, out zipFilename))
					return false;

				// then unzip the .zip file
				if (this.Unzip(progressViewer, downloadDescriptor, zipFilename))
				{	// delete the zip file
					File.Delete(zipFilename);
					return true;
				}
			}
			catch(ThreadAbortException)
			{
				try
				{
					// delete the zip file
					File.Delete(zipFilename);
				}
				catch(Exception)
				{

				}
			}

			return false;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="manager"></param>
		/// <param name="progressViewer"></param>
		/// <param name="downloadDescriptor"></param>
		public AutoUpdateManagerWithDownloadDescriptorEventArgs(AutoUpdateManager manager, IProgressViewer progressViewer, AutoUpdateDownloadDescriptor downloadDescriptor) : base(manager, progressViewer)
		{
			_downloadDescriptor = downloadDescriptor;
		}
        /// <summary>
        /// Instructs the AutoUpdateDownloader to download the update specified by the update descriptor
        /// </summary>
        /// <param name="progressViewer">The progress viewer by which progress should be displayed</param>
        /// <param name="downloadDescriptor">The download descriptor that describes the download that should occur</param>
        /// <returns></returns>
        public virtual bool Download(
            IProgressViewer progressViewer,
            AutoUpdateDownloadDescriptor downloadDescriptor)
        {
            FileStream localStream     = null;
            Stream     remoteStream    = null;
            string     myTraceCategory = string.Format("'{0}'", this.GetType().Name);

            try
            {
                // set the progress to zero for the start
                this.SetDownloadProgress(progressViewer, 0, downloadDescriptor.Manifest.SizeOfUpdate);

                // format the downloaded path where the .update file will be when the download is finished
                downloadDescriptor.DownloadedPath = Path.Combine(downloadDescriptor.Options.DownloadPath, Path.GetFileName(downloadDescriptor.Manifest.UrlOfUpdate));

                Debug.WriteLine(string.Format("Preparing to download update.\n\tThe update will be downloaded from '{0}'.\n\tThe update will be downloaded to '{1}'.", downloadDescriptor.Manifest.UrlOfUpdate, downloadDescriptor.DownloadedPath), myTraceCategory);

                // if the url where this update is supposed to be located is not set, just quit as there isn't anything else we can do
                if (downloadDescriptor.Manifest.UrlOfUpdate == null || downloadDescriptor.Manifest.UrlOfUpdate == string.Empty)
                {
                    return(false);
                }

                // create a new web client to download the file
                WebClient wc = new WebClient();

                // open a remote stream to the download
                Debug.WriteLine(string.Format("Preparing to download update.\n\tOpening stream to the remote url '{0}'.", downloadDescriptor.Manifest.UrlOfUpdate), myTraceCategory);
                remoteStream = wc.OpenRead(downloadDescriptor.Manifest.UrlOfUpdate);

                // open a local file stream where the update will be downloaded
                Debug.WriteLine(string.Format("Preparing to download update.\n\tOpening stream to the local url '{0}'.", downloadDescriptor.DownloadedPath), myTraceCategory);
                localStream = new FileStream(downloadDescriptor.DownloadedPath, FileMode.Create, FileAccess.Write, FileShare.None);

                // if successfull we'll receive the data in segments
                if (remoteStream != null)
                {
                    long bytesDownloaded = 0;
                    while (true)
                    {
                        // figure out how many bytes we have to download
                        long bytesToReceive = downloadDescriptor.Manifest.SizeOfUpdate - bytesDownloaded;

                        // correct it if it's more than the segment size
                        if (bytesToReceive > _segmentSize)
                        {
                            bytesToReceive = (long)_segmentSize;
                        }

                        byte[] segment = new byte[bytesToReceive];

                        // read a segment off the socket
                        int bytesReceived = remoteStream.Read(segment, 0, (int)bytesToReceive);

                        // bail if nothing read
                        if (bytesReceived == 0)
                        {
                            break;
                        }

                        // if we received anything
                        if (bytesReceived > 0)
                        {
                            // write it to the update file
                            localStream.Write(segment, 0, bytesReceived);
                            // update the position
                            bytesDownloaded += bytesReceived;
                        }

                        // update the progress viewer
                        this.SetDownloadProgress(progressViewer, bytesDownloaded, downloadDescriptor.Manifest.SizeOfUpdate);
                    }
                }

                Debug.WriteLine(string.Format("The update was successfully downloaded to '{0}'.", downloadDescriptor.DownloadedPath), myTraceCategory);

                return(true);
            }
            catch (ThreadAbortException)
            {
            }
            catch (Exception ex)
            {
                try
                {
                    if (localStream != null)
                    {
                        localStream.Close();
                    }

                    if (remoteStream != null)
                    {
                        remoteStream.Close();
                    }
                }
                catch (Exception)
                {
                }

                try
                {
                    // something broke, make sure we delete the .update file
                    File.Delete(downloadDescriptor.DownloadedPath);
                }
                catch (Exception)
                {
                }

                throw ex;
            }
            finally
            {
                /*
                 * make sure the streams are closed
                 * */

                try
                {
                    if (localStream != null)
                    {
                        localStream.Close();
                    }
                    if (remoteStream != null)
                    {
                        remoteStream.Close();
                    }
                }
                catch {}

                try
                {
                }
                catch {}
            }

            // if it's made it this far something went wrong
            return(false);
        }
示例#16
0
 /// <summary>
 /// Initializes a new instance of the AutoUpdateDownloadDescriptorEventArgs class
 /// </summary>
 /// <param name="updateDescriptor"></param>
 public AutoUpdateDownloadDescriptorEventArgs(AutoUpdateDownloadDescriptor downloadDescriptor) : base()
 {
     _descriptor = downloadDescriptor;
 }
		/// <summary>
		/// Instructs the AutoUpdateDownloader to download the update specified by the update descriptor
		/// </summary>
		/// <param name="progressViewer">The progress viewer by which progress should be displayed</param>
		/// <param name="downloadDescriptor">The download descriptor that describes the download that should occur</param>
		/// <returns></returns>
		public virtual bool Download(
			IProgressViewer progressViewer,
			AutoUpdateDownloadDescriptor downloadDescriptor)
		{
			FileStream localStream = null;
			Stream remoteStream = null;
			string myTraceCategory = string.Format("'{0}'", this.GetType().Name);

			try
			{
				// set the progress to zero for the start
				this.SetDownloadProgress(progressViewer, 0, downloadDescriptor.Manifest.SizeOfUpdate);

				// format the downloaded path where the .update file will be when the download is finished				
				downloadDescriptor.DownloadedPath = Path.Combine(downloadDescriptor.Options.DownloadPath, Path.GetFileName(downloadDescriptor.Manifest.UrlOfUpdate));

				Debug.WriteLine(string.Format("Preparing to download update.\n\tThe update will be downloaded from '{0}'.\n\tThe update will be downloaded to '{1}'.", downloadDescriptor.Manifest.UrlOfUpdate, downloadDescriptor.DownloadedPath), myTraceCategory);
				
				// if the url where this update is supposed to be located is not set, just quit as there isn't anything else we can do
				if (downloadDescriptor.Manifest.UrlOfUpdate == null || downloadDescriptor.Manifest.UrlOfUpdate == string.Empty)
					return false;

				// create a new web client to download the file
				WebClient wc = new WebClient();

				// open a remote stream to the download
				Debug.WriteLine(string.Format("Preparing to download update.\n\tOpening stream to the remote url '{0}'.", downloadDescriptor.Manifest.UrlOfUpdate), myTraceCategory);
				remoteStream = wc.OpenRead(downloadDescriptor.Manifest.UrlOfUpdate);

				// open a local file stream where the update will be downloaded
				Debug.WriteLine(string.Format("Preparing to download update.\n\tOpening stream to the local url '{0}'.", downloadDescriptor.DownloadedPath), myTraceCategory);
				localStream = new FileStream(downloadDescriptor.DownloadedPath, FileMode.Create, FileAccess.Write, FileShare.None);

				// if successfull we'll receive the data in segments
				if (remoteStream != null)
				{
					long bytesDownloaded = 0;
					while (true)
					{
						// figure out how many bytes we have to download
						long bytesToReceive = downloadDescriptor.Manifest.SizeOfUpdate - bytesDownloaded;

						// correct it if it's more than the segment size
						if (bytesToReceive > _segmentSize)
							bytesToReceive = (long)_segmentSize;

						byte[] segment = new byte[bytesToReceive];

						// read a segment off the socket
						int bytesReceived = remoteStream.Read(segment, 0, (int)bytesToReceive);

						// bail if nothing read
						if (bytesReceived == 0)
							break;

						// if we received anything
						if (bytesReceived > 0)
						{
							// write it to the update file
							localStream.Write(segment, 0, bytesReceived);
							// update the position
							bytesDownloaded += bytesReceived;
						}

						// update the progress viewer
						this.SetDownloadProgress(progressViewer, bytesDownloaded, downloadDescriptor.Manifest.SizeOfUpdate);
					}
				}

				Debug.WriteLine(string.Format("The update was successfully downloaded to '{0}'.", downloadDescriptor.DownloadedPath), myTraceCategory);

				return true;
			}
			catch(ThreadAbortException)
			{

			}
			catch(Exception ex)
			{							
				try 
				{ 
					if (localStream != null) 
						localStream.Close(); 

					if (remoteStream != null) 
						remoteStream.Close();
				} 
				catch(Exception)
				{
				
				}

				try
				{
					// something broke, make sure we delete the .update file
					File.Delete(downloadDescriptor.DownloadedPath);
				}
				catch(Exception)
				{

				}

				throw ex;
			}
			finally
			{
				/* 
				 * make sure the streams are closed
				 * */

				try 
				{ 
					if (localStream != null) localStream.Close(); 
					if (remoteStream != null) remoteStream.Close();
				} 
				catch {}

				try
				{
					
				}
				catch {}
			}

			// if it's made it this far something went wrong
			return false;

		}
		/// <summary>
		/// Initializes a new instance of the AutoUpdateDownloadDescriptorEventArgs class
		/// </summary>
		/// <param name="updateDescriptor"></param>
		public AutoUpdateDownloadDescriptorEventArgs(AutoUpdateDownloadDescriptor downloadDescriptor) : base()
		{
			_descriptor = downloadDescriptor;
		}
		/// <summary>
		/// Instructs the AutoUpdateDownloader to cleanup after an install
		/// </summary>
		/// <param name="progressViewer">The progress viewer by which progress should be displayed</param>
		/// <param name="downloadDescriptor">The download descriptor that describes the download that occurred and was installed</param>
		public virtual bool FinalizeInstallation(			
			IProgressViewer progressViewer,
			AutoUpdateDownloadDescriptor downloadDescriptor)
		{
			return true;			
		}
		/// <summary>
		/// Bubble sorts the elements in the descriptor array using their product verion (The newest version will be at element 0).
		/// </summary>
		/// <param name="updates"></param>
		/// <returns></returns>
		public static AutoUpdateDownloadDescriptor[] Sort(AutoUpdateDownloadDescriptor[] updates)
		{
			// front to back - 1 
			for(int i = 0; i < updates.Length - 1; i++)
			{
				// front + 1 to back
				for(int j = i + 1; j < updates.Length; j++)
				{			
					if (updates[i].Manifest.Product.Version < updates[j].Manifest.Product.Version)
					{											 
						// swap i with j, where i=1 and j=2
						AutoUpdateDownloadDescriptor update = updates[j];
						updates[j] = updates[i];
						updates[i] = update;
					}													
				}
			}
			return updates;
		}
 /// <summary>
 /// Initializes a new instance of the
 /// </summary>
 /// <param name="cancel"></param>
 /// <param name="updateDescriptor"></param>
 public AutoUpdateDownloadDescriptorCancelEventArgs(bool cancel, AutoUpdateDownloadDescriptor downloadDescriptor) : base(downloadDescriptor)
 {
     _cancel = cancel;
 }
		/// <summary>
		/// Sorts the available downloads and returns the newest download descriptor as the one that should be downloaded
		/// </summary>
		/// <param name="updates"></param>
		/// <returns></returns>
		protected virtual AutoUpdateDownloadDescriptor SelectTheDownloadWithTheNewestUpdate(AutoUpdateDownloadDescriptor[] downloadDescriptors)
		{
			try
			{
				// if there are no downloads
				if (downloadDescriptors == null)
					// then simply say so
					return null;

				if (downloadDescriptors.Length > 0)
				{
					// otherwise, sort them into descending order with the newest version at index zero.
					downloadDescriptors = AutoUpdateDownloadDescriptor.Sort(downloadDescriptors);
				
					// simply return the first one
					return downloadDescriptors[0];
				}
			}
			catch(ThreadAbortException)
			{

			}

			// otherwise don't
			return null;
		}