@@ -2,33 +2,29 @@ package org.ooni.probe.data
2
2
3
3
import co.touchlab.kermit.Logger
4
4
import org.ooni.probe.APP_ID
5
+ import org.ooni.probe.data.models.DeepLink
5
6
import tk.pratanumandal.unique4j.Unique4j
6
7
import tk.pratanumandal.unique4j.exception.Unique4jException
7
- import java.io.File
8
- import java.nio.file.Files
9
- import java.nio.file.Paths
10
8
import java.util.Date
11
9
12
10
class DeepLinkHandler {
13
11
private var unique: Unique4j ? = null
14
- private val messageListeners = mutableListOf< (String ? ) -> Unit > ()
12
+ private val messageListeners = mutableListOf< (DeepLink . AddDescriptor ? ) -> Unit > ()
15
13
16
- fun addMessageListener ( listener : ( String? ) -> Unit ) {
17
- messageListeners.add(listener)
14
+ companion object {
15
+ private const val OONI_PROTOCOL = " ooni:// "
18
16
}
19
17
20
- fun parseDeepLink (deepLink : String ): Map <String , String > {
21
- val params = mutableMapOf<String , String >()
22
-
23
- if (deepLink.startsWith(" ooni://" )) {
24
- val parts = deepLink.removePrefix(" ooni://" ).split(" /" )
25
- if (parts.size >= 2 && parts[0 ] == " runv2" ) {
26
- params[" command" ] = " runv2"
27
- params[" testId" ] = parts[1 ]
28
- }
29
- }
18
+ fun addMessageListener (listener : (DeepLink .AddDescriptor ? ) -> Unit ) {
19
+ messageListeners.add(listener)
20
+ }
30
21
31
- return params
22
+ fun parseDeepLink (deepLink : String ): DeepLink .AddDescriptor ? {
23
+ return deepLink.takeIf { it.startsWith(OONI_PROTOCOL ) }
24
+ ?.removePrefix(OONI_PROTOCOL )
25
+ ?.split(" /" )
26
+ ?.takeIf { it.size >= 2 && it[0 ] == " runv2" }
27
+ ?.let { DeepLink .AddDescriptor (it[1 ]) }
32
28
}
33
29
34
30
fun initialize (args : Array <String >) {
@@ -37,10 +33,10 @@ class DeepLinkHandler {
37
33
override fun receiveMessage (message : String ) {
38
34
Logger .d(" Received message: $message " )
39
35
40
- // Parse deep link if it starts with ooni://
41
- if (message.startsWith( " ooni:// " )) {
42
- val params = parseDeepLink(message )
43
- handleDeepLinkCommand(message, params)
36
+ parseDeepLink(message)?. let { deepLink ->
37
+ messageListeners.forEach { listener ->
38
+ listener(deepLink )
39
+ }
44
40
}
45
41
}
46
42
@@ -64,17 +60,13 @@ class DeepLinkHandler {
64
60
if (lockFlag) {
65
61
Logger .d(" Application instance started successfully" )
66
62
67
- // If this is the first instance and has a deep link, handle it directly
68
- val deepLink = findDeepLinkInArgs(args)
69
- if (deepLink != null ) {
70
- val params = parseDeepLink (deepLink)
71
- // messageListeners.forEach { it("Initial deep link: $deepLink, params: $params") }
72
- handleDeepLinkCommand(deepLink, params)
63
+ findDeepLinkInArgs(args)?. let { deepLink ->
64
+ parseDeepLink(deepLink)?. let { deepLink ->
65
+ messageListeners.forEach { listener ->
66
+ listener (deepLink)
67
+ }
68
+ }
73
69
}
74
-
75
- // TODO: Implement with conveyor
76
- // Register as a protocol handler if not already registered
77
- registerProtocolHandler()
78
70
} else {
79
71
Logger .d(" Could not acquire lock - this should not happen" )
80
72
}
@@ -94,7 +86,7 @@ class DeepLinkHandler {
94
86
// Windows may process URLs differently, so handle various formats
95
87
for (arg in args) {
96
88
// Standard format
97
- if (arg.startsWith(" ooni:// " )) {
89
+ if (arg.startsWith(OONI_PROTOCOL )) {
98
90
return arg
99
91
}
100
92
@@ -112,167 +104,7 @@ class DeepLinkHandler {
112
104
return null
113
105
}
114
106
115
- private fun handleDeepLinkCommand (
116
- deepLink : String ,
117
- params : Map <String , String >,
118
- ) {
119
- // Handle different commands based on the deep link
120
- when (params[" command" ]) {
121
- " runv2" -> {
122
- val testId = params[" testId" ]
123
- Logger .d(" Running test with ID: $testId " )
124
- // Here you would implement the actual test running logic
125
- messageListeners.forEach { it(testId) }
126
- }
127
- }
128
- }
129
-
130
107
fun shutdown () {
131
108
unique?.freeLock()
132
109
}
133
-
134
- // TODO: Move this to conveyor
135
-
136
- // Register the application as a handler for ooni:// protocol
137
- private fun registerProtocolHandler () {
138
- val isWindows = System .getProperty(" os.name" ).lowercase().contains(" win" )
139
-
140
- if (isWindows) {
141
- registerWindowsProtocolHandler()
142
- } else {
143
- registerLinuxProtocolHandler()
144
- }
145
- }
146
-
147
- // Register protocol handler for Linux using XDG
148
- private fun registerLinuxProtocolHandler () {
149
- try {
150
- // Get the current executable path
151
- val executablePath = getExecutablePath()
152
- if (executablePath != null ) {
153
- // Create desktop entry for protocol handler
154
- val desktopEntryPath = Paths .get(
155
- System .getProperty(" user.home" ),
156
- " .local" ,
157
- " share" ,
158
- " applications" ,
159
- " ooni-handler.desktop" ,
160
- )
161
-
162
- val desktopEntry = """
163
- [Desktop Entry]
164
- Type=Application
165
- Name=OONI Deep Link Handler
166
- Exec=$executablePath %u
167
- Terminal=false
168
- MimeType=x-scheme-handler/ooni;
169
- NoDisplay=true
170
- Comment=Handler for OONI deep links
171
- """ .trimIndent()
172
-
173
- // Create parent directories if they don't exist
174
- Files .createDirectories(desktopEntryPath.parent)
175
-
176
- // Write the desktop entry file
177
- Files .write(desktopEntryPath, desktopEntry.toByteArray())
178
-
179
- // Register the desktop entry as the handler for the ooni:// protocol
180
- try {
181
- val processBuilder = ProcessBuilder (
182
- " xdg-mime" ,
183
- " default" ,
184
- " ooni-handler.desktop" ,
185
- " x-scheme-handler/ooni" ,
186
- )
187
- val process = processBuilder.start()
188
- process.waitFor()
189
- Logger .d(" Registered as handler for ooni:// protocol on Linux" )
190
- } catch (e: Exception ) {
191
- Logger .d(" Failed to register protocol handler on Linux: ${e.message} " )
192
- }
193
- }
194
- } catch (e: Exception ) {
195
- Logger .e(e) { " Failed to register protocol handler on Linux" }
196
- }
197
- }
198
-
199
- // Register protocol handler for Windows using Registry
200
- private fun registerWindowsProtocolHandler () {
201
- try {
202
- val executablePath = getExecutablePath()
203
- if (executablePath != null ) {
204
- // Escape backslashes in the path for command prompt
205
- val escapedPath = executablePath.replace(" \\ " , " \\\\ " )
206
-
207
- // Create the registry entry for the ooni:// protocol
208
- val command = """
209
- reg add "HKCU\Software\Classes\ooni" /ve /d "URL:OONI Protocol" /f
210
- reg add "HKCU\Software\Classes\ooni" /v "URL Protocol" /d "" /f
211
- reg add "HKCU\Software\Classes\ooni\DefaultIcon" /ve /d "$escapedPath ,1" /f
212
- reg add "HKCU\Software\Classes\ooni\shell\open\command" /ve /d "\"$escapedPath \" \"%1\"" /f
213
- """ .trimIndent()
214
-
215
- // Execute the registry command
216
- try {
217
- val process = Runtime .getRuntime().exec(arrayOf(" cmd.exe" , " /c" , command))
218
- val exitCode = process.waitFor()
219
-
220
- if (exitCode == 0 ) {
221
- Logger .d(" Successfully registered ooni:// protocol handler on Windows" )
222
- } else {
223
- Logger .d(" Failed to register protocol handler on Windows, exit code: $exitCode " )
224
- }
225
- } catch (e: Exception ) {
226
- Logger .e(e) { " Failed to execute registry command" }
227
- }
228
- }
229
- } catch (e: Exception ) {
230
- Logger .e(e) { " Failed to register protocol handler on Windows" }
231
- }
232
- }
233
-
234
- // Get the path to the current executable based on the operating system
235
- private fun getExecutablePath (): String? {
236
- val isWindows = System .getProperty(" os.name" ).lowercase().contains(" win" )
237
-
238
- // Try to get the path from the conveyor system property if using Conveyor
239
- val conveyorPath = System .getProperty(" app.home" )
240
- if (conveyorPath != null ) {
241
- val conveyorExecutable = if (isWindows) {
242
- File (conveyorPath, " bin/app.exe" )
243
- } else {
244
- File (conveyorPath, " bin/app" )
245
- }
246
-
247
- if (conveyorExecutable.exists()) {
248
- return conveyorExecutable.absolutePath
249
- }
250
- }
251
-
252
- if (isWindows) {
253
- // For Windows, try to use jpackage executable path
254
- val processPath = System .getProperty(" jpackage.app-path" )
255
- if (processPath != null ) {
256
- return processPath
257
- }
258
-
259
- // Alternatively, use the Java executable with classpath
260
- val javaHome = System .getProperty(" java.home" )
261
- val javaBin = File (javaHome, " bin/java.exe" ).absolutePath
262
- val classpath = System .getProperty(" java.class.path" )
263
- return " \" $javaBin \" -cp \" $classpath \" MainKt"
264
- } else {
265
- // For Linux, try to get the symbolic link of the current process
266
- try {
267
- val currentPid = ProcessHandle .current().pid()
268
- val processPath = Files .readSymbolicLink(Paths .get(" /proc/$currentPid /exe" ))
269
- return processPath.toString()
270
- } catch (e: Exception ) {
271
- Logger .e(e) { " Could not determine executable path" }
272
- }
273
-
274
- // If all else fails, just use java with the classpath
275
- return " java -cp ${System .getProperty(" java.class.path" )} MainKt"
276
- }
277
- }
278
110
}
0 commit comments