~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric

« back to all changes in this revision

Viewing changes to src/core/MonoDevelop.Ide/MonoDevelop.Ide.Updater/UpdateService.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-06-27 17:03:13 UTC
  • mto: (1.8.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: james.westby@ubuntu.com-20110627170313-6cvz3s19x6e9hqe9
ImportĀ upstreamĀ versionĀ 2.5.92+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
using System.Text;
32
32
using System.Linq;
33
33
using MonoDevelop.Core;
 
34
using MonoDevelop.Core.Setup;
34
35
using MonoDevelop.Ide;
 
36
using MonoDevelop.Ide.Gui;
 
37
using System.Globalization;
 
38
using Mono.Addins;
 
39
using Mono.Addins.Setup;
 
40
using MonoDevelop.Core.ProgressMonitoring;
35
41
 
36
42
namespace MonoDevelop.Ide.Updater
37
43
{
38
 
        enum UpdateLevel
39
 
        {
40
 
                Stable = 0,
41
 
                Beta = 1,
42
 
                Alpha = 2,
43
 
                Test = 3
44
 
        }
45
 
        
46
44
        static class UpdateService
47
45
        {
48
46
                const int formatVersion = 1;
50
48
                const string updateLevelKey = "AppUpdate.UpdateLevel";
51
49
                
52
50
                static UpdateInfo[] updateInfos;
 
51
                static List<string> tags = new List<string> ();
53
52
                
54
53
                static UpdateInfo[] LoadUpdateInfos ()
55
54
                {
77
76
                static UpdateService ()
78
77
                {
79
78
                        updateInfos = LoadUpdateInfos ();
 
79
                        AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Ide/UpdateTags", OnTagExtensionChanged);
 
80
                }
 
81
                
 
82
                static void OnTagExtensionChanged (object sender, ExtensionNodeEventArgs args)
 
83
                {
 
84
                        if (args.Change == ExtensionChange.Add)
 
85
                                tags.Add (args.ExtensionNode.Id);
 
86
                        else
 
87
                                tags.Remove (args.ExtensionNode.Id);
 
88
                }
 
89
                
 
90
                public static void AddUpdateTag (string tag)
 
91
                {
 
92
                        tags.Add (tag);
 
93
                }
 
94
                
 
95
                public static void RemoteUpdateTag (string tag)
 
96
                {
 
97
                        tags.Remove (tag);
80
98
                }
81
99
                
82
100
                public static bool CanUpdate {
83
 
                        get { return DefaultUpdateInfos.Length > 0; }
 
101
                        get { return true; }
84
102
                }
85
103
                
86
104
                public static UpdateInfo[] DefaultUpdateInfos {
159
177
                
160
178
                public static void QueryUpdateServer (UpdateInfo[] updateInfos, UpdateLevel level, Action<UpdateResult> callback)
161
179
                {
 
180
                        if (!string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("MONODEVELOP_UPDATER_TEST")))
 
181
                                level = UpdateLevel.Test;
 
182
                        
162
183
                        if (updateInfos == null || updateInfos.Length == 0) {
163
 
                                string error = GettextCatalog.GetString ("No updatable products detected");
164
 
                                callback (new UpdateResult (null, level, error, null));
 
184
                                QueryAddinUpdates (level, callback);
165
185
                                return;
166
186
                        }
167
187
                        
172
192
                        foreach (var info in updateInfos)
173
193
                                query.AppendFormat ("&{0}={1}", info.AppId, info.VersionId);
174
194
                        
175
 
                        if (!string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("MONODEVELOP_UPDATER_TEST")))
176
 
                                level = UpdateLevel.Test;
177
 
                        
178
195
                        if (level != UpdateLevel.Stable) {
179
196
                                query.Append ("&level=");
180
197
                                query.Append (level.ToString ().ToLower ());
213
230
                        
214
231
                        try {
215
232
                                using (var response = (HttpWebResponse) request.EndGetResponse (ar)) {
216
 
                                        var encoding = Encoding.GetEncoding (response.CharacterSet);
 
233
                                        var encoding = !string.IsNullOrEmpty (response.CharacterSet) ? Encoding.GetEncoding (response.CharacterSet) : Encoding.UTF8;
217
234
                                        using (var reader = new StreamReader (response.GetResponseStream(), encoding)) {
218
235
                                                var doc = System.Xml.Linq.XDocument.Load (reader);
219
236
                                                updates = (from x in doc.Root.Elements ("Application")
234
251
                                                        }).ToList ();
235
252
                                        }
236
253
                                }
 
254
                                
 
255
                                CheckAddinUpdates (updates, level);
 
256
                                
237
257
                        } catch (Exception ex) {
 
258
                                LoggingService.LogError ("Could not retrieve update information", ex);
238
259
                                error = GettextCatalog.GetString ("Error retrieving update information");
239
260
                                errorDetail = ex;
240
261
                        }
 
262
                        
241
263
                        callback (new UpdateResult (updates, level, error, errorDetail));
242
264
                }
 
265
                
 
266
                public static void QueryAddinUpdates (UpdateLevel level, Action<UpdateResult> callback)
 
267
                {
 
268
                        System.Threading.ThreadPool.QueueUserWorkItem (delegate {
 
269
                                List<Update> updates = new List<Update> ();
 
270
                                string error = null;
 
271
                                Exception errorDetail = null;
 
272
                                
 
273
                                try {
 
274
                                        CheckAddinUpdates (updates, level);
 
275
                                        
 
276
                                } catch (Exception ex) {
 
277
                                        LoggingService.LogError ("Could not retrieve update information", ex);
 
278
                                        error = GettextCatalog.GetString ("Error retrieving update information");
 
279
                                        errorDetail = ex;
 
280
                                }
 
281
                                
 
282
                                callback (new UpdateResult (updates, level, error, errorDetail));
 
283
                        });
 
284
                }
 
285
                
 
286
                static void CheckAddinUpdates (List<Update> updates, UpdateLevel level)
 
287
                {
 
288
                        for (UpdateLevel n=UpdateLevel.Stable; n<=level; n++)
 
289
                                Runtime.AddinSetupService.RegisterMainRepository ((UpdateLevel)n, true);
 
290
                        
 
291
                        AddinUpdateHandler.QueryAddinUpdates ();
 
292
                        
 
293
                        updates.AddRange (GetAddinUpdates (UpdateLevel.Stable));
 
294
                        if (level >= UpdateLevel.Beta)
 
295
                                updates.AddRange (GetAddinUpdates (UpdateLevel.Beta));
 
296
                        if (level >= UpdateLevel.Alpha)
 
297
                                updates.AddRange (GetAddinUpdates (UpdateLevel.Alpha));
 
298
                        if (level == UpdateLevel.Test)
 
299
                                updates.AddRange (GetAddinUpdates (UpdateLevel.Test));
 
300
                }
 
301
                
 
302
                static IEnumerable<Update> GetAddinUpdates (UpdateLevel level)
 
303
                {
 
304
                        List<Update> res = new List<Update> ();
 
305
                        string url = Runtime.AddinSetupService.GetMainRepositoryUrl (level);
 
306
                        List<AddinRepositoryEntry> list = new List<AddinRepositoryEntry> ();
 
307
                        list.AddRange (Runtime.AddinSetupService.Repositories.GetAvailableUpdates (url));
 
308
                        FilterOldVersions (list);
 
309
                        foreach (var ventry in list) {
 
310
                                var entry = ventry;
 
311
                                UpdateRank rank = GetUpdateRank (entry.Addin.Properties.GetPropertyValue ("UpdateRank"));
 
312
                                if (rank != UpdateRank.Important)
 
313
                                        continue;
 
314
                                string sdate = entry.Addin.Properties.GetPropertyValue ("ReleaseDate");
 
315
                                DateTime date;
 
316
                                if (!string.IsNullOrEmpty (sdate))
 
317
                                        date = DateTime.Parse (sdate, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
 
318
                                else
 
319
                                        date = DateTime.Now;
 
320
                                res.Add (new Update () {
 
321
                                        Name = entry.Addin.Name,
 
322
                                        InstallAction = (m) => InstallAddin (m, entry),
 
323
                                        Version = entry.Addin.Version,
 
324
                                        Level = level,
 
325
                                        Date = date,
 
326
                                        Releases = ParseReleases (entry.Addin.Id, entry).ToList ()
 
327
                                });
 
328
                        }
 
329
                        return res;
 
330
                }
 
331
                
 
332
                static void FilterOldVersions (List<AddinRepositoryEntry> addins)
 
333
                {
 
334
                        Dictionary<string,string> versions = new Dictionary<string, string> ();
 
335
                        foreach (AddinRepositoryEntry a in addins) {
 
336
                                string last;
 
337
                                string id, version;
 
338
                                Addin.GetIdParts (a.Addin.Id, out id, out version);
 
339
                                if (!versions.TryGetValue (id, out last) || Addin.CompareVersions (last, version) > 0)
 
340
                                        versions [id] = version;
 
341
                        }
 
342
                        for (int n=0; n<addins.Count; n++) {
 
343
                                AddinRepositoryEntry a = addins [n];
 
344
                                string id, version;
 
345
                                Addin.GetIdParts (a.Addin.Id, out id, out version);
 
346
                                if (versions [id] != version)
 
347
                                        addins.RemoveAt (n--);
 
348
                        }
 
349
                }
 
350
                
 
351
                static IEnumerable<Release> ParseReleases (string addinId, AddinRepositoryEntry entry)
 
352
                {
 
353
                        // Format of release notes is:
 
354
                        // {{version1,date1}} release note text {{version2,date2}} release note text ...
 
355
                        // Releases msyu
 
356
                        // for example:
 
357
                        // {{1.1,2011-01-10}} Release notes for 1.1 {{1.2,2011-03-22}} Release notes for 2.3
 
358
                        
 
359
                        string releaseNotes = entry.Addin.Properties.GetPropertyValue ("ReleaseNotes");
 
360
                        if (releaseNotes.Length == 0) {
 
361
                                string file = entry.Addin.Properties.GetPropertyValue ("ReleaseNotesFile");
 
362
                                if (file.Length > 0) {
 
363
                                        IAsyncResult res = entry.BeginDownloadSupportFile (file, null, null);
 
364
                                        res.AsyncWaitHandle.WaitOne ();
 
365
                                        try {
 
366
                                                using (Stream s = entry.EndDownloadSupportFile (res)) {
 
367
                                                        StreamReader sr = new StreamReader (s);
 
368
                                                        releaseNotes = sr.ReadToEnd ();
 
369
                                                }
 
370
                                        } catch (Exception ex) {
 
371
                                                LoggingService.LogError ("Could not download release notes", ex);
 
372
                                        }
 
373
                                }
 
374
                        }
 
375
                        
 
376
                        if (releaseNotes.Length == 0)
 
377
                                yield break;
 
378
                        
 
379
                        var addin = AddinManager.Registry.GetAddin (Addin.GetIdName (addinId));
 
380
                        string currentVersion = addin != null ? addin.Version : null;
 
381
                        
 
382
                        int i = releaseNotes.IndexOf ("{{");
 
383
                        while (i != -1) {
 
384
                                int j = releaseNotes.IndexOf ("}}", i + 2);
 
385
                                if (j == -1)
 
386
                                        break;
 
387
                                string[] h = releaseNotes.Substring (i + 2, j - i - 2).Trim ().Split (',');
 
388
                                if (h.Length != 2)
 
389
                                        break;
 
390
                                DateTime date;
 
391
                                if (!DateTime.TryParse (h[1].Trim (), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out date))
 
392
                                        break;
 
393
                                int k = releaseNotes.IndexOf ("{{", j + 2);
 
394
                                string version = h[0].Trim ();
 
395
                                if (currentVersion == null || Addin.CompareVersions (currentVersion, version) > 0) {
 
396
                                        string txt = k != -1 ? releaseNotes.Substring (j + 2, k - j - 2) : releaseNotes.Substring (j + 2);
 
397
                                        yield return new Release () {
 
398
                                                Version = version,
 
399
                                                Date = date,
 
400
                                                Notes = txt.Trim (' ','\t','\r','\n')
 
401
                                        };
 
402
                                }
 
403
                                i = k;
 
404
                        }
 
405
                }
 
406
                
 
407
                static IAsyncOperation InstallAddin (IProgressMonitor monitor, AddinRepositoryEntry addin)
 
408
                {
 
409
                        // Add-in engine changes must be done in the gui thread since mono.addins is not thread safe
 
410
                        DispatchService.GuiDispatch (delegate {
 
411
                                using (monitor) {
 
412
                                        Runtime.AddinSetupService.Install (new ProgressStatusMonitor (monitor), addin);
 
413
                                }
 
414
                        });
 
415
                        return monitor.AsyncOperation;
 
416
                }
 
417
 
 
418
                /// <summary>
 
419
                /// Parses an update rank string
 
420
                /// </summary>
 
421
                /// <returns>
 
422
                /// The update rank
 
423
                /// </returns>
 
424
                /// <param name='updateRankString'>
 
425
                /// Update rank string.
 
426
                /// </param>
 
427
                /// <remarks>
 
428
                /// This method parses an update rank string. If the rank is conditioned to an update tag, it will
 
429
                /// check the presence of the tag
 
430
                /// </remarks>
 
431
                internal static UpdateRank GetUpdateRank (string updateRankString)
 
432
                {
 
433
                        string slevel = null;
 
434
                        foreach (string cond in updateRankString.Split (new char[] {' '}, StringSplitOptions.RemoveEmptyEntries)) {
 
435
                                int i = cond.IndexOf (':');
 
436
                                if (i == -1) {
 
437
                                        slevel = cond;
 
438
                                        break;
 
439
                                }
 
440
                                if (tags.Contains (cond.Substring (0, i))) {
 
441
                                        slevel = cond.Substring (i+1);
 
442
                                        break;
 
443
                                }
 
444
                        }
 
445
                        UpdateRank level;
 
446
                        if (slevel != null && Enum.TryParse<UpdateRank> (slevel, out level))
 
447
                                return level;
 
448
                        else
 
449
                                return UpdateRank.Normal;
 
450
                }
243
451
        }
244
452
}