/// <summary> /// Creates a temporary directory from a retrieval method. Sets missing properties in the process. /// </summary> /// <param name="retrievalMethod">The retrieval method.</param> /// <param name="handler">A callback object used when the the user is to be informed about progress.</param> /// <param name="localPath">An optional local file path where the <paramref name="retrievalMethod"/> has already been downloaded. Leave <c>null</c> to download automatically.</param> /// <returns>A temporary directory built using the retrieval method.</returns> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="WebException">A file could not be downloaded from the internet.</exception> /// <exception cref="IOException">There is a problem writing a temporary file.</exception> /// <exception cref="UnauthorizedAccessException">Write access to a temporary file is not permitted.</exception> public static TemporaryDirectory ToTempDir(this DownloadRetrievalMethod retrievalMethod, ITaskHandler handler, string?localPath = null) { #region Sanity checks if (retrievalMethod == null) { throw new ArgumentNullException(nameof(retrievalMethod)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } #endregion var tempDir = new TemporaryDirectory("0publish"); try { var builder = new DirectoryBuilder(tempDir); builder.Add(retrievalMethod, new SimpleCommandExecutor(), handler, localPath); return(tempDir); } catch { tempDir.Dispose(); throw; } }
/// <summary> /// Downloads a <see cref="DownloadRetrievalMethod"/> to a temporary file. /// </summary> /// <param name="retrievalMethod">The file to download.</param> /// <param name="tag">The <see cref="ITask.Tag"/> to set for the download process.</param> /// <returns>The downloaded temporary file.</returns> /// <exception cref="OperationCanceledException">A download was canceled from another thread.</exception> /// <exception cref="WebException">A file could not be downloaded from the internet.</exception> /// <exception cref="IOException">A downloaded file could not be written to the disk or.</exception> /// <exception cref="UnauthorizedAccessException">Write access to <see cref="IImplementationStore"/> is not permitted.</exception> protected virtual TemporaryFile Download(DownloadRetrievalMethod retrievalMethod, object?tag = null) { #region Sanity checks if (retrievalMethod == null) { throw new ArgumentNullException(nameof(retrievalMethod)); } #endregion retrievalMethod.Validate(); var tempFile = new TemporaryFile("0install-fetcher"); try { Handler.RunTask(new DownloadFile(retrievalMethod.Href, tempFile, retrievalMethod.DownloadSize) { Tag = tag }); return(tempFile); } #region Error handling catch { tempFile.Dispose(); throw; } #endregion }
/// <inheritdoc/> protected override TemporaryFile Download(DownloadRetrievalMethod retrievalMethod, object tag = null) { #region Sanity checks if (retrievalMethod == null) { throw new ArgumentNullException(nameof(retrievalMethod)); } #endregion retrievalMethod.Validate(); try { return(base.Download(retrievalMethod, tag)); } catch (WebException ex) when(!retrievalMethod.Href.IsLoopback) { Log.Warn(ex); Log.Info("Trying mirror"); try { var mirrored = (DownloadRetrievalMethod)retrievalMethod.CloneRecipeStep(); mirrored.Href = GetMirrorUrl(retrievalMethod.Href); return(base.Download(mirrored, tag)); } catch (WebException) { // Report the original problem instead of mirror errors throw ex.PreserveStack(); } } }
/// <summary> /// Sets missing properties on the retrieval method if they can be inferred. /// </summary> /// <param name="retrievalMethod">The retrieval method.</param> /// <param name="executor">Used to modify properties in an undoable fashion.</param> /// <param name="localPath">An optional local file path where the <paramref name="retrievalMethod"/> has already been downloaded.</param> public static void SetMissing(this DownloadRetrievalMethod retrievalMethod, ICommandExecutor executor, string?localPath = null) { #region Sanity checks if (retrievalMethod == null) { throw new ArgumentNullException(nameof(retrievalMethod)); } if (executor == null) { throw new ArgumentNullException(nameof(executor)); } #endregion switch (retrievalMethod) { case Archive archive when string.IsNullOrEmpty(archive.MimeType): executor.Execute(SetValueCommand.For(() => archive.MimeType, Archive.GuessMimeType(localPath ?? archive.Href.OriginalString))); break; case SingleFile file when string.IsNullOrEmpty(file.Destination): executor.Execute(SetValueCommand.For(() => file.Destination, Path.GetFileName(localPath ?? file.Href.GetLocalFileName()))); break; } }
/// <inheritdoc/> protected override TemporaryFile Download(DownloadRetrievalMethod retrievalMethod, object?tag = null) { #region Sanity checks if (retrievalMethod == null) { throw new ArgumentNullException(nameof(retrievalMethod)); } #endregion retrievalMethod.Validate(); try { return(base.Download(retrievalMethod, tag)); } catch (WebException ex) when(!retrievalMethod.Href.IsLoopback && _config.FeedMirror != null) { Log.Warn(ex); Log.Info("Trying mirror"); try { var mirrored = (DownloadRetrievalMethod)retrievalMethod.Clone(); mirrored.Href = new Uri($"{_config.FeedMirror.EnsureTrailingSlash().AbsoluteUri}archive/{retrievalMethod.Href.Scheme}/{retrievalMethod.Href.Host}/{string.Concat(retrievalMethod.Href.Segments).TrimStart('/').Replace("/", "%23")}"); return(base.Download(mirrored, tag)); } catch (WebException) { // Report the original problem instead of mirror errors throw ex.PreserveStack(); } } }
public static TemporaryFile Download([NotNull] this DownloadRetrievalMethod retrievalMethod, [NotNull] ITaskHandler handler, [CanBeNull] ICommandExecutor executor = null) { #region Sanity checks if (retrievalMethod == null) { throw new ArgumentNullException("retrievalMethod"); } if (handler == null) { throw new ArgumentNullException("handler"); } #endregion if (executor == null) { executor = new SimpleCommandExecutor(); } if (retrievalMethod.Href == null) { throw new ArgumentException(Resources.HrefMissing, "retrievalMethod"); } new PerTypeDispatcher <DownloadRetrievalMethod>(ignoreMissing: false) { // ReSharper disable AccessToDisposedClosure (Archive archive) => { // Guess MIME types now because the file ending is not known later if (string.IsNullOrEmpty(archive.MimeType)) { string mimeType = Archive.GuessMimeType(archive.Href.OriginalString); executor.Execute(new SetValueCommand <string>(() => archive.MimeType, value => archive.MimeType = value, mimeType)); } }, (SingleFile file) => { // Guess file name based on URL if (string.IsNullOrEmpty(file.Destination)) { string destination = file.Href.OriginalString.GetRightPartAtLastOccurrence('/').StripCharacters(Path.GetInvalidFileNameChars()); executor.Execute(new SetValueCommand <string>(() => file.Destination, value => file.Destination = value, destination)); } } // ReSharper restore AccessToDisposedClosure }.Dispatch(retrievalMethod); // Download the file var href = ModelUtils.GetAbsoluteHref(retrievalMethod.Href, string.IsNullOrEmpty(executor.Path) ? null : new FeedUri(executor.Path)); var downloadedFile = new TemporaryFile("0publish"); handler.RunTask(new DownloadFile(href, downloadedFile)); // Defer task to handler // Set downloaded file size long newSize = new FileInfo(downloadedFile).Length; if (retrievalMethod.Size != newSize) { executor.Execute(new SetValueCommand <long>(() => retrievalMethod.Size, value => retrievalMethod.Size = value, newSize)); } return(downloadedFile); }
/// <summary> /// Applies a retrieval method to the implementation. Sets missing properties in the process. /// </summary> /// <param name="builder">The builder.</param> /// <param name="retrievalMethod">The retrieval method.</param> /// <param name="executor">Used to modify properties in an undoable fashion.</param> /// <param name="handler">A callback object used when the the user needs to be informed about IO tasks.</param> /// <param name="localPath">An optional local file path where the <paramref name="retrievalMethod"/> has already been downloaded.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="WebException">A file could not be downloaded from the internet.</exception> /// <exception cref="IOException">There is a problem accessing <paramref name="localPath"/>.</exception> /// <exception cref="UnauthorizedAccessException">Read access to <paramref name="localPath"/> is not permitted.</exception> public static void Add(this IBuilder builder, DownloadRetrievalMethod retrievalMethod, ICommandExecutor executor, ITaskHandler handler, string?localPath = null) { #region Sanity checks if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (retrievalMethod == null) { throw new ArgumentNullException(nameof(retrievalMethod)); } if (executor == null) { throw new ArgumentNullException(nameof(executor)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } #endregion void Process(Stream stream) { retrievalMethod.SetMissing(executor, localPath); builder.Add(retrievalMethod, stream, handler); long size = stream.Length; if (retrievalMethod is Archive archive) { size -= archive.StartOffset; } if (retrievalMethod.Size != size) { executor.Execute(SetValueCommand.For(() => retrievalMethod.Size, newValue: size)); } } if (localPath == null) { try { handler.RunTask(new DownloadFile(ModelUtils.GetAbsoluteHref(retrievalMethod.Href, executor.Path), Process)); } #region Error handling catch (UriFormatException ex) { // Wrap exception since only certain exception types are allowed throw new WebException(ex.Message, ex); } #endregion } else { handler.RunTask(new ReadFile(localPath, Process)); } }
private void Retrieve(DownloadRetrievalMethod retrievalMethod) { _feedBuilder.RetrievalMethod = retrievalMethod; using (var handler = new GuiTaskHandler(this)) { _feedBuilder.TemporaryDirectory = checkLocalCopy.Checked ? retrievalMethod.LocalApply(textBoxLocalPath.Text, handler) : retrievalMethod.DownloadAndApply(handler); } }
private void Retrieve(DownloadRetrievalMethod retrievalMethod, string localPath) { _feedBuilder.RetrievalMethod = retrievalMethod; using (var handler = new DialogTaskHandler(this)) { _feedBuilder.TemporaryDirectory = (localPath == null) ? retrievalMethod.DownloadAndApply(handler) : retrievalMethod.LocalApply(localPath, handler); } }
/// <summary> /// Adds a downloaded file to the implementation. /// </summary> /// <param name="builder">The builder.</param> /// <param name="retrievalMethod">The metadata of the file.</param> /// <param name="stream">The contents of the file.</param> /// <param name="handler">A callback object used when the the user needs to be informed about IO tasks.</param> /// <param name="tag">A <see cref="ITask.Tag"/> used to group progress bars. Usually <see cref="ManifestDigest.Best"/>.</param> /// <exception cref="UnauthorizedAccessException">Access to a resource was denied.</exception> /// <exception cref="IOException">An IO operation failed.</exception> public static void Add(this IBuilder builder, DownloadRetrievalMethod retrievalMethod, Stream stream, ITaskHandler handler, object?tag = null) { switch (retrievalMethod) { case SingleFile singleFile: builder.AddFile(singleFile, stream); break; case Archive archive: builder.AddArchive(archive, stream, handler, tag); break; default: throw new NotSupportedException($"Unknown download retrieval method: ${retrievalMethod}"); } }
/// <summary> /// Downloads and applies a <see cref="RetrievalMethod"/> and adds missing properties. /// </summary> /// <param name="retrievalMethod">The <see cref="RetrievalMethod"/> to be downloaded.</param> /// <param name="handler">A callback object used when the the user is to be informed about progress.</param> /// <param name="executor">Used to apply properties in an undoable fashion.</param> /// <returns>A temporary directory containing the extracted content.</returns> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="UriFormatException"><see cref="DownloadRetrievalMethod.Href"/> inside <paramref name="retrievalMethod"/> is a relative URI that cannot be resolved.</exception> /// <exception cref="IOException">There is a problem access a temporary file.</exception> /// <exception cref="WebException">A file could not be downloaded from the internet.</exception> /// <exception cref="UnauthorizedAccessException">Read or write access to a temporary file is not permitted.</exception> /// <exception cref="NotSupportedException">A <see cref="Archive.MimeType"/> is not supported.</exception> public static TemporaryDirectory DownloadAndApply(this RetrievalMethod retrievalMethod, ITaskHandler handler, ICommandExecutor?executor = null) { #region Sanity checks if (retrievalMethod == null) { throw new ArgumentNullException(nameof(retrievalMethod)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } #endregion return(retrievalMethod switch { DownloadRetrievalMethod download => download.DownloadAndApply(handler, executor), Recipe recipe => recipe.DownloadAndApply(handler, executor), _ => throw new NotSupportedException(Resources.UnknownRetrievalMethodType) });
public static TemporaryDirectory DownloadAndApply([NotNull] this DownloadRetrievalMethod retrievalMethod, [NotNull] ITaskHandler handler, [CanBeNull] ICommandExecutor executor = null) { #region Sanity checks if (retrievalMethod == null) { throw new ArgumentNullException("retrievalMethod"); } if (handler == null) { throw new ArgumentNullException("handler"); } #endregion using (var downloadedFile = retrievalMethod.Download(handler, executor)) { var extractionDir = new TemporaryDirectory("0publish"); try { new PerTypeDispatcher <DownloadRetrievalMethod>(ignoreMissing: false) { // ReSharper disable AccessToDisposedClosure (Archive archive) => archive.Apply(downloadedFile, extractionDir, handler), (SingleFile file) => file.Apply(downloadedFile, extractionDir, handler) // ReSharper restore AccessToDisposedClosure }.Dispatch(retrievalMethod); } #region Error handling catch { extractionDir.Dispose(); throw; } #endregion return(extractionDir); } }
public static TemporaryDirectory LocalApply([NotNull] this DownloadRetrievalMethod retrievalMethod, string localPath, [NotNull] ITaskHandler handler, [CanBeNull] ICommandExecutor executor = null) { #region Sanity checks if (retrievalMethod == null) { throw new ArgumentNullException("retrievalMethod"); } if (string.IsNullOrEmpty(localPath)) { throw new ArgumentNullException("localPath"); } if (handler == null) { throw new ArgumentNullException("handler"); } #endregion if (executor == null) { executor = new SimpleCommandExecutor(); } // Set local file size long newSize = new FileInfo(localPath).Length; if (retrievalMethod.Size != newSize) { executor.Execute(new SetValueCommand <long>(() => retrievalMethod.Size, value => retrievalMethod.Size = value, newSize)); } var extractionDir = new TemporaryDirectory("0publish"); try { new PerTypeDispatcher <DownloadRetrievalMethod>(ignoreMissing: true) { // ReSharper disable AccessToDisposedClosure (Archive archive) => { // Guess MIME types now because the file ending is not known later if (string.IsNullOrEmpty(archive.MimeType)) { string mimeType = Archive.GuessMimeType(localPath); executor.Execute(new SetValueCommand <string>(() => archive.MimeType, value => archive.MimeType = value, mimeType)); } archive.Apply(localPath, extractionDir, handler); }, (SingleFile file) => { // Guess file name based on local path if (string.IsNullOrEmpty(file.Destination)) { string destination = Path.GetFileName(localPath); executor.Execute(new SetValueCommand <string>(() => file.Destination, value => file.Destination = value, destination)); } file.Apply(localPath, extractionDir, handler); } // ReSharper restore AccessToDisposedClosure }.Dispatch(retrievalMethod); } #region Error handling catch { extractionDir.Dispose(); throw; } #endregion return(extractionDir); }