Пример #1
0
        /// <summary>
        /// Creates a new or updates an existing stub EXE that executes the "0install run" command.
        /// </summary>
        /// <seealso cref="BuildRunStub"/>
        /// <param name="target">The application to be launched via the stub.</param>
        /// <param name="path">The target path to store the generated EXE file.</param>
        /// <param name="command">The command argument to be passed to the the "0install run" command; can be <c>null</c>.</param>
        /// <param name="needsTerminal"><c>true</c> to build a CLI stub, <c>false</c> to build a GUI stub.</param>
        /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="InvalidOperationException">There was a compilation error while generating the stub EXE.</exception>
        /// <exception cref="IOException">A problem occurs while writing to the filesystem.</exception>
        /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception>
        private static void CreateOrUpdateRunStub(FeedTarget target, [NotNull] string path, [CanBeNull] string command, bool needsTerminal, [NotNull] ITaskHandler handler)
        {
            if (File.Exists(path))
            {     // Existing stub
                if (File.GetLastWriteTimeUtc(path) < _libraryInstallTimestamp)
                { // Built by older version of this library, try to rebuild
                    try
                    {
                        File.Delete(path);
                    }
                    #region Error handling
                    catch (IOException ex)
                    {
                        Log.Warn(string.Format(Resources.UnableToReplaceStub, path));
                        Log.Warn(ex);
                        return;
                    }
                    catch (UnauthorizedAccessException ex)
                    {
                        Log.Warn(string.Format(Resources.UnableToReplaceStub, path));
                        Log.Warn(ex);
                        return;
                    }
                    #endregion

                    BuildRunStub(target, path, handler, needsTerminal, command);
                }
            }
            else
            { // No existing stub, build new one
                BuildRunStub(target, path, handler, needsTerminal, command);
            }
        }
Пример #2
0
        /// <summary>
        /// Creates a new Windows shortcut.
        /// </summary>
        /// <param name="path">The location to place the shortcut at.</param>
        /// <param name="target">The target the shortcut shall point to.</param>
        /// <param name="command">The command within <paramref name="target"/> the shortcut shall point to; can be <c>null</c>.</param>
        /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
        private static void Create(string path, FeedTarget target, string?command, IIconStore iconStore)
        {
            if (string.IsNullOrEmpty(command))
            {
                command = Command.NameRun;
            }

            var  entryPoint    = target.Feed.GetEntryPoint(command);
            bool needsTerminal = (entryPoint != null && entryPoint.NeedsTerminal);

            string targetPath = Path.Combine(Locations.InstallBase, needsTerminal ? "0install.exe" : "0install-win.exe");

            string arguments = "run ";

            if (!needsTerminal)
            {
                arguments += "--no-wait ";
            }
            if (command != Command.NameRun)
            {
                arguments += "--command " + command.EscapeArgument() + " ";
            }
            arguments += target.Uri.ToStringRfc().EscapeArgument();

            var icon = target.Feed.GetIcon(Icon.MimeTypeIco, command);

            Create(path, targetPath, arguments,
                   iconLocation: (icon == null) ? null : iconStore.GetPath(icon),
                   description: target.Feed.GetBestSummary(CultureInfo.CurrentUICulture, command));
        }
Пример #3
0
        /// <summary>
        /// Registers a <see cref="Verb"/> in a registry key.
        /// </summary>
        /// <param name="verbKey">The registry key to write the new data to.</param>
        /// <param name="target">The application being integrated.</param>
        /// <param name="verb">The verb to register.</param>
        /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
        /// <param name="machineWide">Assume <paramref name="verbKey"/> is effective machine-wide instead of just for the current user.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception>
        /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
        /// <exception cref="InvalidDataException">The data in <paramref name="verb"/> is invalid.</exception>
        public static void Register(RegistryKey verbKey, FeedTarget target, Verb verb, IIconStore iconStore, bool machineWide)
        {
            string?description = verb.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture);

            if (!string.IsNullOrEmpty(description))
            {
                verbKey.SetValue("", description);
                verbKey.SetValue("MUIVerb", description);
            }

            if (verb.Extended)
            {
                verbKey.SetValue("Extended", "");
            }

            var icon = target.Feed.GetIcon(Icon.MimeTypeIco, verb.Command);

            if (icon != null)
            {
                verbKey.SetValue("Icon", iconStore.GetPath(icon));
            }

            using var commandKey = verbKey.CreateSubKeyChecked("command");
            commandKey.SetValue("", GetLaunchCommandLine(target, verb, iconStore, machineWide));
        }
Пример #4
0
        /// <summary>
        /// Creates a new or updates an existing stub EXE that executes the "0install run" command.
        /// </summary>
        /// <seealso cref="BuildRunStub"/>
        /// <param name="target">The application to be launched via the stub.</param>
        /// <param name="path">The target path to store the generated EXE file.</param>
        /// <param name="command">The command argument to be passed to the the "0install run" command; can be <see langword="null"/>.</param>
        /// <param name="needsTerminal"><see langword="true"/> to build a CLI stub, <see langword="false"/> to build a GUI stub.</param>
        /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="InvalidOperationException">There was a compilation error while generating the stub EXE.</exception>
        /// <exception cref="IOException">A problem occurs while writing to the filesystem.</exception>
        /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception>
        private static void CreateOrUpdateRunStub(this FeedTarget target, [NotNull] string path, [CanBeNull] string command, bool needsTerminal, [NotNull] ITaskHandler handler)
        {
            if (File.Exists(path))
            { // Existing stub
                // TODO: Find better rebuild discriminator
                if (File.GetLastWriteTime(path) < Process.GetCurrentProcess().StartTime)
                { // Outdated, try to rebuild
                    try
                    {
                        File.Delete(path);
                    }
                    #region Error handling
                    catch (IOException ex)
                    {
                        Log.Warn(string.Format(Resources.UnableToReplaceStub, path));
                        Log.Warn(ex);
                        return;
                    }
                    catch (UnauthorizedAccessException ex)
                    {
                        Log.Warn(string.Format(Resources.UnableToReplaceStub, path));
                        Log.Warn(ex);
                        return;
                    }
                    #endregion

                    target.BuildRunStub(path, handler, needsTerminal, command);
                }
            }
            else
            { // No existing stub, build new one
                target.BuildRunStub(path, handler, needsTerminal, command);
            }
        }
Пример #5
0
        /// <summary>
        /// Registers a URL protocol in the current system.
        /// </summary>
        /// <param name="target">The application being integrated.</param>
        /// <param name="urlProtocol">The URL protocol to register.</param>
        /// <param name="machineWide">Register the URL protocol machine-wide instead of just for the current user.</param>
        /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
        /// <param name="accessPoint">Indicates that the handler shall become the default handler for the protocol.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception>
        /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
        /// <exception cref="InvalidDataException">The data in <paramref name="urlProtocol"/> is invalid.</exception>
        public static void Register(FeedTarget target, Model.Capabilities.UrlProtocol urlProtocol, IIconStore iconStore, bool machineWide, bool accessPoint = false)
        {
            #region Sanity checks
            if (urlProtocol == null)
            {
                throw new ArgumentNullException(nameof(urlProtocol));
            }
            if (iconStore == null)
            {
                throw new ArgumentNullException(nameof(iconStore));
            }
            #endregion

            if (string.IsNullOrEmpty(urlProtocol.ID))
            {
                throw new InvalidDataException("Missing ID");
            }

            using var classesKey = RegistryClasses.OpenHive(machineWide);

            if (urlProtocol.KnownPrefixes.Count == 0)
            {
                if (accessPoint)
                { // Can only be registered invasively by registering protocol ProgID (will replace existing and become default)
                    using var progIDKey = classesKey.CreateSubKeyChecked(urlProtocol.ID);
                    progIDKey.SetValue("", urlProtocol.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture) ?? urlProtocol.ID);
                    RegistryClasses.Register(progIDKey, target, urlProtocol, iconStore, machineWide);
                    progIDKey.SetValue(ProtocolIndicator, "");
                }
            }
            else
            { // Can be registered non-invasively by registering custom ProgID (without becoming default)
                using (var progIDKey = classesKey.CreateSubKeyChecked(RegistryClasses.Prefix + urlProtocol.ID))
                {
                    progIDKey.SetValue("", urlProtocol.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture) ?? urlProtocol.ID);
                    progIDKey.SetValue(accessPoint ? RegistryClasses.PurposeFlagAccessPoint : RegistryClasses.PurposeFlagCapability, "");
                    RegistryClasses.Register(progIDKey, target, urlProtocol, iconStore, machineWide);
                    progIDKey.SetValue(ProtocolIndicator, "");
                }

                if (accessPoint)
                {
                    foreach (var prefix in urlProtocol.KnownPrefixes)
                    {
                        if (WindowsUtils.IsWindowsVista && !machineWide)
                        {
                            using var userChoiceKey = Registry.CurrentUser.CreateSubKeyChecked($@"{RegKeyUserVistaUrlAssoc}\{prefix.Value}\UserChoice");
                            userChoiceKey.SetValue("ProgID", RegistryClasses.Prefix + urlProtocol.ID);
                        }
                        else
                        {
                            // Setting default invasively by registering protocol ProgID
                            using var progIDKey = classesKey.CreateSubKeyChecked(prefix.Value);
                            RegistryClasses.Register(progIDKey, target, urlProtocol, iconStore, machineWide);
                            progIDKey.SetValue(ProtocolIndicator, "");
                        }
                    }
                }
            }
        }
Пример #6
0
        /// <inheritdoc/>
        public override void Apply(AppEntry appEntry, Feed feed, ITaskHandler handler, bool machineWide)
        {
            #region Sanity checks
            if (appEntry == null)
            {
                throw new ArgumentNullException("appEntry");
            }
            if (handler == null)
            {
                throw new ArgumentNullException("handler");
            }
            #endregion

            var capability = appEntry.GetCapability <Store.Model.Capabilities.UrlProtocol>(Capability);
            if (capability == null)
            {
                return;
            }

            var target = new FeedTarget(appEntry.InterfaceUri, feed);
            if (WindowsUtils.IsWindows)
            {
                Windows.UrlProtocol.Register(target, capability, machineWide, handler, accessPoint: true);
            }
        }
Пример #7
0
    private void CreateOrUpdateRunStub(string path, FeedTarget target, bool gui, string?command)
    {
        if (File.Exists(path))
        {     // Existing stub
            if (File.GetLastWriteTimeUtc(path) < _libraryInstallTimestamp)
            { // Built by older version of this library, try to rebuild
                try
                {
                    File.Delete(path);
                }
                #region Error handling
                catch (Exception ex) when(ex is IOException or UnauthorizedAccessException)
                {
                    Log.Warn(string.Format(Resources.UnableToReplaceStub, path));
                    Log.Warn(ex);
                    return;
                }
                #endregion

                BuildRunStub(path, target, command, gui);
            }
        }
        else
        { // No existing stub, build new one
            BuildRunStub(path, target, command, gui);
        }
    }
Пример #8
0
        /// <summary>
        /// Creates an application alias in the current system.
        /// </summary>
        /// <param name="target">The application being integrated.</param>
        /// <param name="command">The command within <paramref name="target"/> the alias shall point to; can be <see langword="null"/>.</param>
        /// <param name="aliasName">The name of the alias to be created.</param>
        /// <param name="machineWide">Create the alias machine-wide instead of just for the current user.</param>
        /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="IOException">A problem occurs while writing to the filesystem or registry.</exception>
        /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
        public static void Create(FeedTarget target, [CanBeNull] string command, [NotNull] string aliasName, bool machineWide, [NotNull] ITaskHandler handler)
        {
            #region Sanity checks
            if (string.IsNullOrEmpty(aliasName))
            {
                throw new ArgumentNullException("aliasName");
            }
            if (handler == null)
            {
                throw new ArgumentNullException("handler");
            }
            #endregion

            if (string.IsNullOrEmpty(aliasName) || aliasName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1)
            {
                throw new IOException(string.Format(Resources.NameInvalidChars, aliasName));
            }

            string stubDirPath  = Locations.GetIntegrationDirPath("0install.net", machineWide, "desktop-integration", "aliases");
            string stubFilePath = Path.Combine(stubDirPath, aliasName + ".exe");

            target.BuildRunStub(stubFilePath, handler, needsTerminal: true, command: command);
            AddToPath(stubDirPath, machineWide);
            AddToAppPaths(aliasName + ".exe", stubFilePath, machineWide);
        }
Пример #9
0
    /// <summary>
    /// Returns a command-line for executing the "0install run" command. Generates and returns a stub EXE if possible, falls back to directly pointing to the "0install" binary otherwise.
    /// </summary>
    /// <param name="target">The application to be launched.</param>
    /// <param name="command">The command argument to be passed to the the "0install run" command; can be <c>null</c>.</param>
    /// <param name="machineWide"><c>true</c> place the generated stub in a machine-wide location; <c>false</c> to place it in the current user profile.</param>
    /// <returns></returns>
    /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
    /// <exception cref="InvalidOperationException">There was a compilation error while generating the stub EXE.</exception>
    /// <exception cref="IOException">A problem occurred while writing to the filesystem.</exception>
    /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
    /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception>
    public IReadOnlyList <string> GetRunCommandLine(FeedTarget target, string?command = null, bool machineWide = false)
    {
        var  entryPoint = target.Feed.GetEntryPoint(command);
        bool gui        = entryPoint is not {
            NeedsTerminal : true
        };

        try
        {
#if NETFRAMEWORK
            string hash    = (target.Uri + "#" + command).Hash(SHA256.Create());
            string exeName = (entryPoint == null)
                ? FeedUri.Escape(target.Feed.Name)
                : entryPoint.BinaryName ?? entryPoint.Command;
            string path = Path.Combine(
                IntegrationManager.GetDir(machineWide, "stubs", hash),
                exeName + ".exe");

            CreateOrUpdateRunStub(path, target, gui, command);
            return(new[] { path });
#else
            return(GetArguments(target.Uri, command, gui)
                   .Prepend(GetBinary(gui))
                   .ToList());
#endif
        }
        #region Error handling
        catch (InvalidOperationException ex)
        {
            // Wrap exception since only certain exception types are allowed
            throw new IOException(ex.Message, ex);
        }
        #endregion
    }
Пример #10
0
    private string?GetIconPath(FeedTarget target, string?command)
    {
        var icon = target.Feed.GetBestIcon(Icon.MimeTypeIco, command);

        if (icon == null)
        {
            return(null);
        }

        try
        {
            string iconPath = _iconStore.GetFresh(icon);
            new System.Drawing.Icon(iconPath).Dispose(); // Try to parse icon to ensure it is valid
            return(iconPath);
        }
        #region Error handling
        catch (Exception ex) when(ex is UriFormatException or WebException)
        {
            Log.Warn(ex);
        }
        catch (Exception ex) when(ex is IOException or UnauthorizedAccessException)
        {
            Log.Warn($"Failed to store {icon}");
            Log.Warn(ex);
        }
        catch (ArgumentException ex)
        {
            Log.Warn($"Failed to parse {icon}");
            Log.Warn(ex);
        }
        #endregion

        return(null);
    }
Пример #11
0
    /// <summary>
    /// Builds a stub EXE that executes the "0install run" command at a specific path.
    /// </summary>
    /// <param name="path">The path to store the generated EXE file.</param>
    /// <param name="target">The application to be launched.</param>
    /// <param name="command">The command argument to be passed to the the "0install run" command; can be <c>null</c>.</param>
    /// <param name="gui"><c>true</c> to build a GUI stub, <c>false</c> to build a CLI stub.</param>
    /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
    /// <exception cref="InvalidOperationException">There was a compilation error while generating the stub EXE.</exception>
    /// <exception cref="IOException">A problem occurred while writing to the filesystem.</exception>
    /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
    /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception>
    public void BuildRunStub(string path, FeedTarget target, string?command = null, bool gui = false)
    {
        #region Sanity checks
        if (string.IsNullOrEmpty(path))
        {
            throw new ArgumentNullException(nameof(path));
        }
        #endregion

        var compilerParameters = new CompilerParameters
        {
            OutputAssembly        = path,
            GenerateExecutable    = true,
            TreatWarningsAsErrors = true,
            ReferencedAssemblies  = { "System.dll" },
            CompilerOptions       = gui ? "/target:winexe" : "/target:exe"
        };

        string?iconPath = GetIconPath(target, command);
        if (iconPath != null)
        {
            compilerParameters.CompilerOptions += " /win32icon:" + iconPath.EscapeArgument();
        }

        compilerParameters.CompileCSharp(
            GetCode(
                exe: GetBinary(gui),
                arguments: GetArguments(target.Uri, command, gui),
                title: target.Feed.GetBestName(CultureInfo.CurrentUICulture, command)),
            Manifest);
    }
Пример #12
0
        /// <summary>
        /// Creates an application alias in the current system.
        /// </summary>
        /// <param name="target">The application being integrated.</param>
        /// <param name="command">The command within <paramref name="target"/> the alias shall point to; can be <c>null</c>.</param>
        /// <param name="aliasName">The name of the alias to be created.</param>
        /// <param name="machineWide">Create the alias machine-wide instead of just for the current user.</param>
        /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="IOException">A problem occurs while writing to the filesystem or registry.</exception>
        /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
        public static void Create(FeedTarget target, [CanBeNull] string command, [NotNull] string aliasName, bool machineWide, [NotNull] ITaskHandler handler)
        {
            #region Sanity checks
            if (string.IsNullOrEmpty(aliasName))
            {
                throw new ArgumentNullException(nameof(aliasName));
            }
            if (handler == null)
            {
                throw new ArgumentNullException(nameof(handler));
            }
            #endregion

            if (string.IsNullOrEmpty(aliasName) || aliasName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1)
            {
                throw new IOException(string.Format(Resources.NameInvalidChars, aliasName));
            }

            string stubDirPath = GetStubDir(machineWide);
            PathEnv.AddDir(stubDirPath, machineWide);

            string stubFilePath = Path.Combine(stubDirPath, aliasName + ".exe");
            StubBuilder.BuildRunStub(target, stubFilePath, handler, needsTerminal: true, command: command);
            AddToAppPaths(aliasName + ".exe", stubFilePath, machineWide);
        }
Пример #13
0
        /// <summary>
        /// Creates a new Windows shortcut.
        /// </summary>
        /// <param name="path">The location to place the shorcut at.</param>
        /// <param name="target">The target the shortcut shall point to.</param>
        /// <param name="command">The command within <paramref name="target"/> the shorcut shall point to; can be <see langword="null"/>.</param>
        /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param>
        /// <param name="machineWide">Create the shortcut machine-wide instead of just for the current user.</param>
        private static void Create([NotNull] string path, FeedTarget target, [CanBeNull] string command, [NotNull] ITaskHandler handler, bool machineWide = false)
        {
            if (string.IsNullOrEmpty(command))
            {
                command = Command.NameRun;
            }

            var  entryPoint    = target.Feed.GetEntryPoint(command);
            bool needsTerminal = (entryPoint != null && entryPoint.NeedsTerminal);

            string arguments = "run ";

            if (!needsTerminal)
            {
                arguments += "--no-wait ";
            }
            if (command != Command.NameRun)
            {
                arguments += "--command " + command.EscapeArgument() + " ";
            }
            arguments += target.Uri.ToStringRfc().EscapeArgument();

            var icon = target.Feed.GetIcon(Icon.MimeTypeIco, command);

            Create(path,
                   targetPath: Path.Combine(Locations.InstallBase, needsTerminal ? "0install.exe" : "0install-win.exe"),
                   arguments: arguments,
                   iconLocation: (icon == null) ? null : IconProvider.GetIconPath(icon, handler, machineWide),
                   description: target.Feed.GetBestSummary(CultureInfo.CurrentUICulture, command));
        }
Пример #14
0
        /// <inheritdoc/>
        public override void Apply(AppEntry appEntry, Feed feed, ITaskHandler handler, bool machineWide)
        {
            #region Sanity checks
            if (appEntry == null)
            {
                throw new ArgumentNullException("appEntry");
            }
            if (handler == null)
            {
                throw new ArgumentNullException("handler");
            }
            #endregion

            var capability = appEntry.GetCapability <Store.Model.Capabilities.ContextMenu>(Capability);
            if (capability == null)
            {
                return;
            }

            var target = new FeedTarget(appEntry.InterfaceUri, feed);
            if (WindowsUtils.IsWindows)
            {
                Windows.ContextMenu.Apply(target, capability, machineWide, handler);
            }
            else if (UnixUtils.IsUnix)
            {
                Unix.ContextMenu.Apply(target, capability, machineWide, handler);
            }
        }
Пример #15
0
    /// <summary>
    /// Registers a <see cref="Verb"/> in a registry key.
    /// </summary>
    /// <param name="verbKey">The registry key to write the new data to.</param>
    /// <param name="target">The application being integrated.</param>
    /// <param name="verb">The verb to register.</param>
    /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
    /// <param name="machineWide">Assume <paramref name="verbKey"/> is effective machine-wide instead of just for the current user.</param>
    /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
    /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception>
    /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
    /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
    public static void Register(RegistryKey verbKey, FeedTarget target, Verb verb, IIconStore iconStore, bool machineWide)
    {
        string?description = verb.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture);

        verbKey.SetOrDelete("", description);
        verbKey.SetOrDelete("MUIVerb", description);

        verbKey.SetOrDelete("MultiSelectModel", verb.SingleElementOnly ? "Single" : null);

        if (verb.Extended)
        {
            verbKey.SetValue("Extended", "");
        }
        else
        {
            verbKey.DeleteValue("Extended", throwOnMissingValue: false);
        }

        var icon = target.Feed.GetBestIcon(Icon.MimeTypeIco, verb.Command)
                   ?? target.Feed.Icons.GetIcon(Icon.MimeTypeIco);

        verbKey.SetOrDelete("Icon", icon?.To(iconStore.GetFresh));

        using var commandKey = verbKey.CreateSubKeyChecked("command");
        commandKey.SetValue("", GetLaunchCommandLine(target, verb, iconStore, machineWide));
    }
Пример #16
0
    /// <summary>
    /// Creates an application alias in the current system.
    /// </summary>
    /// <param name="target">The application being integrated.</param>
    /// <param name="command">The command within <paramref name="target"/> the alias shall point to; can be <c>null</c>.</param>
    /// <param name="aliasName">The name of the alias to be created.</param>
    /// <param name="machineWide">Create the alias machine-wide instead of just for the current user.</param>
    /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
    /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
    /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception>
    /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
    /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
    public static void Create(FeedTarget target, string?command, string aliasName, IIconStore iconStore, bool machineWide)
    {
        #region Sanity checks
        if (string.IsNullOrEmpty(aliasName))
        {
            throw new ArgumentNullException(nameof(aliasName));
        }
        if (iconStore == null)
        {
            throw new ArgumentNullException(nameof(iconStore));
        }
        #endregion

#if NETFRAMEWORK
        string stubDirPath = GetStubDir(machineWide);
        PathEnv.AddDir(stubDirPath, machineWide);

        string stubFilePath = Path.Combine(stubDirPath, aliasName + ".exe");
        new StubBuilder(iconStore).BuildRunStub(stubFilePath, target, command);

        if (machineWide || WindowsUtils.IsWindows7)
        {
            var hive = machineWide ? Registry.LocalMachine : Registry.CurrentUser;
            using var appPathsKey = hive.CreateSubKeyChecked(RegKeyAppPaths);
            using var exeKey      = appPathsKey.CreateSubKeyChecked(aliasName + ".exe");
            exeKey.SetValue("", stubFilePath);
        }
#else
        throw new PlatformNotSupportedException("Generating Windows aliases is not supported by the .NET Core version of Zero Install.");
#endif
    }
Пример #17
0
    /// <summary>
    /// Generates a command-line string for launching a <see cref="Verb"/>.
    /// </summary>
    /// <param name="target">The application being integrated.</param>
    /// <param name="verb">The verb to get to launch command for.</param>
    /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
    /// <param name="machineWide">Store the stub in a machine-wide directory instead of just for the current user.</param>
    /// <exception cref="IOException">A problem occurred while writing to the filesystem.</exception>
    /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
    /// <exception cref="InvalidOperationException">Write access to the filesystem is not permitted.</exception>
    internal static string GetLaunchCommandLine(FeedTarget target, Verb verb, IIconStore iconStore, bool machineWide)
    {
        IEnumerable <string> GetCommandLine()
        {
            try
            {
                return(new StubBuilder(iconStore).GetRunCommandLine(target, verb.Command, machineWide));
            }
            #region Error handling
            catch (InvalidOperationException ex)
            {
                // Wrap exception since only certain exception types are allowed
                throw new IOException(ex.Message, ex);
            }
            #endregion
        }

        if (verb.Arguments.Count == 0)
        {
            string arguments = string.IsNullOrEmpty(verb.ArgumentsLiteral) ? "\"%V\"" : verb.ArgumentsLiteral;
            return(GetCommandLine().JoinEscapeArguments() + " " + arguments);
        }

        return(GetCommandLine()
               .Concat(verb.Arguments.Select(x => x.Value))
               .JoinEscapeArguments()
               .Replace("${item}", "\"%V\""));
    }
Пример #18
0
    /// <summary>
    /// Registers a <see cref="VerbCapability"/> in a registry key.
    /// </summary>
    /// <param name="registryKey">The registry key to write the new data to.</param>
    /// <param name="target">The application being integrated.</param>
    /// <param name="capability">The capability to register.</param>
    /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
    /// <param name="machineWide">Assume <paramref name="registryKey"/> is effective machine-wide instead of just for the current user.</param>
    /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
    /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception>
    /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
    /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
    public static void Register(RegistryKey registryKey, FeedTarget target, VerbCapability capability, IIconStore iconStore, bool machineWide)
    {
        #region Sanity checks
        if (capability == null)
        {
            throw new ArgumentNullException(nameof(capability));
        }
        if (iconStore == null)
        {
            throw new ArgumentNullException(nameof(iconStore));
        }
        #endregion

        if ((capability.GetIcon(Icon.MimeTypeIco) ?? target.Feed.Icons.GetIcon(Icon.MimeTypeIco)) is {} icon)
        {
            using var iconKey = registryKey.CreateSubKeyChecked("DefaultIcon");
            iconKey.SetValue("", iconStore.GetFresh(icon) + ",0");
        }

        foreach (var verb in capability.Verbs)
        {
            using var verbKey = registryKey.CreateSubKeyChecked($@"shell\{verb.Name}");
            Register(verbKey, target, verb, iconStore, machineWide);
        }

        // Prevent conflicts with existing entries
        registryKey.DeleteSubKeyTree(@"shell\ddeexec", throwOnMissingSubKey: false);
    }
Пример #19
0
    /// <inheritdoc/>
    public override void Apply(AppEntry appEntry, Feed feed, IIconStore iconStore, bool machineWide)
    {
        #region Sanity checks
        if (appEntry == null)
        {
            throw new ArgumentNullException(nameof(appEntry));
        }
        if (iconStore == null)
        {
            throw new ArgumentNullException(nameof(iconStore));
        }
        #endregion

        ValidateName();

        var target = new FeedTarget(appEntry.InterfaceUri, feed);
        if (WindowsUtils.IsWindows)
        {
            Windows.AppAlias.Create(target, Command, Name, iconStore, machineWide);
        }
        else if (UnixUtils.IsUnix)
        {
            Unix.AppAlias.Create(target, Command, Name, iconStore, machineWide);
        }
    }
Пример #20
0
        /// <inheritdoc/>
        public AppEntry AddApp(FeedTarget target)
        {
            var appEntry = AddAppInternal(target);

            Finish();
            return(appEntry);
        }
Пример #21
0
    /// <summary>
    /// Returns a command-line for executing the "0install run" command.
    /// Generates and returns a stub EXE if possible, falls back to directly pointing to the "0install" EXE otherwise.
    /// </summary>
    /// <param name="target">The application to be launched.</param>
    /// <param name="command">The command argument to be passed to the the "0install run" command; can be <c>null</c>.</param>
    /// <param name="machineWide"><c>true</c> place the generated stub in a machine-wide location; <c>false</c> to place it in the current user profile.</param>
    /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
    /// <exception cref="InvalidOperationException">There was a compilation error while generating the stub EXE.</exception>
    /// <exception cref="IOException">A problem occurred while writing to the filesystem.</exception>
    /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
    /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception>
    public IReadOnlyList <string> GetRunCommandLine(FeedTarget target, string?command = null, bool machineWide = false)
    {
        string targetKey = target.Uri + "#" + command;

        var  entryPoint = target.Feed.GetEntryPoint(command);
        bool gui        = entryPoint is not {
            NeedsTerminal : true
        };

        string targetHash = targetKey.Hash(SHA256.Create());
        string exeName    = (entryPoint == null)
            ? FeedUri.Escape(target.Feed.Name)
            : entryPoint.BinaryName ?? entryPoint.Command;
        string path = Path.Combine(
            IntegrationManager.GetDir(machineWide, "stubs", targetHash),
            exeName + ".exe");

#if !DEBUG
        try
#endif
        {
            CreateOrUpdateRunStub(path, target, gui, command);
            return(new[] { path });
        }
#if !DEBUG
        catch (Exception ex)
        {
            var exe = GetExe(gui);
            Log.Error($"Failed to generate stub EXE for {targetKey}. Falling back to using '{exe}' directly.", ex);
            return(GetArguments(target.Uri, command, gui)
                   .Prepend(Path.Combine(Locations.InstallBase, exe))
                   .ToList());
        }
#endif
    }
Пример #22
0
        /// <summary>
        /// Registers a <see cref="Store.Model.Capabilities.VerbCapability"/> in a registry key.
        /// </summary>
        /// <param name="registryKey">The registry key to write the new data to.</param>
        /// <param name="target">The application being integrated.</param>
        /// <param name="capability">The capability to register.</param>
        /// <param name="machineWide">Assume <paramref name="registryKey"/> is effective machine-wide instead of just for the current user.</param>
        /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="IOException">A problem occurs while writing to the filesystem or registry.</exception>
        /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
        /// <exception cref="InvalidDataException">The data in <paramref name="capability"/> is invalid.</exception>
        internal static void RegisterVerbCapability(RegistryKey registryKey, FeedTarget target, Store.Model.Capabilities.VerbCapability capability, bool machineWide, ITaskHandler handler)
        {
            #region Sanity checks
            if (capability == null)
            {
                throw new ArgumentNullException(nameof(capability));
            }
            if (handler == null)
            {
                throw new ArgumentNullException(nameof(handler));
            }
            #endregion

            if (capability is Store.Model.Capabilities.UrlProtocol)
            {
                registryKey.SetValue(UrlProtocol.ProtocolIndicator, "");
            }

            string description = capability.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture);
            if (description != null)
            {
                registryKey.SetValue("", description);
            }

            // Write verb command information
            using (var shellKey = registryKey.CreateSubKeyChecked("shell"))
            {
                foreach (var verb in capability.Verbs)
                {
                    using (var verbKey = shellKey.CreateSubKeyChecked(verb.Name))
                    {
                        string verbDescription = verb.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture);
                        if (verbDescription != null)
                        {
                            verbKey.SetValue("", verbDescription);
                        }
                        if (verb.Extended)
                        {
                            verbKey.SetValue(RegValueExtended, "");
                        }

                        using (var commandKey = verbKey.CreateSubKeyChecked("command"))
                            commandKey.SetValue("", GetLaunchCommandLine(target, verb, machineWide, handler));

                        // Prevent conflicts with existing entries
                        shellKey.DeleteSubKey("ddeexec", throwOnMissingSubKey: false);
                    }
                }
            }

            // Set specific icon if available, fall back to referencing the icon embedded in the stub EXE
            var icon = capability.GetIcon(Icon.MimeTypeIco) ?? target.Feed.GetIcon(Icon.MimeTypeIco);
            if (icon != null)
            {
                using (var iconKey = registryKey.CreateSubKeyChecked(RegSubKeyIcon))
                    iconKey.SetValue("", IconProvider.GetIconPath(icon, handler, machineWide) + ",0");
            }
        }
Пример #23
0
        public void TestBuildStubNeedsTerminal()
        {
            Skip.IfNot(WindowsUtils.IsWindows, "StubBuilder is only used on Windows");

            var target = new FeedTarget(FeedTest.Test1Uri, FeedTest.CreateTestFeed());

            using var tempFile = new TemporaryFile("0install-unit-tests");
            StubBuilder.BuildRunStub(target, tempFile, _iconStoreMock.Object, needsTerminal: true);
        }
Пример #24
0
        /// <summary>
        /// Registers a URL protocol in the current system.
        /// </summary>
        /// <param name="target">The application being integrated.</param>
        /// <param name="urlProtocol">The URL protocol to register.</param>
        /// <param name="machineWide">Register the URL protocol machine-wide instead of just for the current user.</param>
        /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param>
        /// <param name="accessPoint">Indicates that the handler shall become the default handler for the protocol.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="IOException">A problem occurs while writing to the filesystem or registry.</exception>
        /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
        /// <exception cref="InvalidDataException">The data in <paramref name="urlProtocol"/> is invalid.</exception>
        public static void Register(FeedTarget target, [NotNull] Store.Model.Capabilities.UrlProtocol urlProtocol, bool machineWide, [NotNull] ITaskHandler handler, bool accessPoint = false)
        {
            #region Sanity checks
            if (urlProtocol == null)
            {
                throw new ArgumentNullException(nameof(urlProtocol));
            }
            if (handler == null)
            {
                throw new ArgumentNullException(nameof(handler));
            }
            #endregion

            if (string.IsNullOrEmpty(urlProtocol.ID))
            {
                throw new InvalidDataException("Missing ID");
            }

            var hive = machineWide ? Registry.LocalMachine : Registry.CurrentUser;

            if (urlProtocol.KnownPrefixes.Count == 0)
            {
                if (accessPoint)
                { // Can only be registered invasively by registering protocol ProgID (will replace existing and become default)
                    using (var progIDKey = hive.CreateSubKeyChecked(FileType.RegKeyClasses + @"\" + urlProtocol.ID))
                        FileType.RegisterVerbCapability(progIDKey, target, urlProtocol, machineWide, handler);
                }
            }
            else
            { // Can be registered non-invasively by registering custom ProgID (without becoming default)
                using (var progIDKey = hive.CreateSubKeyChecked(FileType.RegKeyClasses + @"\" + FileType.RegKeyPrefix + urlProtocol.ID))
                {
                    // Add flag to remember whether created for capability or access point
                    progIDKey.SetValue(accessPoint ? FileType.PurposeFlagAccessPoint : FileType.PurposeFlagCapability, "");

                    FileType.RegisterVerbCapability(progIDKey, target, urlProtocol, machineWide, handler);
                }

                if (accessPoint)
                {
                    foreach (var prefix in urlProtocol.KnownPrefixes)
                    {
                        if (WindowsUtils.IsWindowsVista && !machineWide)
                        {
                            using (var someKey = Registry.CurrentUser.CreateSubKeyChecked(RegKeyUserVistaUrlAssoc + @"\" + prefix.Value + @"\UserChoice"))
                                someKey.SetValue("ProgID", FileType.RegKeyPrefix + urlProtocol.ID);
                        }
                        else
                        {
                            // Setting default invasively by registering protocol ProgID
                            using (var progIDKey = hive.CreateSubKeyChecked(FileType.RegKeyClasses + @"\" + prefix.Value))
                                FileType.RegisterVerbCapability(progIDKey, target, urlProtocol, machineWide, handler);
                        }
                    }
                }
            }
        }
Пример #25
0
        /// <summary>
        /// Registers an application as a candidate for a default program for some service in the current system. This can only be applied machine-wide, not per user.
        /// </summary>
        /// <param name="target">The application being integrated.</param>
        /// <param name="defaultProgram">The default program information to be registered.</param>
        /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
        /// <param name="accessPoint">Indicates that the program should be set as the current default for the service it provides.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception>
        /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
        /// <exception cref="InvalidDataException">The data in <paramref name="defaultProgram"/> is invalid.</exception>
        public static void Register(FeedTarget target, Model.Capabilities.DefaultProgram defaultProgram, IIconStore iconStore, bool accessPoint = false)
        {
            #region Sanity checks
            if (defaultProgram == null)
            {
                throw new ArgumentNullException(nameof(defaultProgram));
            }
            if (iconStore == null)
            {
                throw new ArgumentNullException(nameof(iconStore));
            }
            #endregion

            if (string.IsNullOrEmpty(defaultProgram.ID))
            {
                throw new InvalidDataException("Missing ID");
            }
            if (string.IsNullOrEmpty(defaultProgram.Service))
            {
                throw new InvalidDataException("Missing Service");
            }

            using var serviceKey = Registry.LocalMachine.CreateSubKeyChecked($@"{RegKeyMachineClients}\{defaultProgram.Service}");
            using (var appKey = serviceKey.CreateSubKeyChecked(defaultProgram.ID))
            {
                appKey.SetValue("", target.Feed.Name);
                appKey.SetValue(accessPoint ? RegistryClasses.PurposeFlagAccessPoint : RegistryClasses.PurposeFlagCapability, "");
                RegistryClasses.Register(appKey, target, defaultProgram, iconStore, machineWide: true);

                // Set callbacks for Windows SPAD
                using (var installInfoKey = appKey.CreateSubKeyChecked(RegSubKeyInstallInfo))
                {
                    string exePath = Path.Combine(Locations.InstallBase, "0install-win.exe");
                    installInfoKey.SetValue(RegValueReinstallCommand, new[] { exePath, "integrate", "--machine", "--batch", "--add", "defaults", target.Uri.ToStringRfc() }.JoinEscapeArguments());
                    installInfoKey.SetValue(RegValueShowIconsCommand, new[] { exePath, "integrate", "--machine", "--batch", "--add", MenuEntry.CategoryName, "--add", DesktopIcon.CategoryName, target.Uri.ToStringRfc() }.JoinEscapeArguments());
                    installInfoKey.SetValue(RegValueHideIconsCommand, new[] { exePath, "integrate", "--machine", "--batch", "--remove", MenuEntry.CategoryName, "--remove", DesktopIcon.CategoryName, target.Uri.ToStringRfc() }.JoinEscapeArguments());
                    installInfoKey.SetValue(RegValueIconsVisible, 0, RegistryValueKind.DWord);
                }

                if (defaultProgram.Service == Model.Capabilities.DefaultProgram.ServiceMail)
                {
                    var mailToProtocol = new Model.Capabilities.UrlProtocol {
                        Verbs = { new Verb {
                                      Name = Verb.NameOpen
                                  } }
                    };
                    using var mailToKey = appKey.CreateSubKeyChecked(@"Protocols\mailto");
                    RegistryClasses.Register(mailToKey, target, mailToProtocol, iconStore, machineWide: true);
                }
            }

            if (accessPoint)
            {
                serviceKey.SetValue("", defaultProgram.ID);
            }
        }
Пример #26
0
        public void TestBuildStubNeedsTerminal()
        {
            if (!WindowsUtils.IsWindows)
            {
                Assert.Ignore("StubBuilder is only used on Windows");
            }

            var target = new FeedTarget(FeedTest.Test1Uri, FeedTest.CreateTestFeed());

            using (var tempFile = new TemporaryFile("0install-unit-tests"))
                StubBuilder.BuildRunStub(target, tempFile, new SilentTaskHandler(), needsTerminal: true);
        }
Пример #27
0
    private AppEntry CreateAppEntry(IIntegrationManager integrationManager, FeedTarget target)
    {
        BackgroundDownload(target.Uri);

        try
        {
            Log.Info("Creating app entry for " + target.Uri.ToStringRfc());
            return(integrationManager.AddApp(target));
        }
        catch (InvalidOperationException ex) when(ex.GetType() == typeof(InvalidOperationException))
        {
            Log.Warn("Attempting to handle race condition while creating app entry for " + target.Uri.ToStringRfc());
            return(integrationManager.AppList[target.Uri]);
        }
    }
Пример #28
0
    /// <summary>
    /// Registers a COM server in the current system.
    /// </summary>
    /// <param name="target">The application being integrated.</param>
    /// <param name="comServer">The COM server to be registered.</param>
    /// <param name="machineWide">Register the COM server machine-wide instead of just for the current user.</param>
    /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
    /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
    /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception>
    /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
    /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
    public static void Register(FeedTarget target, Model.Capabilities.ComServer comServer, IIconStore iconStore, bool machineWide)
    {
        #region Sanity checks
        if (comServer == null)
        {
            throw new ArgumentNullException(nameof(comServer));
        }
        if (iconStore == null)
        {
            throw new ArgumentNullException(nameof(iconStore));
        }
        #endregion

        // TODO: Implement
    }
Пример #29
0
    /// <summary>
    /// Adds a context menu entry to the current system.
    /// </summary>
    /// <param name="target">The application being integrated.</param>
    /// <param name="contextMenu">The context menu entry to add.</param>
    /// <param name="machineWide">Add the context menu entry machine-wide instead of just for the current user.</param>
    /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
    /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
    /// <exception cref="IOException">A problem occurred while writing to the filesystem.</exception>
    /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
    /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception>
    public static void Apply(FeedTarget target, Model.Capabilities.ContextMenu contextMenu, IIconStore iconStore, bool machineWide)
    {
        #region Sanity checks
        if (contextMenu == null)
        {
            throw new ArgumentNullException(nameof(contextMenu));
        }
        if (iconStore == null)
        {
            throw new ArgumentNullException(nameof(iconStore));
        }
        #endregion

        // TODO: Implement
    }
Пример #30
0
    /// <summary>
    /// Registers an application as a candidate for a default program for some service in the current system.
    /// </summary>
    /// <param name="target">The application being integrated.</param>
    /// <param name="defaultProgram">The default program information to be registered.</param>
    /// <param name="machineWide">Apply the registration machine-wide instead of just for the current user.</param>
    /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param>
    /// <param name="accessPoint">Indicates that the program should be set as the current default for the service it provides.</param>
    /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
    /// <exception cref="IOException">A problem occurred while writing to the filesystem.</exception>
    /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception>
    /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception>
    public static void Register(FeedTarget target, Model.Capabilities.DefaultProgram defaultProgram, IIconStore iconStore, bool machineWide, bool accessPoint = false)
    {
        #region Sanity checks
        if (defaultProgram == null)
        {
            throw new ArgumentNullException(nameof(defaultProgram));
        }
        if (iconStore == null)
        {
            throw new ArgumentNullException(nameof(iconStore));
        }
        #endregion

        // TODO: Implement
    }