~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/SetupService.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// SetupService.cs
 
3
//
 
4
// Author:
 
5
//   Lluis Sanchez Gual
 
6
//
 
7
// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
 
8
//
 
9
// Permission is hereby granted, free of charge, to any person obtaining
 
10
// a copy of this software and associated documentation files (the
 
11
// "Software"), to deal in the Software without restriction, including
 
12
// without limitation the rights to use, copy, modify, merge, publish,
 
13
// distribute, sublicense, and/or sell copies of the Software, and to
 
14
// permit persons to whom the Software is furnished to do so, subject to
 
15
// the following conditions:
 
16
// 
 
17
// The above copyright notice and this permission notice shall be
 
18
// included in all copies or substantial portions of the Software.
 
19
// 
 
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
27
//
 
28
 
 
29
 
 
30
using System;
 
31
using System.Xml;
 
32
using System.IO;
 
33
using System.Collections;
 
34
using System.Collections.Generic;
 
35
using ICSharpCode.SharpZipLib.Zip;
 
36
using Mono.Addins.Description;
 
37
using Mono.Addins.Setup.ProgressMonitoring;
 
38
using Microsoft.Win32;
 
39
using System.Diagnostics;
 
40
using Mono.PkgConfig;
 
41
 
 
42
namespace Mono.Addins.Setup
 
43
{
 
44
        /// <summary>
 
45
        /// Provides tools for managing add-ins
 
46
        /// </summary>
 
47
        /// <remarks>
 
48
        /// This class can be used to manage the add-ins of an application. It allows installing and uninstalling
 
49
        /// add-ins, taking into account add-in dependencies. It provides methods for installing add-ins from on-line
 
50
        /// repositories and tools for generating those repositories.
 
51
        /// </remarks>
 
52
        public class SetupService
 
53
        {
 
54
                RepositoryRegistry repositories;
 
55
                string applicationNamespace;
 
56
                string installDirectory;
 
57
                AddinStore store;
 
58
                AddinSystemConfiguration config;
 
59
                const string addinFilesDir = "_addin_files";
 
60
                
 
61
                AddinRegistry registry;
 
62
                
 
63
                /// <summary>
 
64
                /// Initializes a new instance
 
65
                /// </summary>
 
66
                /// <remarks>
 
67
                /// If the add-in manager is initialized (AddinManager.Initialize has been called), then this instance
 
68
                /// will manage the add-in registry of the initialized engine.
 
69
                /// </remarks>
 
70
                public SetupService ()
 
71
                {
 
72
                        if (AddinManager.IsInitialized)
 
73
                                registry = AddinManager.Registry;
 
74
                        else
 
75
                                registry = AddinRegistry.GetGlobalRegistry ();
 
76
                        
 
77
                        repositories = new RepositoryRegistry (this);
 
78
                        store = new AddinStore (this);
 
79
                }
 
80
                
 
81
                /// <summary>
 
82
                /// Initializes a new instance
 
83
                /// </summary>
 
84
                /// <param name="registry">
 
85
                /// Add-in registry to manage
 
86
                /// </param>
 
87
                public SetupService (AddinRegistry registry)
 
88
                {
 
89
                        this.registry = registry;
 
90
                        repositories = new RepositoryRegistry (this);
 
91
                        store = new AddinStore (this);
 
92
                }
 
93
                
 
94
                /// <summary>
 
95
                /// The add-in registry being managed
 
96
                /// </summary>
 
97
                public AddinRegistry Registry {
 
98
                        get { return registry; }
 
99
                }
 
100
                
 
101
                internal string RepositoryCachePath {
 
102
                        get { return Path.Combine (registry.RegistryPath, "repository-cache"); }
 
103
                }
 
104
                
 
105
                string RootConfigFile {
 
106
                        get { return Path.Combine (registry.RegistryPath, "addins-setup.config"); }
 
107
                }
 
108
                
 
109
                /// <summary>
 
110
                /// Default add-in namespace of the application (optional). If set, only add-ins that belong to that namespace
 
111
                /// will be shown in add-in lists.
 
112
                /// </summary>
 
113
                public string ApplicationNamespace {
 
114
                        get { return applicationNamespace; }
 
115
                        set { applicationNamespace = value; }
 
116
                }
 
117
                
 
118
                /// <summary>
 
119
                /// Directory where to install add-ins. If not specified, the 'addins' subdirectory of the
 
120
                /// registry location is used.
 
121
                /// </summary>
 
122
                public string InstallDirectory {
 
123
                        get {
 
124
                                if (installDirectory != null && installDirectory.Length > 0)
 
125
                                        return installDirectory;
 
126
                                else
 
127
                                        return registry.DefaultAddinsFolder;
 
128
                        }
 
129
                        set { installDirectory = value; }
 
130
                }
 
131
                
 
132
                /// <summary>
 
133
                /// Returns a RepositoryRegistry which can be used to manage on-line repository references
 
134
                /// </summary>
 
135
                public RepositoryRegistry Repositories {
 
136
                        get { return repositories; }
 
137
                }
 
138
                
 
139
                internal AddinStore Store {
 
140
                        get { return store; }
 
141
                }
 
142
                
 
143
                /// <summary>
 
144
                /// Resolves add-in dependencies.
 
145
                /// </summary>
 
146
                /// <param name="statusMonitor">
 
147
                /// Progress monitor where to show progress status
 
148
                /// </param>
 
149
                /// <param name="addins">
 
150
                /// List of add-ins to check
 
151
                /// </param>
 
152
                /// <param name="resolved">
 
153
                /// Packages that need to be installed.
 
154
                /// </param>
 
155
                /// <param name="toUninstall">
 
156
                /// Packages that need to be uninstalled.
 
157
                /// </param>
 
158
                /// <param name="unresolved">
 
159
                /// Add-in dependencies that could not be resolved.
 
160
                /// </param>
 
161
                /// <returns>
 
162
                /// True if all dependencies could be resolved.
 
163
                /// </returns>
 
164
                /// <remarks>
 
165
                /// This method can be used to get a list of all packages that have to be installed in order to install
 
166
                /// an add-in or set of add-ins. The list of packages to install will include the package that provides the
 
167
                /// add-in, and all packages that provide the add-in dependencies. In some cases, packages may need to
 
168
                /// be installed (for example, when an installed add-in needs to be upgraded).
 
169
                /// </remarks>
 
170
                public bool ResolveDependencies (IProgressStatus statusMonitor, AddinRepositoryEntry[] addins, out PackageCollection resolved, out PackageCollection toUninstall, out DependencyCollection unresolved)
 
171
                {
 
172
                        return store.ResolveDependencies (statusMonitor, addins, out resolved, out toUninstall, out unresolved);
 
173
                }
 
174
                
 
175
                /// <summary>
 
176
                /// Resolves add-in dependencies.
 
177
                /// </summary>
 
178
                /// <param name="statusMonitor">
 
179
                /// Progress monitor where to show progress status
 
180
                /// </param>
 
181
                /// <param name="packages">
 
182
                /// Packages that need to be installed.
 
183
                /// </param>
 
184
                /// <param name="toUninstall">
 
185
                /// Packages that need to be uninstalled.
 
186
                /// </param>
 
187
                /// <param name="unresolved">
 
188
                /// Add-in dependencies that could not be resolved.
 
189
                /// </param>
 
190
                /// <returns>
 
191
                /// True if all dependencies could be resolved.
 
192
                /// </returns>
 
193
                /// <remarks>
 
194
                /// This method can be used to get a list of all packages that have to be installed in order to satisfy
 
195
                /// the dependencies of a package or set of packages. The 'packages' argument must have the list of packages
 
196
                /// to be resolved. When resolving dependencies, if there is any additional package that needs to be installed,
 
197
                /// it will be added to the same 'packages' collection. In some cases, packages may need to
 
198
                /// be installed (for example, when an installed add-in needs to be upgraded). Those packages will be added
 
199
                /// to the 'toUninstall' collection. Packages that could not be resolved are added to the 'unresolved'
 
200
                /// collection.
 
201
                /// </remarks>
 
202
                public bool ResolveDependencies (IProgressStatus statusMonitor, PackageCollection packages, out PackageCollection toUninstall, out DependencyCollection unresolved)
 
203
                {
 
204
                        return store.ResolveDependencies (statusMonitor, packages, out toUninstall, out unresolved);
 
205
                }
 
206
                
 
207
                /// <summary>
 
208
                /// Installs add-in packages
 
209
                /// </summary>
 
210
                /// <param name="statusMonitor">
 
211
                /// Progress monitor where to show progress status
 
212
                /// </param>
 
213
                /// <param name="files">
 
214
                /// Paths to the packages to install
 
215
                /// </param>
 
216
                /// <returns>
 
217
                /// True if the installation succeeded
 
218
                /// </returns>
 
219
                public bool Install (IProgressStatus statusMonitor, params string[] files)
 
220
                {
 
221
                        return store.Install (statusMonitor, files);
 
222
                }
 
223
                
 
224
                /// <summary>
 
225
                /// Installs add-in packages from on-line repositories
 
226
                /// </summary>
 
227
                /// <param name="statusMonitor">
 
228
                /// Progress monitor where to show progress status
 
229
                /// </param>
 
230
                /// <param name="addins">
 
231
                /// References to the add-ins to be installed
 
232
                /// </param>
 
233
                /// <returns>
 
234
                /// True if the installation succeeded
 
235
                /// </returns>
 
236
                public bool Install (IProgressStatus statusMonitor, params AddinRepositoryEntry[] addins)
 
237
                {
 
238
                        return store.Install (statusMonitor, addins);
 
239
                }
 
240
                
 
241
                /// <summary>
 
242
                /// Installs add-in packages
 
243
                /// </summary>
 
244
                /// <param name="statusMonitor">
 
245
                /// Progress monitor where to show progress status
 
246
                /// </param>
 
247
                /// <param name="packages">
 
248
                /// Packages to install
 
249
                /// </param>
 
250
                /// <returns>
 
251
                /// True if the installation succeeded
 
252
                /// </returns>
 
253
                public bool Install (IProgressStatus statusMonitor, PackageCollection packages)
 
254
                {
 
255
                        return store.Install (statusMonitor, packages);
 
256
                }
 
257
                
 
258
                /// <summary>
 
259
                /// Uninstalls an add-in.
 
260
                /// </summary>
 
261
                /// <param name="statusMonitor">
 
262
                /// Progress monitor where to show progress status
 
263
                /// </param>
 
264
                /// <param name="id">
 
265
                /// Full identifier of the add-in to uninstall.
 
266
                /// </param>
 
267
                public void Uninstall (IProgressStatus statusMonitor, string id)
 
268
                {
 
269
                        store.Uninstall (statusMonitor, id);
 
270
                }
 
271
                
 
272
                /// <summary>
 
273
                /// Uninstalls a set of add-ins
 
274
                /// </summary>
 
275
                /// <param name='statusMonitor'>
 
276
                /// Progress monitor where to show progress status
 
277
                /// </param>
 
278
                /// <param name='ids'>
 
279
                /// Full identifiers of the add-ins to uninstall.
 
280
                /// </param>
 
281
                public void Uninstall (IProgressStatus statusMonitor, IEnumerable<string> ids)
 
282
                {
 
283
                        store.Uninstall (statusMonitor, ids);
 
284
                }
 
285
                
 
286
                /// <summary>
 
287
                /// Gets information about an add-in
 
288
                /// </summary>
 
289
                /// <param name="addin">
 
290
                /// The add-in
 
291
                /// </param>
 
292
                /// <returns>
 
293
                /// Add-in header data
 
294
                /// </returns>
 
295
                public static AddinHeader GetAddinHeader (Addin addin)
 
296
                {
 
297
                        return AddinInfo.ReadFromDescription (addin.Description);
 
298
                }
 
299
                
 
300
                /// <summary>
 
301
                /// Gets a list of add-ins which depend on an add-in
 
302
                /// </summary>
 
303
                /// <param name="id">
 
304
                /// Full identifier of an add-in.
 
305
                /// </param>
 
306
                /// <param name="recursive">
 
307
                /// When set to True, dependencies will be gathered recursivelly
 
308
                /// </param>
 
309
                /// <returns>
 
310
                /// List of dependent add-ins.
 
311
                /// </returns>
 
312
                /// <remarks>
 
313
                /// This methods returns a list of add-ins which have the add-in identified by 'id' as a direct
 
314
                /// (or indirect if recursive=True) dependency.
 
315
                /// </remarks>
 
316
                public Addin[] GetDependentAddins (string id, bool recursive)
 
317
                {
 
318
                        return store.GetDependentAddins (id, recursive);
 
319
                }
 
320
                
 
321
                /// <summary>
 
322
                /// Packages an add-in
 
323
                /// </summary>
 
324
                /// <param name="statusMonitor">
 
325
                /// Progress monitor where to show progress status
 
326
                /// </param>
 
327
                /// <param name="targetDirectory">
 
328
                /// Directory where to generate the package
 
329
                /// </param>
 
330
                /// <param name="filePaths">
 
331
                /// Paths to the add-ins to be packaged. Paths can be either the main assembly of an add-in, or an add-in
 
332
                /// manifest (.addin or .addin.xml).
 
333
                /// </param>
 
334
                /// <remarks>
 
335
                /// This method can be used to create a package for an add-in, which can then be pushed to an on-line
 
336
                /// repository. The package will include the main assembly or manifest of the add-in and any external
 
337
                /// file declared in the add-in metadata.
 
338
                /// </remarks>
 
339
                public string[] BuildPackage (IProgressStatus statusMonitor, string targetDirectory, params string[] filePaths)
 
340
                {
 
341
                        List<string> outFiles = new List<string> ();
 
342
                        foreach (string file in filePaths) {
 
343
                                string f = BuildPackageInternal (statusMonitor, targetDirectory, file);
 
344
                                if (f != null)
 
345
                                        outFiles.Add (f);
 
346
                        }
 
347
                        return outFiles.ToArray ();
 
348
                }
 
349
                
 
350
                string BuildPackageInternal (IProgressStatus monitor, string targetDirectory, string filePath)
 
351
                {
 
352
                        AddinDescription conf = registry.GetAddinDescription (monitor, filePath);
 
353
                        if (conf == null) {
 
354
                                monitor.ReportError ("Could not read add-in file: " + filePath, null);
 
355
                                return null;
 
356
                        }
 
357
                        
 
358
                        string basePath = Path.GetDirectoryName (filePath);
 
359
                        
 
360
                        if (targetDirectory == null)
 
361
                                targetDirectory = basePath;
 
362
 
 
363
                        // Generate the file name
 
364
                        
 
365
                        string name;
 
366
                        if (conf.LocalId.Length == 0)
 
367
                                name = Path.GetFileNameWithoutExtension (filePath);
 
368
                        else
 
369
                                name = conf.LocalId;
 
370
                        name = Addin.GetFullId (conf.Namespace, name, conf.Version);
 
371
                        name = name.Replace (',','_').Replace (".__", ".");
 
372
                        
 
373
                        string outFilePath = Path.Combine (targetDirectory, name) + ".mpack";
 
374
                        
 
375
                        ZipOutputStream s = new ZipOutputStream (File.Create (outFilePath));
 
376
                        s.SetLevel(5);
 
377
                        
 
378
                        // Generate a stripped down description of the add-in in a file, since the complete
 
379
                        // description may be declared as assembly attributes
 
380
                        
 
381
                        XmlDocument doc = new XmlDocument ();
 
382
                        doc.PreserveWhitespace = false;
 
383
                        doc.LoadXml (conf.SaveToXml ().OuterXml);
 
384
                        CleanDescription (doc.DocumentElement);
 
385
                        MemoryStream ms = new MemoryStream ();
 
386
                        XmlTextWriter tw = new XmlTextWriter (ms, System.Text.Encoding.UTF8);
 
387
                        tw.Formatting = Formatting.Indented;
 
388
                        doc.WriteTo (tw);
 
389
                        tw.Flush ();
 
390
                        byte[] data = ms.ToArray ();
 
391
                        
 
392
                        ZipEntry infoEntry = new ZipEntry ("addin.info");
 
393
                        s.PutNextEntry (infoEntry);
 
394
                        s.Write (data, 0, data.Length);
 
395
                        
 
396
                        // Now add the add-in files
 
397
                        
 
398
                        ArrayList list = new ArrayList ();
 
399
                        if (!conf.AllFiles.Contains (Path.GetFileName (filePath)))
 
400
                                list.Add (Path.GetFileName (filePath));
 
401
                        foreach (string f in conf.AllFiles) {
 
402
                                list.Add (f);
 
403
                        }
 
404
                        
 
405
                        foreach (var prop in conf.Properties) {
 
406
                                try {
 
407
                                        if (File.Exists (Path.Combine (basePath, prop.Value)))
 
408
                                                list.Add (prop.Value);
 
409
                                } catch {
 
410
                                        // Ignore errors
 
411
                                }
 
412
                        }
 
413
                        
 
414
                        monitor.Log ("Creating package " + Path.GetFileName (outFilePath));
 
415
                        
 
416
                        foreach (string file in list) {
 
417
                                string fp = Path.Combine (basePath, file);
 
418
                                using (FileStream fs = File.OpenRead (fp)) {
 
419
                                        byte[] buffer = new byte [fs.Length];
 
420
                                        fs.Read (buffer, 0, buffer.Length);
 
421
                                        
 
422
                                        ZipEntry entry = new ZipEntry (file);
 
423
                                        s.PutNextEntry (entry);
 
424
                                        s.Write (buffer, 0, buffer.Length);
 
425
                                }
 
426
                        }
 
427
                        
 
428
                        s.Finish();
 
429
                        s.Close();              
 
430
                        return outFilePath;
 
431
                }
 
432
                
 
433
                void CleanDescription (XmlElement parent)
 
434
                {
 
435
                        ArrayList todelete = new ArrayList ();
 
436
                        
 
437
                        foreach (XmlNode nod in parent.ChildNodes) {
 
438
                                XmlElement elem = nod as XmlElement;
 
439
                                if (elem == null) {
 
440
                                        todelete.Add (nod);
 
441
                                        continue;
 
442
                                }
 
443
                                if (elem.LocalName == "Module")
 
444
                                        CleanDescription (elem);
 
445
                                else if (elem.LocalName != "Dependencies" && elem.LocalName != "Runtime" && elem.LocalName != "Header")
 
446
                                        todelete.Add (elem);
 
447
                        }
 
448
                        foreach (XmlNode e in todelete)
 
449
                                parent.RemoveChild (e);
 
450
                }
 
451
                
 
452
                /// <summary>
 
453
                /// Generates an on-line repository
 
454
                /// </summary>
 
455
                /// <param name="statusMonitor">
 
456
                /// Progress monitor where to show progress status
 
457
                /// </param>
 
458
                /// <param name="path">
 
459
                /// Path to the directory that contains the add-ins and that is going to be published
 
460
                /// </param>
 
461
                /// <remarks>
 
462
                /// This method generates the index files required to publish a directory as an online repository
 
463
                /// of add-ins.
 
464
                /// </remarks>
 
465
                public void BuildRepository (IProgressStatus statusMonitor, string path)
 
466
                {
 
467
                        string mainPath = Path.Combine (path, "main.mrep");
 
468
                        ArrayList allAddins = new ArrayList ();
 
469
                        
 
470
                        Repository rootrep = (Repository) AddinStore.ReadObject (mainPath, typeof(Repository));
 
471
                        if (rootrep == null)
 
472
                                rootrep = new Repository ();
 
473
                        
 
474
                        IProgressMonitor monitor = ProgressStatusMonitor.GetProgressMonitor (statusMonitor);
 
475
                        BuildRepository (monitor, rootrep, path, "root.mrep", allAddins);
 
476
                        AddinStore.WriteObject (mainPath, rootrep);
 
477
                        GenerateIndexPage (rootrep, allAddins, path);
 
478
                        monitor.Log.WriteLine ("Updated main.mrep");
 
479
                }
 
480
                
 
481
                void BuildRepository (IProgressMonitor monitor, Repository rootrep, string rootPath, string relFilePath, ArrayList allAddins)
 
482
                {
 
483
                        DateTime lastModified = DateTime.MinValue;
 
484
                        
 
485
                        string mainFile = Path.Combine (rootPath, relFilePath);
 
486
                        string mainPath = Path.GetDirectoryName (mainFile);
 
487
                        string supportFileDir = Path.Combine (mainPath, addinFilesDir);
 
488
                        
 
489
                        if (File.Exists (mainFile))
 
490
                                lastModified = File.GetLastWriteTime (mainFile);
 
491
                        
 
492
                        Repository mainrep = (Repository) AddinStore.ReadObject (mainFile, typeof(Repository));
 
493
                        if (mainrep == null) {
 
494
                                mainrep = new Repository ();
 
495
                        }
 
496
                        
 
497
                        ReferenceRepositoryEntry repEntry = (ReferenceRepositoryEntry) rootrep.FindEntry (relFilePath);
 
498
                        DateTime rootLastModified = repEntry != null ? repEntry.LastModified : DateTime.MinValue;
 
499
                        
 
500
                        bool modified = false;
 
501
                        
 
502
                        monitor.Log.WriteLine ("Checking directory: " + mainPath);
 
503
                        foreach (string file in Directory.GetFiles (mainPath, "*.mpack")) {
 
504
                                
 
505
                                DateTime date = File.GetLastWriteTime (file);
 
506
                                string fname = Path.GetFileName (file);
 
507
                                PackageRepositoryEntry entry = (PackageRepositoryEntry) mainrep.FindEntry (fname);
 
508
                                
 
509
                                if (entry != null && date > rootLastModified) {
 
510
                                        mainrep.RemoveEntry (entry);
 
511
                                        DeleteSupportFiles (supportFileDir, entry.Addin);
 
512
                                        entry = null;
 
513
                                }
 
514
 
 
515
                                if (entry == null) {
 
516
                                        entry = new PackageRepositoryEntry ();
 
517
                                        AddinPackage p = (AddinPackage) Package.FromFile (file);
 
518
                                        entry.Addin = (AddinInfo) p.Addin;
 
519
                                        entry.Url = fname;
 
520
                                        entry.Addin.Properties.SetPropertyValue ("DownloadSize", new FileInfo (file).Length.ToString ());
 
521
                                        ExtractSupportFiles (supportFileDir, file, entry.Addin);
 
522
                                        mainrep.AddEntry (entry);
 
523
                                        modified = true;
 
524
                                        monitor.Log.WriteLine ("Added addin: " + fname);
 
525
                                }
 
526
                                allAddins.Add (entry);
 
527
                        }
 
528
                        
 
529
                        ArrayList toRemove = new ArrayList ();
 
530
                        foreach (PackageRepositoryEntry entry in mainrep.Addins) {
 
531
                                if (!File.Exists (Path.Combine (mainPath, entry.Url))) {
 
532
                                        toRemove.Add (entry);
 
533
                                        modified = true;
 
534
                                }
 
535
                        }
 
536
                                        
 
537
                        foreach (PackageRepositoryEntry entry in toRemove) {
 
538
                                DeleteSupportFiles (supportFileDir, entry.Addin);
 
539
                                mainrep.RemoveEntry (entry);
 
540
                        }
 
541
                        
 
542
                        if (modified) {
 
543
                                AddinStore.WriteObject (mainFile, mainrep);
 
544
                                monitor.Log.WriteLine ("Updated " + relFilePath);
 
545
                                lastModified = File.GetLastWriteTime (mainFile);
 
546
                        }
 
547
 
 
548
                        if (repEntry != null) {
 
549
                                if (repEntry.LastModified < lastModified)
 
550
                                        repEntry.LastModified = lastModified;
 
551
                        } else if (modified) {
 
552
                                repEntry = new ReferenceRepositoryEntry ();
 
553
                                repEntry.LastModified = lastModified;
 
554
                                repEntry.Url = relFilePath;
 
555
                                rootrep.AddEntry (repEntry);
 
556
                        }
 
557
                        
 
558
                        foreach (string dir in Directory.GetDirectories (mainPath)) {
 
559
                                if (Path.GetFileName (dir) == addinFilesDir)
 
560
                                        continue;
 
561
                                string based = dir.Substring (rootPath.Length + 1);
 
562
                                BuildRepository (monitor, rootrep, rootPath, Path.Combine (based, "main.mrep"), allAddins);
 
563
                        }
 
564
                }
 
565
                
 
566
                void DeleteSupportFiles (string targetDir, AddinInfo ainfo)
 
567
                {
 
568
                        foreach (var prop in ainfo.Properties) {
 
569
                                if (prop.Value.StartsWith (addinFilesDir + Path.DirectorySeparatorChar)) {
 
570
                                        string file = Path.Combine (targetDir, Path.GetFileName (prop.Value));
 
571
                                        if (File.Exists (file))
 
572
                                                File.Delete (file);
 
573
                                }
 
574
                        }
 
575
                        if (Directory.Exists (targetDir) && Directory.GetFileSystemEntries (targetDir).Length == 0)
 
576
                                Directory.Delete (targetDir, true);
 
577
                }
 
578
                
 
579
                void ExtractSupportFiles (string targetDir, string file, AddinInfo ainfo)
 
580
                {
 
581
                        Random r = new Random ();
 
582
                        ZipFile zfile = new ZipFile (file);
 
583
                        foreach (var prop in ainfo.Properties) {
 
584
                                ZipEntry ze = zfile.GetEntry (prop.Value);
 
585
                                if (ze != null) {
 
586
                                        string fname;
 
587
                                        do {
 
588
                                                fname = Path.Combine (targetDir, r.Next().ToString ("x") + Path.GetExtension (prop.Value));
 
589
                                        } while (File.Exists (fname));
 
590
                                        
 
591
                                        if (!Directory.Exists (targetDir))
 
592
                                                Directory.CreateDirectory (targetDir);
 
593
                                        
 
594
                                        using (var f = File.OpenWrite (fname)) {
 
595
                                                using (Stream s = zfile.GetInputStream (ze)) {
 
596
                                                        byte[] buffer = new byte [8092];
 
597
                                                        int nr = 0;
 
598
                                                        while ((nr = s.Read (buffer, 0, buffer.Length)) > 0)
 
599
                                                                f.Write (buffer, 0, nr);
 
600
                                                }
 
601
                                        }
 
602
                                        prop.Value = Path.Combine (addinFilesDir, Path.GetFileName (fname));
 
603
                                }
 
604
                        }
 
605
                }
 
606
                
 
607
                void GenerateIndexPage (Repository rep, ArrayList addins, string basePath)
 
608
                {
 
609
                        StreamWriter sw = new StreamWriter (Path.Combine (basePath, "index.html"));
 
610
                        sw.WriteLine ("<html><body>");
 
611
                        sw.WriteLine ("<h1>Add-in Repository</h1>");
 
612
                        if (rep.Name != null && rep.Name != "")
 
613
                                sw.WriteLine ("<h2>" + rep.Name + "</h2>");
 
614
                        sw.WriteLine ("<p>This is a list of add-ins available in this repository.</p>");
 
615
                        sw.WriteLine ("<table border=1><thead><tr><th>Add-in</th><th>Version</th><th>Description</th></tr></thead>");
 
616
                        
 
617
                        foreach (PackageRepositoryEntry entry in addins) {
 
618
                                sw.WriteLine ("<tr><td>" + entry.Addin.Name + "</td><td>" + entry.Addin.Version + "</td><td>" + entry.Addin.Description + "</td></tr>");
 
619
                        }
 
620
                        
 
621
                        sw.WriteLine ("</table>");
 
622
                        sw.WriteLine ("</body></html>");
 
623
                        sw.Close ();
 
624
                }
 
625
                
 
626
                internal AddinSystemConfiguration Configuration {
 
627
                        get {
 
628
                                if (config == null) {
 
629
                                        config = (AddinSystemConfiguration) AddinStore.ReadObject (RootConfigFile, typeof(AddinSystemConfiguration));
 
630
                                        if (config == null)
 
631
                                                config = new AddinSystemConfiguration ();
 
632
                                }
 
633
                                return config;
 
634
                        }
 
635
                }
 
636
                
 
637
                internal void SaveConfiguration ()
 
638
                {
 
639
                        if (config != null) {
 
640
                                AddinStore.WriteObject (RootConfigFile, config); 
 
641
                        }
 
642
                }
 
643
 
 
644
                internal void ResetConfiguration ()
 
645
                {
 
646
                        if (File.Exists (RootConfigFile))
 
647
                                File.Delete (RootConfigFile);
 
648
                        ResetAddinInfo ();
 
649
                }
 
650
                                
 
651
                internal void ResetAddinInfo ()
 
652
                {
 
653
                        if (Directory.Exists (RepositoryCachePath))
 
654
                                Directory.Delete (RepositoryCachePath, true);
 
655
                }
 
656
                
 
657
                /// <summary>
 
658
                /// Gets a reference to an extensible application
 
659
                /// </summary>
 
660
                /// <param name="name">
 
661
                /// Name of the application
 
662
                /// </param>
 
663
                /// <returns>
 
664
                /// The Application object. Null if not found.
 
665
                /// </returns>
 
666
                public static Application GetExtensibleApplication (string name)
 
667
                {
 
668
                        return GetExtensibleApplication (name, null);
 
669
                }
 
670
                
 
671
                /// <summary>
 
672
                /// Gets a reference to an extensible application
 
673
                /// </summary>
 
674
                /// <param name="name">
 
675
                /// Name of the application
 
676
                /// </param>
 
677
                /// <param name="searchPaths">
 
678
                /// Custom paths where to look for the application.
 
679
                /// </param>
 
680
                /// <returns>
 
681
                /// The Application object. Null if not found.
 
682
                /// </returns>
 
683
                public static Application GetExtensibleApplication (string name, IEnumerable<string> searchPaths)
 
684
                {
 
685
                        AddinsPcFileCache pcc = GetAddinsPcFileCache (searchPaths);
 
686
                        PackageInfo pi = pcc.GetPackageInfoByName (name, searchPaths);
 
687
                        if (pi != null)
 
688
                                return new Application (pi);
 
689
                        else
 
690
                                return null;
 
691
                }
 
692
                
 
693
                /// <summary>
 
694
                /// Gets a lis of all known extensible applications
 
695
                /// </summary>
 
696
                /// <returns>
 
697
                /// A list of applications.
 
698
                /// </returns>
 
699
                public static Application[] GetExtensibleApplications ()
 
700
                {
 
701
                        return GetExtensibleApplications (null);
 
702
                }
 
703
                
 
704
                /// <summary>
 
705
                /// Gets a lis of all known extensible applications
 
706
                /// </summary>
 
707
                /// <param name="searchPaths">
 
708
                /// Custom paths where to look for applications.
 
709
                /// </param>
 
710
                /// <returns>
 
711
                /// A list of applications.
 
712
                /// </returns>
 
713
                public static Application[] GetExtensibleApplications (IEnumerable<string> searchPaths)
 
714
                {
 
715
                        List<Application> list = new List<Application> ();
 
716
                        
 
717
                        AddinsPcFileCache pcc = GetAddinsPcFileCache (searchPaths);
 
718
                        foreach (PackageInfo pinfo in pcc.GetPackages (searchPaths)) {
 
719
                                if (pinfo.IsValidPackage)
 
720
                                        list.Add (new Application (pinfo));
 
721
                        }
 
722
                        return list.ToArray ();
 
723
                }
 
724
                
 
725
                static AddinsPcFileCache pcFileCache;
 
726
                
 
727
                static AddinsPcFileCache GetAddinsPcFileCache (IEnumerable<string> searchPaths)
 
728
                {
 
729
                        if (pcFileCache == null) {
 
730
                                pcFileCache = new AddinsPcFileCache ();
 
731
                                if (searchPaths != null)
 
732
                                        pcFileCache.Update (searchPaths);
 
733
                                else
 
734
                                        pcFileCache.Update ();
 
735
                        }
 
736
                        return pcFileCache;
 
737
                }
 
738
        }
 
739
        
 
740
        class AddinsPcFileCacheContext: IPcFileCacheContext
 
741
        {
 
742
                public bool IsCustomDataComplete (string pcfile, PackageInfo pkg)
 
743
                {
 
744
                        return true;
 
745
                }
 
746
                
 
747
                public void StoreCustomData (Mono.PkgConfig.PcFile pcfile, PackageInfo pkg)
 
748
                {
 
749
                }
 
750
 
 
751
                public void ReportError (string message, System.Exception ex)
 
752
                {
 
753
                        Console.WriteLine (message);
 
754
                        Console.WriteLine (ex);
 
755
                }
 
756
        }
 
757
        
 
758
        class AddinsPcFileCache: PcFileCache
 
759
        {
 
760
                public AddinsPcFileCache (): base (new AddinsPcFileCacheContext ())
 
761
                {
 
762
                }
 
763
                
 
764
                protected override string CacheDirectory {
 
765
                        get {
 
766
                                string path = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
 
767
                                path = Path.Combine (path, "mono.addins");
 
768
                                return path;
 
769
                        }
 
770
                }
 
771
                
 
772
                protected override void ParsePackageInfo (PcFile file, PackageInfo pinfo)
 
773
                {
 
774
                        string rootPath = file.GetVariable ("MonoAddinsRoot");
 
775
                        string regPath = file.GetVariable ("MonoAddinsRegistry");
 
776
                        string addinsPath = file.GetVariable ("MonoAddinsInstallPath");
 
777
                        string databasePath = file.GetVariable ("MonoAddinsCachePath");
 
778
                        string testCmd = file.GetVariable ("MonoAddinsTestCommand");
 
779
                        if (string.IsNullOrEmpty (rootPath) || string.IsNullOrEmpty (regPath))
 
780
                                return;
 
781
                        pinfo.SetData ("MonoAddinsRoot", rootPath);
 
782
                        pinfo.SetData ("MonoAddinsRegistry", regPath);
 
783
                        pinfo.SetData ("MonoAddinsInstallPath", addinsPath);
 
784
                        pinfo.SetData ("MonoAddinsCachePath", databasePath);
 
785
                        pinfo.SetData ("MonoAddinsTestCommand", testCmd);
 
786
                }
 
787
        }
 
788
        
 
789
        /// <summary>
 
790
        /// A registered extensible application
 
791
        /// </summary>
 
792
        public class Application
 
793
        {
 
794
                AddinRegistry registry;
 
795
                string description;
 
796
                string name;
 
797
                string testCommand;
 
798
                string startupPath;
 
799
                string registryPath;
 
800
                string addinsPath;
 
801
                string databasePath;
 
802
                
 
803
                internal Application (PackageInfo pinfo)
 
804
                {
 
805
                        name = pinfo.Name;
 
806
                        description = pinfo.Description;
 
807
                        startupPath = pinfo.GetData ("MonoAddinsRoot");
 
808
                        registryPath = pinfo.GetData ("MonoAddinsRegistry");
 
809
                        addinsPath = pinfo.GetData ("MonoAddinsInstallPath");
 
810
                        databasePath = pinfo.GetData ("MonoAddinsCachePath");
 
811
                        testCommand = pinfo.GetData ("MonoAddinsTestCommand");
 
812
                }
 
813
                
 
814
                /// <summary>
 
815
                /// Add-in registry of the application
 
816
                /// </summary>
 
817
                public AddinRegistry Registry {
 
818
                        get {
 
819
                                if (registry == null)
 
820
                                        registry = new AddinRegistry (RegistryPath, StartupPath, AddinsPath, AddinCachePath);
 
821
                                return registry;
 
822
                        }
 
823
                }
 
824
 
 
825
                /// <summary>
 
826
                /// Description of the application
 
827
                /// </summary>
 
828
                public string Description {
 
829
                        get {
 
830
                                return description;
 
831
                        }
 
832
                }
 
833
 
 
834
                /// <summary>
 
835
                /// Name of the application
 
836
                /// </summary>
 
837
                public string Name {
 
838
                        get {
 
839
                                return name;
 
840
                        }
 
841
                }
 
842
 
 
843
                /// <summary>
 
844
                /// Path to the add-in registry
 
845
                /// </summary>
 
846
                public string RegistryPath {
 
847
                        get {
 
848
                                return registryPath;
 
849
                        }
 
850
                }
 
851
 
 
852
                /// <summary>
 
853
                /// Path to the directory that contains the main executable assembly of the application
 
854
                /// </summary>
 
855
                public string StartupPath {
 
856
                        get {
 
857
                                return startupPath;
 
858
                        }
 
859
                }
 
860
 
 
861
                /// <summary>
 
862
                /// Command to be used to execute the application in add-in development mode.
 
863
                /// </summary>
 
864
                public string TestCommand {
 
865
                        get {
 
866
                                return testCommand;
 
867
                        }
 
868
                }
 
869
 
 
870
                /// <summary>
 
871
                /// Path to the default add-ins directory for the aplpication
 
872
                /// </summary>
 
873
                public string AddinsPath {
 
874
                        get {
 
875
                                return addinsPath;
 
876
                        }
 
877
                }
 
878
 
 
879
                /// <summary>
 
880
                /// Path to the add-in cache for the application
 
881
                /// </summary>
 
882
                public string AddinCachePath {
 
883
                        get {
 
884
                                return databasePath;
 
885
                        }
 
886
                }
 
887
        }
 
888
}