• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

KSP-CKAN / CKAN / 15432034930

04 Jun 2025 02:19AM UTC coverage: 30.322% (+0.04%) from 30.28%
15432034930

Pull #4386

github

web-flow
Merge f8b59bcd0 into 4cf303cc8
Pull Request #4386: Mod list multi-select

4063 of 14340 branches covered (28.33%)

Branch coverage included in aggregate %.

55 of 170 new or added lines in 12 files covered. (32.35%)

59 existing lines in 5 files now uncovered.

13712 of 44281 relevant lines covered (30.97%)

0.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/GUI/URLHandlers.cs
1
using System;
2
using System.Diagnostics;
3
using System.IO;
4
using System.Reflection;
5
using Microsoft.Win32;
6
#if NET5_0_OR_GREATER
7
using System.Runtime.Versioning;
8
#endif
9

10
using IniParser;
11
using IniParser.Exceptions;
12
using IniParser.Model;
13
using log4net;
14

15
namespace CKAN.GUI
16
{
17
    #if NET5_0_OR_GREATER
18
    [SupportedOSPlatform("windows")]
19
    #endif
20
    public static class URLHandlers
21
    {
22
        private static readonly ILog log = LogManager.GetLogger(typeof(URLHandlers));
×
23
        public  const  string UrlRegistrationArgument = "registerUrl";
24

25
        private static readonly string MimeAppsListPath = "mimeapps.list";
×
26
        private static readonly string ApplicationsPath = ".local/share/applications/";
×
27
        private const           string HandlerFileName  = "ckan-handler.desktop";
28

29
        static URLHandlers()
30
        {
×
31
            if (Platform.IsUnix)
×
32
            {
×
33
                var XDGDataHome = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
×
34
                if (XDGDataHome != null)
×
35
                {
×
36
                    ApplicationsPath = Path.Combine(XDGDataHome, "applications");
×
37
                }
×
38
                else
39
                {
×
40
                    string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
×
41
                    ApplicationsPath = Path.Combine(home, ApplicationsPath);
×
42
                }
×
43
                Directory.CreateDirectory(ApplicationsPath);
×
44
                MimeAppsListPath = Path.Combine(ApplicationsPath, MimeAppsListPath);
×
45
            }
×
46
        }
×
47

48
        public static void RegisterURLHandler(GUIConfiguration? config, GameInstance? instance, IUser? user)
49
        {
×
50
            try
51
            {
×
52
                if (Platform.IsUnix)
×
53
                {
×
54
                    RegisterURLHandler_Linux();
×
55
                }
×
56
                else if (Platform.IsWindows)
×
57
                {
×
58
                    try
59
                    {
×
60
                        RegisterURLHandler_Win32();
×
61
                    }
×
62
                    catch (UnauthorizedAccessException)
×
63
                    {
×
NEW
64
                        if (config == null || config.URLHandlerNoNag || instance == null)
×
65
                        {
×
66
                            return;
×
67
                        }
68

69
                        if (Assembly.GetEntryAssembly() is Assembly ass
×
70
                            && user != null
71
                            && user.RaiseYesNoDialog(Properties.Resources.URLHandlersPrompt))
72
                        {
×
73
                            // we need elevation to write to the registry
74
                            Process.Start(new ProcessStartInfo(ass.Location)
×
75
                            {
76
                                // trigger a UAC prompt (if UAC is enabled)
77
                                Verb      = "runas",
78
                                // .NET ignores Verb without this
79
                                UseShellExecute = true,
80
                                Arguments = $"gui --asroot {UrlRegistrationArgument}"
81
                            });
82
                        }
×
83
                        config.URLHandlerNoNag = true;
×
NEW
84
                        config.Save(instance);
×
85
                        // Don't re-throw the exception because we just dealt with it
86
                    }
×
87
                }
×
88
                else if (Platform.IsMac)
×
89
                {
×
90
                    //TODO
91
                }
×
92
            }
×
93
            catch (Exception ex)
×
94
            {
×
95
                log.ErrorFormat(
×
96
                    "There was an error while registering the URL handler for ckan:// - {0}",
97
                    ex.Message
98
                );
99
                log.ErrorFormat("{0}", ex.StackTrace);
×
100
            }
×
101
        }
×
102

103
        #if NET5_0_OR_GREATER
104
        [SupportedOSPlatform("windows")]
105
        #endif
106
        private static void RegisterURLHandler_Win32()
107
        {
×
108
            log.InfoFormat("Adding URL handler to registry");
×
109
            string      urlCmd = $"{Assembly.GetExecutingAssembly().Location} gui %1";
×
110
            RegistryKey root   = Microsoft.Win32.Registry.ClassesRoot;
×
111
            var ckanKey = root.OpenSubKey("ckan");
×
112
            if (ckanKey != null)
×
113
            {
×
114
                try
115
                {
×
116
                    var path = ckanKey?.OpenSubKey("shell")
×
117
                                      ?.OpenSubKey("open")
118
                                      ?.OpenSubKey("command")
119
                                      ?.GetValue("")
120
                                      ?.ToString();
121

122
                    if (path == urlCmd)
×
123
                    {
×
124
                        log.InfoFormat("URL handler already exists with the same path");
×
125
                        return;
×
126
                    }
127
                    // Valid key not found, delete it
128
                    root.DeleteSubKeyTree("ckan");
×
129
                }
×
130
                catch (Exception) { }
×
131
            }
×
132
            ckanKey = root.CreateSubKey("ckan");
×
133
            ckanKey.SetValue("", "URL: ckan Protocol");
×
134
            ckanKey.SetValue("URL Protocol", "");
×
135
            ckanKey.CreateSubKey("shell")
×
136
                .CreateSubKey("open")
137
                .CreateSubKey("command")
138
                .SetValue("", urlCmd);
139
        }
×
140

141
        #if NET5_0_OR_GREATER
142
        [SupportedOSPlatform("linux")]
143
        #endif
144
        private static void RegisterURLHandler_Linux()
145
        {
×
146
            var parser = new FileIniDataParser();
×
147

148
            // Yes, 'Assigment' is the spelling used by the library.
149
            parser.Parser.Configuration.AssigmentSpacer = "";
×
150
            IniData data;
151

152
            log.InfoFormat("Trying to register URL handler");
×
153

154
            if (!File.Exists(MimeAppsListPath))
×
155
            {
×
156
                log.InfoFormat("{0} does not exist, trying to create it", MimeAppsListPath);
×
157
                File.WriteAllLines(MimeAppsListPath, new string[] { "[Default Applications]" });
×
158
            }
×
159

160
            try
161
            {
×
162
                data = parser.ReadFile(MimeAppsListPath);
×
163
            }
×
164
            catch (DirectoryNotFoundException ex)
×
165
            {
×
166
                log.InfoFormat("Skipping URL handler: {0}", ex.Message);
×
167
                return;
×
168
            }
169
            catch (FileNotFoundException ex)
×
170
            {
×
171
                log.InfoFormat("Skipping URL handler: {0}", ex.Message);
×
172
                return;
×
173
            }
174
            catch (ParsingException ex)
×
175
            {
×
176
                log.InfoFormat("Skipping URL handler: {0}", ex.Message);
×
177
                return;
×
178
            }
179

180
            if (data["Added Associations"] == null)
×
181
            {
×
182
                data.Sections.AddSection("Added Associations");
×
183
            }
×
184
            data["Added Associations"].RemoveKey("x-scheme-handler/ckan");
×
185
            data["Added Associations"].AddKey("x-scheme-handler/ckan", HandlerFileName);
×
186

187
            parser.WriteFile(MimeAppsListPath, data);
×
188

189
            var handlerPath = Path.Combine(ApplicationsPath, HandlerFileName);
×
190

191
            if (File.Exists(handlerPath))
×
192
            {
×
193
                File.Delete(handlerPath);
×
194
            }
×
195

196
            File.WriteAllText(handlerPath, "");
×
197
            data = parser.ReadFile(handlerPath);
×
198
            data.Sections.AddSection("Desktop Entry");
×
199
            data["Desktop Entry"].AddKey("Version", "1.0");
×
200
            data["Desktop Entry"].AddKey("Type", "Application");
×
201
            data["Desktop Entry"].AddKey("Exec", "mono \"" + Assembly.GetExecutingAssembly().Location + "\" gui %u");
×
202
            data["Desktop Entry"].AddKey("Icon", "ckan");
×
203
            data["Desktop Entry"].AddKey("StartupNotify", "true");
×
204
            data["Desktop Entry"].AddKey("NoDisplay", "true");
×
205
            data["Desktop Entry"].AddKey("Terminal", "false");
×
206
            data["Desktop Entry"].AddKey("Categories", "Utility");
×
207
            data["Desktop Entry"].AddKey("MimeType", "x-scheme-handler/ckan");
×
208
            data["Desktop Entry"].AddKey("Name", "CKAN Launcher");
×
209
            data["Desktop Entry"].AddKey("Comment", "Launch CKAN");
×
210

211
            parser.WriteFile(handlerPath, data);
×
212
            AutoUpdate.SetExecutable(handlerPath);
×
213
        }
×
214
    }
215
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc