1 //
2 // AddinStore.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 using System;
30 using System.Collections;
31 using System.Collections.Specialized;
32 using System.IO;
33 using System.Xml;
34 using System.Xml.Serialization;
35 using System.Reflection;
36 using System.Diagnostics;
37 using System.Net;
38 using System.Runtime.Serialization;
39 using System.Runtime.Serialization.Formatters.Binary;
40 
41 using ICSharpCode.SharpZipLib.Zip;
42 using Mono.Addins;
43 using Mono.Addins.Setup.ProgressMonitoring;
44 using Mono.Addins.Description;
45 using Mono.Addins.Serialization;
46 using System.Collections.Generic;
47 using System.Linq;
48 using System.Threading;
49 
50 namespace Mono.Addins.Setup
51 {
52 	internal class AddinStore
53 	{
54 		SetupService service;
55 
AddinStore(SetupService service)56 		public AddinStore (SetupService service)
57 		{
58 			this.service = service;
59 		}
60 
ResetCachedData()61 		internal void ResetCachedData ()
62 		{
63 		}
64 
65 		public AddinRegistry Registry {
66 			get { return service.Registry; }
67 		}
68 
Install(IProgressStatus statusMonitor, params string[] files)69 		public bool Install (IProgressStatus statusMonitor, params string[] files)
70 		{
71 			Package[] packages = new Package [files.Length];
72 			for (int n=0; n<files.Length; n++)
73 				packages [n] = AddinPackage.FromFile (files [n]);
74 
75 			return Install (statusMonitor, packages);
76 		}
77 
Install(IProgressStatus statusMonitor, params AddinRepositoryEntry[] addins)78 		public bool Install (IProgressStatus statusMonitor, params AddinRepositoryEntry[] addins)
79 		{
80 			Package[] packages = new Package [addins.Length];
81 			for (int n=0; n<addins.Length; n++)
82 				packages [n] = AddinPackage.FromRepository (addins [n]);
83 
84 			return Install (statusMonitor, packages);
85 		}
86 
Install(IProgressStatus monitor, params Package[] packages)87 		internal bool Install (IProgressStatus monitor, params Package[] packages)
88 		{
89 			PackageCollection packs = new PackageCollection ();
90 			packs.AddRange (packages);
91 			return Install (monitor, packs);
92 		}
93 
Install(IProgressStatus statusMonitor, PackageCollection packs)94 		internal bool Install (IProgressStatus statusMonitor, PackageCollection packs)
95 		{
96 			// Make sure the registry is up to date
97 			service.Registry.Update (statusMonitor);
98 
99 			IProgressMonitor monitor = ProgressStatusMonitor.GetProgressMonitor (statusMonitor);
100 
101 			PackageCollection toUninstall;
102 			DependencyCollection unresolved;
103 			if (!ResolveDependencies (monitor, packs, out toUninstall, out unresolved)) {
104 				monitor.ReportError ("Not all dependencies could be resolved.", null);
105 				return false;
106 			}
107 
108 			ArrayList prepared = new ArrayList ();
109 			ArrayList uninstallPrepared = new ArrayList ();
110 			bool rollback = false;
111 
112 			monitor.BeginTask ("Installing add-ins...", 100);
113 
114 			// Prepare install
115 
116 			monitor.BeginStepTask ("Initializing installation", toUninstall.Count + packs.Count + 1, 75);
117 
118 			foreach (Package mpack in toUninstall) {
119 				try {
120 					mpack.PrepareUninstall (monitor, this);
121 					uninstallPrepared.Add (mpack);
122 					if (monitor.IsCancelRequested)
123 						throw new InstallException ("Installation cancelled.");
124 					monitor.Step (1);
125 				} catch (Exception ex) {
126 					ReportException (monitor, ex);
127 					rollback = true;
128 					break;
129 				}
130 			}
131 
132 			monitor.Step (1);
133 
134 			foreach (Package mpack in packs) {
135 				try {
136 					mpack.PrepareInstall (monitor, this);
137 					if (monitor.IsCancelRequested)
138 						throw new InstallException ("Installation cancelled.");
139 					prepared.Add (mpack);
140 					monitor.Step (1);
141 				} catch (Exception ex) {
142 					ReportException (monitor, ex);
143 					rollback = true;
144 					break;
145 				}
146 			}
147 
148 			monitor.EndTask ();
149 
150 			monitor.BeginStepTask ("Installing", toUninstall.Count + packs.Count + 1, 20);
151 
152 			// Commit install
153 
154 			if (!rollback) {
155 				foreach (Package mpack in toUninstall) {
156 					try {
157 						mpack.CommitUninstall (monitor, this);
158 						if (monitor.IsCancelRequested)
159 							throw new InstallException ("Installation cancelled.");
160 						monitor.Step (1);
161 					} catch (Exception ex) {
162 						ReportException (monitor, ex);
163 						rollback = true;
164 						break;
165 					}
166 				}
167 			}
168 
169 			monitor.Step (1);
170 
171 			if (!rollback) {
172 				foreach (Package mpack in packs) {
173 					try {
174 						mpack.CommitInstall (monitor, this);
175 						if (monitor.IsCancelRequested)
176 							throw new InstallException ("Installation cancelled.");
177 						monitor.Step (1);
178 					} catch (Exception ex) {
179 						ReportException (monitor, ex);
180 						rollback = true;
181 						break;
182 					}
183 				}
184 			}
185 
186 			monitor.EndTask ();
187 
188 			// Rollback if failed
189 
190 			if (monitor.IsCancelRequested)
191 				monitor = new NullProgressMonitor ();
192 
193 			if (rollback) {
194 				monitor.BeginStepTask ("Finishing installation", (prepared.Count + uninstallPrepared.Count)*2 + 1, 5);
195 
196 				foreach (Package mpack in prepared) {
197 					try {
198 						mpack.RollbackInstall (monitor, this);
199 						monitor.Step (1);
200 					} catch (Exception ex) {
201 						ReportException (monitor, ex);
202 					}
203 				}
204 
205 				foreach (Package mpack in uninstallPrepared) {
206 					try {
207 						mpack.RollbackUninstall (monitor, this);
208 						monitor.Step (1);
209 					} catch (Exception ex) {
210 						ReportException (monitor, ex);
211 					}
212 				}
213 			} else
214 				monitor.BeginStepTask ("Finishing installation", prepared.Count + uninstallPrepared.Count + 1, 5);
215 
216 			// Cleanup
217 
218 			foreach (Package mpack in prepared) {
219 				try {
220 					mpack.EndInstall (monitor, this);
221 					monitor.Step (1);
222 				} catch (Exception ex) {
223 					monitor.Log.WriteLine (ex);
224 				}
225 			}
226 
227 			monitor.Step (1);
228 
229 			foreach (Package mpack in uninstallPrepared) {
230 				try {
231 					mpack.EndUninstall (monitor, this);
232 					monitor.Step (1);
233 				} catch (Exception ex) {
234 					monitor.Log.WriteLine (ex);
235 				}
236 			}
237 
238 			// Update the extension maps
239 			service.Registry.Update (statusMonitor);
240 
241 			monitor.EndTask ();
242 
243 			monitor.EndTask ();
244 
245 			service.SaveConfiguration ();
246 			ResetCachedData ();
247 
248 			return !rollback;
249 		}
250 
ReportException(IProgressMonitor statusMonitor, Exception ex)251 		void ReportException (IProgressMonitor statusMonitor, Exception ex)
252 		{
253 			if (ex is InstallException)
254 				statusMonitor.ReportError (ex.Message, null);
255 			else
256 				statusMonitor.ReportError (null, ex);
257 		}
258 
Uninstall(IProgressStatus statusMonitor, string id)259 		public void Uninstall (IProgressStatus statusMonitor, string id)
260 		{
261 			Uninstall (statusMonitor, new string[] { id });
262 		}
263 
Uninstall(IProgressStatus statusMonitor, IEnumerable<string> ids)264 		public void Uninstall (IProgressStatus statusMonitor, IEnumerable<string> ids)
265 		{
266 			IProgressMonitor monitor = ProgressStatusMonitor.GetProgressMonitor (statusMonitor);
267 			monitor.BeginTask ("Uninstalling add-ins", ids.Count ());
268 
269 			foreach (string id in ids) {
270 				bool rollback = false;
271 				ArrayList toUninstall = new ArrayList ();
272 				ArrayList uninstallPrepared = new ArrayList ();
273 
274 				Addin ia = service.Registry.GetAddin (id);
275 				if (ia == null)
276 					throw new InstallException ("The add-in '" + id + "' is not installed.");
277 
278 				toUninstall.Add (AddinPackage.FromInstalledAddin (ia));
279 
280 				Addin[] deps = GetDependentAddins (id, true);
281 				foreach (Addin dep in deps)
282 					toUninstall.Add (AddinPackage.FromInstalledAddin (dep));
283 
284 				monitor.BeginTask ("Deleting files", toUninstall.Count*2 + uninstallPrepared.Count + 1);
285 
286 				// Prepare install
287 
288 				foreach (Package mpack in toUninstall) {
289 					try {
290 						mpack.PrepareUninstall (monitor, this);
291 						monitor.Step (1);
292 						uninstallPrepared.Add (mpack);
293 					} catch (Exception ex) {
294 						ReportException (monitor, ex);
295 						rollback = true;
296 						break;
297 					}
298 				}
299 
300 				// Commit install
301 
302 				if (!rollback) {
303 					foreach (Package mpack in toUninstall) {
304 						try {
305 							mpack.CommitUninstall (monitor, this);
306 							monitor.Step (1);
307 						} catch (Exception ex) {
308 							ReportException (monitor, ex);
309 							rollback = true;
310 							break;
311 						}
312 					}
313 				}
314 
315 				// Rollback if failed
316 
317 				if (rollback) {
318 					monitor.BeginTask ("Rolling back uninstall", uninstallPrepared.Count);
319 					foreach (Package mpack in uninstallPrepared) {
320 						try {
321 							mpack.RollbackUninstall (monitor, this);
322 						} catch (Exception ex) {
323 							ReportException (monitor, ex);
324 						}
325 					}
326 					monitor.EndTask ();
327 				}
328 				monitor.Step (1);
329 
330 				// Cleanup
331 
332 				foreach (Package mpack in uninstallPrepared) {
333 					try {
334 						mpack.EndUninstall (monitor, this);
335 						monitor.Step (1);
336 					} catch (Exception ex) {
337 						monitor.Log.WriteLine (ex);
338 					}
339 				}
340 
341 				monitor.EndTask ();
342 				monitor.Step (1);
343 			}
344 
345 			// Update the extension maps
346 			service.Registry.Update (statusMonitor);
347 
348 			monitor.EndTask ();
349 
350 			service.SaveConfiguration ();
351 			ResetCachedData ();
352 		}
353 
GetDependentAddins(string id, bool recursive)354 		public Addin[] GetDependentAddins (string id, bool recursive)
355 		{
356 			ArrayList list = new ArrayList ();
357 			FindDependentAddins (list, id, recursive);
358 			return (Addin[]) list.ToArray (typeof (Addin));
359 		}
360 
FindDependentAddins(ArrayList list, string id, bool recursive)361 		void FindDependentAddins (ArrayList list, string id, bool recursive)
362 		{
363 			foreach (Addin iaddin in service.Registry.GetAddins ()) {
364 				if (list.Contains (iaddin))
365 					continue;
366 				foreach (Dependency dep in iaddin.Description.MainModule.Dependencies) {
367 					AddinDependency adep = dep as AddinDependency;
368 					if (adep != null && adep.AddinId == id) {
369 						list.Add (iaddin);
370 						if (recursive)
371 							FindDependentAddins (list, iaddin.Id, true);
372 					}
373 				}
374 			}
375 		}
376 
ResolveDependencies(IProgressStatus statusMonitor, AddinRepositoryEntry[] addins, out PackageCollection resolved, out PackageCollection toUninstall, out DependencyCollection unresolved)377 		public bool ResolveDependencies (IProgressStatus statusMonitor, AddinRepositoryEntry[] addins, out PackageCollection resolved, out PackageCollection toUninstall, out DependencyCollection unresolved)
378 		{
379 			resolved = new PackageCollection ();
380 			for (int n=0; n<addins.Length; n++)
381 				resolved.Add (AddinPackage.FromRepository (addins [n]));
382 			return ResolveDependencies (statusMonitor, resolved, out toUninstall, out unresolved);
383 		}
384 
ResolveDependencies(IProgressStatus statusMonitor, PackageCollection packages, out PackageCollection toUninstall, out DependencyCollection unresolved)385 		public bool ResolveDependencies (IProgressStatus statusMonitor, PackageCollection packages, out PackageCollection toUninstall, out DependencyCollection unresolved)
386 		{
387 			IProgressMonitor monitor = ProgressStatusMonitor.GetProgressMonitor (statusMonitor);
388 			return ResolveDependencies (monitor, packages, out toUninstall, out unresolved);
389 		}
390 
ResolveDependencies(IProgressMonitor monitor, PackageCollection packages, out PackageCollection toUninstall, out DependencyCollection unresolved)391 		internal bool ResolveDependencies (IProgressMonitor monitor, PackageCollection packages, out PackageCollection toUninstall, out DependencyCollection unresolved)
392 		{
393 			PackageCollection requested = new PackageCollection();
394 			requested.AddRange (packages);
395 
396 			unresolved = new DependencyCollection ();
397 			toUninstall = new PackageCollection ();
398 			PackageCollection installedRequired = new PackageCollection ();
399 
400 			for (int n=0; n<packages.Count; n++) {
401 				Package p = packages [n];
402 				p.Resolve (monitor, this, packages, toUninstall, installedRequired, unresolved);
403 			}
404 
405 			if (unresolved.Count != 0) {
406 				foreach (Dependency dep in unresolved)
407 					monitor.ReportError (string.Format ("The package '{0}' could not be found in any repository", dep.Name), null);
408 				return false;
409 			}
410 
411 			// Check that we are not uninstalling packages that are required
412 			// by packages being installed.
413 
414 			foreach (Package p in installedRequired) {
415 				if (toUninstall.Contains (p)) {
416 					// Only accept to uninstall this package if we are
417 					// going to install a newer version.
418 					bool foundUpgrade = false;
419 					foreach (Package tbi in packages)
420 						if (tbi.Equals (p) || tbi.IsUpgradeOf (p)) {
421 							foundUpgrade = true;
422 							break;
423 						}
424 					if (!foundUpgrade)
425 						return false;
426 				}
427 			}
428 
429 			// Check that we are not trying to uninstall from a directory from
430 			// which we don't have write permissions
431 
432 			foreach (Package p in toUninstall) {
433 				AddinPackage ap = p as AddinPackage;
434 				if (ap != null) {
435 					Addin ia = service.Registry.GetAddin (ap.Addin.Id);
436 					if (File.Exists (ia.AddinFile) && !HasWriteAccess (ia.AddinFile) && IsUserAddin (ia.AddinFile)) {
437 						monitor.ReportError (GetUninstallErrorNoRoot (ap.Addin), null);
438 						return false;
439 					}
440 				}
441 			}
442 
443 			// Check that we are not installing two versions of the same addin
444 
445 			PackageCollection resolved = new PackageCollection();
446 			resolved.AddRange (packages);
447 
448 			bool error = false;
449 
450 			for (int n=0; n<packages.Count; n++) {
451 				AddinPackage ap = packages [n] as AddinPackage;
452 				if (ap == null) continue;
453 
454 				for (int k=n+1; k<packages.Count; k++) {
455 					AddinPackage otherap = packages [k] as AddinPackage;
456 					if (otherap == null) continue;
457 
458 					if (ap.Addin.Id == otherap.Addin.Id) {
459 						if (ap.IsUpgradeOf (otherap)) {
460 							if (requested.Contains (otherap)) {
461 								monitor.ReportError ("Can't install two versions of the same add-in: '" + ap.Addin.Name + "'.", null);
462 								error = true;
463 							} else {
464 								packages.RemoveAt (k);
465 							}
466 						} else if (otherap.IsUpgradeOf (ap)) {
467 							if (requested.Contains (ap)) {
468 								monitor.ReportError ("Can't install two versions of the same add-in: '" + ap.Addin.Name + "'.", null);
469 								error = true;
470 							} else {
471 								packages.RemoveAt (n);
472 								n--;
473 							}
474 						} else {
475 							error = true;
476 							monitor.ReportError ("Can't install two versions of the same add-in: '" + ap.Addin.Name + "'.", null);
477 						}
478 						break;
479 					}
480 				}
481 			}
482 
483 			// Don't allow installing add-ins which are scheduled for uninstall
484 
485 			foreach (Package p in packages) {
486 				AddinPackage ap = p as AddinPackage;
487 				if (ap != null && Registry.IsRegisteredForUninstall (ap.Addin.Id)) {
488 					error = true;
489 					monitor.ReportError ("The addin " + ap.Addin.Name + " v" + ap.Addin.Version + " is scheduled for uninstallation. Please restart the application before trying to re-install it.", null);
490 				}
491 			}
492 
493 			return !error;
494 		}
495 
ResolveDependency(IProgressMonitor monitor, Dependency dep, AddinPackage parentPackage, PackageCollection toInstall, PackageCollection toUninstall, PackageCollection installedRequired, DependencyCollection unresolved)496 		internal void ResolveDependency (IProgressMonitor monitor, Dependency dep, AddinPackage parentPackage, PackageCollection toInstall, PackageCollection toUninstall, PackageCollection installedRequired, DependencyCollection unresolved)
497 		{
498 			AddinDependency adep = dep as AddinDependency;
499 			if (adep == null)
500 				return;
501 
502 			string nsid = Addin.GetFullId (parentPackage.Addin.Namespace, adep.AddinId, null);
503 
504 			foreach (Package p in toInstall) {
505 				AddinPackage ap = p as AddinPackage;
506 				if (ap != null) {
507 					if (Addin.GetIdName (ap.Addin.Id) == nsid && ((AddinInfo)ap.Addin).SupportsVersion (adep.Version))
508 						return;
509 				}
510 			}
511 
512 			ArrayList addins = new ArrayList ();
513 			addins.AddRange (service.Registry.GetAddins ());
514 			addins.AddRange (service.Registry.GetAddinRoots ());
515 
516 			foreach (Addin addin in addins) {
517 				if (Addin.GetIdName (addin.Id) == nsid && addin.SupportsVersion (adep.Version)) {
518 					AddinPackage p = AddinPackage.FromInstalledAddin (addin);
519 					if (!installedRequired.Contains (p))
520 						installedRequired.Add (p);
521 					return;
522 				}
523 			}
524 
525 			AddinRepositoryEntry[] avaddins = service.Repositories.GetAvailableAddins ();
526 			foreach (PackageRepositoryEntry avAddin in avaddins) {
527 				if (Addin.GetIdName (avAddin.Addin.Id) == nsid && ((AddinInfo)avAddin.Addin).SupportsVersion (adep.Version)) {
528 					toInstall.Add (AddinPackage.FromRepository (avAddin));
529 					return;
530 				}
531 			}
532 			unresolved.Add (adep);
533 		}
534 
GetAddinDirectory(AddinInfo info)535 		internal string GetAddinDirectory (AddinInfo info)
536 		{
537 			return Path.Combine (service.InstallDirectory, info.Id.Replace (',','.'));
538 		}
539 
RegisterAddin(IProgressMonitor monitor, AddinInfo info, string sourceDir)540 		internal void RegisterAddin (IProgressMonitor monitor, AddinInfo info, string sourceDir)
541 		{
542 			monitor.Log.WriteLine ("Installing " + info.Name + " v" + info.Version);
543 			string addinDir = GetAddinDirectory (info);
544 			if (!Directory.Exists (addinDir))
545 				Directory.CreateDirectory (addinDir);
546 			CopyDirectory (sourceDir, addinDir);
547 
548 			ResetCachedData ();
549 		}
550 
CopyDirectory(string src, string dest)551 		void CopyDirectory (string src, string dest)
552 		{
553 			CopyDirectory (src, dest, "");
554 		}
555 
CopyDirectory(string src, string dest, string subdir)556 		void CopyDirectory (string src, string dest, string subdir)
557 		{
558 			string destDir = Path.Combine (dest, subdir);
559 
560 			if (!Directory.Exists (destDir))
561 				Directory.CreateDirectory (destDir);
562 
563 			foreach (string file in Directory.GetFiles (src)) {
564 				if (Path.GetFileName (file) != "addin.info")
565 					File.Copy (file, Path.Combine (destDir, Path.GetFileName (file)), true);
566 			}
567 
568 			foreach (string dir in Directory.GetDirectories (src))
569 				CopyDirectory (dir, dest, Path.Combine (subdir, Path.GetFileName (dir)));
570 		}
571 
DownloadObject(IProgressMonitor monitor, string url, Type type)572 		internal object DownloadObject (IProgressMonitor monitor, string url, Type type)
573 		{
574 			string file = null;
575 			try {
576 				file = DownloadFile (monitor, url);
577 				return ReadObject (file, type);
578 			} finally {
579 				if (file != null)
580 					File.Delete (file);
581 			}
582 		}
583 
GetSerializer(Type type)584 		static XmlSerializer GetSerializer (Type type)
585 		{
586 			if (type == typeof(AddinSystemConfiguration))
587 				return new AddinSystemConfigurationSerializer ();
588 			else if (type == typeof(Repository))
589 				return new RepositorySerializer ();
590 			else
591 				return new XmlSerializer (type);
592 		}
593 
ReadObject(string file, Type type)594 		internal static object ReadObject (string file, Type type)
595 		{
596 			if (!File.Exists (file))
597 				return null;
598 
599 			StreamReader r = new StreamReader (file);
600 			try {
601 				XmlSerializer ser = GetSerializer (type);
602 				return ser.Deserialize (r);
603 			} catch {
604 				return null;
605 			} finally {
606 				r.Close ();
607 			}
608 		}
609 
WriteObject(string file, object obj)610 		internal static void WriteObject (string file, object obj)
611 		{
612 			string dir = Path.GetDirectoryName (file);
613 			if (!Directory.Exists (dir))
614 				Directory.CreateDirectory (dir);
615 			StreamWriter s = new StreamWriter (file);
616 			try {
617 				XmlSerializer ser = GetSerializer (obj.GetType());
618 				ser.Serialize (s, obj);
619 				s.Close ();
620 			} catch {
621 				s.Close ();
622 				if (File.Exists (file))
623 					File.Delete (file);
624 				throw;
625 			}
626 		}
627 
DownloadFile(IProgressMonitor monitor, string url)628 		internal string DownloadFile (IProgressMonitor monitor, string url)
629 		{
630 			if (url.StartsWith ("file://", StringComparison.Ordinal)) {
631 				string tmpfile = Path.GetTempFileName ();
632 				string path = new Uri (url).LocalPath;
633 				File.Delete (tmpfile);
634 				File.Copy (path, tmpfile);
635 				return tmpfile;
636 			}
637 
638 			string file = null;
639 			FileStream fs = null;
640 			Stream s = null;
641 
642 			try {
643 				monitor.BeginTask ("Requesting " + url, 2);
644 				var resp = WebRequestHelper.GetResponse (
645 					() => (HttpWebRequest)WebRequest.Create (url),
646 					r => r.Headers ["Pragma"] = "no-cache"
647 				);
648 				monitor.Step (1);
649 				monitor.BeginTask ("Downloading " + url, (int) resp.ContentLength);
650 
651 				file = Path.GetTempFileName ();
652 				fs = new FileStream (file, FileMode.Create, FileAccess.Write);
653 					s = resp.GetResponseStream ();
654 				byte[] buffer = new byte [4096];
655 
656 				int n;
657 				while ((n = s.Read (buffer, 0, buffer.Length)) != 0) {
658 					monitor.Step (n);
659 					fs.Write (buffer, 0, n);
660 					if (monitor.IsCancelRequested)
661 						throw new InstallException ("Installation cancelled.");
662 				}
663 				fs.Close ();
664 				s.Close ();
665 				return file;
666 			} catch {
667 				if (fs != null)
668 					fs.Close ();
669 				if (s != null)
670 					s.Close ();
671 				if (file != null)
672 					File.Delete (file);
673 				throw;
674 			} finally {
675 				monitor.EndTask ();
676 				monitor.EndTask ();
677 			}
678 		}
679 
HasWriteAccess(string file)680 		internal bool HasWriteAccess (string file)
681 		{
682 			FileInfo f = new FileInfo (file);
683 			return !f.Exists || !f.IsReadOnly;
684 		}
685 
IsUserAddin(string addinFile)686 		internal bool IsUserAddin (string addinFile)
687 		{
688 			string installPath = service.InstallDirectory;
689 			if (installPath [installPath.Length - 1] != Path.DirectorySeparatorChar)
690 				installPath += Path.DirectorySeparatorChar;
691 			return Path.GetFullPath (addinFile).StartsWith (installPath);
692 		}
693 
GetUninstallErrorNoRoot(AddinHeader ainfo)694 		internal static string GetUninstallErrorNoRoot (AddinHeader ainfo)
695 		{
696 			return string.Format ("The add-in '{0} v{1}' can't be uninstalled with the current user permissions.", ainfo.Name, ainfo.Version);
697 		}
698 	}
699 }
700