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

KSP-CKAN / CKAN / 15655026327

14 Jun 2025 06:40PM UTC coverage: 27.152% (-3.2%) from 30.329%
15655026327

Pull #4392

github

web-flow
Merge bb1dadfef into 1b4a54286
Pull Request #4392: Writethrough when saving files, add Netkan tests

3703 of 12085 branches covered (30.64%)

Branch coverage included in aggregate %.

19 of 32 new or added lines in 18 files covered. (59.38%)

9 existing lines in 7 files now uncovered.

8041 of 31168 relevant lines covered (25.8%)

0.53 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 CKAN.Extensions;
11

12
using IniParser;
13
using IniParser.Exceptions;
14
using IniParser.Model;
15
using log4net;
16

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

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

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

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

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

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

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

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

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

154
            log.InfoFormat("Trying to register URL handler");
×
155

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

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

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

189
            parser.WriteFile(MimeAppsListPath, data);
×
190

191
            var handlerPath = Path.Combine(ApplicationsPath, HandlerFileName);
×
192

193
            if (File.Exists(handlerPath))
×
194
            {
×
195
                File.Delete(handlerPath);
×
196
            }
×
197

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

213
            parser.WriteFile(handlerPath, data);
×
214
            AutoUpdate.SetExecutable(handlerPath);
×
215
        }
×
216
    }
217
}
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