source: branches/eraser6/Eraser/UpdateForm.cs @ 517

Revision 517, 25.6 KB checked in by lowjoel, 5 years ago (diff)

-Fixed a mirror parsing bug
-Allow the selection of mirrors by the user

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
RevLine 
[513]1/*
2 * $Id$
3 * Copyright 2008 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.ComponentModel;
25using System.Data;
26using System.Drawing;
27using System.Text;
28using System.Windows.Forms;
29using System.Net;
30using System.Reflection;
31using System.IO;
32using System.Xml;
33using Eraser.Util;
34using System.Net.Cache;
35
36namespace Eraser
37{
38    public partial class UpdateForm : Form
39    {
40        /// <summary>
41        /// Constructor.
42        /// </summary>
43        public UpdateForm()
44        {
45            InitializeComponent();
46
47            updates = new UpdateManager();
48            updateListDownloader.RunWorkerAsync();
49        }
50
51        #region Update List retrieval
52        /// <summary>
53        /// Downloads and parses the list of updates available for this client.
54        /// </summary>
55        /// <param name="sender">The object triggering this event/</param>
56        /// <param name="e">Event argument.</param>
57        private void updateListDownloader_DoWork(object sender, DoWorkEventArgs e)
58        {
59            try
60            {
[514]61                updates.OnProgressEvent += updateListDownloader_ProgressChanged;
[513]62                updates.GetUpdates();
63            }
64            finally
65            {
[514]66                updates.OnProgressEvent -= updateListDownloader_ProgressChanged;
[513]67            }
68        }
69
70        /// <summary>
71        /// Called when progress has been made in the update list download.
72        /// </summary>
73        /// <param name="sender">The object triggering this event/</param>
74        /// <param name="e">Event argument.</param>
[514]75        private void updateListDownloader_ProgressChanged(object sender, UpdateManager.ProgressEventArgs e)
[513]76        {
[514]77            if (InvokeRequired)
78            {
79                Invoke(new UpdateManager.ProgressEventFunction(updateListDownloader_ProgressChanged),
80                    sender, e);
81                return;
82            }
83
[513]84            progressPb.Style = ProgressBarStyle.Continuous;
[514]85            progressPb.Value = (int)(e.OverallProgressPercentage * 100);
86            progressLbl.Text = e.Message;
[513]87
88            if (progressPb.Value == 100)
89                progressProgressLbl.Text = S._("Processing update list...");
90        }
91
92        /// <summary>
93        /// Displays the parsed updates on the updates list view, filtering and displaying
94        /// only those relevant to the current system's architecture.
95        /// </summary>
96        /// <param name="sender">The object triggering this event/</param>
97        /// <param name="e">Event argument.</param>
98        private void updateListDownloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
99        {
[516]100            //The Error property will normally be null unless there are errors during the download.
[514]101            if (e.Error != null)
[513]102            {
[514]103                MessageBox.Show(this, e.Error.Message, S._("Eraser"),
[513]104                    MessageBoxButtons.OK, MessageBoxIcon.Error);
[514]105                Close();
[513]106                return;
107            }
108
109            progressPanel.Visible = false;
110            updatesPanel.Show();
111
[517]112            //First list all available mirrors
113            Dictionary<string, UpdateManager.Mirror>.Enumerator iter =
114                updates.Mirrors.GetEnumerator();
115            while (iter.MoveNext())
116                updatesMirrorCmb.Items.Add(iter.Current.Value);
117            updatesMirrorCmb.SelectedIndex = 0;
118
[513]119            //Get a list of translatable categories (this will change as more categories
120            //are added)
121            Dictionary<string, string> updateCategories = new Dictionary<string, string>();
122            updateCategories.Add("updates", S._("Updates"));
123            updateCategories.Add("plugins", S._("Plugins"));
124
125            //Only include those whose architecture is compatible with ours.
126            List<string> compatibleArchs = new List<string>();
127            {
128                //any is always compatible.
129                compatibleArchs.Add("any");
130
131                KernelAPI.SYSTEM_INFO info = new KernelAPI.SYSTEM_INFO();
132                KernelAPI.GetSystemInfo(out info);
133                switch (info.processorArchitecture)
134                {
135                    case KernelAPI.SYSTEM_INFO.ProcessorArchitecture.PROCESSOR_ARCHITECTURE_AMD64:
136                        compatibleArchs.Add("x64");
137                        break;
138
139                    case KernelAPI.SYSTEM_INFO.ProcessorArchitecture.PROCESSOR_ARCHITECTURE_IA64:
140                        compatibleArchs.Add("ia64");
141                        break;
142
143                    case KernelAPI.SYSTEM_INFO.ProcessorArchitecture.PROCESSOR_ARCHITECTURE_INTEL:
144                        compatibleArchs.Add("x86");
145                        break;
146                }
147            }
148
149            foreach (string key in updates.Categories)
150            {
151                ListViewGroup group = new ListViewGroup(updateCategories.ContainsKey(key) ?
152                    updateCategories[key] : key);
153                updatesLv.Groups.Add(group);
154
155                foreach (UpdateManager.Update update in updates[key])
156                {
157                    //Skip if this update won't work on our current architecture.
158                    if (compatibleArchs.IndexOf(update.Architecture) == -1)
159                        continue;
160
161                    ListViewItem item = new ListViewItem(update.Name);
162                    item.SubItems.Add(update.Version.ToString());
163                    item.SubItems.Add(update.Publisher);
164                    item.SubItems.Add(update.FileSize.ToString());
165
166                    item.Tag = update;
167                    item.Group = group;
168                    item.Checked = true;
169
170                    updatesLv.Items.Add(item);
[516]171                    uiUpdates.Add(update, new UpdateData(update, item));
[513]172                }
173            }
174        }
175        #endregion
176
177        #region Update downloader
[514]178        /// <summary>
179        /// Handles the Install button click; fetches and installs the updates selected.
180        /// </summary>
181        /// <param name="sender">The object triggering this event/</param>
182        /// <param name="e">Event argument.</param>
[513]183        private void updatesBtn_Click(object sender, EventArgs e)
184        {
185            updatesPanel.Visible = false;
186            downloadingPnl.Show();
187            List<UpdateManager.Update> updatesToInstall =
188                new List<UpdateManager.Update>();
189
[517]190            //Set the mirror
191            updates.SelectedMirror = (UpdateManager.Mirror)
192                updatesMirrorCmb.SelectedItem;
193
[513]194            //Collect the items that need to be installed
195            foreach (ListViewItem item in updatesLv.Items)
196                if (item.Checked)
197                {
[516]198                    item.Remove();
199                    item.SubItems.RemoveAt(1);
200                    item.SubItems.RemoveAt(1);
201                    downloadingLv.Items.Add(item);
[513]202
203                    updatesToInstall.Add((UpdateManager.Update)item.Tag);
204                }
[516]205                else
206                    uiUpdates.Remove((UpdateManager.Update)item.Tag);
[513]207
208            //Then run the thread.
209            downloader.RunWorkerAsync(updatesToInstall);
210        }
211
[514]212        /// <summary>
213        /// Background thread to do the downloading and installing of updates.
214        /// </summary>
215        /// <param name="sender">The object triggering this event/</param>
216        /// <param name="e">Event argument.</param>
[513]217        private void downloader_DoWork(object sender, DoWorkEventArgs e)
218        {
[514]219            try
220            {
221                updates.OnProgressEvent += downloader_ProgressChanged;
222                object downloadedUpdates = updates.DownloadUpdates((List<UpdateManager.Update>)e.Argument);
223                e.Result = downloadedUpdates;
224            }
225            finally
226            {
227                updates.OnProgressEvent -= downloader_ProgressChanged;
228            }
229        }
230
231        /// <summary>
232        /// Handles the download progress changed event.
233        /// </summary>
234        /// <param name="sender">The object triggering this event/</param>
235        /// <param name="e">Event argument.</param>
236        private void downloader_ProgressChanged(object sender, UpdateManager.ProgressEventArgs e)
237        {
238            if (InvokeRequired)
239            {
240                Invoke(new UpdateManager.ProgressEventFunction(downloader_ProgressChanged),
241                    sender, e);
242                return;
243            }
244
[516]245            UpdateData update = uiUpdates[(UpdateManager.Update)e.UserState];
246            int amountLeft = (int)((1 - e.ProgressPercentage) * update.Update.FileSize);
247
248            if (e is UpdateManager.ProgressErrorEventArgs)
249            {
250                update.Error = ((UpdateManager.ProgressErrorEventArgs)e).Exception;
251                update.LVItem.ImageIndex = 3;
252                update.LVItem.SubItems[1].Text = S._("Error");
253                update.LVItem.ToolTipText = update.Error.Message;
254            }
255            else
256            {
257                if (amountLeft == 0)
[513]258                {
[516]259                    update.LVItem.ImageIndex = -1;
260                    update.LVItem.SubItems[1].Text = S._("Downloaded");
[514]261                }
[516]262                else
263                {
264                    update.LVItem.ImageIndex = 0;
265                    update.LVItem.SubItems[1].Text = amountLeft.ToString();
266                }
267            }
[513]268
[514]269            downloadingItemLbl.Text = e.Message;
270            downloadingItemPb.Value = (int)(e.ProgressPercentage * 100);
271            downloadingOverallPb.Value = (int)(e.OverallProgressPercentage * 100);
[513]272
[514]273            long amountToDownload = 0;
[516]274            foreach (ListViewItem lvItem in downloadingLv.Items)
[514]275                try
276                {
277                    amountToDownload +=
[516]278                        Convert.ToInt32(lvItem.SubItems[1].Text);
[514]279                }
280                catch (FormatException)
281                {
282                }
[513]283
[514]284            downloadingOverallLbl.Text = string.Format(S._("Overall progress: {0} bytes left"),
285                amountToDownload);
286        }
287
288        /// <summary>
289        /// Handles the completion of updating event
290        /// </summary>
291        /// <param name="sender">The object triggering this event/</param>
292        /// <param name="e">Event argument.</param>
293        private void downloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
294        {
[515]295            if (e.Error != null)
296            {
297                MessageBox.Show(this, e.Error.Message, S._("Eraser"), MessageBoxButtons.OK,
298                    MessageBoxIcon.Error);
299                Close();
[516]300                return;
[515]301            }
302
[514]303            downloadingPnl.Visible = false;
304            installingPnl.Show();
305
306            foreach (ListViewItem item in downloadingLv.Items)
307            {
[516]308                item.Remove();
309                installingLv.Items.Add(item);
310
311                UpdateData update = uiUpdates[(UpdateManager.Update)item.Tag];
312                if (update.Error == null)
313                    item.SubItems[1].Text = string.Empty;
314                else
315                    item.SubItems[1].Text = string.Format(S._("Error: {0}"),
316                        update.Error.Message);
[514]317            }
318
319            installer.RunWorkerAsync(e.Result);
320        }
321        #endregion
322
323        #region Update installer
[515]324        /// <summary>
325        /// Background thread to install downloaded updates
326        /// </summary>
327        /// <param name="sender">The object triggering this event/</param>
328        /// <param name="e">Event argument.</param>
[514]329        private void installer_DoWork(object sender, DoWorkEventArgs e)
330        {
[513]331            try
332            {
[514]333                updates.OnProgressEvent += installer_ProgressChanged;
334                updates.InstallUpdates(e.Argument);
[513]335            }
336            finally
337            {
[514]338                updates.OnProgressEvent -= installer_ProgressChanged;
[513]339            }
340        }
341
[515]342        /// <summary>
343        /// Handles the progress events generated during update installation.
344        /// </summary>
345        /// <param name="sender">The object triggering this event/</param>
346        /// <param name="e">Event argument.</param>
[514]347        private void installer_ProgressChanged(object sender, ProgressChangedEventArgs e)
[513]348        {
[514]349            if (InvokeRequired)
350            {
351                Invoke(new UpdateManager.ProgressEventFunction(installer_ProgressChanged),
352                    sender, e);
353                return;
354            }
[513]355
[516]356            UpdateData update = uiUpdates[(UpdateManager.Update)e.UserState];
357            if (e is UpdateManager.ProgressErrorEventArgs)
358            {
359                update.Error = ((UpdateManager.ProgressErrorEventArgs)e).Exception;
360                update.LVItem.ImageIndex = 3;
361                update.LVItem.SubItems[1].Text = string.Format(S._("Error: {0}"),
362                    update.Error.Message);
363            }
364            else
365                switch (update.LVItem.ImageIndex)
[514]366                {
[516]367                    case -1:
368                        update.LVItem.ImageIndex = 1;
369                        break;
370                    case 1:
371                        update.LVItem.ImageIndex = 2;
372                        break;
[514]373                }
[513]374        }
375        #endregion
376
377        /// <summary>
378        /// The Update manager instance used by this form.
379        /// </summary>
380        UpdateManager updates;
[516]381
382        /// <summary>
383        /// Maps listview items to the UpdateManager.Update object.
384        /// </summary>
385        Dictionary<UpdateManager.Update, UpdateData> uiUpdates =
386            new Dictionary<UpdateManager.Update, UpdateData>();
387
388        /// <summary>
389        /// Manages information associated with the update.
390        /// </summary>
391        private class UpdateData
392        {
393            /// <summary>
394            /// Constructor.
395            /// </summary>
396            /// <param name="update">The UpdateManager.Update object containing the
397            /// internal representation of the update.</param>
398            /// <param name="item">The ListViewItem used for the display of the
399            /// update.</param>
400            public UpdateData(UpdateManager.Update update, ListViewItem item)
401            {
402                Update = update;
403                LVItem = item;
404                Error = null;
405            }
406
407            /// <summary>
408            /// The UpdateManager.Update object containing the internal representation
409            /// of the update.
410            /// </summary>
411            public UpdateManager.Update Update;
412
413            /// <summary>
414            /// The ListViewItem used for the display of the update.
415            /// </summary>
416            public ListViewItem LVItem;
417
418            /// <summary>
419            /// The error raised when downloading/installing the update, if any. Null
420            /// otherwise.
421            /// </summary>
422            public Exception Error;
423        }
[513]424    }
425
426    public class UpdateManager
427    {
428        /// <summary>
[517]429        /// Represents a download mirror.
430        /// </summary>
431        public struct Mirror
432        {
433            public Mirror(string location, string link)
434            {
435                Location = location;
436                Link = link;
437            }
438
439            /// <summary>
440            /// The location where the mirror is at.
441            /// </summary>
442            public string Location;
443
444            /// <summary>
445            /// The URL prefix to utilise the mirror.
446            /// </summary>
447            public string Link;
448
449            public override string ToString()
450            {
451                return Location;
452            }
453        }
454
455        /// <summary>
[513]456        /// Represents an update available on the server.
457        /// </summary>
458        public struct Update
459        {
460            public string Name;
461            public Version Version;
462            public string Publisher;
463            public string Architecture;
464            public long FileSize;
465            public string Link;
466        }
467
468        /// <summary>
[514]469        /// Specialised progress event argument, containing message describing
470        /// current action, and overall progress percentage.
471        /// </summary>
472        public class ProgressEventArgs : ProgressChangedEventArgs
473        {
474            public ProgressEventArgs(float progressPercentage, float overallPercentage,
475                object userState, string message)
476                : base((int)(progressPercentage * 100), userState)
477            {
478                this.progressPercentage = progressPercentage;
479                this.overallProgressPercentage = overallPercentage;
480                this.message = message;
481            }
482
483            /// <summary>
484            /// Gets the asynchronous task progress percentage.
485            /// </summary>
486            public new float ProgressPercentage
487            {
488                get
489                {
490                    return progressPercentage;
491                }
492            }
493
494            /// <summary>
495            /// Gets the asynchronous task overall progress percentage.
496            /// </summary>
497            public float OverallProgressPercentage
498            {
499                get
500                {
501                    return overallProgressPercentage;
502                }
503            }
504
505            /// <summary>
506            /// Gets the message associated with the current task.
507            /// </summary>
508            public string Message
509            {
510                get
511                {
512                    return message;
513                }
514            }
515
516            float progressPercentage;
517            float overallProgressPercentage;
518            string message;
519        }
520
521        /// <summary>
522        /// Extends the ProgressEventArgs further by allowing for the inclusion of
523        /// an exception.
524        /// </summary>
525        public class ProgressErrorEventArgs : ProgressEventArgs
526        {
527            /// <summary>
528            /// Constructor.
529            /// </summary>
530            /// <param name="e">The base ProgressEventArgs object.</param>
531            /// <param name="ex">The exception</param>
532            public ProgressErrorEventArgs(ProgressEventArgs e, Exception ex)
533                : base(e.ProgressPercentage, e.OverallProgressPercentage, e.UserState, e.Message)
534            {
535                this.exception = ex;
536            }
537
538            /// <summary>
539            /// The exception associated with the progress event.
540            /// </summary>
541            public Exception Exception
542            {
543                get
544                {
545                    return exception;
546                }
547            }
548
549            private Exception exception;
550        }
551
552        /// <summary>
[513]553        /// Retrieves the update list from the server.
554        /// </summary>
555        public void GetUpdates()
556        {
557            WebRequest.DefaultCachePolicy = new HttpRequestCachePolicy(
558                HttpRequestCacheLevel.Refresh);
559            HttpWebRequest req = (HttpWebRequest)
560                WebRequest.Create("http://eraser.sourceforge.net/updates?action=listupdates&" +
561                    "version=" + Assembly.GetExecutingAssembly().GetName().Version.ToString());
562           
563            using (WebResponse resp = req.GetResponse())
564            using (Stream strm = resp.GetResponseStream())
565            {
566                //Download the response
567                int bytesRead = 0;
568                byte[] buffer = new byte[16384];
569                List<byte> responseBuffer = new List<byte>();
570                while ((bytesRead = strm.Read(buffer, 0, buffer.Length)) != 0)
571                {
572                    byte[] tmpDest = new byte[bytesRead];
573                    Buffer.BlockCopy(buffer, 0, tmpDest, 0, bytesRead);
574                    responseBuffer.AddRange(tmpDest);
575
576                    float progress = responseBuffer.Count / (float)resp.ContentLength;
[514]577                    OnProgress(new ProgressEventArgs(progress, progress, null,
578                        string.Format(S._("{0} of {1} bytes downloaded"),
579                            responseBuffer.Count, resp.ContentLength)));
[513]580                }
581
582                //Parse it.
583                using (MemoryStream mStrm = new MemoryStream(responseBuffer.ToArray()))
584                    ParseUpdateList(mStrm);
585            }
586        }
587
588        /// <summary>
589        /// Parses the list of updates provided by the server
590        /// </summary>
591        /// <param name="strm">The stream containing the XML data.</param>
592        private void ParseUpdateList(Stream strm)
593        {
594            //Move the XmlReader to the root node
595            updates.Clear();
596            mirrors.Clear();
597            XmlReader rdr = XmlReader.Create(strm);
598            rdr.ReadToFollowing("updateList");
599
600            //Read the descendants of the updateList node (which are categories,
601            //except for the <mirrors> element)
602            XmlReader categories = rdr.ReadSubtree();
[517]603            bool cont = categories.ReadToDescendant("mirrors");
[513]604            while (cont)
605            {
606                if (categories.NodeType == XmlNodeType.Element)
607                {
608                    if (categories.Name == "mirrors")
609                    {
610                        Dictionary<string, string> mirrorsList =
611                            ParseMirror(categories.ReadSubtree());
612                        Dictionary<string, string>.Enumerator e = mirrorsList.GetEnumerator();
613                        while (e.MoveNext())
[517]614                            this.mirrors.Add(e.Current.Key,
615                                new Mirror(e.Current.Value, e.Current.Key));
[513]616                    }
617                    else
618                        updates.Add(categories.Name, ParseUpdateCategory(categories.ReadSubtree()));
619                }
620
621                cont = categories.Read();
622            }
623        }
624
625        /// <summary>
626        /// Parses a list of mirrors.
627        /// </summary>
628        /// <param name="rdr">The XML reader object representing the &lt;mirrors&gt; node</param>
629        /// <returns>The list of mirrors defined by the element.</returns>
630        private static Dictionary<string, string> ParseMirror(XmlReader rdr)
631        {
632            Dictionary<string, string> result = new Dictionary<string,string>();
633            if (!rdr.ReadToDescendant("mirror"))
634                return result;
635
636            //Load every element.
637            do
638            {
[517]639                if (rdr.NodeType != XmlNodeType.Element || rdr.Name != "mirror")
[513]640                    continue;
641
[517]642                string location = rdr.GetAttribute("location");
643                result.Add(rdr.ReadElementContentAsString(), location);
[513]644            }
645            while (rdr.ReadToNextSibling("mirror"));
646
647            return result;
648        }
649
650        /// <summary>
651        /// Parses a specific category and its assocaited updates.
652        /// </summary>
653        /// <param name="rdr">The XML reader object representing the element and its children.</param>
654        /// <returns>A list of updates in the category.</returns>
655        private static List<Update> ParseUpdateCategory(XmlReader rdr)
656        {
657            List<Update> result = new List<Update>();
658            if (!rdr.ReadToDescendant("item"))
659                return result;
660
661            //Load every element.
662            do
663            {
664                if (rdr.Name != "item")
665                    continue;
666
667                Update update = new Update();
668                update.Name = rdr.GetAttribute("name");
669                update.Version = new Version(rdr.GetAttribute("version"));
670                update.Publisher = rdr.GetAttribute("publisher");
671                update.Architecture = rdr.GetAttribute("architecture");
672                update.FileSize = Convert.ToInt64(rdr.GetAttribute("filesize"));
673                update.Link = rdr.ReadElementContentAsString();
674
675                result.Add(update);
676            }
677            while (rdr.ReadToNextSibling("item"));
678
679            return result;
680        }
681
682        /// <summary>
[514]683        /// Downloads the list of updates.
[513]684        /// </summary>
685        /// <param name="updates">The updates to retrieve and install.</param>
[514]686        /// <returns>An opaque object for use with InstallUpdates.</returns>
687        public object DownloadUpdates(List<Update> updates)
[513]688        {
689            //Create a folder to hold all our updates.
690            DirectoryInfo tempDir = new DirectoryInfo(Path.GetTempPath());
691            tempDir = tempDir.CreateSubdirectory("eraser" + Environment.TickCount.ToString());
692
[514]693            int currUpdate = 0;
694            Dictionary<string, Update> tempFilesMap = new Dictionary<string, Update>();
695            foreach (Update update in updates)
[513]696            {
[514]697                try
[513]698                {
[514]699                    //Decide on the URL to connect to. The Link of the update may
700                    //be a relative path (relative to the selected mirror) or an
701                    //absolute path (which we have no choice)
[517]702                    Uri reqUri = null;
703                    if (Uri.IsWellFormedUriString(update.Link, UriKind.Absolute))
704                        reqUri = new Uri(update.Link);
705                    else
706                        reqUri = new Uri(new Uri(SelectedMirror.Link), update.Link);
707                   
[514]708                    //Then grab the download.
709                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(reqUri);
710                    using (WebResponse resp = req.GetResponse())
[513]711                    {
[514]712                        byte[] tempBuffer = new byte[16384];
713                        string tempFilePath = Path.Combine(
714                            tempDir.FullName, string.Format("{0}-{1}", ++currUpdate,
[515]715                            Path.GetFileName(reqUri.GetComponents(UriComponents.Path, UriFormat.Unescaped))));
[513]716
[514]717                        using (Stream strm = resp.GetResponseStream())
718                        using (FileStream tempStrm = new FileStream(tempFilePath, FileMode.CreateNew))
719                        using (BufferedStream bufStrm = new BufferedStream(tempStrm))
[513]720                        {
[514]721                            //Copy the information into the file stream
722                            int readBytes = 0;
723                            while ((readBytes = strm.Read(tempBuffer, 0, tempBuffer.Length)) != 0)
[513]724                            {
[514]725                                bufStrm.Write(tempBuffer, 0, readBytes);
[513]726
[514]727                                //Compute progress
728                                float itemProgress = tempStrm.Position / (float)resp.ContentLength;
729                                float overallProgress = (currUpdate - 1 + itemProgress) / updates.Count;
730                                OnProgress(new ProgressEventArgs(itemProgress, overallProgress,
731                                    update, string.Format(S._("Downloading: {0}"), update.Name)));
[513]732                            }
[514]733                        }
[513]734
[514]735                        //Store the filename-to-update mapping
736                        tempFilesMap.Add(tempFilePath, update);
737
738                        //Let the event handler know the download is complete.
739                        OnProgress(new ProgressEventArgs(1.0f, currUpdate - 1,
740                            update, string.Format(S._("Downloaded: {0}"), update.Name)));
[513]741                    }
742                }
[515]743                catch (Exception e)
[514]744                {
745                    OnProgress(new ProgressErrorEventArgs(new ProgressEventArgs(1.0f,
[516]746                        (float)currUpdate / updates.Count, update,
[514]747                            string.Format(S._("Error downloading {0}: {1}"), update.Name, e.Message)),
748                        e));
749                }
750            }
[513]751
[514]752            return tempFilesMap;
753        }
754
755        public void InstallUpdates(object downloadObject)
756        {
757            Dictionary<string, Update> tempFiles = (Dictionary<string, Update>)downloadObject;
758            Dictionary<string, Update>.KeyCollection files = tempFiles.Keys;
759            int currItem = 0;
760
761            try
762            {
[513]763                foreach (string path in files)
764                {
[514]765                    Update item = tempFiles[path];
766                    float progress = (float)currItem++ / files.Count;
767                    OnProgress(new ProgressEventArgs(0.0f, progress,
768                        item, string.Format(S._("Installing {0}"), item.Name)));
769
770                    System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo();
771                    info.FileName = path;
772                    info.UseShellExecute = true;
773
774                    System.Diagnostics.Process process = System.Diagnostics.Process.Start(info);
775                    process.WaitForExit(Int32.MaxValue);
776                    if (process.ExitCode == 0)
777                        OnProgress(new ProgressEventArgs(1.0f, progress,
778                            item, string.Format(S._("Installed {0}"), item.Name)));
779                    else
780                        OnProgress(new ProgressErrorEventArgs(new ProgressEventArgs(1.0f,
781                            progress, item, string.Format(S._("Error installing {0}"), item.Name)),
782                            new Exception(string.Format(S._("The installer exited with an error code {0}"),
783                                process.ExitCode))));
[513]784                }
785            }
786            finally
787            {
788                //Clean up after ourselves
[514]789                foreach (string file in files)
790                {
791                    DirectoryInfo tempDir = null;
792                    {
793                        FileInfo info = new FileInfo(file);
794                        tempDir = info.Directory;
795                    }
796
797                    tempDir.Delete(true);
798                }
[513]799            }
800        }
801
802        /// <summary>
803        /// Prototype of the callback functions from this object.
804        /// </summary>
[514]805        /// <param name="sender">The source of the event.</param>
806        /// <param name="arg">The ProgressEventArgs object holding information
807        /// about the progress of the current operation.</param>
808        public delegate void ProgressEventFunction(object sender, ProgressEventArgs arg);
[513]809
810        /// <summary>
811        /// Called when the progress of the operation changes.
812        /// </summary>
813        public ProgressEventFunction OnProgressEvent;
814
815        /// <summary>
816        /// Helper function: invokes the OnProgressEvent delegate.
817        /// </summary>
[514]818        /// <param name="arg">The ProgressEventArgs object holding information
819        /// about the progress of the current operation.</param>
820        private void OnProgress(ProgressEventArgs arg)
[513]821        {
822            if (OnProgressEvent != null)
[514]823                OnProgressEvent(this, arg);
[513]824        }
825
826        /// <summary>
[517]827        /// Retrieves the list of mirrors which the server has indicated to exist.
828        /// </summary>
829        public Dictionary<string, Mirror> Mirrors
830        {
831            get
832            {
833                return mirrors;
834            }
835        }
836
837        /// <summary>
838        /// Gets or sets the active mirror to use to download mirrored updates.
839        /// </summary>
840        public Mirror SelectedMirror
841        {
842            get
843            {
844                if (selectedMirror.Link.Length == 0)
845                {
846                    Dictionary<string, Mirror>.Enumerator iter = mirrors.GetEnumerator();
847                    if (iter.MoveNext())
848                        return iter.Current.Value;
849                }
850                return selectedMirror;
851            }
852            set
853            {
854                foreach (Mirror mirror in Mirrors.Values)
855                    if (mirror.Equals(value))
856                    {
857                        selectedMirror = value;
858                        return;
859                    }
860
861                throw new IndexOutOfRangeException();
862            }
863        }
864
865        /// <summary>
[513]866        /// Retrieves the categories available.
867        /// </summary>
868        public Dictionary<string, List<Update>>.KeyCollection Categories
869        {
870            get
871            {
872                return updates.Keys;
873            }
874        }
875
876        /// <summary>
877        /// Retrieves all updates available.
878        /// </summary>
879        public Dictionary<string, List<Update>> Updates
880        {
881            get
882            {
883                return updates;
884            }
885        }
886
887        /// <summary>
888        /// Retrieves the updates in the given category.
889        /// </summary>
890        /// <param name="key">The category to retrieve.</param>
891        /// <returns>All updates in the given category.</returns>
892        public List<Update> this[string key]
893        {
894            get
895            {
896                return updates[key];
897            }
898        }
899
900        /// <summary>
901        /// The list of mirrors to download updates from.
902        /// </summary>
[517]903        private Dictionary<string, Mirror> mirrors =
904            new Dictionary<string, Mirror>();
[513]905
906        /// <summary>
[517]907        /// The currently selected mirror.
908        /// </summary>
909        private Mirror selectedMirror;
910
911        /// <summary>
[513]912        /// The list of updates downloaded.
913        /// </summary>
914        private Dictionary<string, List<Update>> updates =
915            new Dictionary<string, List<Update>>();
916    }
917}
Note: See TracBrowser for help on using the repository browser.