Changeset 1747


Ignore:
Timestamp:
2/4/2010 2:31:56 AM (2 years ago)
Author:
lowjoel
Message:

Rewrote and redesigned the entire Updater infrastructure to use the ProgressManager? classes. Addresses #307: Eraser Updater restructuring

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/eraser6/CodeReview/Eraser/UpdateForm.cs

    r1709 r1747  
    2222using System; 
    2323using System.Collections.Generic; 
    24 using System.ComponentModel; 
    25 using System.Data; 
    26 using System.Drawing; 
    27 using System.Text; 
    2824using System.Windows.Forms; 
    29 using System.Net; 
     25 
     26using System.Diagnostics; 
    3027using System.Reflection; 
    3128using System.IO; 
     29using System.Linq; 
    3230using System.Xml; 
    33 using Eraser.Util; 
     31 
     32using System.Net; 
    3433using System.Net.Cache; 
    3534using System.Net.Mime; 
    3635using System.Globalization; 
    3736 
    38 using ProgressChangedEventArgs = System.ComponentModel.ProgressChangedEventArgs; 
     37using Eraser.Util; 
     38 
     39using ProgressChangedEventArgs = Eraser.Util.ProgressChangedEventArgs; 
     40using DoWorkEventArgs = System.ComponentModel.DoWorkEventArgs; 
     41using RunWorkerCompletedEventArgs = System.ComponentModel.RunWorkerCompletedEventArgs; 
    3942 
    4043namespace Eraser 
     
    8790        private void updateListDownloader_DoWork(object sender, DoWorkEventArgs e) 
    8891        { 
    89             try 
    90             { 
    91                 updates.OnProgressEvent += updateListDownloader_ProgressChanged; 
    92                 updates.DownloadUpdateList(); 
    93             } 
    94             finally 
    95             { 
    96                 updates.OnProgressEvent -= updateListDownloader_ProgressChanged; 
    97             } 
     92            e.Result = DownloadManager.GetDownloads(updateListDownloader_ProgressChanged); 
    9893        } 
    9994 
     
    10398        /// <param name="sender">The object triggering this event/</param> 
    10499        /// <param name="e">Event argument.</param> 
    105         private void updateListDownloader_ProgressChanged(object sender, ProgressEventArgs e) 
     100        private void updateListDownloader_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    106101        { 
    107102            if (InvokeRequired) 
     
    110105                    throw new OperationCanceledException(); 
    111106 
    112                 Invoke((EventHandler<ProgressEventArgs>)updateListDownloader_ProgressChanged, 
     107                Invoke((EventHandler<ProgressChangedEventArgs>)updateListDownloader_ProgressChanged, 
    113108                    sender, e); 
    114109                return; 
     
    116111 
    117112            progressPb.Style = ProgressBarStyle.Continuous; 
    118             progressPb.Value = (int)(e.OverallProgressPercentage * 100); 
    119             progressProgressLbl.Text = e.Message; 
     113            progressPb.Value = (int)(e.Progress.Progress * 100); 
     114            progressProgressLbl.Text = e.UserState as string; 
    120115 
    121116            if (progressPb.Value == 100) 
     
    147142 
    148143            //First list all available mirrors 
    149             Dictionary<string, Mirror>.Enumerator iter = updates.Mirrors.GetEnumerator(); 
    150             while (iter.MoveNext()) 
    151                 updatesMirrorCmb.Items.Add(iter.Current.Value); 
    152             updatesMirrorCmb.SelectedIndex = 0; 
     144            IList<DownloadInfo> downloads = (IList<DownloadInfo>)e.Result; 
    153145 
    154146            //Get a list of translatable categories (this will change as more categories 
    155147            //are added) 
    156             Dictionary<string, string> updateCategories = new Dictionary<string, string>(); 
    157             updateCategories.Add("update", S._("Updates")); 
    158             updateCategories.Add("plugin", S._("Plugins")); 
     148            Dictionary<DownloadType, string> categories = new Dictionary<DownloadType, string>(); 
     149            categories.Add(DownloadType.Update, S._("Updates")); 
     150            categories.Add(DownloadType.Plugin, S._("Plugins")); 
    159151 
    160152            //Only include those whose architecture is compatible with ours. 
    161             List<string> compatibleArchs = new List<string>(); 
     153            List<string> architectures = new List<string>(); 
    162154            { 
    163155                //any is always compatible. 
    164                 compatibleArchs.Add("any"); 
     156                architectures.Add("any"); 
    165157 
    166158                switch (SystemInfo.ProcessorArchitecture) 
    167159                { 
    168160                    case ProcessorArchitecture.Amd64: 
    169                         compatibleArchs.Add("x64"); 
     161                        architectures.Add("x64"); 
    170162                        break; 
    171163                    case ProcessorArchitecture.IA64: 
    172                         compatibleArchs.Add("ia64"); 
     164                        architectures.Add("ia64"); 
    173165                        break; 
    174166                    case ProcessorArchitecture.X86: 
    175                         compatibleArchs.Add("x86"); 
     167                        architectures.Add("x86"); 
    176168                        break; 
    177169                } 
    178170            } 
    179171 
    180             foreach (string key in updates.Categories) 
    181             { 
    182                 ListViewGroup group = new ListViewGroup(updateCategories.ContainsKey(key) ? 
    183                     updateCategories[key] : key); 
    184                 updatesLv.Groups.Add(group); 
    185  
    186                 foreach (UpdateInfo update in updates.Updates[key]) 
    187                 { 
    188                     //Skip if this update won't work on our current architecture. 
    189                     if (compatibleArchs.IndexOf(update.Architecture) == -1) 
    190                         continue; 
    191  
    192                     ListViewItem item = new ListViewItem(update.Name); 
    193                     item.SubItems.Add(update.Version.ToString()); 
    194                     item.SubItems.Add(update.Publisher); 
    195                     item.SubItems.Add(FileSize.ToString(update.FileSize)); 
    196  
    197                     item.Tag = update; 
    198                     item.Group = group; 
    199                     item.Checked = true; 
    200  
    201                     updatesLv.Items.Add(item); 
    202                     uiUpdates.Add(update, new UpdateData(update, item)); 
    203                 } 
     172            foreach (DownloadInfo download in downloads) 
     173            { 
     174                //Skip this download if it is not for our current architecture. 
     175                if (architectures.IndexOf(download.Architecture) == -1) 
     176                    continue; 
     177 
     178                //Create or retrieve the ListViewGroup object to categorise our downloads. 
     179                string categoryText = categories.ContainsKey(download.Type) ? 
     180                    categories[download.Type] : download.Type.ToString(); 
     181                int groupIndex = updatesLv.Groups.IndexOf(new ListViewGroup(categoryText)); 
     182                if (groupIndex == -1) 
     183                    groupIndex = updatesLv.Groups.Add(new ListViewGroup(categoryText)); 
     184                ListViewGroup group = updatesLv.Groups[groupIndex]; 
     185 
     186                //Add the item to the list of downloads available. 
     187                ListViewItem item = new ListViewItem(download.Name); 
     188                item.SubItems.Add(download.Version.ToString()); 
     189                item.SubItems.Add(download.Publisher); 
     190                item.SubItems.Add(FileSize.ToString(download.FileSize)); 
     191 
     192                item.Tag = download; 
     193                item.Group = group; 
     194                item.Checked = true; 
     195 
     196                updatesLv.Items.Add(item); 
    204197            } 
    205198 
     
    226219        private void updatesLv_ItemChecked(object sender, ItemCheckedEventArgs e) 
    227220        { 
    228             if (selectedUpdates == -1 || updatesCount != updatesLv.Items.Count) 
    229             { 
    230                 updatesCount = updatesLv.Items.Count; 
    231                 selectedUpdates = 0; 
    232                 foreach (ListViewItem item in updatesLv.Items) 
    233                     if (item.Checked) 
    234                         ++selectedUpdates; 
    235             } 
    236             else 
    237                 selectedUpdates += e.Item.Checked ? 1 : -1; 
    238             updatesBtn.Text = selectedUpdates == 0 ? S._("Close") : S._("Install"); 
     221            updatesBtn.Text = updatesLv.CheckedIndices.Count == 0 ? S._("Close") : S._("Install"); 
    239222        } 
    240223 
     
    248231            updatesPanel.Visible = false; 
    249232            downloadingPnl.Visible = true; 
    250             List<UpdateInfo> updatesToInstall = new List<UpdateInfo>(); 
    251  
    252             //Set the mirror 
    253             updates.SelectedMirror = (Mirror)updatesMirrorCmb.SelectedItem; 
     233            List<DownloadInfo> updatesToInstall = new List<DownloadInfo>(); 
    254234 
    255235            //Collect the items that need to be installed 
    256             foreach (ListViewItem item in updatesLv.Items) 
    257                 if (item.Checked) 
    258                 { 
    259                     item.Remove(); 
    260                     item.SubItems.RemoveAt(1); 
    261                     item.SubItems.RemoveAt(1); 
    262                     downloadingLv.Items.Add(item); 
    263  
    264                     updatesToInstall.Add((UpdateInfo)item.Tag); 
    265                 } 
    266                 else 
    267                     uiUpdates.Remove((UpdateInfo)item.Tag); 
     236            foreach (ListViewItem item in updatesLv.CheckedItems) 
     237            { 
     238                item.Remove(); 
     239                item.SubItems.RemoveAt(1); 
     240                item.SubItems.RemoveAt(1); 
     241                downloadingLv.Items.Add(item); 
     242 
     243                DownloadInfo download = (DownloadInfo)item.Tag; 
     244                updatesToInstall.Add(download); 
     245                DownloadItems.Add(download, new DownloadUIInfo(download, item)); 
     246            } 
    268247 
    269248            //Then run the thread if there are updates. 
     
    281260        private void downloader_DoWork(object sender, DoWorkEventArgs e) 
    282261        { 
    283             try 
    284             { 
    285                 updates.OnProgressEvent += downloader_ProgressChanged; 
    286                 object downloadedUpdates = updates.DownloadUpdates((List<UpdateInfo>)e.Argument); 
    287                 e.Result = downloadedUpdates; 
    288             } 
    289             finally 
    290             { 
    291                 updates.OnProgressEvent -= downloader_ProgressChanged; 
    292             } 
     262            List<DownloadInfo> downloads = (List<DownloadInfo>)e.Argument; 
     263            SteppedProgressManager overallProgress = new SteppedProgressManager(); 
     264            long totalDownloadSize = downloads.Sum(delegate(DownloadInfo download) 
     265                { 
     266                    return download.FileSize; 
     267                }); 
     268             
     269            foreach (DownloadInfo download in downloads) 
     270            { 
     271                ProgressManagerBase downloadProgress = null; 
     272                ProgressChangedEventHandler localHandler = 
     273                    delegate(object sender2, ProgressChangedEventArgs e2) 
     274                    { 
     275                        DownloadInfo downloadInfo = (DownloadInfo)sender2; 
     276                        if (downloadProgress == null) 
     277                        { 
     278                            downloadProgress = e2.Progress; 
     279                            overallProgress.Steps.Add(new SteppedProgressManagerStep( 
     280                                e2.Progress, download.FileSize / (float)totalDownloadSize)); 
     281                        } 
     282 
     283                        downloader_ProgressChanged(sender2, 
     284                            new ProgressChangedEventArgs(overallProgress, e2.UserState)); 
     285                    }; 
     286 
     287                download.Download(localHandler); 
     288            } 
     289 
     290            e.Result = e.Argument; 
    293291        } 
    294292 
     
    298296        /// <param name="sender">The object triggering this event/</param> 
    299297        /// <param name="e">Event argument.</param> 
    300         private void downloader_ProgressChanged(object sender, ProgressEventArgs e) 
     298        private void downloader_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    301299        { 
    302300            if (InvokeRequired) 
     
    305303                    throw new OperationCanceledException(); 
    306304 
    307                 Invoke((EventHandler<ProgressEventArgs>)downloader_ProgressChanged, 
     305                Invoke((EventHandler<ProgressChangedEventArgs>)downloader_ProgressChanged, 
    308306                    sender, e); 
    309307                return; 
    310308            } 
    311309 
    312             UpdateData update = uiUpdates[(UpdateInfo)e.UserState]; 
    313  
    314             if (e is ProgressErrorEventArgs) 
    315             { 
    316                 update.Error = ((ProgressErrorEventArgs)e).Exception; 
    317                 update.LVItem.ImageIndex = 3; 
    318                 update.LVItem.SubItems[1].Text = S._("Error"); 
    319                 update.LVItem.ToolTipText = update.Error.Message; 
     310            DownloadInfo download = (DownloadInfo)sender; 
     311            DownloadUIInfo downloadUIInfo = DownloadItems[download]; 
     312            SteppedProgressManager overallProgress = (SteppedProgressManager)e.Progress; 
     313 
     314            if (e.UserState is Exception) 
     315            { 
     316                downloadUIInfo.ListViewItem.ImageIndex = 3; 
     317                downloadUIInfo.ListViewItem.SubItems[1].Text = S._("Error"); 
     318                downloadUIInfo.ListViewItem.ToolTipText = ((Exception)e.UserState).Message; 
    320319            } 
    321320            else 
    322321            { 
    323                 if (e.ProgressPercentage >= 1.0f) 
    324                 { 
    325                     update.LVItem.ImageIndex = -1; 
    326                     update.LVItem.SubItems[1].Text = S._("Downloaded"); 
     322                if (e.Progress.Progress >= 1.0f) 
     323                { 
     324                    downloadUIInfo.ListViewItem.ImageIndex = -1; 
     325                    downloadUIInfo.ListViewItem.SubItems[1].Text = S._("Downloaded"); 
    327326                } 
    328327                else 
    329328                { 
    330                     update.amountDownloaded = (long)(e.ProgressPercentage * update.Update.FileSize); 
    331                     update.LVItem.ImageIndex = 0; 
    332                     update.LVItem.SubItems[1].Text = FileSize.ToString( 
    333                         update.Update.FileSize - update.amountDownloaded); 
     329                    downloadUIInfo.Downloaded = (long)(overallProgress.Progress * download.FileSize); 
     330                    downloadUIInfo.ListViewItem.ImageIndex = 0; 
     331                    downloadUIInfo.ListViewItem.SubItems[1].Text = FileSize.ToString(download.FileSize - 
     332                        downloadUIInfo.Downloaded); 
    334333                } 
    335334            } 
    336335 
    337             downloadingItemLbl.Text = e.Message; 
    338             downloadingItemPb.Value = (int)(e.ProgressPercentage * 100); 
    339             downloadingOverallPb.Value = (int)(e.OverallProgressPercentage * 100); 
    340  
    341             long amountToDownload = 0; 
    342             foreach (UpdateData upd in uiUpdates.Values) 
    343                 amountToDownload += upd.Update.FileSize - upd.amountDownloaded; 
     336            downloadingItemLbl.Text = S._("Downloading: {0}", download.Name); 
     337            downloadingItemPb.Value = (int)(overallProgress.CurrentStep.Progress.Progress * 100); 
     338            downloadingOverallPb.Value = (int)(overallProgress.Progress * 100); 
    344339            downloadingOverallLbl.Text = S._("Overall progress: {0} left", 
    345                 FileSize.ToString(amountToDownload)); 
    346         } 
    347  
    348         /// <summary> 
    349         /// Handles the completion of updating event 
     340                FileSize.ToString(DownloadItems.Values.Sum(delegate(DownloadUIInfo item) 
     341                    { 
     342                        return item.Download.FileSize - item.Downloaded; 
     343                    } 
     344            ))); 
     345        } 
     346 
     347        /// <summary> 
     348        /// Handles the completion of download event 
    350349        /// </summary> 
    351350        /// <param name="sender">The object triggering this event/</param> 
     
    368367            installingPnl.Visible = true; 
    369368 
    370             foreach (ListViewItem item in downloadingLv.Items) 
    371             { 
    372                 item.Remove(); 
    373                 installingLv.Items.Add(item); 
    374  
    375                 UpdateData update = uiUpdates[(UpdateInfo)item.Tag]; 
    376                 if (update.Error == null) 
    377                     item.SubItems[1].Text = string.Empty; 
     369            foreach (DownloadUIInfo download in DownloadItems.Values) 
     370            { 
     371                download.ListViewItem.Remove(); 
     372                installingLv.Items.Add(download.ListViewItem); 
     373 
     374                if (download.Error == null) 
     375                    download.ListViewItem.SubItems[1].Text = string.Empty; 
    378376                else 
    379                     item.SubItems[1].Text = S._("Error: {0}", update.Error.Message); 
     377                    download.ListViewItem.SubItems[1].Text = S._("Error: {0}", download.Error.Message); 
    380378            } 
    381379 
     
    392390        private void installer_DoWork(object sender, DoWorkEventArgs e) 
    393391        { 
    394             try 
    395             { 
    396                 updates.OnProgressEvent += installer_ProgressChanged; 
    397                 updates.InstallUpdates(e.Argument); 
    398             } 
    399             finally 
    400             { 
    401                 updates.OnProgressEvent -= installer_ProgressChanged; 
    402             } 
     392            List<DownloadInfo> downloads = (List<DownloadInfo>)e.Argument; 
     393            ProgressManager progress = new ProgressManager(); 
     394            progress.Total = downloads.Count; 
     395 
     396            foreach (DownloadInfo download in downloads) 
     397            { 
     398                ++progress.Completed; 
     399                int exitCode = download.Install(); 
     400                 
     401                if (exitCode == 0) 
     402                    installer_ProgressChanged(download, 
     403                        new ProgressChangedEventArgs(progress, null)); 
     404                else 
     405                    installer_ProgressChanged(download, 
     406                        new ProgressChangedEventArgs(progress, 
     407                            new ApplicationException(S._( 
     408                                "The installer exited with an error code {0}", exitCode)))); 
     409            } 
     410 
     411            e.Result = e.Argument; 
    403412        } 
    404413 
     
    415424                    throw new OperationCanceledException(); 
    416425 
    417                 Invoke((EventHandler<ProgressEventArgs>)installer_ProgressChanged, 
     426                Invoke((EventHandler<ProgressChangedEventArgs>)installer_ProgressChanged, 
    418427                    sender, e); 
    419428                return; 
    420429            } 
    421430 
    422             UpdateData update = uiUpdates[(UpdateInfo)e.UserState]; 
    423             if (e is ProgressErrorEventArgs) 
    424             { 
    425                 update.Error = ((ProgressErrorEventArgs)e).Exception; 
    426                 update.LVItem.ImageIndex = 3; 
    427                 update.LVItem.SubItems[1].Text = S._("Error: {0}", update.Error.Message); 
     431            DownloadInfo download = (DownloadInfo)sender; 
     432            DownloadUIInfo downloadUIInfo = DownloadItems[download]; 
     433 
     434            if (e.UserState is Exception) 
     435            { 
     436                downloadUIInfo.Error = (Exception)e.UserState; 
     437                downloadUIInfo.ListViewItem.ImageIndex = 3; 
     438                downloadUIInfo.ListViewItem.SubItems[1].Text = 
     439                    S._("Error: {0}", downloadUIInfo.Error.Message); 
    428440            } 
    429441            else 
    430                 switch (update.LVItem.ImageIndex) 
     442            { 
     443                downloadUIInfo.ListViewItem.SubItems[1].Text = S._("Installed {0}", download.Name); 
     444                switch (downloadUIInfo.ListViewItem.ImageIndex) 
    431445                { 
    432446                    case -1: 
    433                         update.LVItem.ImageIndex = 1; 
     447                        downloadUIInfo.ListViewItem.ImageIndex = 1; 
    434448                        break; 
    435449                    case 1: 
    436                         update.LVItem.ImageIndex = 2; 
     450                        downloadUIInfo.ListViewItem.ImageIndex = 2; 
    437451                        break; 
    438452                } 
     453            } 
    439454        } 
    440455 
     
    454469 
    455470        /// <summary> 
    456         /// The Update manager instance used by this form. 
    457         /// </summary> 
    458         UpdateManager updates = new UpdateManager(); 
    459  
    460         /// <summary> 
    461         /// Maps listview items to the UpdateManager.Update object. 
    462         /// </summary> 
    463         Dictionary<UpdateInfo, UpdateData> uiUpdates = new Dictionary<UpdateInfo, UpdateData>(); 
    464  
    465         /// <summary> 
    466471        /// Manages information associated with the update. 
    467472        /// </summary> 
    468         private class UpdateData 
     473        private class DownloadUIInfo 
    469474        { 
    470475            /// <summary> 
    471476            /// Constructor. 
    472477            /// </summary> 
    473             /// <param name="update">The UpdateManager.Update object containing the 
    474             /// internal representation of the update.</param> 
     478            /// <param name="update">The DownloadInfo object containing the 
     479            /// information about the download.</param> 
    475480            /// <param name="item">The ListViewItem used for the display of the 
    476481            /// update.</param> 
    477             public UpdateData(UpdateInfo update, ListViewItem item) 
    478             { 
    479                 Update = update; 
    480                 LVItem = item; 
     482            public DownloadUIInfo(DownloadInfo download, ListViewItem item) 
     483            { 
     484                Download = download; 
     485                ListViewItem = item; 
    481486            } 
    482487 
    483488            /// <summary> 
    484             /// The UpdateManager.Update object containing the internal representation 
    485             /// of the update. 
     489            /// The DownloadInfo object containing information about the update. 
    486490            /// </summary> 
    487             public UpdateInfo Update; 
     491            public DownloadInfo Download { get; private set; } 
    488492 
    489493            /// <summary> 
    490494            /// The ListViewItem used for the display of the update. 
    491495            /// </summary> 
    492             public ListViewItem LVItem; 
     496            public ListViewItem ListViewItem { get; private set; } 
    493497 
    494498            /// <summary> 
    495499            /// The amount of the download already completed. 
    496500            /// </summary> 
    497             public long amountDownloaded; 
     501            public long Downloaded { get; set; } 
    498502 
    499503            /// <summary> 
     
    501505            /// otherwise. 
    502506            /// </summary> 
    503             public Exception Error; 
    504         } 
    505  
    506         /// <summary> 
    507         /// The number of updates selected for download. 
    508         /// </summary> 
    509         private int selectedUpdates = -1; 
    510  
    511         /// <summary> 
    512         /// The number of updates present in the previous count, so the Selected 
    513         /// Updates number can be deemed invalid. 
    514         /// </summary> 
    515         private int updatesCount = -1; 
     507            public Exception Error { get; set; } 
     508        } 
     509 
     510        /// <summary> 
     511        /// Maps downloads to the list view items. 
     512        /// </summary> 
     513        private Dictionary<DownloadInfo, DownloadUIInfo> DownloadItems = 
     514            new Dictionary<DownloadInfo, DownloadUIInfo>(); 
    516515    } 
    517516 
    518     public class UpdateManager 
     517    /// <summary> 
     518    /// Manages the list of downloads that can be retrieved from the Eraser update server. 
     519    /// </summary> 
     520    public static class DownloadManager 
    519521    { 
    520         /// <summary> 
    521         /// Constructor. 
    522         /// </summary> 
    523         public UpdateManager() 
    524         { 
    525             Updates = new UpdateCategoriesDictionary(); 
    526         } 
    527  
    528         /// <summary> 
    529         /// Retrieves the update list from the server. 
    530         /// </summary> 
    531         public void DownloadUpdateList() 
     522        public static IList<DownloadInfo> GetDownloads(Eraser.Util.ProgressChangedEventHandler handler) 
    532523        { 
    533524            WebRequest.DefaultCachePolicy = new HttpRequestCachePolicy( 
    534                 HttpRequestCacheLevel.Refresh); 
    535             HttpWebRequest req = (HttpWebRequest) 
    536                 WebRequest.Create(new Uri("http://eraser.heidi.ie/scripts/updates?" + 
    537                     "action=listupdates&version=" + 
     525                HttpRequestCacheLevel.Revalidate); 
     526            HttpWebRequest request = (HttpWebRequest)WebRequest.Create( 
     527                new Uri("http://eraser.heidi.ie/scripts/updates?action=listupdates&version=" + 
    538528                    Assembly.GetExecutingAssembly().GetName().Version.ToString())); 
    539              
    540             using (WebResponse response = req.GetResponse()) 
     529 
     530            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) 
    541531            using (Stream responseStream = response.GetResponseStream()) 
    542532            using (MemoryStream memoryStream = new MemoryStream()) 
     
    552542                    memoryStream.Write(buffer, 0, lastRead); 
    553543                    progress.Completed = memoryStream.Position; 
    554                     OnProgress(new ProgressEventArgs(progress.Progress, progress.Progress, null, 
    555                         S._("{0} of {1} downloaded", 
    556                             FileSize.ToString(progress.Completed), 
    557                             FileSize.ToString(progress.Total)))); 
     544                    if (handler != null) 
     545                        handler(null, new Eraser.Util.ProgressChangedEventArgs(progress, 
     546                            S._("{0} of {1} downloaded", FileSize.ToString(progress.Completed), 
     547                                FileSize.ToString(progress.Total)))); 
    558548                } 
    559549 
    560550                //Parse it. 
    561551                memoryStream.Position = 0; 
    562                 ParseUpdateList(memoryStream); 
     552                return ParseDownloadList(memoryStream).AsReadOnly(); 
    563553            } 
    564554        } 
     
    568558        /// </summary> 
    569559        /// <param name="strm">The stream containing the XML data.</param> 
    570         private void ParseUpdateList(Stream strm) 
     560        private static List<DownloadInfo> ParseDownloadList(Stream strm) 
    571561        { 
    572562            //Move the XmlReader to the root node 
    573             Updates.Clear(); 
    574             mirrors.Clear(); 
    575563            XmlReader rdr = XmlReader.Create(strm); 
    576564            rdr.ReadToFollowing("updateList"); 
    577565 
    578             //Read the descendants of the updateList node (which are categories, 
    579             //except for the <mirrors> element) 
     566            //Read the descendants of the updateList node (ignoring the <mirrors> element) 
     567            //These are categories. 
    580568            XmlReader categories = rdr.ReadSubtree(); 
    581569            bool cont = categories.ReadToDescendant("mirrors"); 
     570            if (!cont) 
     571                throw new InvalidDataException(); 
     572            cont = categories.Read(); 
     573 
     574            List<DownloadInfo> result = new List<DownloadInfo>(); 
    582575            while (cont) 
    583576            { 
    584577                if (categories.NodeType == XmlNodeType.Element) 
    585578                { 
    586                     if (categories.Name == "mirrors") 
    587                     { 
    588                         Dictionary<string, string> mirrorsList = 
    589                             ParseMirror(categories.ReadSubtree()); 
    590                         Dictionary<string, string>.Enumerator e = mirrorsList.GetEnumerator(); 
    591                         while (e.MoveNext()) 
    592                             this.mirrors.Add(e.Current.Key, 
    593                                 new Mirror(e.Current.Value, e.Current.Key)); 
    594                     } 
    595                     else 
    596                         Updates.Add(categories.Name, ParseUpdateCategory(categories.ReadSubtree())); 
     579                    result.AddRange(ParseDownloadCategory(categories.Name, categories.ReadSubtree())); 
    597580                } 
    598581 
    599582                cont = categories.Read(); 
    600583            } 
    601         } 
    602  
    603         /// <summary> 
    604         /// Parses a list of mirrors. 
    605         /// </summary> 
    606         /// <param name="rdr">The XML reader object representing the &lt;mirrors&gt; node</param> 
    607         /// <returns>The list of mirrors defined by the element.</returns> 
    608         private static Dictionary<string, string> ParseMirror(XmlReader rdr) 
    609         { 
    610             Dictionary<string, string> result = new Dictionary<string,string>(); 
    611             if (!rdr.ReadToDescendant("mirror")) 
     584 
     585            return result; 
     586        } 
     587 
     588        /// <summary> 
     589        /// Parses a specific category and its assocaited updates. 
     590        /// </summary> 
     591        /// <param name="category">The name of the category.</param> 
     592        /// <param name="rdr">The XML reader object representing the element and its children.</param> 
     593        /// <returns>A list of downloads in the category.</returns> 
     594        private static List<DownloadInfo> ParseDownloadCategory(string category, XmlReader rdr) 
     595        { 
     596            List<DownloadInfo> result = new List<DownloadInfo>(); 
     597            if (!rdr.ReadToDescendant("item")) 
    612598                return result; 
    613599 
     
    615601            do 
    616602            { 
    617                 if (rdr.NodeType != XmlNodeType.Element || rdr.Name != "mirror") 
    618                     continue; 
    619  
    620                 string location = rdr.GetAttribute("location"); 
    621                 result.Add(rdr.ReadElementContentAsString(), location); 
    622             } 
    623             while (rdr.ReadToNextSibling("mirror")); 
    624  
    625             return result; 
    626         } 
    627  
    628         /// <summary> 
    629         /// Parses a specific category and its assocaited updates. 
    630         /// </summary> 
    631         /// <param name="rdr">The XML reader object representing the element and its children.</param> 
    632         /// <returns>A list of updates in the category.</returns> 
    633         private static UpdateCollection ParseUpdateCategory(XmlReader rdr) 
    634         { 
    635             UpdateCollection result = new UpdateCollection(); 
    636             if (!rdr.ReadToDescendant("item")) 
    637                 return result; 
    638  
    639             //Load every element. 
    640             do 
    641             { 
    642603                if (rdr.Name != "item") 
    643604                    continue; 
    644605 
    645                 UpdateInfo update = new UpdateInfo(); 
    646                 update.Name = rdr.GetAttribute("name"); 
    647                 update.Version = new Version(rdr.GetAttribute("version")); 
    648                 update.Publisher = rdr.GetAttribute("publisher"); 
    649                 update.Architecture = rdr.GetAttribute("architecture"); 
    650                 update.FileSize = Convert.ToInt64(rdr.GetAttribute("filesize"), 
    651                     CultureInfo.InvariantCulture); 
    652                 update.Link = rdr.ReadElementContentAsString(); 
    653  
    654                 result.Add(update); 
     606                result.Add(new DownloadInfo(rdr.GetAttribute("name"), 
     607                    (DownloadType)Convert.ChangeType(category, typeof(DownloadType)), 
     608                    new Version(rdr.GetAttribute("version")), rdr.GetAttribute("publisher"), 
     609                    rdr.GetAttribute("architecture"), Convert.ToInt64(rdr.GetAttribute("filesize")), 
     610                    new Uri(rdr.ReadElementContentAsString()))); 
    655611            } 
    656612            while (rdr.ReadToNextSibling("item")); 
     
    658614            return result; 
    659615        } 
    660  
    661         /// <summary> 
    662         /// Downloads the list of updates. 
    663         /// </summary> 
    664         /// <param name="updates">The updates to retrieve and install.</param> 
    665         /// <returns>An opaque object for use with InstallUpdates.</returns> 
    666         public object DownloadUpdates(ICollection<UpdateInfo> downloadQueue) 
    667         { 
    668             //Create a folder to hold all our updates. 
    669             DirectoryInfo tempDir = new DirectoryInfo(Path.GetTempPath()); 
    670             tempDir = tempDir.CreateSubdirectory("eraser" + Environment.TickCount.ToString( 
    671                 CultureInfo.InvariantCulture)); 
    672  
    673             int currUpdate = 0; 
    674             Dictionary<string, UpdateInfo> tempFilesMap = new Dictionary<string, UpdateInfo>(); 
    675             Util.SteppedProgressManager progress = new Util.SteppedProgressManager(); 
    676             foreach (UpdateInfo update in downloadQueue) 
    677             { 
    678                 try 
    679                 { 
    680                     //Add the update to the overall progress. 
    681                     Util.ProgressManager step = new Eraser.Util.ProgressManager(); 
    682                     progress.Steps.Add(new Util.SteppedProgressManagerStep( 
    683                         step, 1.0f / downloadQueue.Count)); 
    684  
    685                     //Decide on the URL to connect to. The Link of the update may 
    686                     //be a relative path (relative to the selected mirror) or an 
    687                     //absolute path (which we have no choice) 
    688                     Uri reqUri = null; 
    689                     if (Uri.IsWellFormedUriString(update.Link, UriKind.Absolute)) 
    690                         reqUri = new Uri(update.Link); 
    691                     else 
    692                         reqUri = new Uri(new Uri(SelectedMirror.Link), new Uri(update.Link)); 
    693                      
    694                     //Then grab the download. 
    695                     HttpWebRequest req = (HttpWebRequest)WebRequest.Create(reqUri); 
    696                     using (WebResponse resp = req.GetResponse()) 
    697                     { 
    698                         //Check for a suggested filename. 
    699                         ContentDisposition contentDisposition = null; 
    700                         foreach (string header in resp.Headers.AllKeys) 
    701                             if (header.ToUpperInvariant() == "CONTENT-DISPOSITION") 
    702                                 contentDisposition = new ContentDisposition(resp.Headers[header]); 
    703  
    704                         string tempFilePath = Path.Combine( 
    705                             tempDir.FullName, string.Format(CultureInfo.InvariantCulture, "{0}-{1}", 
    706                             ++currUpdate, 
    707                             contentDisposition == null ? 
    708                                 Path.GetFileName(reqUri.GetComponents(UriComponents.Path, 
    709                                 UriFormat.Unescaped)) : contentDisposition.FileName)); 
    710  
    711                         using (Stream responseStream = resp.GetResponseStream()) 
    712                         using (FileStream fileStream = new FileStream(tempFilePath, FileMode.CreateNew)) 
    713                         { 
    714                             //Update the progress of this step 
    715                             step.Total = resp.ContentLength; 
    716  
    717                             //Copy the information into the file stream 
    718                             int lastRead = 0; 
    719                             byte[] buffer = new byte[16384]; 
    720                             while ((lastRead = responseStream.Read(buffer, 0, buffer.Length)) != 0) 
    721                             { 
    722                                 fileStream.Write(buffer, 0, lastRead); 
    723  
    724                                 //Compute progress 
    725                                 step.Completed = fileStream.Position; 
    726                                 OnProgress(new ProgressEventArgs(step.Progress, progress.Progress, 
    727                                     update, S._("Downloading: {0}", update.Name))); 
    728                             } 
    729                         } 
    730  
    731                         //Store the filename-to-update mapping 
    732                         tempFilesMap.Add(tempFilePath, update); 
    733  
    734                         //Let the event handler know the download is complete. 
    735                         step.MarkComplete(); 
    736                         OnProgress(new ProgressEventArgs(step.Progress, progress.Progress, 
    737                             update, S._("Downloaded: {0}", update.Name))); 
    738                     } 
    739                 } 
    740                 catch (Exception e) 
    741                 { 
    742                     OnProgress(new ProgressErrorEventArgs(new ProgressEventArgs(1.0f, 
    743                         (float)currUpdate / downloadQueue.Count, update, 
    744                             S._("Error downloading {0}: {1}", update.Name, e.Message)), 
    745                         e)); 
    746                 } 
    747             } 
    748  
    749             return tempFilesMap; 
    750         } 
    751  
    752         /// <summary> 
    753         /// Installs all updates downloaded. 
    754         /// </summary> 
    755         /// <param name="value">The value returned from a call to 
    756         /// <see cref="DownloadUpdates"/>.</param> 
    757         public void InstallUpdates(object value) 
    758         { 
    759             Util.ProgressManager progress = new Util.ProgressManager(); 
    760             Dictionary<string, UpdateInfo> tempFiles = (Dictionary<string, UpdateInfo>)value; 
    761             Dictionary<string, UpdateInfo>.KeyCollection files = tempFiles.Keys; 
    762  
    763             try 
    764             { 
    765                 progress.Total = files.Count; 
    766                 foreach (string path in files) 
    767                 { 
    768                     UpdateInfo item = tempFiles[path]; 
    769                     ++progress.Completed; 
    770                     OnProgress(new ProgressEventArgs(0.0f, progress.Progress, 
    771                         item, S._("Installing {0}", item.Name))); 
    772  
    773                     System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo(); 
    774                     info.FileName = path; 
    775                     info.UseShellExecute = true; 
    776  
    777                     System.Diagnostics.Process process = System.Diagnostics.Process.Start(info); 
    778                     process.WaitForExit(Int32.MaxValue); 
    779                     if (process.ExitCode == 0) 
    780                         OnProgress(new ProgressEventArgs(1.0f, progress.Progress, 
    781                             item, S._("Installed {0}", item.Name))); 
    782                     else 
    783                         OnProgress(new ProgressErrorEventArgs(new ProgressEventArgs(1.0f, 
    784                             progress.Progress, item, S._("Error installing {0}", item.Name)), 
    785                             new ApplicationException(S._("The installer exited with an error code {0}", 
    786                                 process.ExitCode)))); 
    787                 } 
    788             } 
    789             finally 
    790             { 
    791                 //Clean up after ourselves 
    792                 foreach (string file in files) 
    793                 { 
    794                     DirectoryInfo tempDir = null; 
    795                     { 
    796                         FileInfo info = new FileInfo(file); 
    797                         tempDir = info.Directory; 
    798                     } 
    799  
    800                     tempDir.Delete(true); 
    801                     break; 
    802                 } 
    803             } 
    804         } 
    805  
    806         /// <summary> 
    807         /// Called when the progress of the operation changes. 
    808         /// </summary> 
    809         public EventHandler<ProgressEventArgs> OnProgressEvent { get; set; } 
    810  
    811         /// <summary> 
    812         /// Helper function: invokes the OnProgressEvent delegate. 
    813         /// </summary> 
    814         /// <param name="arg">The ProgressEventArgs object holding information 
    815         /// about the progress of the current operation.</param> 
    816         private void OnProgress(ProgressEventArgs arg) 
    817         { 
    818             if (OnProgressEvent != null) 
    819                 OnProgressEvent(this, arg); 
    820         } 
    821  
    822         /// <summary> 
    823         /// Retrieves the list of mirrors which the server has indicated to exist. 
    824         /// </summary> 
    825         public Dictionary<string, Mirror> Mirrors 
    826         { 
    827             get 
    828             { 
    829                 return mirrors; 
    830             } 
    831         } 
    832  
    833         /// <summary> 
    834         /// Gets or sets the active mirror to use to download mirrored updates. 
    835         /// </summary> 
    836         public Mirror SelectedMirror 
    837         { 
    838             get 
    839             { 
    840                 if (selectedMirror.Link.Length == 0) 
    841                 { 
    842                     Dictionary<string, Mirror>.Enumerator iter = mirrors.GetEnumerator(); 
    843                     if (iter.MoveNext()) 
    844                         return iter.Current.Value; 
    845                 } 
    846                 return selectedMirror; 
    847             } 
    848             set 
    849             { 
    850                 foreach (Mirror mirror in Mirrors.Values) 
    851                     if (mirror.Equals(value)) 
    852                     { 
    853                         selectedMirror = value; 
    854                         return; 
    855                     } 
    856  
    857                 throw new ArgumentException(S._("Unknown mirror selected.")); 
    858             } 
    859         } 
    860  
    861         /// <summary> 
    862         /// Retrieves the categories available. 
    863         /// </summary> 
    864         public ICollection<string> Categories 
    865         { 
    866             get 
    867             { 
    868                 return Updates.Keys; 
    869             } 
    870         } 
    871  
    872         /// <summary> 
    873         /// Retrieves all updates available. 
    874         /// </summary> 
    875         public UpdateCategoriesDictionary Updates { get; private set; } 
    876  
    877         /// <summary> 
    878         /// The list of mirrors to download updates from. 
    879         /// </summary> 
    880         private Dictionary<string, Mirror> mirrors = 
    881             new Dictionary<string, Mirror>(); 
    882  
    883         /// <summary> 
    884         /// The currently selected mirror. 
    885         /// </summary> 
    886         private Mirror selectedMirror; 
    887616    } 
    888617 
    889618    /// <summary> 
    890     /// Manages a list of categories, mapping categories to a list of updates. 
     619    /// The types of downloads we support. 
    891620    /// </summary> 
    892     public class UpdateCategoriesDictionary : IDictionary<string, UpdateCollection>, 
    893         ICollection<KeyValuePair<string, UpdateCollection>>, 
    894         IEnumerable<KeyValuePair<string, UpdateCollection>> 
     621    public enum DownloadType 
    895622    { 
    896         #region IDictionary<string,UpdateList> Members 
    897         public void Add(string key, UpdateCollection value) 
    898         { 
    899             dictionary.Add(key, value); 
    900         } 
    901  
    902         public bool ContainsKey(string key) 
    903         { 
    904             return dictionary.ContainsKey(key); 
    905         } 
    906  
    907         public ICollection<string> Keys 
    908         { 
    909             get { return dictionary.Keys; } 
    910         } 
    911  
    912         public bool Remove(string key) 
    913         { 
    914             return dictionary.Remove(key); 
    915         } 
    916  
    917         public bool TryGetValue(string key, out UpdateCollection value) 
    918         { 
    919             return dictionary.TryGetValue(key, out value); 
    920         } 
    921  
    922         public ICollection<UpdateCollection> Values 
    923         { 
    924             get { return dictionary.Values; } 
    925         } 
    926  
    927         public UpdateCollection this[string key] 
    928         { 
    929             get 
    930             { 
    931                 return dictionary[key]; 
    932             } 
    933             set 
    934             { 
    935                 dictionary[key] = value; 
    936             } 
    937         } 
    938         #endregion 
    939  
    940         #region ICollection<KeyValuePair<string,UpdateList>> Members 
    941         public void Add(KeyValuePair<string, UpdateCollection> item) 
    942         { 
    943             dictionary.Add(item.Key, item.Value); 
    944         } 
    945  
    946         public void Clear() 
    947         { 
    948             dictionary.Clear(); 
    949         } 
    950  
    951         public bool Contains(KeyValuePair<string, UpdateCollection> item) 
    952         { 
    953             return dictionary.ContainsKey(item.Key) && dictionary[item.Key] == item.Value; 
    954         } 
    955  
    956         public void CopyTo(KeyValuePair<string, UpdateCollection>[] array, int arrayIndex) 
    957         { 
    958             throw new NotImplementedException(); 
    959         } 
    960  
    961         public int Count 
    962         { 
    963             get { return dictionary.Count; } 
    964         } 
    965  
    966         public bool IsReadOnly 
    967         { 
    968             get { return true; } 
    969         } 
    970  
    971         public bool Remove(KeyValuePair<string, UpdateCollection> item) 
    972         { 
    973             return dictionary.Remove(item.Key); 
    974         } 
    975         #endregion 
    976  
    977         #region IEnumerable<KeyValuePair<string,UpdateList>> Members 
    978         public IEnumerator<KeyValuePair<string, UpdateCollection>> GetEnumerator() 
    979         { 
    980             return dictionary.GetEnumerator(); 
    981         } 
    982         #endregion 
    983  
    984         #region IEnumerable Members 
    985         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    986         { 
    987             return GetEnumerator(); 
    988         } 
    989         #endregion 
    990  
    991         /// <summary> 
    992         /// The store for the current object. 
    993         /// </summary> 
    994         private Dictionary<string, UpdateCollection> dictionary = 
    995             new Dictionary<string, UpdateCollection>(); 
    996     } 
    997  
    998     /// <summary> 
    999     /// Manages a category, containing a list of updates. 
    1000     /// </summary> 
    1001     public class UpdateCollection : IList<UpdateInfo>, ICollection<UpdateInfo>, 
    1002         IEnumerable<UpdateInfo> 
    1003     { 
    1004         #region IList<UpdateInfo> Members 
    1005         public int IndexOf(UpdateInfo item) 
    1006         { 
    1007             return list.IndexOf(item); 
    1008         } 
    1009  
    1010         public void Insert(int index, UpdateInfo item) 
    1011         { 
    1012             list.Insert(index, item); 
    1013         } 
    1014  
    1015         public void RemoveAt(int index) 
    1016         { 
    1017             list.RemoveAt(index); 
    1018         } 
    1019  
    1020         public UpdateInfo this[int index] 
    1021         { 
    1022             get 
    1023             { 
    1024                 return list[index]; 
    1025             } 
    1026             set 
    1027             { 
    1028                 list[index] = value; 
    1029             } 
    1030         } 
    1031         #endregion 
    1032  
    1033         #region ICollection<UpdateInfo> Members 
    1034         public void Add(UpdateInfo item) 
    1035         { 
    1036             list.Add(item); 
    1037         } 
    1038  
    1039         public void Clear() 
    1040         { 
    1041             list.Clear(); 
    1042         } 
    1043  
    1044         public bool Contains(UpdateInfo item) 
    1045         { 
    1046             return list.Contains(item); 
    1047         } 
    1048  
    1049         public void CopyTo(UpdateInfo[] array, int arrayIndex) 
    1050         { 
    1051             list.CopyTo(array, arrayIndex); 
    1052         } 
    1053  
    1054         public int Count 
    1055         { 
    1056             get { return list.Count; } 
    1057         } 
    1058  
    1059         public bool IsReadOnly 
    1060         { 
    1061             get { return true; } 
    1062         } 
    1063  
    1064         public bool Remove(UpdateInfo item) 
    1065         { 
    1066             return list.Remove(item); 
    1067         } 
    1068         #endregion 
    1069  
    1070         #region IEnumerable<UpdateInfo> Members 
    1071         public IEnumerator<UpdateInfo> GetEnumerator() 
    1072         { 
    1073             return list.GetEnumerator(); 
    1074         } 
    1075         #endregion 
    1076  
    1077         #region IEnumerable Members 
    1078         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    1079         { 
    1080             return list.GetEnumerator(); 
    1081         } 
    1082         #endregion 
    1083  
    1084         /// <summary> 
    1085         /// The store for this object. 
    1086         /// </summary> 
    1087         private List<UpdateInfo> list = new List<UpdateInfo>(); 
    1088     } 
    1089  
    1090     /// <summary> 
    1091     /// Represents a download mirror. 
    1092     /// </summary> 
    1093     public struct Mirror 
    1094     { 
    1095         public Mirror(string location, string link) 
    1096             : this() 
    1097         { 
    1098             Location = location; 
    1099             Link = link; 
    1100         } 
    1101  
    1102         /// <summary> 
    1103         /// The location where the mirror is at. 
    1104         /// </summary> 
    1105         public string Location { get; set; } 
    1106  
    1107         /// <summary> 
    1108         /// The URL prefix to utilise the mirror. 
    1109         /// </summary> 
    1110         public string Link { get; set; } 
    1111  
    1112         public override string ToString() 
    1113         { 
    1114             return Location; 
    1115         } 
    1116  
    1117         public override bool Equals(object obj) 
    1118         { 
    1119             if (!(obj is Mirror)) 
    1120                 return false; 
    1121             return Equals((Mirror)obj); 
    1122         } 
    1123  
    1124         public bool Equals(Mirror other) 
    1125         { 
    1126             return Link == other.Link; 
    1127         } 
    1128  
    1129         public static bool operator ==(Mirror mirror1, Mirror mirror2) 
    1130         { 
    1131             return mirror1.Equals(mirror2); 
    1132         } 
    1133  
    1134         public static bool operator !=(Mirror mirror1, Mirror mirror2) 
    1135         { 
    1136             return !mirror1.Equals(mirror2); 
    1137         } 
    1138  
    1139         public override int GetHashCode() 
    1140         { 
    1141             return Link.GetHashCode(); 
    1142         } 
     623        /// <summary> 
     624        /// The type of the download is unknown. 
     625        /// </summary> 
     626        Unknown, 
     627 
     628        /// <summary> 
     629        /// The download is an update. 
     630        /// </summary> 
     631        Update, 
     632 
     633        /// <summary> 
     634        /// The download is a plugin. 
     635        /// </summary> 
     636        Plugin 
    1143637    } 
    1144638 
     
    1146640    /// Represents an update available on the server. 
    1147641    /// </summary> 
    1148     public struct UpdateInfo 
     642    public class DownloadInfo 
    1149643    { 
    1150         public string Name { get; set; } 
    1151         public Version Version { get; set; } 
    1152         public string Publisher { get; set; } 
    1153         public string Architecture { get; set; } 
    1154         public long FileSize { get; set; } 
    1155         public string Link { get; set; } 
    1156  
    1157         public override bool Equals(object obj) 
    1158         { 
    1159             if (!(obj is UpdateInfo)) 
    1160                 return false; 
    1161             return Equals((UpdateInfo)obj); 
    1162         } 
    1163  
    1164         public bool Equals(UpdateInfo other) 
    1165         { 
    1166             return Link == other.Link; 
    1167         } 
    1168  
    1169         public static bool operator ==(UpdateInfo update1, UpdateInfo update2) 
    1170         { 
    1171             return update1.Equals(update2); 
    1172         } 
    1173  
    1174         public static bool operator !=(UpdateInfo update1, UpdateInfo update2) 
    1175         { 
    1176             return !update1.Equals(update2); 
    1177         } 
    1178  
    1179         public override int GetHashCode() 
    1180         { 
    1181             return Link.GetHashCode(); 
    1182         } 
    1183     } 
    1184  
    1185     /// <summary> 
    1186     /// Specialised progress event argument, containing message describing 
    1187     /// current action, and overall progress percentage. 
    1188     /// </summary> 
    1189     public class ProgressEventArgs : ProgressChangedEventArgs 
    1190     { 
    1191         public ProgressEventArgs(float progressPercentage, float overallPercentage, 
    1192             object userState, string message) 
    1193             : base((int)(progressPercentage * 100), userState) 
    1194         { 
    1195             ProgressPercentage = progressPercentage; 
    1196             OverallProgressPercentage = overallPercentage; 
    1197             Message = message; 
    1198         } 
    1199  
    1200         /// <summary> 
    1201         /// Gets the asynchronous task progress percentage. 
    1202         /// </summary> 
    1203         public new float ProgressPercentage { get; private set; } 
    1204  
    1205         /// <summary> 
    1206         /// Gets the asynchronous task overall progress percentage. 
    1207         /// </summary> 
    1208         public float OverallProgressPercentage { get; private set; } 
    1209  
    1210         /// <summary> 
    1211         /// Gets the message associated with the current task. 
    1212         /// </summary> 
    1213         public string Message { get; private set; } 
    1214     } 
    1215  
    1216     /// <summary> 
    1217     /// Extends the ProgressEventArgs further by allowing for the inclusion of 
    1218     /// an exception. 
    1219     /// </summary> 
    1220     public class ProgressErrorEventArgs : ProgressEventArgs 
    1221     { 
    1222644        /// <summary> 
    1223645        /// Constructor. 
    1224646        /// </summary> 
    1225         /// <param name="e">The base ProgressEventArgs object.</param> 
    1226         /// <param name="ex">The exception</param> 
    1227         public ProgressErrorEventArgs(ProgressEventArgs e, Exception ex) 
    1228             : base(e.ProgressPercentage, e.OverallProgressPercentage, e.UserState, e.Message) 
    1229         { 
    1230             Exception = ex; 
    1231         } 
    1232  
    1233         /// <summary> 
    1234         /// The exception associated with the progress event. 
    1235         /// </summary> 
    1236         public Exception Exception { get; private set; } 
     647        /// <param name="name">The name of the download.</param> 
     648        /// <param name="type">The type of the download.</param> 
     649        /// <param name="version">The version of the download.</param> 
     650        /// <param name="publisher">The publisher of the download.</param> 
     651        /// <param name="architecture">The architecture of the binaries.</param> 
     652        /// <param name="fileSize">The size of the download.</param> 
     653        /// <param name="link">The link to the download.</param> 
     654        internal DownloadInfo(string name, DownloadType type, Version version, 
     655            string publisher, string architecture, long fileSize, Uri link) 
     656        { 
     657            Name = name; 
     658            Type = type; 
     659            Version = version; 
     660            Publisher = publisher; 
     661            Architecture = architecture; 
     662            FileSize = fileSize; 
     663            Link = link; 
     664        } 
     665 
     666        public string Name { get; private set; } 
     667        public DownloadType Type { get; private set; } 
     668        public Version Version { get; private set; } 
     669        public string Publisher { get; private set; } 
     670        public string Architecture { get; private set; } 
     671        public long FileSize { get; private set; } 
     672        public Uri Link { get; private set; } 
     673 
     674        /// <summary> 
     675        /// Downloads the file to disk, storing the path into the DownloadedFile field. 
     676        /// </summary> 
     677        public void Download(Eraser.Util.ProgressChangedEventHandler handler) 
     678        { 
     679            if (DownloadedFile != null && DownloadedFile.Length > 0) 
     680                throw new InvalidOperationException("The Download method cannot be called " + 
     681                    "before the Download method has been called."); 
     682 
     683            //Create a folder to hold all our updates. 
     684            lock (TempPathLock) 
     685            { 
     686                if (TempPath == null) 
     687                { 
     688                    TempPath = new DirectoryInfo(Path.GetTempPath()); 
     689                    TempPath = TempPath.CreateSubdirectory("eraser" + Environment.TickCount.ToString( 
     690                        CultureInfo.InvariantCulture)); 
     691                } 
     692            } 
     693 
     694            //Create the progress manager for this download. 
     695            ProgressManager progress = new ProgressManager(); 
     696 
     697            try 
     698            { 
     699                //Request the download. 
     700                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Link); 
     701                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) 
     702                { 
     703                    //Do the progress calculations 
     704                    progress.Total = response.ContentLength; 
     705 
     706                    //Check for a suggested filename. 
     707                    ContentDisposition contentDisposition = null; 
     708                    foreach (string header in response.Headers.AllKeys) 
     709                        if (header.ToUpperInvariant() == "CONTENT-DISPOSITION") 
     710                            contentDisposition = new ContentDisposition(response.Headers[header]); 
     711 
     712                    //Create the file name. 
     713                    DownloadedFile = new FileInfo(Path.Combine( 
     714                        TempPath.FullName, contentDisposition == null ? 
     715                            Path.GetFileName(Link.GetComponents(UriComponents.Path, UriFormat.Unescaped)) : 
     716                            contentDisposition.FileName)); 
     717 
     718                    using (Stream responseStream = response.GetResponseStream()) 
     719                    using (FileStream fileStream = DownloadedFile.OpenWrite()) 
     720                    { 
     721                        //Copy the information into the file stream 
     722                        int lastRead = 0; 
     723                        byte[] buffer = new byte[16384]; 
     724                        while ((lastRead = responseStream.Read(buffer, 0, buffer.Length)) != 0) 
     725                        { 
     726                            fileStream.Write(buffer, 0, lastRead); 
     727 
     728                            //Compute progress 
     729                            progress.Completed = fileStream.Position; 
     730 
     731                            //Call the progress handler 
     732                            if (handler != null) 
     733                                handler(this, new ProgressChangedEventArgs(progress, null)); 
     734                        } 
     735                    } 
     736 
     737                    //Let the event handler know the download is complete. 
     738                    progress.MarkComplete(); 
     739                    if (handler != null) 
     740                        handler(this, new ProgressChangedEventArgs(progress, null)); 
     741                } 
     742            } 
     743            catch (Exception e) 
     744            { 
     745                if (handler != null) 
     746                    handler(this, new ProgressChangedEventArgs(progress, e)); 
     747            } 
     748        } 
     749 
     750        /// <summary> 
     751        /// Installs the file, by calling Process.Start on the file. 
     752        /// </summary> 
     753        /// <returns>The exit code of the program.</returns> 
     754        public int Install() 
     755        { 
     756            if (DownloadedFile == null || !DownloadedFile.Exists || DownloadedFile.Length == 0) 
     757                throw new InvalidOperationException("The Install method cannot be called " + 
     758                    "before the Download method has been called."); 
     759 
     760            ProcessStartInfo info = new ProcessStartInfo(); 
     761            info.FileName = DownloadedFile.FullName; 
     762            info.UseShellExecute = true; 
     763            //info.Verb = "runas"; 
     764 
     765            Process process = Process.Start(info); 
     766            process.WaitForExit(Int32.MaxValue); 
     767            return process.ExitCode; 
     768        } 
     769 
     770        /// <summary> 
     771        /// The lock object for the TempPath field. 
     772        /// </summary> 
     773        private static object TempPathLock = new object(); 
     774 
     775        /// <summary> 
     776        /// The temporary path we are storing our downloads in. 
     777        /// </summary> 
     778        private static DirectoryInfo TempPath; 
     779 
     780        /// <summary> 
     781        /// Stores information about the temporary file which the download was stored into. 
     782        /// </summary> 
     783        private FileInfo DownloadedFile; 
    1237784    } 
    1238785} 
Note: See TracChangeset for help on using the changeset viewer.