1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright (C) 2014 Руслан Ижбулатов
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Authors: Alexander Larsson <alexl@redhat.com>
20 * Руслан Ижбулатов <lrn1986@gmail.com>
21 */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include "gcontenttype.h"
28 #include "gwin32appinfo.h"
29 #include "gappinfo.h"
30 #include "gioerror.h"
31 #include "gfile.h"
32 #include <glib/gstdio.h>
33 #include "glibintl.h"
34 #include <gio/gwin32registrykey.h>
35
36 #include <windows.h>
37
38 /* We need to watch 8 places:
39 * 0) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations
40 * (anything below that key)
41 * On change: re-enumerate subkeys, read their values.
42 * 1) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts
43 * (anything below that key)
44 * On change: re-enumerate subkeys
45 * 2) HKEY_CURRENT_USER\\Software\\Clients (anything below that key)
46 * On change: re-read the whole hierarchy of handlers
47 * 3) HKEY_LOCAL_MACHINE\\Software\\Clients (anything below that key)
48 * On change: re-read the whole hierarchy of handlers
49 * 4) HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications (values of that key)
50 * On change: re-read the value list of registered applications
51 * 5) HKEY_CURRENT_USER\\Software\\RegisteredApplications (values of that key)
52 * On change: re-read the value list of registered applications
53 * 6) HKEY_CLASSES_ROOT\\Applications (anything below that key)
54 * On change: re-read the whole hierarchy of apps
55 * 7) HKEY_CLASSES_ROOT (only its subkeys)
56 * On change: re-enumerate subkeys, try to filter out wrong names.
57 *
58 */
59
60 typedef struct _GWin32AppInfoURLSchema GWin32AppInfoURLSchema;
61 typedef struct _GWin32AppInfoFileExtension GWin32AppInfoFileExtension;
62 typedef struct _GWin32AppInfoHandler GWin32AppInfoHandler;
63 typedef struct _GWin32AppInfoApplication GWin32AppInfoApplication;
64
65 typedef struct _GWin32AppInfoURLSchemaClass GWin32AppInfoURLSchemaClass;
66 typedef struct _GWin32AppInfoFileExtensionClass GWin32AppInfoFileExtensionClass;
67 typedef struct _GWin32AppInfoHandlerClass GWin32AppInfoHandlerClass;
68 typedef struct _GWin32AppInfoApplicationClass GWin32AppInfoApplicationClass;
69
70 struct _GWin32AppInfoURLSchemaClass
71 {
72 GObjectClass parent_class;
73 };
74
75 struct _GWin32AppInfoFileExtensionClass
76 {
77 GObjectClass parent_class;
78 };
79
80 struct _GWin32AppInfoHandlerClass
81 {
82 GObjectClass parent_class;
83 };
84
85 struct _GWin32AppInfoApplicationClass
86 {
87 GObjectClass parent_class;
88 };
89
90 struct _GWin32AppInfoURLSchema {
91 GObject parent_instance;
92
93 /* url schema (stuff before ':') */
94 gunichar2 *schema;
95
96 /* url schema (stuff before ':'), in UTF-8 */
97 gchar *schema_u8;
98
99 /* url schema (stuff before ':'), in UTF-8, folded */
100 gchar *schema_folded;
101
102 /* Handler currently selected for this schema */
103 GWin32AppInfoHandler *chosen_handler;
104
105 /* Maps folded handler IDs -> to other handlers for this schema */
106 GHashTable *handlers;
107 };
108
109 struct _GWin32AppInfoHandler {
110 GObject parent_instance;
111
112 /* Class name in HKCR */
113 gunichar2 *handler_id;
114
115 /* Handler registry key (HKCR\\handler_id). Can be used to watch this handler. */
116 GWin32RegistryKey *key;
117
118 /* Class name in HKCR, UTF-8, folded */
119 gchar *handler_id_folded;
120
121 /* shell/open/command default value for the class named by class_id */
122 gunichar2 *handler_command;
123
124 /* If handler_id class has no command, it might point to another class */
125 gunichar2 *proxy_id;
126
127 /* Proxy registry key (HKCR\\proxy_id). Can be used to watch handler's proxy. */
128 GWin32RegistryKey *proxy_key;
129
130 /* shell/open/command default value for the class named by proxy_id */
131 gunichar2 *proxy_command;
132
133 /* Executable of the program (for matching, in folded form; UTF-8) */
134 gchar *executable_folded;
135
136 /* Executable of the program (UTF-8) */
137 gchar *executable;
138
139 /* Pointer to a location within @executable */
140 gchar *executable_basename;
141
142 /* Icon of the application for this handler */
143 GIcon *icon;
144
145 /* The application that is linked to this handler. */
146 GWin32AppInfoApplication *app;
147 };
148
149 struct _GWin32AppInfoFileExtension {
150 GObject parent_instance;
151
152 /* File extension (with leading '.') */
153 gunichar2 *extension;
154
155 /* File extension (with leading '.'), in UTF-8 */
156 gchar *extension_u8;
157
158 /* handler currently selected for this extension */
159 GWin32AppInfoHandler *chosen_handler;
160
161 /* Maps folded handler IDs -> to other handlers for this extension */
162 GHashTable *handlers;
163
164 /* Maps folded app exename -> to apps that support this extension.
165 * ONLY for apps that are not reachable via handlers (i.e. apps from
166 * the HKCR/Applications, which have no handlers). */
167 GHashTable *other_apps;
168 };
169
170 struct _GWin32AppInfoApplication {
171 GObject parent_instance;
172
173 /* Canonical name (used for key names). Can be NULL. */
174 gunichar2 *canonical_name;
175
176 /* Canonical name (used for key names), in UTF-8. Can be NULL. */
177 gchar *canonical_name_u8;
178
179 /* Canonical name (used for key names), in UTF-8, folded. Can be NULL. */
180 gchar *canonical_name_folded;
181
182 /* Human-readable name in English. Can be NULL */
183 gunichar2 *pretty_name;
184
185 /* Human-readable name in English, UTF-8. Can be NULL */
186 gchar *pretty_name_u8;
187
188 /* Human-readable name in user's language. Can be NULL */
189 gunichar2 *localized_pretty_name;
190
191 /* Human-readable name in user's language, UTF-8. Can be NULL */
192 gchar *localized_pretty_name_u8;
193
194 /* Description, could be in user's language. Can be NULL */
195 gunichar2 *description;
196
197 /* Description, could be in user's language, UTF-8. Can be NULL */
198 gchar *description_u8;
199
200 /* shell/open/command for the application. Can be NULL (see executable). */
201 gunichar2 *command;
202
203 /* shell/open/command for the application. Can be NULL (see executable). */
204 gchar *command_u8;
205
206 /* Executable of the program (for matching, in folded form;
207 * UTF-8). Never NULL. */
208 gchar *executable_folded;
209
210 /* Executable of the program (UTF-8). Never NULL. */
211 gchar *executable;
212
213 /* Pointer to a location within @executable */
214 gchar *executable_basename;
215
216 /* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema,
217 * UTF-8, folded) -> a GWin32AppInfoHandler
218 * Schema can be used as a key in the urls hashmap.
219 */
220 GHashTable *supported_urls;
221
222 /* Explicitly supported extensions, hashmap from map-owned gchar ptr
223 * (.extension, UTF-8, folded) -> a GWin32AppInfoHandler
224 * Extension can be used as a key in the extensions hashmap.
225 */
226 GHashTable *supported_exts;
227
228 /* Icon of the application (remember, handler can have its own icon too) */
229 GIcon *icon;
230
231 /* Set to TRUE to prevent this app from appearing in lists of apps for
232 * opening files. This will not prevent it from appearing in lists of apps
233 * just for running, or lists of apps for opening exts/urls for which this
234 * app reports explicit support.
235 */
236 gboolean no_open_with;
237
238 /* Set to TRUE for applications from HKEY_CURRENT_USER.
239 * Give them priority over applications from HKEY_LOCAL_MACHINE, when all
240 * other things are equal.
241 */
242 gboolean user_specific;
243
244 /* Set to TRUE for applications that are machine-wide defaults (i.e. default
245 * browser) */
246 gboolean default_app;
247 };
248
249 #define G_TYPE_WIN32_APPINFO_URL_SCHEMA (g_win32_appinfo_url_schema_get_type ())
250 #define G_WIN32_APPINFO_URL_SCHEMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_URL_SCHEMA, GWin32AppInfoURLSchema))
251
252 #define G_TYPE_WIN32_APPINFO_FILE_EXTENSION (g_win32_appinfo_file_extension_get_type ())
253 #define G_WIN32_APPINFO_FILE_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_FILE_EXTENSION, GWin32AppInfoFileExtension))
254
255 #define G_TYPE_WIN32_APPINFO_HANDLER (g_win32_appinfo_handler_get_type ())
256 #define G_WIN32_APPINFO_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_HANDLER, GWin32AppInfoHandler))
257
258 #define G_TYPE_WIN32_APPINFO_APPLICATION (g_win32_appinfo_application_get_type ())
259 #define G_WIN32_APPINFO_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_APPLICATION, GWin32AppInfoApplication))
260
261 GType g_win32_appinfo_url_schema_get_type (void) G_GNUC_CONST;
262 GType g_win32_appinfo_file_extension_get_type (void) G_GNUC_CONST;
263 GType g_win32_appinfo_handler_get_type (void) G_GNUC_CONST;
264 GType g_win32_appinfo_application_get_type (void) G_GNUC_CONST;
265
G_DEFINE_TYPE(GWin32AppInfoURLSchema,g_win32_appinfo_url_schema,G_TYPE_OBJECT)266 G_DEFINE_TYPE (GWin32AppInfoURLSchema, g_win32_appinfo_url_schema, G_TYPE_OBJECT)
267 G_DEFINE_TYPE (GWin32AppInfoFileExtension, g_win32_appinfo_file_extension, G_TYPE_OBJECT)
268 G_DEFINE_TYPE (GWin32AppInfoHandler, g_win32_appinfo_handler, G_TYPE_OBJECT)
269 G_DEFINE_TYPE (GWin32AppInfoApplication, g_win32_appinfo_application, G_TYPE_OBJECT)
270
271 static void
272 g_win32_appinfo_url_schema_dispose (GObject *object)
273 {
274 GWin32AppInfoURLSchema *url = G_WIN32_APPINFO_URL_SCHEMA (object);
275
276 g_clear_pointer (&url->schema, g_free);
277 g_clear_pointer (&url->schema_u8, g_free);
278 g_clear_pointer (&url->schema_folded, g_free);
279 g_clear_object (&url->chosen_handler);
280 g_clear_pointer (&url->handlers, g_hash_table_destroy);
281 G_OBJECT_CLASS (g_win32_appinfo_url_schema_parent_class)->dispose (object);
282 }
283
284
285 static void
g_win32_appinfo_handler_dispose(GObject * object)286 g_win32_appinfo_handler_dispose (GObject *object)
287 {
288 GWin32AppInfoHandler *handler = G_WIN32_APPINFO_HANDLER (object);
289
290 g_clear_pointer (&handler->handler_id, g_free);
291 g_clear_pointer (&handler->handler_id_folded, g_free);
292 g_clear_pointer (&handler->handler_command, g_free);
293 g_clear_pointer (&handler->proxy_id, g_free);
294 g_clear_pointer (&handler->proxy_command, g_free);
295 g_clear_pointer (&handler->executable_folded, g_free);
296 g_clear_pointer (&handler->executable, g_free);
297 g_clear_object (&handler->key);
298 g_clear_object (&handler->proxy_key);
299 g_clear_object (&handler->icon);
300 g_clear_object (&handler->app);
301 G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object);
302 }
303
304 static void
g_win32_appinfo_file_extension_dispose(GObject * object)305 g_win32_appinfo_file_extension_dispose (GObject *object)
306 {
307 GWin32AppInfoFileExtension *ext = G_WIN32_APPINFO_FILE_EXTENSION (object);
308
309 g_clear_pointer (&ext->extension, g_free);
310 g_clear_pointer (&ext->extension_u8, g_free);
311 g_clear_object (&ext->chosen_handler);
312 g_clear_pointer (&ext->handlers, g_hash_table_destroy);
313 g_clear_pointer (&ext->other_apps, g_hash_table_destroy);
314 G_OBJECT_CLASS (g_win32_appinfo_file_extension_parent_class)->dispose (object);
315 }
316
317 static void
g_win32_appinfo_application_dispose(GObject * object)318 g_win32_appinfo_application_dispose (GObject *object)
319 {
320 GWin32AppInfoApplication *app = G_WIN32_APPINFO_APPLICATION (object);
321
322 g_clear_pointer (&app->canonical_name_u8, g_free);
323 g_clear_pointer (&app->canonical_name_folded, g_free);
324 g_clear_pointer (&app->canonical_name, g_free);
325 g_clear_pointer (&app->pretty_name, g_free);
326 g_clear_pointer (&app->localized_pretty_name, g_free);
327 g_clear_pointer (&app->description, g_free);
328 g_clear_pointer (&app->command, g_free);
329 g_clear_pointer (&app->pretty_name_u8, g_free);
330 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
331 g_clear_pointer (&app->description_u8, g_free);
332 g_clear_pointer (&app->command_u8, g_free);
333 g_clear_pointer (&app->executable_folded, g_free);
334 g_clear_pointer (&app->executable, g_free);
335 g_clear_pointer (&app->supported_urls, g_hash_table_destroy);
336 g_clear_pointer (&app->supported_exts, g_hash_table_destroy);
337 g_clear_object (&app->icon);
338 G_OBJECT_CLASS (g_win32_appinfo_application_parent_class)->dispose (object);
339 }
340
341 static void
g_win32_appinfo_url_schema_class_init(GWin32AppInfoURLSchemaClass * klass)342 g_win32_appinfo_url_schema_class_init (GWin32AppInfoURLSchemaClass *klass)
343 {
344 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
345
346 gobject_class->dispose = g_win32_appinfo_url_schema_dispose;
347 }
348
349 static void
g_win32_appinfo_file_extension_class_init(GWin32AppInfoFileExtensionClass * klass)350 g_win32_appinfo_file_extension_class_init (GWin32AppInfoFileExtensionClass *klass)
351 {
352 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
353
354 gobject_class->dispose = g_win32_appinfo_file_extension_dispose;
355 }
356
357 static void
g_win32_appinfo_handler_class_init(GWin32AppInfoHandlerClass * klass)358 g_win32_appinfo_handler_class_init (GWin32AppInfoHandlerClass *klass)
359 {
360 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
361
362 gobject_class->dispose = g_win32_appinfo_handler_dispose;
363 }
364
365 static void
g_win32_appinfo_application_class_init(GWin32AppInfoApplicationClass * klass)366 g_win32_appinfo_application_class_init (GWin32AppInfoApplicationClass *klass)
367 {
368 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
369
370 gobject_class->dispose = g_win32_appinfo_application_dispose;
371 }
372
373 static void
g_win32_appinfo_url_schema_init(GWin32AppInfoURLSchema * self)374 g_win32_appinfo_url_schema_init (GWin32AppInfoURLSchema *self)
375 {
376 self->handlers = g_hash_table_new_full (g_str_hash,
377 g_str_equal,
378 g_free,
379 g_object_unref);
380 }
381
382 static void
g_win32_appinfo_file_extension_init(GWin32AppInfoFileExtension * self)383 g_win32_appinfo_file_extension_init (GWin32AppInfoFileExtension *self)
384 {
385 self->handlers = g_hash_table_new_full (g_str_hash,
386 g_str_equal,
387 g_free,
388 g_object_unref);
389 self->other_apps = g_hash_table_new_full (g_str_hash,
390 g_str_equal,
391 g_free,
392 g_object_unref);
393 }
394
395 static void
g_win32_appinfo_handler_init(GWin32AppInfoHandler * self)396 g_win32_appinfo_handler_init (GWin32AppInfoHandler *self)
397 {
398 }
399
400 static void
g_win32_appinfo_application_init(GWin32AppInfoApplication * self)401 g_win32_appinfo_application_init (GWin32AppInfoApplication *self)
402 {
403 self->supported_urls = g_hash_table_new_full (g_str_hash,
404 g_str_equal,
405 g_free,
406 g_object_unref);
407 self->supported_exts = g_hash_table_new_full (g_str_hash,
408 g_str_equal,
409 g_free,
410 g_object_unref);
411 }
412
413 G_LOCK_DEFINE_STATIC (gio_win32_appinfo);
414
415 /* Map of owned ".ext" (with '.', UTF-8, folded)
416 * to GWin32AppInfoFileExtension ptr
417 */
418 static GHashTable *extensions = NULL;
419
420 /* Map of owned "schema" (without ':', UTF-8, folded)
421 * to GWin32AppInfoURLSchema ptr
422 */
423 static GHashTable *urls = NULL;
424
425 /* Map of owned "appID" (UTF-8, folded) to
426 * GWin32AppInfoApplication ptr
427 */
428 static GHashTable *apps_by_id = NULL;
429
430 /* Map of owned "app.exe" (UTF-8, folded) to
431 * GWin32AppInfoApplication ptr.
432 * This map and its values are separate from apps_by_id. The fact that an app
433 * with known ID has the same executable [base]name as an app in this map does
434 * not mean that they are the same application.
435 */
436 static GHashTable *apps_by_exe = NULL;
437
438 /* Map of owned "handler id" (UTF-8, folded)
439 * to GWin32AppInfoHandler ptr
440 */
441 static GHashTable *handlers = NULL;
442
443 /* Watch this whole subtree */
444 static GWin32RegistryKey *url_associations_key;
445
446 /* Watch this whole subtree */
447 static GWin32RegistryKey *file_exts_key;
448
449 /* Watch this whole subtree */
450 static GWin32RegistryKey *user_clients_key;
451
452 /* Watch this whole subtree */
453 static GWin32RegistryKey *system_clients_key;
454
455 /* Watch this key */
456 static GWin32RegistryKey *user_registered_apps_key;
457
458 /* Watch this key */
459 static GWin32RegistryKey *system_registered_apps_key;
460
461 /* Watch this whole subtree */
462 static GWin32RegistryKey *applications_key;
463
464 /* Watch this key */
465 static GWin32RegistryKey *classes_root_key;
466
467 static gunichar2 *
g_wcsdup(const gunichar2 * str,gssize str_size)468 g_wcsdup (const gunichar2 *str, gssize str_size)
469 {
470 if (str_size == -1)
471 {
472 str_size = wcslen (str) + 1;
473 str_size *= sizeof (gunichar2);
474 }
475 return g_memdup (str, str_size);
476 }
477
478 #define URL_ASSOCIATIONS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
479 #define USER_CHOICE L"\\UserChoice"
480 #define OPEN_WITH_PROGIDS L"\\OpenWithProgids"
481 #define FILE_EXTS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"
482 #define HKCR L"HKEY_CLASSES_ROOT\\"
483 #define HKCU L"HKEY_CURRENT_USER\\"
484 #define HKLM L"HKEY_LOCAL_MACHINE\\"
485 #define SHELL_OPEN_COMMAND L"\\shell\\open\\command"
486 #define REG_PATH_MAX 256
487 #define REG_PATH_MAX_SIZE (REG_PATH_MAX * sizeof (gunichar2))
488
489 static gunichar2 *
read_resource_string(gunichar2 * res)490 read_resource_string (gunichar2 *res)
491 {
492 gunichar2 *id_str;
493 gunichar2 *id_str_end;
494 gunichar2 *filename_str;
495 unsigned long id;
496 HMODULE resource_module;
497 gunichar2 *buffer;
498 int string_length;
499 int buffer_length;
500
501 if (res == NULL || res[0] != L'@')
502 return res;
503
504 id_str = wcsrchr (res, L'-');
505
506 if (id_str == NULL || id_str[-1] != L',')
507 return res;
508
509 id_str += 1;
510
511 id = wcstoul (id_str, &id_str_end, 10);
512
513 if (id_str_end == id_str || id_str_end[0] != L'\0' || id == ULONG_MAX)
514 return res;
515
516 filename_str = &res[1];
517 id_str[-2] = L'\0';
518
519 resource_module = LoadLibraryExW (filename_str,
520 0,
521 LOAD_LIBRARY_AS_DATAFILE |
522 LOAD_LIBRARY_AS_IMAGE_RESOURCE);
523
524 g_free (res);
525
526 if (resource_module == NULL)
527 return NULL;
528
529 buffer_length = 256;
530 string_length = buffer_length - 1;
531
532 while (TRUE)
533 {
534 buffer = g_malloc (buffer_length * sizeof (gunichar2));
535 string_length = LoadStringW (resource_module, id, buffer, buffer_length);
536
537 if (string_length != 0 && string_length == buffer_length - 1)
538 {
539 g_free (buffer);
540 buffer_length *= 2;
541 }
542 else
543 {
544 if (string_length == 0)
545 g_clear_pointer (&buffer, g_free);
546
547 break;
548 }
549 }
550
551 FreeLibrary (resource_module);
552
553 if (buffer)
554 {
555 gunichar2 *result = g_wcsdup (buffer, -1);
556 g_free (buffer);
557 return result;
558 }
559
560 return NULL;
561 }
562
563 static void
read_handler_icon(GWin32RegistryKey * proxy_key,GWin32RegistryKey * program_key,GIcon ** icon_out)564 read_handler_icon (GWin32RegistryKey *proxy_key,
565 GWin32RegistryKey *program_key,
566 GIcon **icon_out)
567 {
568 gint counter;
569 GWin32RegistryKey *key;
570
571 *icon_out = NULL;
572
573 for (counter = 0; counter < 2; counter++)
574 {
575 GWin32RegistryKey *icon_key;
576
577 if (counter == 0)
578 key = proxy_key;
579 else
580 key = program_key;
581
582 if (!key)
583 continue;
584
585 icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL);
586
587 if (icon_key != NULL)
588 {
589 GWin32RegistryValueType default_type;
590 gchar *default_value;
591
592 if (g_win32_registry_key_get_value (icon_key,
593 TRUE,
594 "",
595 &default_type,
596 (gpointer *) &default_value,
597 NULL,
598 NULL))
599 {
600 if (default_type == G_WIN32_REGISTRY_VALUE_STR ||
601 default_value[0] != '\0')
602 *icon_out = g_themed_icon_new (default_value);
603
604 g_clear_pointer (&default_value, g_free);
605 }
606
607 g_object_unref (icon_key);
608 }
609
610 if (*icon_out)
611 break;
612 }
613 }
614
615 static gboolean build_registry_path (gunichar2 *output, gsize output_size, ...) G_GNUC_NULL_TERMINATED;
616 static gboolean build_registry_pathv (gunichar2 *output, gsize output_size, va_list components);
617
618 static GWin32RegistryKey *_g_win32_registry_key_build_and_new_w (GError **error, ...) G_GNUC_NULL_TERMINATED;
619
620 /* output_size is in *bytes*, not gunichar2s! */
621 static gboolean
build_registry_path(gunichar2 * output,gsize output_size,...)622 build_registry_path (gunichar2 *output, gsize output_size, ...)
623 {
624 va_list ap;
625 gboolean result;
626
627 va_start (ap, output_size);
628
629 result = build_registry_pathv (output, output_size, ap);
630
631 va_end (ap);
632
633 return result;
634 }
635
636 /* output_size is in *bytes*, not gunichar2s! */
637 static gboolean
build_registry_pathv(gunichar2 * output,gsize output_size,va_list components)638 build_registry_pathv (gunichar2 *output, gsize output_size, va_list components)
639 {
640 va_list lentest;
641 gunichar2 *p;
642 gunichar2 *component;
643 gsize length;
644
645 if (output == NULL)
646 return FALSE;
647
648 G_VA_COPY (lentest, components);
649
650 for (length = 0, component = va_arg (lentest, gunichar2 *);
651 component != NULL;
652 component = va_arg (lentest, gunichar2 *))
653 {
654 length += wcslen (component);
655 }
656
657 va_end (lentest);
658
659 if ((length >= REG_PATH_MAX_SIZE) ||
660 (length * sizeof (gunichar2) >= output_size))
661 return FALSE;
662
663 output[0] = L'\0';
664
665 for (p = output, component = va_arg (components, gunichar2 *);
666 component != NULL;
667 component = va_arg (components, gunichar2 *))
668 {
669 length = wcslen (component);
670 wcscat (p, component);
671 p += length;
672 }
673
674 return TRUE;
675 }
676
677
678 static GWin32RegistryKey *
_g_win32_registry_key_build_and_new_w(GError ** error,...)679 _g_win32_registry_key_build_and_new_w (GError **error, ...)
680 {
681 va_list ap;
682 gunichar2 key_path[REG_PATH_MAX_SIZE + 1];
683 GWin32RegistryKey *key;
684
685 va_start (ap, error);
686
687 key = NULL;
688
689 if (build_registry_pathv (key_path, sizeof (key_path), ap))
690 key = g_win32_registry_key_new_w (key_path, error);
691
692 va_end (ap);
693
694 return key;
695 }
696
697
698 static gboolean
utf8_and_fold(const gunichar2 * str,gchar ** str_u8,gchar ** str_u8_folded)699 utf8_and_fold (const gunichar2 *str,
700 gchar **str_u8,
701 gchar **str_u8_folded)
702 {
703 gchar *u8;
704 gchar *folded;
705 u8 = g_utf16_to_utf8 (str, -1, NULL, NULL, NULL);
706
707 if (u8 == NULL)
708 return FALSE;
709
710 folded = g_utf8_casefold (u8, -1);
711
712 if (folded == NULL)
713 {
714 g_free (u8);
715 return FALSE;
716 }
717
718 if (str_u8)
719 *str_u8 = u8;
720 else
721 g_free (u8);
722
723 if (str_u8_folded)
724 *str_u8_folded = folded;
725 else
726 g_free (folded);
727
728 return TRUE;
729 }
730
731
732 static gboolean
follow_class_chain_to_handler(const gunichar2 * program_id,gsize program_id_size,gunichar2 ** program_command,GWin32RegistryKey ** program_key,gunichar2 ** proxy_id,gunichar2 ** proxy_command,GWin32RegistryKey ** proxy_key,gchar ** program_id_u8,gchar ** program_id_folded)733 follow_class_chain_to_handler (const gunichar2 *program_id,
734 gsize program_id_size,
735 gunichar2 **program_command,
736 GWin32RegistryKey **program_key,
737 gunichar2 **proxy_id,
738 gunichar2 **proxy_command,
739 GWin32RegistryKey **proxy_key,
740 gchar **program_id_u8,
741 gchar **program_id_folded)
742 {
743 GWin32RegistryKey *key;
744 GWin32RegistryValueType val_type;
745 gsize proxy_id_size;
746 gboolean got_value;
747
748 g_assert (program_id && program_command && proxy_id && proxy_command);
749
750 *program_command = NULL;
751 *proxy_id = NULL;
752 *proxy_command = NULL;
753
754 if (program_key)
755 *program_key = NULL;
756
757 if (proxy_key)
758 *proxy_key = NULL;
759
760
761 key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id,
762 SHELL_OPEN_COMMAND, NULL);
763
764 if (key != NULL)
765 {
766 got_value = g_win32_registry_key_get_value_w (key,
767 TRUE,
768 L"",
769 &val_type,
770 (void **) program_command,
771 NULL,
772 NULL);
773 if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR)
774 {
775 if ((program_id_u8 != NULL || program_id_folded != NULL) &&
776 !utf8_and_fold (program_id, program_id_u8, program_id_folded))
777 {
778 g_object_unref (key);
779 g_free (program_command);
780
781 return FALSE;
782 }
783 if (program_key == NULL)
784 g_object_unref (key);
785 else
786 *program_key = key;
787
788 return TRUE;
789 }
790 else if (got_value)
791 g_clear_pointer (program_command, g_free);
792
793 g_object_unref (key);
794 }
795
796 key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id, NULL);
797
798 if (key == NULL)
799 return FALSE;
800
801 got_value = g_win32_registry_key_get_value_w (key,
802 TRUE,
803 L"",
804 &val_type,
805 (void **) proxy_id,
806 &proxy_id_size,
807 NULL);
808 if (!got_value ||
809 (val_type != G_WIN32_REGISTRY_VALUE_STR))
810 {
811 g_object_unref (key);
812 g_clear_pointer (proxy_id, g_free);
813 return FALSE;
814 }
815
816 if (proxy_key)
817 *proxy_key = key;
818 else
819 g_object_unref (key);
820
821 key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, *proxy_id,
822 SHELL_OPEN_COMMAND, NULL);
823
824 if (key == NULL)
825 {
826 g_clear_pointer (proxy_id, g_free);
827 if (proxy_key)
828 g_clear_object (proxy_key);
829 return FALSE;
830 }
831
832 got_value = g_win32_registry_key_get_value_w (key,
833 TRUE,
834 L"",
835 &val_type,
836 (void **) proxy_command,
837 NULL,
838 NULL);
839 g_object_unref (key);
840
841 if (!got_value ||
842 val_type != G_WIN32_REGISTRY_VALUE_STR ||
843 ((program_id_u8 != NULL || program_id_folded != NULL) &&
844 !utf8_and_fold (program_id, program_id_u8, program_id_folded)))
845 {
846 g_clear_pointer (proxy_id, g_free);
847 g_clear_pointer (proxy_command, g_free);
848 if (proxy_key)
849 g_clear_object (proxy_key);
850 return FALSE;
851 }
852
853 return TRUE;
854 }
855
856
857 static void
extract_executable(gunichar2 * commandline,gchar ** ex_out,gchar ** ex_basename_out,gchar ** ex_folded_out,gchar ** ex_folded_basename_out)858 extract_executable (gunichar2 *commandline,
859 gchar **ex_out,
860 gchar **ex_basename_out,
861 gchar **ex_folded_out,
862 gchar **ex_folded_basename_out)
863 {
864 gchar *ex;
865 gchar *ex_folded;
866 gunichar2 *p;
867 gboolean quoted;
868 size_t len;
869 size_t execlen;
870 gunichar2 *exepart;
871 gboolean found;
872
873 quoted = FALSE;
874 execlen = 0;
875 found = FALSE;
876 len = wcslen (commandline);
877 p = commandline;
878
879 while (p < &commandline[len])
880 {
881 switch (p[0])
882 {
883 case L'"':
884 quoted = !quoted;
885 break;
886 case L' ':
887 if (!quoted)
888 {
889 execlen = p - commandline;
890 p = &commandline[len];
891 found = TRUE;
892 }
893 break;
894 default:
895 break;
896 }
897 p += 1;
898 }
899
900 if (!found)
901 execlen = len;
902
903 exepart = g_wcsdup (commandline, (execlen + 1) * sizeof (gunichar2));
904 exepart[execlen] = L'\0';
905
906 p = &exepart[0];
907
908 while (execlen > 0 && exepart[0] == L'"' && exepart[execlen - 1] == L'"')
909 {
910 p = &exepart[1];
911 exepart[execlen - 1] = L'\0';
912 execlen -= 2;
913 }
914
915 if (!utf8_and_fold (p, &ex, &ex_folded))
916 /* Currently no code to handle this case. It shouldn't happen though... */
917 g_assert_not_reached ();
918
919 g_free (exepart);
920
921 if (ex_out)
922 {
923 *ex_out = ex;
924
925 if (ex_basename_out)
926 {
927 *ex_basename_out = &ex[strlen (ex) - 1];
928
929 while (*ex_basename_out > ex)
930 {
931 if ((*ex_basename_out)[0] == '/' ||
932 (*ex_basename_out)[0] == '\\')
933 {
934 *ex_basename_out += 1;
935 break;
936 }
937
938 *ex_basename_out -= 1;
939 }
940 }
941 }
942 else
943 {
944 g_free (ex);
945 }
946
947 if (ex_folded_out)
948 {
949 *ex_folded_out = ex_folded;
950
951 if (ex_folded_basename_out)
952 {
953 *ex_folded_basename_out = &ex_folded[strlen (ex_folded) - 1];
954
955 while (*ex_folded_basename_out > ex_folded)
956 {
957 if ((*ex_folded_basename_out)[0] == '/' ||
958 (*ex_folded_basename_out)[0] == '\\')
959 {
960 *ex_folded_basename_out += 1;
961 break;
962 }
963
964 *ex_folded_basename_out -= 1;
965 }
966 }
967 }
968 else
969 {
970 g_free (ex_folded);
971 }
972 }
973
974 static void
get_url_association(const gunichar2 * schema)975 get_url_association (const gunichar2 *schema)
976 {
977 GWin32AppInfoURLSchema *schema_rec;
978 GWin32AppInfoHandler *handler_rec;
979 GWin32AppInfoHandler *handler_rec_in_url;
980 gchar *schema_u8;
981 gchar *schema_folded;
982 GWin32RegistryKey *user_choice;
983 GWin32RegistryValueType val_type;
984 gunichar2 *program_id;
985 gsize program_id_size;
986 gunichar2 *program_command;
987 gunichar2 *proxy_id;
988 gunichar2 *proxy_command;
989 gchar *program_id_u8;
990 gchar *program_id_folded;
991 GWin32RegistryKey *program_key;
992 GWin32RegistryKey *proxy_key;
993
994 user_choice = _g_win32_registry_key_build_and_new_w (NULL, URL_ASSOCIATIONS,
995 schema, USER_CHOICE,
996 NULL);
997
998 if (user_choice == NULL)
999 return;
1000
1001 if (!utf8_and_fold (schema, &schema_u8, &schema_folded))
1002 {
1003 g_object_unref (user_choice);
1004 return;
1005 }
1006
1007 schema_rec = g_hash_table_lookup (urls, schema_folded);
1008
1009 if (!g_win32_registry_key_get_value_w (user_choice,
1010 TRUE,
1011 L"Progid",
1012 &val_type,
1013 (void **) &program_id,
1014 &program_id_size,
1015 NULL))
1016 {
1017 g_free (schema_u8);
1018 g_free (schema_folded);
1019 g_object_unref (user_choice);
1020 return;
1021 }
1022
1023 if (val_type != G_WIN32_REGISTRY_VALUE_STR)
1024 {
1025 g_free (schema_u8);
1026 g_free (schema_folded);
1027 g_free (program_id);
1028 g_object_unref (user_choice);
1029 return;
1030 }
1031
1032 program_key = proxy_key = NULL;
1033 program_command = proxy_id = proxy_command = NULL;
1034
1035 if (!follow_class_chain_to_handler (program_id,
1036 program_id_size,
1037 &program_command,
1038 &program_key,
1039 &proxy_id,
1040 &proxy_command,
1041 &proxy_key,
1042 &program_id_u8,
1043 &program_id_folded))
1044 {
1045 g_free (schema_u8);
1046 g_free (schema_folded);
1047 g_free (program_id);
1048 g_object_unref (user_choice);
1049 return;
1050 }
1051
1052 handler_rec = g_hash_table_lookup (handlers, program_id_folded);
1053
1054 if (handler_rec == NULL)
1055 {
1056 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1057
1058 handler_rec->proxy_key = proxy_key;
1059 handler_rec->key = program_key;
1060 handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
1061 handler_rec->handler_id_folded =
1062 g_strdup (program_id_folded);
1063 handler_rec->handler_command =
1064 program_command ? g_wcsdup (program_command, -1) : NULL;
1065 handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1066 handler_rec->proxy_command =
1067 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1068 extract_executable (proxy_command ? proxy_command : program_command,
1069 &handler_rec->executable,
1070 &handler_rec->executable_basename,
1071 &handler_rec->executable_folded,
1072 NULL);
1073 read_handler_icon (proxy_key, program_key, &handler_rec->icon);
1074 g_hash_table_insert (handlers,
1075 g_strdup (program_id_folded),
1076 handler_rec);
1077 }
1078 else
1079 {
1080 g_clear_object (&program_key);
1081 g_clear_object (&proxy_key);
1082 }
1083
1084 if (schema_rec == NULL)
1085 {
1086 schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
1087 schema_rec->schema = g_wcsdup (schema, -1);
1088 schema_rec->schema_u8 = g_strdup (schema_u8);
1089 schema_rec->schema_folded = g_strdup (schema_folded);
1090 schema_rec->chosen_handler = g_object_ref (handler_rec);
1091 g_hash_table_insert (urls, g_strdup (schema_folded), schema_rec);
1092 }
1093
1094 if (schema_rec->chosen_handler == NULL)
1095 schema_rec->chosen_handler = g_object_ref (handler_rec);
1096
1097 handler_rec_in_url = g_hash_table_lookup (schema_rec->handlers,
1098 program_id_folded);
1099
1100 if (handler_rec_in_url == NULL && schema_rec->chosen_handler != handler_rec)
1101 g_hash_table_insert (schema_rec->handlers,
1102 g_strdup (program_id_folded),
1103 g_object_ref (handler_rec));
1104
1105 g_free (schema_u8);
1106 g_free (schema_folded);
1107 g_free (program_id);
1108 g_free (program_id_u8);
1109 g_free (program_id_folded);
1110 g_free (program_command);
1111 g_free (proxy_id);
1112 g_free (proxy_command);
1113 g_object_unref (user_choice);
1114 }
1115
1116 static void
get_file_ext(const gunichar2 * ext)1117 get_file_ext (const gunichar2 *ext)
1118 {
1119 GWin32AppInfoFileExtension *file_extn;
1120 gboolean file_ext_known;
1121 GWin32AppInfoHandler *handler_rec;
1122 GWin32AppInfoHandler *handler_rec_in_ext;
1123 gchar *ext_u8;
1124 gchar *ext_folded;
1125 GWin32RegistryKey *user_choice;
1126 GWin32RegistryKey *open_with_progids;
1127 GWin32RegistryValueType val_type;
1128 gsize program_id_size;
1129 gboolean found_handler;
1130 gunichar2 *program_id;
1131 gunichar2 *proxy_id;
1132 gchar *program_id_u8;
1133 gchar *program_id_folded;
1134 GWin32RegistryKey *program_key;
1135 GWin32RegistryKey *proxy_key;
1136 gunichar2 *program_command;
1137 gunichar2 *proxy_command;
1138
1139 open_with_progids = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS,
1140 ext,
1141 OPEN_WITH_PROGIDS,
1142 NULL);
1143
1144 user_choice = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, ext,
1145 USER_CHOICE, NULL);
1146
1147 if (user_choice == NULL && open_with_progids == NULL)
1148 return;
1149
1150 if (!utf8_and_fold (ext, &ext_u8, &ext_folded))
1151 {
1152 g_clear_object (&user_choice);
1153 g_clear_object (&open_with_progids);
1154 return;
1155 }
1156
1157 file_extn = NULL;
1158 file_ext_known = g_hash_table_lookup_extended (extensions,
1159 ext_folded,
1160 NULL,
1161 (void **) &file_extn);
1162
1163 if (!file_ext_known)
1164 file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
1165
1166 found_handler = FALSE;
1167
1168 if (user_choice != NULL)
1169 {
1170 if (g_win32_registry_key_get_value_w (user_choice,
1171 TRUE,
1172 L"Progid",
1173 &val_type,
1174 (void **) &program_id,
1175 &program_id_size,
1176 NULL))
1177 {
1178 program_key = proxy_key = NULL;
1179
1180 if (val_type == G_WIN32_REGISTRY_VALUE_STR &&
1181 follow_class_chain_to_handler (program_id,
1182 program_id_size,
1183 &program_command,
1184 &program_key,
1185 &proxy_id,
1186 &proxy_command,
1187 &proxy_key,
1188 &program_id_u8,
1189 &program_id_folded))
1190 {
1191 handler_rec = g_hash_table_lookup (handlers,
1192 program_id_folded);
1193
1194 if (handler_rec == NULL)
1195 {
1196 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER,
1197 NULL);
1198 handler_rec->proxy_key = proxy_key;
1199 handler_rec->key = program_key;
1200 handler_rec->handler_id =
1201 g_wcsdup (program_id, program_id_size);
1202 handler_rec->handler_id_folded =
1203 g_strdup (program_id_folded);
1204 handler_rec->handler_command =
1205 program_command ? g_wcsdup (program_command, -1) : NULL;
1206 handler_rec->proxy_id =
1207 proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1208 handler_rec->proxy_command =
1209 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1210 extract_executable (proxy_command ? proxy_command : program_command,
1211 &handler_rec->executable,
1212 &handler_rec->executable_basename,
1213 &handler_rec->executable_folded,
1214 NULL);
1215 read_handler_icon (proxy_key,
1216 program_key,
1217 &handler_rec->icon);
1218 g_hash_table_insert (handlers,
1219 g_strdup (program_id_folded),
1220 handler_rec);
1221 }
1222 else
1223 {
1224 g_clear_object (&program_key);
1225 g_clear_object (&proxy_key);
1226 }
1227
1228 found_handler = TRUE;
1229
1230 handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
1231 program_id_folded);
1232
1233 if (file_extn->chosen_handler == NULL)
1234 {
1235 g_hash_table_insert (file_extn->handlers,
1236 g_strdup (program_id_folded),
1237 g_object_ref (handler_rec));
1238 }
1239 else if (handler_rec_in_ext == NULL)
1240 {
1241 if (file_extn->chosen_handler->handler_id_folded &&
1242 strcmp (file_extn->chosen_handler->handler_id_folded,
1243 program_id_folded) != 0)
1244 g_hash_table_insert (file_extn->handlers,
1245 g_strdup (program_id_folded),
1246 g_object_ref (handler_rec));
1247 }
1248
1249 g_free (program_id_u8);
1250 g_free (program_id_folded);
1251 g_free (program_command);
1252 g_free (proxy_id);
1253 g_free (proxy_command);
1254 }
1255
1256 g_free (program_id);
1257 }
1258
1259 g_object_unref (user_choice);
1260 }
1261
1262 if (open_with_progids != NULL)
1263 {
1264 GWin32RegistryValueIter iter;
1265
1266 if (g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
1267 {
1268 gunichar2 *value_name;
1269 gunichar2 *value_data;
1270 gsize value_name_len;
1271 gsize value_data_size;
1272 GWin32RegistryValueType value_type;
1273
1274 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
1275 {
1276 gsize value_name_size;
1277 program_key = proxy_key = NULL;
1278
1279 if ((!g_win32_registry_value_iter_get_value_type (&iter,
1280 &value_type,
1281 NULL)) ||
1282 ((val_type != G_WIN32_REGISTRY_VALUE_STR) &&
1283 (val_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) ||
1284 (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
1285 &value_name_len,
1286 NULL)) ||
1287 (value_name_len <= 0) ||
1288 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
1289 (void **) &value_data,
1290 &value_data_size,
1291 NULL)) ||
1292 (value_data_size < sizeof (gunichar2)) ||
1293 (value_data[0] == L'\0'))
1294 continue;
1295
1296 value_name_size = sizeof (gunichar2) * (value_name_len + 1);
1297
1298 if (!follow_class_chain_to_handler (value_name,
1299 value_name_size,
1300 &program_command,
1301 &program_key,
1302 &proxy_id,
1303 &proxy_command,
1304 &proxy_key,
1305 &program_id_u8,
1306 &program_id_folded))
1307 continue;
1308
1309 handler_rec = g_hash_table_lookup (handlers,
1310 program_id_folded);
1311
1312 if (handler_rec == NULL)
1313 {
1314 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1315
1316 handler_rec->proxy_key = proxy_key;
1317 handler_rec->key = program_key;
1318 handler_rec->handler_id =
1319 g_wcsdup (value_name, value_name_size);
1320 handler_rec->handler_id_folded =
1321 g_strdup (program_id_folded);
1322 handler_rec->handler_command =
1323 program_command ? g_wcsdup (program_command, -1) : NULL;
1324 handler_rec->proxy_id =
1325 proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1326 handler_rec->proxy_command =
1327 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1328 extract_executable (proxy_command ? proxy_command : program_command,
1329 &handler_rec->executable,
1330 &handler_rec->executable_basename,
1331 &handler_rec->executable_folded,
1332 NULL);
1333 read_handler_icon (proxy_key,
1334 program_key,
1335 &handler_rec->icon);
1336 g_hash_table_insert (handlers,
1337 g_strdup (program_id_folded),
1338 handler_rec);
1339 }
1340 else
1341 {
1342 g_clear_object (&program_key);
1343 g_clear_object (&proxy_key);
1344 }
1345
1346 found_handler = TRUE;
1347
1348 handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
1349 program_id_folded);
1350
1351 if (handler_rec_in_ext == NULL)
1352 {
1353 if (file_extn->chosen_handler == NULL)
1354 g_hash_table_insert (file_extn->handlers,
1355 g_strdup (program_id_folded),
1356 g_object_ref (handler_rec));
1357 else if (file_extn->chosen_handler->handler_id_folded &&
1358 strcmp (file_extn->chosen_handler->handler_id_folded,
1359 program_id_folded) != 0)
1360 g_hash_table_insert (file_extn->handlers,
1361 g_strdup (program_id_folded),
1362 g_object_ref (handler_rec));
1363 }
1364
1365 g_free (program_id_u8);
1366 g_free (program_id_folded);
1367 g_free (program_command);
1368 g_free (proxy_id);
1369 g_free (proxy_command);
1370 }
1371
1372 g_win32_registry_value_iter_clear (&iter);
1373 }
1374
1375 g_object_unref (open_with_progids);
1376 }
1377
1378 if (!found_handler)
1379 {
1380 if (!file_ext_known)
1381 g_object_unref (file_extn);
1382 }
1383 else if (!file_ext_known)
1384 {
1385 file_extn->extension = g_wcsdup (ext, -1);
1386 file_extn->extension_u8 = g_strdup (ext_u8);
1387 g_hash_table_insert (extensions, g_strdup (ext_folded), file_extn);
1388 }
1389
1390 g_free (ext_u8);
1391 g_free (ext_folded);
1392 }
1393
1394 static void
collect_capable_apps_from_clients(GPtrArray * capable_apps,GPtrArray * priority_capable_apps,gboolean user_registry)1395 collect_capable_apps_from_clients (GPtrArray *capable_apps,
1396 GPtrArray *priority_capable_apps,
1397 gboolean user_registry)
1398 {
1399 GWin32RegistryKey *clients;
1400 GWin32RegistrySubkeyIter clients_iter;
1401
1402 gunichar2 *client_type_name;
1403 gsize client_type_name_len;
1404
1405
1406 if (user_registry)
1407 clients =
1408 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
1409 NULL);
1410 else
1411 clients =
1412 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
1413 NULL);
1414
1415 if (clients == NULL)
1416 return;
1417
1418 if (!g_win32_registry_subkey_iter_init (&clients_iter, clients, NULL))
1419 {
1420 g_object_unref (clients);
1421 return;
1422 }
1423
1424 while (g_win32_registry_subkey_iter_next (&clients_iter, TRUE, NULL))
1425 {
1426 GWin32RegistrySubkeyIter subkey_iter;
1427 GWin32RegistryKey *system_client_type;
1428 GWin32RegistryValueType default_type;
1429 gunichar2 *default_value = NULL;
1430 gunichar2 *client_name;
1431 gsize client_name_len;
1432
1433 if (!g_win32_registry_subkey_iter_get_name_w (&clients_iter,
1434 &client_type_name,
1435 &client_type_name_len,
1436 NULL))
1437 continue;
1438
1439 system_client_type = g_win32_registry_key_get_child_w (clients,
1440 client_type_name,
1441 NULL);
1442
1443 if (system_client_type == NULL)
1444 continue;
1445
1446 if (g_win32_registry_key_get_value_w (system_client_type,
1447 TRUE,
1448 L"",
1449 &default_type,
1450 (gpointer *) &default_value,
1451 NULL,
1452 NULL))
1453 {
1454 if (default_type != G_WIN32_REGISTRY_VALUE_STR ||
1455 default_value[0] == L'\0')
1456 g_clear_pointer (&default_value, g_free);
1457 }
1458
1459 if (!g_win32_registry_subkey_iter_init (&subkey_iter,
1460 system_client_type,
1461 NULL))
1462 {
1463 g_clear_pointer (&default_value, g_free);
1464 g_object_unref (system_client_type);
1465 continue;
1466 }
1467
1468 while (g_win32_registry_subkey_iter_next (&subkey_iter, TRUE, NULL))
1469 {
1470 GWin32RegistryKey *system_client;
1471 GWin32RegistryKey *system_client_assoc;
1472 gboolean add;
1473 gunichar2 *keyname;
1474
1475 if (!g_win32_registry_subkey_iter_get_name_w (&subkey_iter,
1476 &client_name,
1477 &client_name_len,
1478 NULL))
1479 continue;
1480
1481 system_client = g_win32_registry_key_get_child_w (system_client_type,
1482 client_name,
1483 NULL);
1484
1485 if (system_client == NULL)
1486 continue;
1487
1488 add = FALSE;
1489
1490 system_client_assoc = g_win32_registry_key_get_child_w (system_client,
1491 L"Capabilities\\FileAssociations",
1492 NULL);
1493
1494 if (system_client_assoc != NULL)
1495 {
1496 add = TRUE;
1497 g_object_unref (system_client_assoc);
1498 }
1499 else
1500 {
1501 system_client_assoc = g_win32_registry_key_get_child_w (system_client,
1502 L"Capabilities\\UrlAssociations",
1503 NULL);
1504
1505 if (system_client_assoc != NULL)
1506 {
1507 add = TRUE;
1508 g_object_unref (system_client_assoc);
1509 }
1510 }
1511
1512 if (add)
1513 {
1514 keyname = g_wcsdup (g_win32_registry_key_get_path_w (system_client), -1);
1515
1516 if (default_value && wcscmp (default_value, client_name) == 0)
1517 g_ptr_array_add (priority_capable_apps, keyname);
1518 else
1519 g_ptr_array_add (capable_apps, keyname);
1520 }
1521
1522 g_object_unref (system_client);
1523 }
1524
1525 g_win32_registry_subkey_iter_clear (&subkey_iter);
1526 g_clear_pointer (&default_value, g_free);
1527 g_object_unref (system_client_type);
1528 }
1529
1530 g_win32_registry_subkey_iter_clear (&clients_iter);
1531 g_object_unref (clients);
1532 }
1533
1534 static void
collect_capable_apps_from_registered_apps(GPtrArray * capable_apps,gboolean user_registry)1535 collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
1536 gboolean user_registry)
1537 {
1538 GWin32RegistryValueIter iter;
1539
1540 gunichar2 *value_data;
1541 gsize value_data_size;
1542 GWin32RegistryValueType value_type;
1543 GWin32RegistryKey *registered_apps;
1544
1545 if (user_registry)
1546 registered_apps =
1547 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
1548 NULL);
1549 else
1550 registered_apps =
1551 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
1552 NULL);
1553
1554 if (!registered_apps)
1555 return;
1556
1557 if (!g_win32_registry_value_iter_init (&iter, registered_apps, NULL))
1558 {
1559 g_object_unref (registered_apps);
1560 return;
1561 }
1562
1563 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
1564 {
1565 gunichar2 possible_location[REG_PATH_MAX_SIZE + 1];
1566 GWin32RegistryKey *location = NULL;
1567
1568 if ((!g_win32_registry_value_iter_get_value_type (&iter,
1569 &value_type,
1570 NULL)) ||
1571 (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
1572 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
1573 (void **) &value_data,
1574 &value_data_size,
1575 NULL)) ||
1576 (value_data_size < sizeof (gunichar2)) ||
1577 (value_data[0] == L'\0'))
1578 continue;
1579
1580 if (build_registry_path (possible_location, sizeof (possible_location),
1581 HKCU, value_data, NULL))
1582 location = g_win32_registry_key_new_w (possible_location, NULL);
1583
1584 if (location)
1585 {
1586 gunichar2 *p = wcsrchr (possible_location, L'\\');
1587
1588 if (p)
1589 *p = L'\0';
1590
1591 g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
1592 g_object_unref (location);
1593 continue;
1594 }
1595
1596 if (!build_registry_path (possible_location, sizeof (possible_location),
1597 user_registry ? HKCU : HKLM, value_data, NULL))
1598 continue;
1599
1600 location = g_win32_registry_key_new_w (possible_location, NULL);
1601
1602 if (location)
1603 {
1604 gunichar2 *p = wcsrchr (possible_location, L'\\');
1605 if (p)
1606 *p = L'\0';
1607 g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
1608 g_object_unref (location);
1609 }
1610 }
1611
1612 g_win32_registry_value_iter_clear (&iter);
1613 g_object_unref (registered_apps);
1614 }
1615
1616 static void
read_capable_app(gunichar2 * input_app_key_path,gboolean user_specific,gboolean default_app)1617 read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolean default_app)
1618 {
1619 GWin32AppInfoApplication *app;
1620 gunichar2 *app_key_path;
1621 gunichar2 *canonical_name;
1622 gchar *canonical_name_u8;
1623 gchar *canonical_name_folded;
1624 GWin32RegistryKey *appkey;
1625 gunichar2 *fallback_friendly_name;
1626 GWin32RegistryValueType vtype;
1627 gboolean success;
1628 gunichar2 *friendly_name;
1629 gunichar2 *description;
1630 gunichar2 *narrow_application_name;
1631 gunichar2 *icon_source;
1632 GWin32RegistryKey *capabilities;
1633 GWin32RegistryKey *default_icon_key;
1634 GWin32RegistryKey *shell_open_command_key;
1635 gunichar2 *shell_open_command;
1636 gchar *app_executable;
1637 gchar *app_executable_basename;
1638 gchar *app_executable_folded;
1639 gchar *app_executable_folded_basename;
1640 GWin32RegistryKey *associations;
1641
1642 app_key_path = g_wcsdup (input_app_key_path, -1);
1643
1644 canonical_name = wcsrchr (app_key_path, L'\\');
1645
1646 if (canonical_name == NULL)
1647 {
1648 /* The key must have at least one '\\' */
1649 g_free (app_key_path);
1650 return;
1651 }
1652
1653 canonical_name += 1;
1654
1655 if (!utf8_and_fold (canonical_name, &canonical_name_u8, &canonical_name_folded))
1656 {
1657 g_free (app_key_path);
1658 return;
1659 }
1660
1661 appkey = g_win32_registry_key_new_w (app_key_path, NULL);
1662
1663 if (appkey == NULL)
1664 {
1665 g_free (canonical_name_u8);
1666 g_free (canonical_name_folded);
1667 g_free (app_key_path);
1668 return;
1669 }
1670
1671 capabilities =
1672 g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL);
1673
1674 if (capabilities == NULL)
1675 {
1676 /* Must have capabilities */
1677 g_free (canonical_name_u8);
1678 g_free (canonical_name_folded);
1679 g_free (app_key_path);
1680 return;
1681 }
1682
1683 shell_open_command_key =
1684 g_win32_registry_key_get_child_w (appkey,
1685 L"shell\\open\\command",
1686 NULL);
1687
1688 if (shell_open_command_key == NULL)
1689 {
1690 g_object_unref (capabilities);
1691 g_free (canonical_name_u8);
1692 g_free (canonical_name_folded);
1693 g_free (app_key_path);
1694 g_object_unref (appkey);
1695 return ;
1696 }
1697
1698 shell_open_command = NULL;
1699
1700 success = g_win32_registry_key_get_value_w (shell_open_command_key,
1701 TRUE,
1702 L"",
1703 &vtype,
1704 (gpointer *) &shell_open_command,
1705 NULL,
1706 NULL);
1707
1708 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1709 {
1710 /* Must have a command */
1711 g_clear_pointer (&shell_open_command, g_free);
1712 g_object_unref (capabilities);
1713 g_free (canonical_name_u8);
1714 g_free (canonical_name_folded);
1715 g_free (app_key_path);
1716 g_object_unref (appkey);
1717 return;
1718 }
1719
1720 extract_executable (shell_open_command,
1721 &app_executable,
1722 &app_executable_basename,
1723 &app_executable_folded,
1724 &app_executable_folded_basename);
1725
1726 app = g_hash_table_lookup (apps_by_id, canonical_name_folded);
1727
1728 if (app == NULL)
1729 {
1730 app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
1731
1732 app->canonical_name = g_wcsdup (canonical_name, -1);
1733 app->canonical_name_u8 = g_strdup (canonical_name_u8);
1734 app->canonical_name_folded =
1735 g_strdup (canonical_name_folded);
1736
1737 app->command = g_wcsdup (shell_open_command, -1);
1738 app->command_u8 =
1739 g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL);
1740 app->executable = g_strdup (app_executable);
1741 app->executable_basename =
1742 &app->executable[app_executable_basename - app_executable];
1743 app->executable_folded =
1744 g_strdup (app_executable_folded);
1745
1746 app->no_open_with = FALSE;
1747
1748 app->user_specific = user_specific;
1749 app->default_app = default_app;
1750
1751 g_hash_table_insert (apps_by_id,
1752 g_strdup (canonical_name_folded),
1753 app);
1754 }
1755
1756 fallback_friendly_name = NULL;
1757 success = g_win32_registry_key_get_value_w (appkey,
1758 TRUE,
1759 L"",
1760 &vtype,
1761 (void **) &fallback_friendly_name,
1762 NULL,
1763 NULL);
1764
1765 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1766 g_clear_pointer (&fallback_friendly_name, g_free);
1767
1768 if (fallback_friendly_name && app->pretty_name == NULL)
1769 {
1770 app->pretty_name = g_wcsdup (fallback_friendly_name, -1);
1771 g_clear_pointer (&app->pretty_name_u8, g_free);
1772 app->pretty_name_u8 = g_utf16_to_utf8 (fallback_friendly_name,
1773 -1,
1774 NULL,
1775 NULL,
1776 NULL);
1777 }
1778
1779 friendly_name = NULL;
1780 success = g_win32_registry_key_get_value_w (capabilities,
1781 TRUE,
1782 L"LocalizedString",
1783 &vtype,
1784 (void **) &friendly_name,
1785 NULL,
1786 NULL);
1787
1788 if (success && (vtype != G_WIN32_REGISTRY_VALUE_STR || friendly_name[0] != L'@'))
1789 g_clear_pointer (&friendly_name, g_free);
1790
1791 friendly_name = read_resource_string (friendly_name);
1792
1793 if (friendly_name && app->localized_pretty_name == NULL)
1794 {
1795 app->localized_pretty_name = g_wcsdup (friendly_name, -1);
1796 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
1797 app->localized_pretty_name_u8 = g_utf16_to_utf8 (friendly_name,
1798 -1,
1799 NULL,
1800 NULL,
1801 NULL);
1802 }
1803
1804 description = NULL;
1805 success = g_win32_registry_key_get_value_w (capabilities,
1806 TRUE,
1807 L"ApplicationDescription",
1808 &vtype,
1809 (void **) &description,
1810 NULL,
1811 NULL);
1812
1813 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1814 g_clear_pointer (&description, g_free);
1815
1816 description = read_resource_string (description);
1817
1818 if (description && app->description == NULL)
1819 {
1820 app->description = g_wcsdup (description, -1);
1821 g_clear_pointer (&app->description_u8, g_free);
1822 app->description_u8 = g_utf16_to_utf8 (description, -1, NULL, NULL, NULL);
1823 }
1824
1825 default_icon_key = g_win32_registry_key_get_child_w (appkey,
1826 L"DefaultIcon",
1827 NULL);
1828
1829 icon_source = NULL;
1830
1831 if (default_icon_key != NULL)
1832 {
1833 success = g_win32_registry_key_get_value_w (default_icon_key,
1834 TRUE,
1835 L"",
1836 &vtype,
1837 (void **) &icon_source,
1838 NULL,
1839 NULL);
1840
1841 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1842 g_clear_pointer (&icon_source, g_free);
1843
1844 g_object_unref (default_icon_key);
1845 }
1846
1847 if (icon_source == NULL)
1848 {
1849 success = g_win32_registry_key_get_value_w (capabilities,
1850 TRUE,
1851 L"ApplicationIcon",
1852 &vtype,
1853 (void **) &icon_source,
1854 NULL,
1855 NULL);
1856
1857 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1858 g_clear_pointer (&icon_source, g_free);
1859 }
1860
1861 if (icon_source && app->icon == NULL)
1862 {
1863 gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
1864 app->icon = g_themed_icon_new (name);
1865 g_free (name);
1866 }
1867
1868 narrow_application_name = NULL;
1869 success = g_win32_registry_key_get_value_w (capabilities,
1870 TRUE,
1871 L"ApplicationName",
1872 &vtype,
1873 (void **) &narrow_application_name,
1874 NULL,
1875 NULL);
1876
1877 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1878 g_clear_pointer (&narrow_application_name, g_free);
1879
1880 narrow_application_name = read_resource_string (narrow_application_name);
1881
1882 /* TODO: do something with the narrow name. Maybe make a kind of sub-app?
1883 * Narrow name is a more precise name of the application in given context.
1884 * I.e. Thunderbird's name is "Thunderbird", whereas its narrow name is
1885 * "Thunderbird (news)" when registering it as a news client.
1886 * Maybe we should consider applications with different narrow names as
1887 * different applications altogether?
1888 */
1889
1890 associations = g_win32_registry_key_get_child_w (capabilities,
1891 L"FileAssociations",
1892 NULL);
1893
1894 if (associations != NULL)
1895 {
1896 GWin32RegistryValueIter iter;
1897
1898 if (g_win32_registry_value_iter_init (&iter, associations, NULL))
1899 {
1900 gunichar2 *file_extension;
1901 gunichar2 *extension_handler;
1902 gsize file_extension_len;
1903 gsize extension_handler_size;
1904 GWin32RegistryValueType value_type;
1905
1906 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
1907 {
1908 GWin32AppInfoHandler *handler_rec;
1909 GWin32AppInfoHandler *handler_rec_in_ext;
1910 GWin32AppInfoFileExtension *ext;
1911 gunichar2 *program_command;
1912 gunichar2 *proxy_id;
1913 gunichar2 *proxy_command;
1914 GWin32RegistryKey *program_key;
1915 GWin32RegistryKey *proxy_key;
1916 gchar *program_id_u8;
1917 gchar *program_id_folded;
1918 gchar *file_extension_u8;
1919 gchar *file_extension_folded;
1920
1921 if ((!g_win32_registry_value_iter_get_value_type (&iter,
1922 &value_type,
1923 NULL)) ||
1924 (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
1925 (!g_win32_registry_value_iter_get_name_w (&iter,
1926 &file_extension,
1927 &file_extension_len,
1928 NULL)) ||
1929 (file_extension_len <= 0) ||
1930 (file_extension[0] != L'.') ||
1931 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
1932 (void **) &extension_handler,
1933 &extension_handler_size,
1934 NULL)) ||
1935 (extension_handler_size < sizeof (gunichar2)) ||
1936 (extension_handler[0] == L'\0'))
1937 continue;
1938
1939 if (!follow_class_chain_to_handler (extension_handler,
1940 extension_handler_size,
1941 &program_command,
1942 &program_key,
1943 &proxy_id,
1944 &proxy_command,
1945 &proxy_key,
1946 &program_id_u8,
1947 &program_id_folded))
1948 continue;
1949
1950 handler_rec = g_hash_table_lookup (handlers,
1951 program_id_folded);
1952
1953 if (handler_rec == NULL)
1954 {
1955 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1956
1957 handler_rec->proxy_key = proxy_key;
1958 handler_rec->key = program_key;
1959 handler_rec->handler_id =
1960 g_wcsdup (extension_handler,extension_handler_size);
1961 handler_rec->handler_id_folded =
1962 g_strdup (program_id_folded);
1963 handler_rec->handler_command =
1964 program_command ? g_wcsdup (program_command, -1) : NULL;
1965 handler_rec->proxy_id =
1966 proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1967 handler_rec->proxy_command =
1968 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1969 extract_executable (proxy_command ? proxy_command : program_command,
1970 &handler_rec->executable,
1971 &handler_rec->executable_basename,
1972 &handler_rec->executable_folded,
1973 NULL);
1974 read_handler_icon (proxy_key,
1975 program_key,
1976 &handler_rec->icon);
1977 g_hash_table_insert (handlers,
1978 g_strdup (program_id_folded),
1979 handler_rec);
1980 }
1981 else
1982 {
1983 g_clear_object (&program_key);
1984 g_clear_object (&proxy_key);
1985 }
1986
1987 if (utf8_and_fold (file_extension,
1988 &file_extension_u8,
1989 &file_extension_folded))
1990 {
1991 ext = g_hash_table_lookup (extensions,
1992 file_extension_folded);
1993
1994 if (ext == NULL)
1995 {
1996 ext = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
1997
1998 ext->extension = g_wcsdup (file_extension, -1);
1999 ext->extension_u8 = g_strdup (file_extension_u8);
2000 g_hash_table_insert (extensions, g_strdup (file_extension_folded), ext);
2001 }
2002
2003 handler_rec_in_ext =
2004 g_hash_table_lookup (ext->handlers,
2005 program_id_folded);
2006
2007 if (handler_rec_in_ext == NULL)
2008 {
2009 if (ext->chosen_handler == NULL)
2010 g_hash_table_insert (ext->handlers,
2011 g_strdup (program_id_folded),
2012 g_object_ref (handler_rec));
2013 else if (ext->chosen_handler->handler_id_folded &&
2014 strcmp (ext->chosen_handler->handler_id_folded,
2015 program_id_folded) != 0)
2016 g_hash_table_insert (ext->handlers,
2017 g_strdup (program_id_folded),
2018 g_object_ref (handler_rec));
2019 }
2020
2021 handler_rec_in_ext =
2022 g_hash_table_lookup (app->supported_exts,
2023 file_extension_folded);
2024
2025 if (handler_rec_in_ext == NULL)
2026 g_hash_table_insert (app->supported_exts,
2027 g_strdup (file_extension_folded),
2028 g_object_ref (handler_rec));
2029
2030 g_free (file_extension_u8);
2031 g_free (file_extension_folded);
2032 }
2033
2034 g_free (program_id_u8);
2035 g_free (program_id_folded);
2036 g_free (program_command);
2037 g_free (proxy_id);
2038 g_free (proxy_command);
2039 }
2040
2041 g_win32_registry_value_iter_clear (&iter);
2042 }
2043
2044 g_object_unref (associations);
2045 }
2046
2047 associations = g_win32_registry_key_get_child_w (capabilities, L"URLAssociations", NULL);
2048
2049 if (associations != NULL)
2050 {
2051 GWin32RegistryValueIter iter;
2052
2053 if (g_win32_registry_value_iter_init (&iter, associations, NULL))
2054 {
2055 gunichar2 *url_schema;
2056 gunichar2 *schema_handler;
2057 gsize url_schema_len;
2058 gsize schema_handler_size;
2059 GWin32RegistryValueType value_type;
2060
2061 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2062 {
2063 GWin32AppInfoHandler *handler_rec;
2064 GWin32AppInfoHandler *handler_rec_in_url;
2065 GWin32AppInfoURLSchema *schema;
2066 gunichar2 *program_command;
2067 gunichar2 *proxy_id;
2068 gunichar2 *proxy_command;
2069 GWin32RegistryKey *program_key;
2070 GWin32RegistryKey *proxy_key;
2071 gchar *program_id_u8;
2072 gchar *program_id_folded;
2073 gchar *schema_u8;
2074 gchar *schema_folded;
2075
2076 if ((!g_win32_registry_value_iter_get_value_type (&iter,
2077 &value_type,
2078 NULL)) ||
2079 ((value_type != G_WIN32_REGISTRY_VALUE_STR) &&
2080 (value_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) ||
2081 (!g_win32_registry_value_iter_get_name_w (&iter,
2082 &url_schema,
2083 &url_schema_len,
2084 NULL)) ||
2085 (url_schema_len <= 0) ||
2086 (url_schema[0] == L'\0') ||
2087 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
2088 (void **) &schema_handler,
2089 &schema_handler_size,
2090 NULL)) ||
2091 (schema_handler_size < sizeof (gunichar2)) ||
2092 (schema_handler[0] == L'\0'))
2093 continue;
2094
2095 if (!follow_class_chain_to_handler (schema_handler,
2096 schema_handler_size,
2097 &program_command,
2098 &program_key,
2099 &proxy_id,
2100 &proxy_command,
2101 &proxy_key,
2102 &program_id_u8,
2103 &program_id_folded))
2104 continue;
2105
2106
2107 handler_rec = g_hash_table_lookup (handlers, program_id_folded);
2108
2109 if (handler_rec == NULL)
2110 {
2111 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
2112
2113 handler_rec->proxy_key = proxy_key;
2114 handler_rec->key = program_key;
2115 handler_rec->handler_id =
2116 g_wcsdup (schema_handler, schema_handler_size);
2117 handler_rec->handler_id_folded =
2118 g_strdup (program_id_folded);
2119 handler_rec->handler_command = program_command ?
2120 g_wcsdup (program_command, -1) : NULL;
2121 handler_rec->proxy_id =
2122 proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
2123 handler_rec->proxy_command =
2124 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
2125 extract_executable (proxy_command ? proxy_command : program_command,
2126 &handler_rec->executable,
2127 &handler_rec->executable_basename,
2128 &handler_rec->executable_folded,
2129 NULL);
2130 read_handler_icon (proxy_key,
2131 program_key,
2132 &handler_rec->icon);
2133 g_hash_table_insert (handlers,
2134 g_strdup (program_id_folded),
2135 handler_rec);
2136 }
2137 else
2138 {
2139 g_clear_object (&program_key);
2140 g_clear_object (&proxy_key);
2141 }
2142
2143 if (utf8_and_fold (url_schema,
2144 &schema_u8,
2145 &schema_folded))
2146 {
2147 schema = g_hash_table_lookup (urls,
2148 schema_folded);
2149
2150 if (schema == NULL)
2151 {
2152 schema = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
2153
2154 schema->schema = g_wcsdup (url_schema, -1);
2155 schema->schema_u8 = g_strdup (schema_u8);
2156 schema->schema_folded =
2157 g_strdup (schema_folded);
2158 g_hash_table_insert (urls,
2159 g_strdup (schema_folded),
2160 schema);
2161 }
2162
2163 handler_rec_in_url =
2164 g_hash_table_lookup (schema->handlers,
2165 program_id_folded);
2166
2167 if (handler_rec_in_url == NULL)
2168 g_hash_table_insert (schema->handlers,
2169 g_strdup (program_id_folded),
2170 g_object_ref (handler_rec));
2171
2172 handler_rec_in_url =
2173 g_hash_table_lookup (app->supported_urls,
2174 schema_folded);
2175
2176 if (handler_rec_in_url == NULL)
2177 g_hash_table_insert (app->supported_urls,
2178 g_strdup (schema_folded),
2179 g_object_ref (handler_rec));
2180
2181 g_free (schema_u8);
2182 g_free (schema_folded);
2183 }
2184
2185 g_free (program_id_u8);
2186 g_free (program_id_folded);
2187 g_free (program_command);
2188 g_free (proxy_id);
2189 g_free (proxy_command);
2190 }
2191
2192 g_win32_registry_value_iter_clear (&iter);
2193 }
2194
2195 g_object_unref (associations);
2196 }
2197
2198 g_clear_pointer (&app_executable, g_free);
2199 g_clear_pointer (&app_executable_folded, g_free);
2200 g_clear_pointer (&fallback_friendly_name, g_free);
2201 g_clear_pointer (&description, g_free);
2202 g_clear_pointer (&icon_source, g_free);
2203 g_clear_pointer (&narrow_application_name, g_free);
2204 g_clear_pointer (&shell_open_command, g_free);
2205
2206 g_object_unref (appkey);
2207 g_object_unref (shell_open_command_key);
2208 g_object_unref (capabilities);
2209 g_free (canonical_name_u8);
2210 g_free (canonical_name_folded);
2211 g_free (app_key_path);
2212 }
2213
2214 static void
read_urls(GWin32RegistryKey * url_associations)2215 read_urls (GWin32RegistryKey *url_associations)
2216 {
2217 GWin32RegistrySubkeyIter url_iter;
2218 gunichar2 *url_schema;
2219 gsize url_schema_len;
2220
2221 if (url_associations == NULL)
2222 return;
2223
2224 if (!g_win32_registry_subkey_iter_init (&url_iter, url_associations, NULL))
2225 return;
2226
2227 while (g_win32_registry_subkey_iter_next (&url_iter, TRUE, NULL))
2228 {
2229 if (!g_win32_registry_subkey_iter_get_name_w (&url_iter,
2230 &url_schema,
2231 &url_schema_len,
2232 NULL))
2233 continue;
2234
2235 get_url_association (url_schema);
2236 }
2237
2238 g_win32_registry_subkey_iter_clear (&url_iter);
2239 }
2240
2241 static void
read_exeapps(void)2242 read_exeapps (void)
2243 {
2244 GWin32RegistryKey *applications_key;
2245 GWin32RegistrySubkeyIter app_iter;
2246 gunichar2 *app_exe_basename;
2247 gsize app_exe_basename_len;
2248
2249 applications_key =
2250 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
2251
2252 if (applications_key == NULL)
2253 return;
2254
2255 if (!g_win32_registry_subkey_iter_init (&app_iter, applications_key, NULL))
2256 {
2257 g_object_unref (applications_key);
2258 return;
2259 }
2260
2261 while (g_win32_registry_subkey_iter_next (&app_iter, TRUE, NULL))
2262 {
2263 GWin32RegistryKey *incapable_app;
2264 gunichar2 *friendly_app_name;
2265 gboolean success;
2266 gboolean no_open_with;
2267 GWin32RegistryValueType vtype;
2268 GWin32RegistryKey *default_icon_key;
2269 gunichar2 *icon_source;
2270 GIcon *icon = NULL;
2271 gchar *appexe;
2272 gchar *appexe_basename;
2273 gchar *appexe_folded;
2274 gchar *appexe_folded_basename;
2275 GWin32AppInfoApplication *app;
2276 GWin32RegistryKey *shell_open_command_key;
2277 gunichar2 *shell_open_command;
2278 GWin32RegistryKey *supported_key;
2279
2280 if (!g_win32_registry_subkey_iter_get_name_w (&app_iter,
2281 &app_exe_basename,
2282 &app_exe_basename_len,
2283 NULL))
2284 continue;
2285
2286 incapable_app =
2287 g_win32_registry_key_get_child_w (applications_key,
2288 app_exe_basename,
2289 NULL);
2290
2291 if (incapable_app == NULL)
2292 continue;
2293
2294 extract_executable (app_exe_basename,
2295 &appexe,
2296 &appexe_basename,
2297 &appexe_folded,
2298 &appexe_folded_basename);
2299
2300 shell_open_command_key =
2301 g_win32_registry_key_get_child_w (incapable_app,
2302 L"shell\\open\\command",
2303 NULL);
2304
2305 shell_open_command = NULL;
2306
2307 if (shell_open_command_key != NULL)
2308 {
2309 success = g_win32_registry_key_get_value_w (shell_open_command_key,
2310 TRUE,
2311 L"",
2312 &vtype,
2313 (gpointer *) &shell_open_command,
2314 NULL,
2315 NULL);
2316
2317 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2318 {
2319 g_clear_pointer (&shell_open_command, g_free);
2320 }
2321
2322 g_object_unref (shell_open_command_key);
2323 }
2324
2325 friendly_app_name = NULL;
2326 success = g_win32_registry_key_get_value_w (incapable_app,
2327 TRUE,
2328 L"FriendlyAppName",
2329 &vtype,
2330 (void **) &friendly_app_name,
2331 NULL,
2332 NULL);
2333
2334 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2335 g_clear_pointer (&friendly_app_name, g_free);
2336
2337 friendly_app_name = read_resource_string (friendly_app_name);
2338
2339 no_open_with = FALSE;
2340 success = g_win32_registry_key_get_value_w (incapable_app,
2341 TRUE,
2342 L"NoOpenWith",
2343 &vtype,
2344 NULL,
2345 NULL,
2346 NULL);
2347
2348 if (success)
2349 no_open_with = TRUE;
2350
2351 default_icon_key =
2352 g_win32_registry_key_get_child_w (incapable_app,
2353 L"DefaultIcon",
2354 NULL);
2355
2356 icon_source = NULL;
2357
2358 if (default_icon_key != NULL)
2359 {
2360 success =
2361 g_win32_registry_key_get_value_w (default_icon_key,
2362 TRUE,
2363 L"",
2364 &vtype,
2365 (void **) &icon_source,
2366 NULL,
2367 NULL);
2368
2369 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2370 g_clear_pointer (&icon_source, g_free);
2371
2372 g_object_unref (default_icon_key);
2373 }
2374
2375 if (icon_source)
2376 {
2377 gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
2378 icon = g_themed_icon_new (name);
2379 g_free (name);
2380 }
2381
2382 app = g_hash_table_lookup (apps_by_exe, appexe_folded_basename);
2383
2384 if (app == NULL)
2385 {
2386 app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
2387
2388 app->command =
2389 shell_open_command ? g_wcsdup (shell_open_command, -1) : NULL;
2390
2391 if (shell_open_command)
2392 app->command_u8 = g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL);
2393
2394 app->executable = g_strdup (appexe);
2395 app->executable_basename = &app->executable[appexe_basename - appexe];
2396 app->executable_folded = g_strdup (appexe_folded);
2397
2398 app->no_open_with = no_open_with;
2399
2400 if (friendly_app_name)
2401 {
2402 app->localized_pretty_name = g_wcsdup (friendly_app_name, -1);
2403 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
2404 app->localized_pretty_name_u8 =
2405 g_utf16_to_utf8 (friendly_app_name, -1, NULL, NULL, NULL);
2406 }
2407
2408 if (icon)
2409 app->icon = g_object_ref (icon);
2410
2411 app->user_specific = FALSE;
2412 app->default_app = FALSE;
2413
2414 g_hash_table_insert (apps_by_exe,
2415 g_strdup (appexe_folded_basename),
2416 app);
2417 }
2418
2419 supported_key =
2420 g_win32_registry_key_get_child_w (incapable_app,
2421 L"SupportedTypes",
2422 NULL);
2423
2424 if (supported_key)
2425 {
2426 GWin32RegistryValueIter sup_iter;
2427 if (g_win32_registry_value_iter_init (&sup_iter, supported_key, NULL))
2428 {
2429 gunichar2 *ext_name;
2430 gsize ext_name_len;
2431
2432 while (g_win32_registry_value_iter_next (&sup_iter, TRUE, NULL))
2433 {
2434 gchar *ext_u8;
2435 gchar *ext_folded;
2436 GWin32AppInfoFileExtension *file_extn;
2437 gboolean file_ext_known;
2438
2439 if ((!g_win32_registry_value_iter_get_name_w (&sup_iter,
2440 &ext_name,
2441 &ext_name_len,
2442 NULL)) ||
2443 (ext_name_len <= 0) ||
2444 (ext_name[0] != L'.') ||
2445 (!utf8_and_fold (ext_name,
2446 &ext_u8,
2447 &ext_folded)))
2448 continue;
2449
2450 file_extn = NULL;
2451 file_ext_known =
2452 g_hash_table_lookup_extended (extensions,
2453 ext_folded,
2454 NULL,
2455 (void **) &file_extn);
2456
2457 if (!file_ext_known)
2458 {
2459 file_extn =
2460 g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
2461 file_extn->extension = g_wcsdup (ext_name, -1);
2462 file_extn->extension_u8 = g_strdup (ext_u8);
2463 g_hash_table_insert (extensions,
2464 g_strdup (ext_folded),
2465 file_extn);
2466 }
2467
2468 g_hash_table_insert (file_extn->other_apps,
2469 g_strdup (appexe_folded),
2470 g_object_ref (app));
2471
2472 g_free (ext_u8);
2473 g_free (ext_folded);
2474 }
2475
2476 g_win32_registry_value_iter_clear (&sup_iter);
2477 }
2478
2479 g_object_unref (supported_key);
2480 }
2481
2482
2483 g_free (appexe);
2484 g_free (appexe_folded);
2485 g_free (shell_open_command);
2486 g_free (friendly_app_name);
2487 g_free (icon_source);
2488
2489 g_clear_object (&icon);
2490 g_clear_object (&incapable_app);
2491 }
2492
2493 g_win32_registry_subkey_iter_clear (&app_iter);
2494 g_object_unref (applications_key);
2495 }
2496
2497
2498 static void
read_exts(GWin32RegistryKey * file_exts)2499 read_exts (GWin32RegistryKey *file_exts)
2500 {
2501 GWin32RegistrySubkeyIter ext_iter;
2502 gunichar2 *file_extension;
2503 gsize file_extension_len;
2504
2505 if (file_exts == NULL)
2506 return;
2507
2508 if (!g_win32_registry_subkey_iter_init (&ext_iter, file_exts, NULL))
2509 return;
2510
2511 while (g_win32_registry_subkey_iter_next (&ext_iter, TRUE, NULL))
2512 {
2513 if (!g_win32_registry_subkey_iter_get_name_w (&ext_iter,
2514 &file_extension,
2515 &file_extension_len,
2516 NULL))
2517 continue;
2518
2519 get_file_ext (file_extension);
2520 }
2521
2522 g_win32_registry_subkey_iter_clear (&ext_iter);
2523 }
2524
2525 static void
read_class_extension(GWin32RegistryKey * classes_root,gunichar2 * class_name,gsize class_name_len)2526 read_class_extension (GWin32RegistryKey *classes_root,
2527 gunichar2 *class_name,
2528 gsize class_name_len)
2529 {
2530 gchar *ext_u8;
2531 gchar *ext_folded;
2532 GWin32AppInfoFileExtension *file_extn;
2533 GWin32AppInfoHandler *handler_rec;
2534 GWin32AppInfoHandler *handler_rec_in_ext;
2535 GWin32RegistryKey *class_key;
2536 gsize program_id_size;
2537 gunichar2 *program_id;
2538 gunichar2 *proxy_id;
2539 GWin32RegistryKey *program_key;
2540 GWin32RegistryKey *proxy_key;
2541 gunichar2 *program_command;
2542 gunichar2 *proxy_command;
2543
2544 class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
2545
2546 if (class_key == NULL)
2547 return;
2548
2549 program_id = class_name;
2550 program_id_size = (class_name_len + 1) * sizeof (gunichar2);
2551 program_key = proxy_key = NULL;
2552 program_command = proxy_command = NULL;
2553
2554 if (!follow_class_chain_to_handler (program_id,
2555 program_id_size,
2556 &program_command,
2557 &program_key,
2558 &proxy_id,
2559 &proxy_command,
2560 &proxy_key,
2561 &ext_u8,
2562 &ext_folded))
2563 {
2564 g_object_unref (class_key);
2565 return;
2566 }
2567
2568
2569 file_extn = g_hash_table_lookup (extensions, ext_folded);
2570 handler_rec = g_hash_table_lookup (handlers, ext_folded);
2571
2572 if (file_extn == NULL)
2573 {
2574 file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
2575 file_extn->extension = g_wcsdup (class_name, -1);
2576 file_extn->extension_u8 = g_strdup (ext_u8);
2577 g_hash_table_insert (extensions, g_strdup (ext_folded), file_extn);
2578 }
2579
2580 if (handler_rec == NULL)
2581 {
2582 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
2583
2584 handler_rec->proxy_key = proxy_key;
2585 handler_rec->key = program_key;
2586 handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
2587 handler_rec->handler_id_folded = g_strdup (ext_folded);
2588 handler_rec->handler_command =
2589 program_command ? g_wcsdup (program_command, -1) : NULL;
2590 handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
2591 handler_rec->proxy_command =
2592 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
2593 extract_executable (proxy_command ? proxy_command : program_command,
2594 &handler_rec->executable,
2595 &handler_rec->executable_basename,
2596 &handler_rec->executable_folded,
2597 NULL);
2598 read_handler_icon (proxy_key, program_key, &handler_rec->icon);
2599 g_hash_table_insert (handlers,
2600 g_strdup (ext_folded),
2601 handler_rec);
2602 }
2603 else
2604 {
2605 g_clear_object (&program_key);
2606 g_clear_object (&proxy_key);
2607 }
2608
2609 handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
2610 ext_folded);
2611
2612 if (file_extn->chosen_handler == NULL)
2613 g_hash_table_insert (file_extn->handlers,
2614 g_strdup (ext_folded),
2615 g_object_ref (handler_rec));
2616 else if (handler_rec_in_ext == NULL)
2617 {
2618 if (file_extn->chosen_handler->handler_id_folded &&
2619 strcmp (file_extn->chosen_handler->handler_id_folded,
2620 ext_folded) != 0)
2621 g_hash_table_insert (file_extn->handlers,
2622 g_strdup (ext_folded),
2623 g_object_ref (handler_rec));
2624 }
2625
2626 g_free (program_command);
2627 g_free (proxy_id);
2628 g_free (proxy_command);
2629 g_free (ext_u8);
2630 g_free (ext_folded);
2631 g_object_unref (class_key);
2632 }
2633
2634 static void
read_class_url(GWin32RegistryKey * classes_root,gunichar2 * class_name,gsize class_name_len)2635 read_class_url (GWin32RegistryKey *classes_root,
2636 gunichar2 *class_name,
2637 gsize class_name_len)
2638 {
2639 GWin32RegistryKey *class_key;
2640 gboolean success;
2641 GWin32RegistryValueType vtype;
2642 GWin32AppInfoURLSchema *schema_rec;
2643 GWin32AppInfoHandler *handler_rec;
2644 GWin32AppInfoHandler *handler_rec_in_url;
2645 gunichar2 *program_id;
2646 gsize program_id_size;
2647 gunichar2 *program_command;
2648 gunichar2 *proxy_id;
2649 gunichar2 *proxy_command;
2650 gchar *program_id_u8;
2651 gchar *program_id_folded;
2652 GWin32RegistryKey *program_key;
2653 GWin32RegistryKey *proxy_key;
2654
2655 class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
2656
2657 if (class_key == NULL)
2658 return;
2659
2660 success = g_win32_registry_key_get_value_w (class_key,
2661 TRUE,
2662 L"URL Protocol",
2663 &vtype,
2664 NULL,
2665 NULL,
2666 NULL);
2667
2668 if (!success ||
2669 vtype != G_WIN32_REGISTRY_VALUE_STR)
2670 {
2671 g_object_unref (class_key);
2672 return;
2673 }
2674
2675 program_id = class_name;
2676 program_id_size = (class_name_len + 1) * sizeof (gunichar2);
2677 proxy_key = program_key = NULL;
2678 program_command = proxy_id = proxy_command = NULL;
2679
2680 if (!follow_class_chain_to_handler (program_id,
2681 program_id_size,
2682 &program_command,
2683 &program_key,
2684 &proxy_id,
2685 &proxy_command,
2686 &proxy_key,
2687 &program_id_u8,
2688 &program_id_folded))
2689 {
2690 g_object_unref (class_key);
2691 return;
2692 }
2693
2694 schema_rec = g_hash_table_lookup (urls, program_id_folded);
2695 handler_rec = g_hash_table_lookup (handlers, program_id_folded);
2696
2697 if (handler_rec == NULL)
2698 {
2699 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
2700
2701 handler_rec->proxy_key = proxy_key;
2702 handler_rec->key = program_key;
2703 handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
2704 handler_rec->handler_id_folded =
2705 g_strdup (program_id_folded);
2706 handler_rec->handler_command =
2707 program_command ? g_wcsdup (program_command, -1) : NULL;
2708 handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
2709 handler_rec->proxy_command =
2710 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
2711 extract_executable (proxy_command ? proxy_command : program_command,
2712 &handler_rec->executable,
2713 &handler_rec->executable_basename,
2714 &handler_rec->executable_folded,
2715 NULL);
2716 read_handler_icon (proxy_key, program_key, &handler_rec->icon);
2717 g_hash_table_insert (handlers,
2718 g_strdup (program_id_folded),
2719 handler_rec);
2720 }
2721 else
2722 {
2723 g_clear_object (&program_key);
2724 g_clear_object (&proxy_key);
2725 }
2726
2727 if (schema_rec == NULL)
2728 {
2729 schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
2730 schema_rec->schema = g_wcsdup (class_name, -1);
2731 schema_rec->schema_u8 = g_strdup (program_id_u8);
2732 schema_rec->schema_folded = g_strdup (program_id_folded);
2733 schema_rec->chosen_handler = g_object_ref (handler_rec);
2734 g_hash_table_insert (urls,
2735 g_strdup (program_id_folded),
2736 schema_rec);
2737 }
2738
2739 if (schema_rec->chosen_handler == NULL)
2740 schema_rec->chosen_handler = g_object_ref (handler_rec);
2741
2742 handler_rec_in_url = g_hash_table_lookup (schema_rec->handlers,
2743 program_id_folded);
2744
2745 if (handler_rec_in_url == NULL && schema_rec->chosen_handler != handler_rec)
2746 g_hash_table_insert (schema_rec->handlers,
2747 g_strdup (program_id_folded),
2748 g_object_ref (handler_rec));
2749
2750 g_free (program_id_u8);
2751 g_free (program_id_folded);
2752 g_free (program_command);
2753 g_free (proxy_id);
2754 g_free (proxy_command);
2755 g_object_unref (class_key);
2756 }
2757
2758 static void
read_classes(GWin32RegistryKey * classes_root)2759 read_classes (GWin32RegistryKey *classes_root)
2760 {
2761 GWin32RegistrySubkeyIter class_iter;
2762 gunichar2 *class_name;
2763 gsize class_name_len;
2764
2765 if (classes_root == NULL)
2766 return;
2767
2768 if (!g_win32_registry_subkey_iter_init (&class_iter, classes_root, NULL))
2769 return;
2770
2771 while (g_win32_registry_subkey_iter_next (&class_iter, TRUE, NULL))
2772 {
2773 if ((!g_win32_registry_subkey_iter_get_name_w (&class_iter,
2774 &class_name,
2775 &class_name_len,
2776 NULL)) ||
2777 (class_name_len <= 1))
2778 continue;
2779
2780 if (class_name[0] == L'.')
2781 read_class_extension (classes_root, class_name, class_name_len);
2782 else
2783 {
2784 gsize i;
2785
2786 for (i = 0; i < class_name_len; i++)
2787 if (!iswalpha (class_name[i]))
2788 break;
2789
2790 if (i == class_name_len)
2791 read_class_url (classes_root, class_name, class_name_len);
2792 }
2793 }
2794
2795 g_win32_registry_subkey_iter_clear (&class_iter);
2796 }
2797
2798 static void
link_chosen_handlers(void)2799 link_chosen_handlers (void)
2800 {
2801 GHashTableIter iter;
2802 GHashTableIter handler_iter;
2803 gchar *schema_folded;
2804 GWin32AppInfoURLSchema *schema;
2805 gchar *handler_id_folded;
2806 GWin32AppInfoHandler *handler;
2807 gchar *ext_folded;
2808 GWin32AppInfoFileExtension *ext;
2809
2810 g_hash_table_iter_init (&iter, urls);
2811
2812 while (g_hash_table_iter_next (&iter,
2813 (gpointer *) &schema_folded,
2814 (gpointer *) &schema))
2815 {
2816 if (schema->chosen_handler != NULL)
2817 continue;
2818
2819 g_hash_table_iter_init (&handler_iter, schema->handlers);
2820
2821 while (g_hash_table_iter_next (&handler_iter,
2822 (gpointer *) &handler_id_folded,
2823 (gpointer *) &handler))
2824 {
2825 gchar *proxy_id_folded;
2826
2827 if (schema->chosen_handler != NULL)
2828 break;
2829
2830 if (strcmp (handler_id_folded, schema_folded) != 0)
2831 continue;
2832
2833 if (handler->proxy_command &&
2834 handler->proxy_id &&
2835 utf8_and_fold (handler->proxy_id,
2836 NULL,
2837 &proxy_id_folded))
2838 {
2839 GWin32AppInfoHandler *proxy;
2840
2841 proxy = g_hash_table_lookup (handlers, proxy_id_folded);
2842
2843 if (proxy)
2844 {
2845 schema->chosen_handler = g_object_ref (proxy);
2846 g_debug ("Linking schema %s to proxy handler %c ? \"%S\" : %S\n",
2847 schema->schema_u8,
2848 schema->chosen_handler->proxy_id ? 'P' : 'T',
2849 schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id,
2850 schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command);
2851 }
2852
2853 g_free (proxy_id_folded);
2854 }
2855
2856 if (schema->chosen_handler == NULL)
2857 {
2858 schema->chosen_handler = g_object_ref (handler);
2859 g_debug ("Linking schema %s to handler %c ? \"%S\" : %S\n",
2860 schema->schema_u8,
2861 schema->chosen_handler->proxy_id ? 'P' : 'T',
2862 schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id,
2863 schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command);
2864 }
2865 }
2866 }
2867
2868 g_hash_table_iter_init (&iter, extensions);
2869
2870 while (g_hash_table_iter_next (&iter,
2871 (gpointer *) &ext_folded,
2872 (gpointer *) &ext))
2873 {
2874 if (ext->chosen_handler != NULL)
2875 continue;
2876
2877 g_hash_table_iter_init (&handler_iter, ext->handlers);
2878
2879 while (g_hash_table_iter_next (&handler_iter,
2880 (gpointer *) &handler_id_folded,
2881 (gpointer *) &handler))
2882 {
2883 gchar *proxy_id_folded;
2884
2885 if (ext->chosen_handler != NULL)
2886 break;
2887
2888 if (strcmp (handler_id_folded, ext_folded) != 0)
2889 continue;
2890
2891 if (handler->proxy_command &&
2892 handler->proxy_id &&
2893 utf8_and_fold (handler->proxy_id,
2894 NULL,
2895 &proxy_id_folded))
2896 {
2897 GWin32AppInfoHandler *proxy;
2898
2899 proxy = g_hash_table_lookup (handlers, proxy_id_folded);
2900
2901 if (proxy)
2902 {
2903 ext->chosen_handler = g_object_ref (proxy);
2904 g_debug ("Linking ext %s to proxy handler %c ? \"%S\" : %S\n",
2905 ext->extension_u8,
2906 ext->chosen_handler->proxy_id ? 'P' : 'T',
2907 ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id,
2908 ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command);
2909 }
2910
2911 g_free (proxy_id_folded);
2912 }
2913
2914 if (ext->chosen_handler == NULL)
2915 {
2916 ext->chosen_handler = g_object_ref (handler);
2917 g_debug ("Linking ext %s to handler %c ? \"%S\" : %S\n",
2918 ext->extension_u8,
2919 ext->chosen_handler->proxy_id ? 'P' : 'T',
2920 ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id,
2921 ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command);
2922 }
2923 }
2924 }
2925 }
2926
2927 static void
link_handlers_to_registered_apps(void)2928 link_handlers_to_registered_apps (void)
2929 {
2930 GHashTableIter iter;
2931 GHashTableIter sup_iter;
2932 gchar *app_id_folded;
2933 GWin32AppInfoApplication *app;
2934 gchar *schema_folded;
2935 GWin32AppInfoURLSchema *schema;
2936 gchar *ext_folded;
2937 GWin32AppInfoFileExtension *ext;
2938 gsize unhandled_exts;
2939
2940 g_hash_table_iter_init (&sup_iter, urls);
2941 while (g_hash_table_iter_next (&sup_iter,
2942 (gpointer *) &schema_folded,
2943 (gpointer *) &schema))
2944 {
2945 if (schema->chosen_handler == NULL)
2946 g_debug ("WARNING: schema %s has no chosen handler\n", schema->schema_u8);
2947 }
2948 unhandled_exts= 0;
2949 g_hash_table_iter_init (&sup_iter, extensions);
2950 while (g_hash_table_iter_next (&sup_iter,
2951 (gpointer *) &ext_folded,
2952 (gpointer *) &ext))
2953 {
2954 if (ext->chosen_handler == NULL)
2955 {
2956 g_debug ("WARNING: extension %s has no chosen handler\n",
2957 ext->extension_u8);
2958 unhandled_exts += 1;
2959 }
2960 }
2961
2962 g_hash_table_iter_init (&iter, apps_by_id);
2963 while (g_hash_table_iter_next (&iter,
2964 (gpointer *) &app_id_folded,
2965 (gpointer *) &app))
2966 {
2967 if (app->supported_urls)
2968 {
2969 GWin32AppInfoHandler *handler;
2970
2971 g_hash_table_iter_init (&sup_iter, app->supported_urls);
2972 while (g_hash_table_iter_next (&sup_iter,
2973 (gpointer *) &schema_folded,
2974 (gpointer *) &handler))
2975 {
2976 schema = g_hash_table_lookup (urls, schema_folded);
2977
2978 g_assert (schema != NULL);
2979
2980 if (schema->chosen_handler != NULL &&
2981 schema->chosen_handler->app == NULL)
2982 {
2983 schema->chosen_handler->app = g_object_ref (app);
2984 g_debug ("Linking %S", app->canonical_name);
2985
2986 if (app->localized_pretty_name)
2987 g_debug (" '%S'", app->localized_pretty_name);
2988 else if (app->pretty_name)
2989 g_debug (" '%S'", app->pretty_name);
2990 else
2991 g_debug (" '%s'", app->executable);
2992
2993 if (app->command)
2994 g_debug (" %S", app->command);
2995
2996 g_debug ("\n to schema %s handler %c ? \"%S\" : %S\n",
2997 schema->schema_u8,
2998 schema->chosen_handler->proxy_id ? 'P' : 'T',
2999 schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id,
3000 schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command);
3001 }
3002 }
3003
3004 g_hash_table_iter_init (&sup_iter, app->supported_urls);
3005 while (g_hash_table_iter_next (&sup_iter,
3006 (gpointer *) &schema_folded,
3007 (gpointer *) &handler))
3008 {
3009 if (handler->app == NULL)
3010 {
3011 handler->app = g_object_ref (app);
3012 g_debug ("Linking %S", app->canonical_name);
3013
3014 if (app->localized_pretty_name)
3015 g_debug (" '%S'", app->localized_pretty_name);
3016 else if (app->pretty_name)
3017 g_debug (" '%S'", app->pretty_name);
3018 else
3019 g_debug (" '%s'", app->executable);
3020
3021 if (app->command)
3022 g_debug (" %S", app->command);
3023
3024 g_debug ("\n directly to schema handler to %c ? \"%S\" : %S\n",
3025 handler->proxy_id ? 'P' : 'T',
3026 handler->proxy_id ? handler->proxy_id : handler->handler_id,
3027 handler->proxy_command ? handler->proxy_command : handler->handler_command);
3028 }
3029 }
3030 }
3031
3032 if (app->supported_exts)
3033 {
3034 GWin32AppInfoHandler *handler;
3035
3036 g_hash_table_iter_init (&sup_iter, app->supported_exts);
3037 while (g_hash_table_iter_next (&sup_iter,
3038 (gpointer *) &ext_folded,
3039 (gpointer *) &handler))
3040 {
3041 ext = g_hash_table_lookup (extensions, ext_folded);
3042
3043 g_assert (ext != NULL);
3044
3045 if (ext->chosen_handler != NULL &&
3046 ext->chosen_handler->app == NULL)
3047 {
3048 ext->chosen_handler->app = g_object_ref (app);
3049 g_debug ("Linking %S", app->canonical_name);
3050
3051 if (app->localized_pretty_name)
3052 g_debug (" '%S'", app->localized_pretty_name);
3053 else if (app->pretty_name)
3054 g_debug (" '%S'", app->pretty_name);
3055 else
3056 g_debug (" '%s'", app->executable);
3057
3058 if (app->command)
3059 g_debug (" %S", app->command);
3060
3061 g_debug ("\n to ext %s handler %c ? \"%S\" : %S\n",
3062 ext->extension_u8,
3063 ext->chosen_handler->proxy_id ? 'P' : 'T',
3064 ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id,
3065 ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command);
3066 }
3067 }
3068
3069 g_hash_table_iter_init (&sup_iter, app->supported_exts);
3070 while (g_hash_table_iter_next (&sup_iter,
3071 (gpointer *) &ext_folded,
3072 (gpointer *) &handler))
3073 {
3074 if (handler->app == NULL)
3075 {
3076 handler->app = g_object_ref (app);
3077 g_debug ("Linking %S", app->canonical_name);
3078
3079 if (app->localized_pretty_name)
3080 g_debug (" '%S'", app->localized_pretty_name);
3081 else if (app->pretty_name)
3082 g_debug (" '%S'", app->pretty_name);
3083 else
3084 g_debug (" '%s'", app->executable);
3085
3086 if (app->command)
3087 g_debug (" %S", app->command);
3088
3089 g_debug ("\n directly to ext handler %c ? \"%S\" : %S\n",
3090 handler->proxy_id ? 'P' : 'T',
3091 handler->proxy_id ? handler->proxy_id : handler->handler_id,
3092 handler->proxy_command ? handler->proxy_command : handler->handler_command);
3093 }
3094 }
3095 }
3096 }
3097
3098 g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts);
3099 unhandled_exts= 0;
3100 g_hash_table_iter_init (&sup_iter, extensions);
3101 while (g_hash_table_iter_next (&sup_iter,
3102 (gpointer *) &ext_folded,
3103 (gpointer *) &ext))
3104 {
3105 if (ext->chosen_handler == NULL)
3106 {
3107 g_debug ("WARNING: extension %s has no chosen handler\n",
3108 ext->extension_u8);
3109 unhandled_exts += 1;
3110 }
3111 }
3112 g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts);
3113 }
3114
3115 static void
link_handlers_to_unregistered_apps(void)3116 link_handlers_to_unregistered_apps (void)
3117 {
3118 GHashTableIter iter;
3119 GHashTableIter app_iter;
3120 GWin32AppInfoHandler *handler;
3121 gchar *handler_id_fc;
3122 GWin32AppInfoApplication *app;
3123 gchar *canonical_name_fc;
3124 gchar *appexe_fc_basename;
3125
3126 g_hash_table_iter_init (&iter, handlers);
3127 while (g_hash_table_iter_next (&iter,
3128 (gpointer *) &handler_id_fc,
3129 (gpointer *) &handler))
3130 {
3131 gchar *hndexe_fc_basename;
3132
3133 if ((handler->app != NULL) ||
3134 (handler->executable_folded == NULL))
3135 continue;
3136
3137 hndexe_fc_basename = g_utf8_casefold (handler->executable_basename, -1);
3138
3139 if (hndexe_fc_basename == NULL)
3140 continue;
3141
3142 g_hash_table_iter_init (&app_iter, apps_by_id);
3143
3144 while (g_hash_table_iter_next (&app_iter,
3145 (gpointer *) &canonical_name_fc,
3146 (gpointer *) &app))
3147 {
3148 if (app->executable_folded == NULL)
3149 continue;
3150
3151 if (strcmp (app->executable_folded,
3152 handler->executable_folded) != 0)
3153 continue;
3154
3155 handler->app = app;
3156 break;
3157 }
3158
3159 if (handler->app != NULL)
3160 continue;
3161
3162 g_hash_table_iter_init (&app_iter, apps_by_exe);
3163
3164 while ((hndexe_fc_basename != NULL) &&
3165 (g_hash_table_iter_next (&app_iter,
3166 (gpointer *) &appexe_fc_basename,
3167 (gpointer *) &app)))
3168 {
3169 /* Use basename because apps_by_exe only has basenames */
3170 if (strcmp (hndexe_fc_basename, appexe_fc_basename) != 0)
3171 continue;
3172
3173 handler->app = app;
3174 break;
3175 }
3176
3177 g_free (hndexe_fc_basename);
3178
3179 if (handler->app == NULL)
3180 g_debug ("WARNING: handler that runs %s has no corresponding app\n",
3181 handler->executable);
3182 }
3183 }
3184
3185
3186 static void
update_registry_data(void)3187 update_registry_data (void)
3188 {
3189 guint i;
3190 GPtrArray *capable_apps_keys;
3191 GPtrArray *user_capable_apps_keys;
3192 GPtrArray *priority_capable_apps_keys;
3193 GWin32RegistryKey *url_associations;
3194 GWin32RegistryKey *file_exts;
3195 GWin32RegistryKey *classes_root;
3196 DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end, postproc_end;
3197
3198 url_associations =
3199 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
3200 NULL);
3201 file_exts =
3202 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
3203 NULL);
3204 classes_root = g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT", NULL);
3205
3206 capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3207 user_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3208 priority_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3209
3210 g_clear_pointer (&apps_by_id, g_hash_table_destroy);
3211 g_clear_pointer (&apps_by_exe, g_hash_table_destroy);
3212 g_clear_pointer (&urls, g_hash_table_destroy);
3213 g_clear_pointer (&extensions, g_hash_table_destroy);
3214 g_clear_pointer (&handlers, g_hash_table_destroy);
3215
3216 collect_start = GetTickCount ();
3217 collect_capable_apps_from_clients (capable_apps_keys,
3218 priority_capable_apps_keys,
3219 FALSE);
3220 collect_capable_apps_from_clients (user_capable_apps_keys,
3221 priority_capable_apps_keys,
3222 TRUE);
3223 collect_capable_apps_from_registered_apps (user_capable_apps_keys,
3224 TRUE);
3225 collect_capable_apps_from_registered_apps (capable_apps_keys,
3226 FALSE);
3227 collect_end = GetTickCount ();
3228
3229 apps_by_id =
3230 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3231 apps_by_exe =
3232 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3233 urls =
3234 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3235 extensions =
3236 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3237 handlers =
3238 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3239 alloc_end = GetTickCount ();
3240
3241 for (i = 0; i < priority_capable_apps_keys->len; i++)
3242 read_capable_app (g_ptr_array_index (priority_capable_apps_keys, i),
3243 TRUE,
3244 TRUE);
3245 for (i = 0; i < user_capable_apps_keys->len; i++)
3246 read_capable_app (g_ptr_array_index (user_capable_apps_keys, i),
3247 TRUE,
3248 FALSE);
3249 for (i = 0; i < capable_apps_keys->len; i++)
3250 read_capable_app (g_ptr_array_index (capable_apps_keys, i),
3251 FALSE,
3252 FALSE);
3253 capable_end = GetTickCount ();
3254
3255 read_urls (url_associations);
3256 url_end = GetTickCount ();
3257 read_exts (file_exts);
3258 ext_end = GetTickCount ();
3259 read_exeapps ();
3260 exeapp_end = GetTickCount ();
3261 read_classes (classes_root);
3262 classes_end = GetTickCount ();
3263 link_chosen_handlers ();
3264 link_handlers_to_registered_apps ();
3265 link_handlers_to_unregistered_apps ();
3266 postproc_end = GetTickCount ();
3267
3268 g_debug ("Collecting capable appnames: %lums\n"
3269 "Allocating hashtables:...... %lums\n"
3270 "Reading capable apps: %lums\n"
3271 "Reading URL associations:... %lums\n"
3272 "Reading extension assocs: %lums\n"
3273 "Reading exe-only apps:...... %lums\n"
3274 "Reading classes: %lums\n"
3275 "Postprocessing:..............%lums\n"
3276 "TOTAL: %lums\n",
3277 collect_end - collect_start,
3278 alloc_end - collect_end,
3279 capable_end - alloc_end,
3280 url_end - capable_end,
3281 ext_end - url_end,
3282 exeapp_end - ext_end,
3283 classes_end - exeapp_end,
3284 postproc_end - classes_end,
3285 postproc_end - collect_start);
3286
3287 g_clear_object (&classes_root);
3288 g_clear_object (&url_associations);
3289 g_clear_object (&file_exts);
3290 g_ptr_array_free (capable_apps_keys, TRUE);
3291 g_ptr_array_free (user_capable_apps_keys, TRUE);
3292 g_ptr_array_free (priority_capable_apps_keys, TRUE);
3293
3294 return;
3295 }
3296
3297 static void
watch_keys(void)3298 watch_keys (void)
3299 {
3300 if (url_associations_key)
3301 g_win32_registry_key_watch (url_associations_key,
3302 TRUE,
3303 G_WIN32_REGISTRY_WATCH_NAME |
3304 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3305 G_WIN32_REGISTRY_WATCH_VALUES,
3306 NULL,
3307 NULL,
3308 NULL);
3309
3310 if (file_exts_key)
3311 g_win32_registry_key_watch (file_exts_key,
3312 TRUE,
3313 G_WIN32_REGISTRY_WATCH_NAME |
3314 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3315 G_WIN32_REGISTRY_WATCH_VALUES,
3316 NULL,
3317 NULL,
3318 NULL);
3319
3320 if (user_clients_key)
3321 g_win32_registry_key_watch (user_clients_key,
3322 TRUE,
3323 G_WIN32_REGISTRY_WATCH_NAME |
3324 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3325 G_WIN32_REGISTRY_WATCH_VALUES,
3326 NULL,
3327 NULL,
3328 NULL);
3329
3330 if (system_clients_key)
3331 g_win32_registry_key_watch (system_clients_key,
3332 TRUE,
3333 G_WIN32_REGISTRY_WATCH_NAME |
3334 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3335 G_WIN32_REGISTRY_WATCH_VALUES,
3336 NULL,
3337 NULL,
3338 NULL);
3339
3340 if (applications_key)
3341 g_win32_registry_key_watch (applications_key,
3342 TRUE,
3343 G_WIN32_REGISTRY_WATCH_NAME |
3344 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3345 G_WIN32_REGISTRY_WATCH_VALUES,
3346 NULL,
3347 NULL,
3348 NULL);
3349
3350 if (user_registered_apps_key)
3351 g_win32_registry_key_watch (user_registered_apps_key,
3352 TRUE,
3353 G_WIN32_REGISTRY_WATCH_NAME |
3354 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3355 G_WIN32_REGISTRY_WATCH_VALUES,
3356 NULL,
3357 NULL,
3358 NULL);
3359
3360 if (system_registered_apps_key)
3361 g_win32_registry_key_watch (system_registered_apps_key,
3362 TRUE,
3363 G_WIN32_REGISTRY_WATCH_NAME |
3364 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3365 G_WIN32_REGISTRY_WATCH_VALUES,
3366 NULL,
3367 NULL,
3368 NULL);
3369
3370 if (classes_root_key)
3371 g_win32_registry_key_watch (classes_root_key,
3372 FALSE,
3373 G_WIN32_REGISTRY_WATCH_NAME |
3374 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3375 G_WIN32_REGISTRY_WATCH_VALUES,
3376 NULL,
3377 NULL,
3378 NULL);
3379 }
3380
3381
3382 static void
g_win32_appinfo_init(void)3383 g_win32_appinfo_init (void)
3384 {
3385 static gsize initialized;
3386
3387 if (g_once_init_enter (&initialized))
3388 {
3389 url_associations_key =
3390 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
3391 NULL);
3392 file_exts_key =
3393 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
3394 NULL);
3395 user_clients_key =
3396 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
3397 NULL);
3398 system_clients_key =
3399 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
3400 NULL);
3401 applications_key =
3402 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications",
3403 NULL);
3404 user_registered_apps_key =
3405 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
3406 NULL);
3407 system_registered_apps_key =
3408 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
3409 NULL);
3410 classes_root_key =
3411 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT",
3412 NULL);
3413
3414 watch_keys ();
3415
3416 update_registry_data ();
3417
3418 g_once_init_leave (&initialized, TRUE);
3419 }
3420
3421 if ((url_associations_key && g_win32_registry_key_has_changed (url_associations_key)) ||
3422 (file_exts_key && g_win32_registry_key_has_changed (file_exts_key)) ||
3423 (user_clients_key && g_win32_registry_key_has_changed (user_clients_key)) ||
3424 (system_clients_key && g_win32_registry_key_has_changed (system_clients_key)) ||
3425 (applications_key && g_win32_registry_key_has_changed (applications_key)) ||
3426 (user_registered_apps_key && g_win32_registry_key_has_changed (user_registered_apps_key)) ||
3427 (system_registered_apps_key && g_win32_registry_key_has_changed (system_registered_apps_key)) ||
3428 (classes_root_key && g_win32_registry_key_has_changed (classes_root_key)))
3429 {
3430 G_LOCK (gio_win32_appinfo);
3431 update_registry_data ();
3432 watch_keys ();
3433 G_UNLOCK (gio_win32_appinfo);
3434 }
3435 }
3436
3437
3438 static void g_win32_app_info_iface_init (GAppInfoIface *iface);
3439
3440 struct _GWin32AppInfo
3441 {
3442 GObject parent_instance;
3443
3444 /*<private>*/
3445 gchar **supported_types;
3446
3447 GWin32AppInfoApplication *app;
3448
3449 GWin32AppInfoHandler *handler;
3450
3451 guint startup_notify : 1;
3452 };
3453
G_DEFINE_TYPE_WITH_CODE(GWin32AppInfo,g_win32_app_info,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,g_win32_app_info_iface_init))3454 G_DEFINE_TYPE_WITH_CODE (GWin32AppInfo, g_win32_app_info, G_TYPE_OBJECT,
3455 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
3456 g_win32_app_info_iface_init))
3457
3458
3459 static void
3460 g_win32_app_info_finalize (GObject *object)
3461 {
3462 GWin32AppInfo *info;
3463
3464 info = G_WIN32_APP_INFO (object);
3465
3466 g_clear_pointer (&info->supported_types, g_free);
3467 g_clear_object (&info->app);
3468 g_clear_object (&info->handler);
3469
3470 G_OBJECT_CLASS (g_win32_app_info_parent_class)->finalize (object);
3471 }
3472
3473 static void
g_win32_app_info_class_init(GWin32AppInfoClass * klass)3474 g_win32_app_info_class_init (GWin32AppInfoClass *klass)
3475 {
3476 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3477
3478 gobject_class->finalize = g_win32_app_info_finalize;
3479 }
3480
3481 static void
g_win32_app_info_init(GWin32AppInfo * local)3482 g_win32_app_info_init (GWin32AppInfo *local)
3483 {
3484 }
3485
3486 static GAppInfo *
g_win32_app_info_new_from_app(GWin32AppInfoApplication * app,GWin32AppInfoHandler * handler)3487 g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
3488 GWin32AppInfoHandler *handler)
3489 {
3490 GWin32AppInfo *new_info;
3491 GHashTableIter iter;
3492 gpointer ext;
3493 int i;
3494
3495 new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
3496
3497 new_info->app = g_object_ref (app);
3498
3499 g_win32_appinfo_init ();
3500 G_LOCK (gio_win32_appinfo);
3501
3502 i = 0;
3503 g_hash_table_iter_init (&iter, new_info->app->supported_exts);
3504
3505 while (g_hash_table_iter_next (&iter, &ext, NULL))
3506 {
3507 if (ext)
3508 i += 1;
3509 }
3510
3511 new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
3512
3513 i = 0;
3514 g_hash_table_iter_init (&iter, new_info->app->supported_exts);
3515
3516 while (g_hash_table_iter_next (&iter, &ext, NULL))
3517 {
3518 if (!ext)
3519 continue;
3520
3521 new_info->supported_types[i] = (gchar *) ext;
3522 i += 1;
3523 }
3524
3525 G_UNLOCK (gio_win32_appinfo);
3526
3527 new_info->supported_types[i] = NULL;
3528
3529 new_info->handler = handler ? g_object_ref (handler) : NULL;
3530
3531 return G_APP_INFO (new_info);
3532 }
3533
3534
3535 static GAppInfo *
g_win32_app_info_dup(GAppInfo * appinfo)3536 g_win32_app_info_dup (GAppInfo *appinfo)
3537 {
3538 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3539 GWin32AppInfo *new_info;
3540
3541 new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
3542
3543 if (info->app)
3544 new_info->app = g_object_ref (info->app);
3545
3546 if (info->handler)
3547 new_info->handler = g_object_ref (info->handler);
3548
3549 new_info->startup_notify = info->startup_notify;
3550
3551 if (info->supported_types)
3552 {
3553 int i;
3554
3555 for (i = 0; info->supported_types[i]; i++)
3556 break;
3557
3558 new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
3559
3560 for (i = 0; info->supported_types[i]; i++)
3561 new_info->supported_types[i] = g_strdup (info->supported_types[i]);
3562
3563 new_info->supported_types[i] = NULL;
3564 }
3565
3566 return G_APP_INFO (new_info);
3567 }
3568
3569 static gboolean
g_win32_app_info_equal(GAppInfo * appinfo1,GAppInfo * appinfo2)3570 g_win32_app_info_equal (GAppInfo *appinfo1,
3571 GAppInfo *appinfo2)
3572 {
3573 GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
3574 GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
3575
3576 if (info1->app == NULL ||
3577 info2->app == NULL)
3578 return info1 == info2;
3579
3580 if (info1->app->canonical_name_folded != NULL &&
3581 info2->app->canonical_name_folded != NULL)
3582 return (strcmp (info1->app->canonical_name_folded,
3583 info2->app->canonical_name_folded)) == 0;
3584
3585 if (info1->app->executable_folded != NULL &&
3586 info2->app->executable_folded != NULL)
3587 return (strcmp (info1->app->executable_folded,
3588 info2->app->executable_folded)) == 0;
3589
3590 return info1->app == info2->app;
3591 }
3592
3593 static const char *
g_win32_app_info_get_id(GAppInfo * appinfo)3594 g_win32_app_info_get_id (GAppInfo *appinfo)
3595 {
3596 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3597
3598 if (info->app == NULL)
3599 return NULL;
3600
3601 if (info->app->canonical_name_u8)
3602 return info->app->canonical_name_u8;
3603
3604 if (info->app->executable_basename)
3605 return info->app->executable_basename;
3606
3607 return NULL;
3608 }
3609
3610 static const char *
g_win32_app_info_get_name(GAppInfo * appinfo)3611 g_win32_app_info_get_name (GAppInfo *appinfo)
3612 {
3613 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3614
3615 if (info->app && info->app->canonical_name_u8)
3616 return info->app->canonical_name_u8;
3617 else
3618 return P_("Unnamed");
3619 }
3620
3621 static const char *
g_win32_app_info_get_display_name(GAppInfo * appinfo)3622 g_win32_app_info_get_display_name (GAppInfo *appinfo)
3623 {
3624 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3625
3626 if (info->app)
3627 {
3628 if (info->app->localized_pretty_name_u8)
3629 return info->app->localized_pretty_name_u8;
3630 else if (info->app->pretty_name_u8)
3631 return info->app->pretty_name_u8;
3632 }
3633
3634 return g_win32_app_info_get_name (appinfo);
3635 }
3636
3637 static const char *
g_win32_app_info_get_description(GAppInfo * appinfo)3638 g_win32_app_info_get_description (GAppInfo *appinfo)
3639 {
3640 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3641
3642 if (info->app == NULL)
3643 return NULL;
3644
3645 return info->app->description_u8;
3646 }
3647
3648 static const char *
g_win32_app_info_get_executable(GAppInfo * appinfo)3649 g_win32_app_info_get_executable (GAppInfo *appinfo)
3650 {
3651 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3652
3653 if (info->app == NULL)
3654 return NULL;
3655
3656 return info->app->executable;
3657 }
3658
3659 static const char *
g_win32_app_info_get_commandline(GAppInfo * appinfo)3660 g_win32_app_info_get_commandline (GAppInfo *appinfo)
3661 {
3662 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3663
3664 if (info->app == NULL)
3665 return NULL;
3666
3667 return info->app->command_u8;
3668 }
3669
3670 static GIcon *
g_win32_app_info_get_icon(GAppInfo * appinfo)3671 g_win32_app_info_get_icon (GAppInfo *appinfo)
3672 {
3673 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3674
3675 if (info->app == NULL)
3676 return NULL;
3677
3678 return info->app->icon;
3679 }
3680
3681 typedef struct _file_or_uri {
3682 gchar *uri;
3683 gchar *file;
3684 } file_or_uri;
3685
3686 static char *
expand_macro_single(char macro,file_or_uri * obj)3687 expand_macro_single (char macro, file_or_uri *obj)
3688 {
3689 char *result = NULL;
3690
3691 switch (macro)
3692 {
3693 case '*':
3694 case '0':
3695 case '1':
3696 case 'l':
3697 case 'd':
3698 case '2':
3699 case '3':
3700 case '4':
3701 case '5':
3702 case '6':
3703 case '7':
3704 case '8':
3705 case '9':
3706 /* TODO: handle 'l' and 'd' differently (longname and desktop name) */
3707 if (obj->uri)
3708 result = g_strdup (obj->uri);
3709 else if (obj->file)
3710 result = g_strdup (obj->file);
3711 break;
3712 case 'u':
3713 case 'U':
3714 if (obj->uri)
3715 result = g_shell_quote (obj->uri);
3716 break;
3717 case 'f':
3718 case 'F':
3719 if (obj->file)
3720 result = g_shell_quote (obj->file);
3721 break;
3722 }
3723
3724 return result;
3725 }
3726
3727 static gboolean
expand_macro(char macro,GString * exec,GWin32AppInfo * info,GList ** stat_obj_list,GList ** obj_list)3728 expand_macro (char macro,
3729 GString *exec,
3730 GWin32AppInfo *info,
3731 GList **stat_obj_list,
3732 GList **obj_list)
3733 {
3734 GList *objs = *obj_list;
3735 char *expanded;
3736 gboolean result = FALSE;
3737
3738 g_return_val_if_fail (exec != NULL, FALSE);
3739
3740 /*
3741 Legend: (from http://msdn.microsoft.com/en-us/library/windows/desktop/cc144101%28v=vs.85%29.aspx)
3742 %* - replace with all parameters
3743 %~ - replace with all parameters starting with and following the second parameter
3744 %0 or %1 the first file parameter. For example "C:\\Users\\Eric\\Destop\\New Text Document.txt". Generally this should be in quotes and the applications command line parsing should accept quotes to disambiguate files with spaces in the name and different command line parameters (this is a security best practice and I believe mentioned in MSDN).
3745 %<n> (where N is 2 - 9), replace with the nth parameter
3746 %s - show command
3747 %h - hotkey value
3748 %i - IDList stored in a shared memory handle is passed here.
3749 %l - long file name form of the first parameter. Note win32 applications will be passed the long file name, win16 applications get the short file name. Specifying %L is preferred as it avoids the need to probe for the application type.
3750 %d - desktop absolute parsing name of the first parameter (for items that don't have file system paths)
3751 %v - for verbs that are none implies all, if there is no parameter passed this is the working directory
3752 %w - the working directory
3753 */
3754
3755 switch (macro)
3756 {
3757 case '*':
3758 case '~':
3759 if (*stat_obj_list)
3760 {
3761 gint i;
3762 GList *o;
3763
3764 for (o = *stat_obj_list, i = 0;
3765 macro == '~' && o && i < 2;
3766 o = o->next, i++);
3767
3768 for (; o; o = o->next)
3769 {
3770 expanded = expand_macro_single (macro, o->data);
3771
3772 if (expanded)
3773 {
3774 if (o != *stat_obj_list)
3775 g_string_append (exec, " ");
3776
3777 g_string_append (exec, expanded);
3778 g_free (expanded);
3779 }
3780 }
3781
3782 objs = NULL;
3783 result = TRUE;
3784 }
3785 break;
3786 case '0':
3787 case '1':
3788 case 'l':
3789 case 'd':
3790 if (*stat_obj_list)
3791 {
3792 GList *o;
3793
3794 o = *stat_obj_list;
3795
3796 if (o)
3797 {
3798 expanded = expand_macro_single (macro, o->data);
3799
3800 if (expanded)
3801 {
3802 if (o != *stat_obj_list)
3803 g_string_append (exec, " ");
3804
3805 g_string_append (exec, expanded);
3806 g_free (expanded);
3807 }
3808 }
3809
3810 if (objs)
3811 objs = objs->next;
3812
3813 result = TRUE;
3814 }
3815 break;
3816 case '2':
3817 case '3':
3818 case '4':
3819 case '5':
3820 case '6':
3821 case '7':
3822 case '8':
3823 case '9':
3824 if (*stat_obj_list)
3825 {
3826 gint i;
3827 GList *o;
3828 gint n;
3829
3830 switch (macro)
3831 {
3832 case '2':
3833 n = 2;
3834 break;
3835 case '3':
3836 n = 3;
3837 break;
3838 case '4':
3839 n = 4;
3840 break;
3841 case '5':
3842 n = 5;
3843 break;
3844 case '6':
3845 n = 6;
3846 break;
3847 case '7':
3848 n = 7;
3849 break;
3850 case '8':
3851 n = 8;
3852 break;
3853 case '9':
3854 n = 9;
3855 break;
3856 }
3857
3858 for (o = *stat_obj_list, i = 0; o && i < n; o = o->next, i++);
3859
3860 if (o)
3861 {
3862 expanded = expand_macro_single (macro, o->data);
3863
3864 if (expanded)
3865 {
3866 if (o != *stat_obj_list)
3867 g_string_append (exec, " ");
3868
3869 g_string_append (exec, expanded);
3870 g_free (expanded);
3871 }
3872 }
3873 result = TRUE;
3874
3875 if (objs)
3876 objs = NULL;
3877 }
3878 break;
3879 case 's':
3880 break;
3881 case 'h':
3882 break;
3883 case 'i':
3884 break;
3885 case 'v':
3886 break;
3887 case 'w':
3888 expanded = g_get_current_dir ();
3889 g_string_append (exec, expanded);
3890 g_free (expanded);
3891 break;
3892 case 'u':
3893 case 'f':
3894 if (objs)
3895 {
3896 expanded = expand_macro_single (macro, objs->data);
3897
3898 if (expanded)
3899 {
3900 g_string_append (exec, expanded);
3901 g_free (expanded);
3902 }
3903 objs = objs->next;
3904 result = TRUE;
3905 }
3906
3907 break;
3908
3909 case 'U':
3910 case 'F':
3911 while (objs)
3912 {
3913 expanded = expand_macro_single (macro, objs->data);
3914
3915 if (expanded)
3916 {
3917 g_string_append (exec, expanded);
3918 g_free (expanded);
3919 }
3920
3921 objs = objs->next;
3922 result = TRUE;
3923
3924 if (objs != NULL && expanded)
3925 g_string_append_c (exec, ' ');
3926 }
3927
3928 break;
3929
3930 case 'c':
3931 if (info->app && info->app->localized_pretty_name_u8)
3932 {
3933 expanded = g_shell_quote (info->app->localized_pretty_name_u8);
3934 g_string_append (exec, expanded);
3935 g_free (expanded);
3936 }
3937 break;
3938
3939 case 'm': /* deprecated */
3940 case 'n': /* deprecated */
3941 case 'N': /* deprecated */
3942 /*case 'd': *//* deprecated */
3943 case 'D': /* deprecated */
3944 break;
3945
3946 case '%':
3947 g_string_append_c (exec, '%');
3948 break;
3949 }
3950
3951 *obj_list = objs;
3952
3953 return result;
3954 }
3955
3956 static gboolean
expand_application_parameters(GWin32AppInfo * info,const gchar * exec_line,GList ** objs,int * argc,char *** argv,GError ** error)3957 expand_application_parameters (GWin32AppInfo *info,
3958 const gchar *exec_line,
3959 GList **objs,
3960 int *argc,
3961 char ***argv,
3962 GError **error)
3963 {
3964 GList *obj_list = *objs;
3965 GList **stat_obj_list = objs;
3966 const char *p = exec_line;
3967 GString *expanded_exec;
3968 gboolean res;
3969 gchar *a_char;
3970
3971 if (exec_line == NULL)
3972 {
3973 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3974 P_("Application registry did not specify"
3975 " a shell\\open\\command"));
3976 return FALSE;
3977 }
3978
3979 expanded_exec = g_string_new (NULL);
3980 res = FALSE;
3981
3982 while (*p)
3983 {
3984 if (p[0] == '%' && p[1] != '\0')
3985 {
3986 if (expand_macro (p[1],
3987 expanded_exec,
3988 info, stat_obj_list,
3989 objs))
3990 res = TRUE;
3991
3992 p++;
3993 }
3994 else
3995 g_string_append_c (expanded_exec, *p);
3996
3997 p++;
3998 }
3999
4000 /* No file substitutions */
4001 if (obj_list == *objs && obj_list != NULL && !res)
4002 {
4003 /* If there is no macro default to %f. This is also what KDE does */
4004 g_string_append_c (expanded_exec, ' ');
4005 expand_macro ('f', expanded_exec, info, stat_obj_list, objs);
4006 }
4007
4008 /* Replace '\\' with '/', because g_shell_parse_argv considers them
4009 * to be escape sequences.
4010 */
4011 for (a_char = expanded_exec->str;
4012 a_char <= &expanded_exec->str[expanded_exec->len];
4013 a_char++)
4014 {
4015 if (*a_char == '\\')
4016 *a_char = '/';
4017 }
4018
4019 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
4020 g_string_free (expanded_exec, TRUE);
4021 return res;
4022 }
4023
4024
4025 static gchar *
get_appath_for_exe(gunichar2 * exe_basename)4026 get_appath_for_exe (gunichar2 *exe_basename)
4027 {
4028 GWin32RegistryKey *apppath_key = NULL;
4029 GWin32RegistryValueType val_type;
4030 gunichar2 *appath = NULL;
4031 gboolean got_value;
4032 gchar *result = NULL;
4033
4034 apppath_key = _g_win32_registry_key_build_and_new_w (NULL, L"HKEY_LOCAL_MACHINE\\"
4035 L"\\SOFTWARE"
4036 L"\\Microsoft"
4037 L"\\Windows"
4038 L"\\CurrentVersion"
4039 L"\\App Paths\\",
4040 exe_basename, NULL);
4041
4042 if (apppath_key == NULL)
4043 return NULL;
4044
4045 got_value = g_win32_registry_key_get_value_w (apppath_key,
4046 TRUE,
4047 L"Path",
4048 &val_type,
4049 (void **) &appath,
4050 NULL,
4051 NULL);
4052
4053 g_object_unref (apppath_key);
4054
4055 if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR)
4056 result = g_utf16_to_utf8 (appath, -1, NULL,NULL, NULL);
4057
4058 g_clear_pointer (&appath, g_free);
4059
4060 return result;
4061 }
4062
4063
4064 static gboolean
g_win32_app_info_launch_internal(GWin32AppInfo * info,GList * objs,GAppLaunchContext * launch_context,GSpawnFlags spawn_flags,GError ** error)4065 g_win32_app_info_launch_internal (GWin32AppInfo *info,
4066 GList *objs,
4067 GAppLaunchContext *launch_context,
4068 GSpawnFlags spawn_flags,
4069 GError **error)
4070 {
4071 gboolean completed = FALSE;
4072 char **argv, **envp;
4073 int argc;
4074 gchar *command;
4075 gchar *apppath;
4076 gunichar2 *exe_basename;
4077
4078 g_return_val_if_fail (info != NULL, FALSE);
4079 g_return_val_if_fail (info->app != NULL, FALSE);
4080
4081 argv = NULL;
4082
4083 if (launch_context)
4084 envp = g_app_launch_context_get_environment (launch_context);
4085 else
4086 envp = g_get_environ ();
4087
4088 command = NULL;
4089 exe_basename = NULL;
4090
4091 if (info->handler)
4092 {
4093 if (info->handler->handler_command)
4094 {
4095 command = g_utf16_to_utf8 (info->handler->handler_command,
4096 -1,
4097 NULL,
4098 NULL,
4099 NULL);
4100 exe_basename = g_utf8_to_utf16 (info->handler->executable_basename,
4101 -1,
4102 NULL,
4103 NULL,
4104 NULL);
4105 }
4106 else if (info->handler->proxy_command)
4107 {
4108 command = g_utf16_to_utf8 (info->handler->proxy_command,
4109 -1,
4110 NULL,
4111 NULL,
4112 NULL);
4113 exe_basename = g_utf8_to_utf16 (info->handler->executable_basename,
4114 -1,
4115 NULL,
4116 NULL,
4117 NULL);
4118 }
4119 }
4120
4121 if (command == NULL)
4122 {
4123 command = g_strdup (info->app->command_u8);
4124 exe_basename = g_utf8_to_utf16 (info->app->executable_basename,
4125 -1,
4126 NULL,
4127 NULL,
4128 NULL);
4129 }
4130
4131 apppath = get_appath_for_exe (exe_basename);
4132
4133 g_free (exe_basename);
4134
4135 if (apppath)
4136 {
4137 gchar **p;
4138 gint p_index;
4139
4140 for (p = envp, p_index = 0; p[0]; p++, p_index++)
4141 if ((p[0][0] == 'p' || p[0][0] == 'P') &&
4142 (p[0][1] == 'a' || p[0][1] == 'A') &&
4143 (p[0][2] == 't' || p[0][2] == 'T') &&
4144 (p[0][3] == 'h' || p[0][3] == 'H') &&
4145 (p[0][4] == '='))
4146 break;
4147
4148 if (p[0] == NULL)
4149 {
4150 gchar **new_envp;
4151 new_envp = g_new (char *, g_strv_length (envp) + 2);
4152 new_envp[0] = g_strdup_printf ("PATH=%s", apppath);
4153
4154 for (p_index = 0; p_index <= g_strv_length (envp); p_index++)
4155 new_envp[1 + p_index] = envp[p_index];
4156
4157 g_free (envp);
4158 envp = new_envp;
4159 }
4160 else
4161 {
4162 gchar *p_path;
4163
4164 p_path = &p[0][5];
4165
4166 if (p_path[0] != '\0')
4167 envp[p_index] = g_strdup_printf ("PATH=%s%c%s",
4168 apppath,
4169 G_SEARCHPATH_SEPARATOR,
4170 p_path);
4171 else
4172 envp[p_index] = g_strdup_printf ("PATH=%s", apppath);
4173
4174 g_free (&p_path[-5]);
4175 }
4176 }
4177
4178 do
4179 {
4180 GPid pid;
4181
4182 if (!expand_application_parameters (info,
4183 command,
4184 &objs,
4185 &argc,
4186 &argv,
4187 error))
4188 goto out;
4189
4190 if (!g_spawn_async (NULL,
4191 argv,
4192 envp,
4193 spawn_flags,
4194 NULL,
4195 NULL,
4196 &pid,
4197 error))
4198 goto out;
4199
4200 if (launch_context != NULL)
4201 {
4202 GVariantBuilder builder;
4203 GVariant *platform_data;
4204
4205 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
4206 g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gint32) pid));
4207
4208 platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
4209 g_signal_emit_by_name (launch_context, "launched", info, platform_data);
4210 g_variant_unref (platform_data);
4211 }
4212
4213 g_strfreev (argv);
4214 argv = NULL;
4215 }
4216 while (objs != NULL);
4217
4218 completed = TRUE;
4219
4220 out:
4221 g_strfreev (argv);
4222 g_strfreev (envp);
4223 g_free (command);
4224
4225 return completed;
4226 }
4227
4228 static void
free_file_or_uri(gpointer ptr)4229 free_file_or_uri (gpointer ptr)
4230 {
4231 file_or_uri *obj = ptr;
4232 g_free (obj->file);
4233 g_free (obj->uri);
4234 g_free (obj);
4235 }
4236
4237
4238 static gboolean
g_win32_app_supports_uris(GWin32AppInfoApplication * app)4239 g_win32_app_supports_uris (GWin32AppInfoApplication *app)
4240 {
4241 gssize num_of_uris_supported;
4242
4243 if (app == NULL)
4244 return FALSE;
4245
4246 num_of_uris_supported = (gssize) g_hash_table_size (app->supported_urls);
4247
4248 if (g_hash_table_lookup (app->supported_urls, "file"))
4249 num_of_uris_supported -= 1;
4250
4251 return num_of_uris_supported > 0;
4252 }
4253
4254
4255 static gboolean
g_win32_app_info_supports_uris(GAppInfo * appinfo)4256 g_win32_app_info_supports_uris (GAppInfo *appinfo)
4257 {
4258 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4259
4260 if (info->app == NULL)
4261 return FALSE;
4262
4263 return g_win32_app_supports_uris (info->app);
4264 }
4265
4266
4267 static gboolean
g_win32_app_info_supports_files(GAppInfo * appinfo)4268 g_win32_app_info_supports_files (GAppInfo *appinfo)
4269 {
4270 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4271
4272 if (info->app == NULL)
4273 return FALSE;
4274
4275 return g_hash_table_size (info->app->supported_exts) > 0;
4276 }
4277
4278
4279 static gboolean
g_win32_app_info_launch_uris(GAppInfo * appinfo,GList * uris,GAppLaunchContext * launch_context,GError ** error)4280 g_win32_app_info_launch_uris (GAppInfo *appinfo,
4281 GList *uris,
4282 GAppLaunchContext *launch_context,
4283 GError **error)
4284 {
4285 gboolean res = FALSE;
4286 gboolean do_files;
4287 GList *objs;
4288
4289 do_files = g_win32_app_info_supports_files (appinfo);
4290
4291 objs = NULL;
4292 while (uris)
4293 {
4294 file_or_uri *obj;
4295 obj = g_new0 (file_or_uri, 1);
4296
4297 if (do_files)
4298 {
4299 GFile *file;
4300 gchar *path;
4301
4302 file = g_file_new_for_uri (uris->data);
4303 path = g_file_get_path (file);
4304 obj->file = path;
4305 g_object_unref (file);
4306 }
4307
4308 obj->uri = g_strdup (uris->data);
4309
4310 objs = g_list_prepend (objs, obj);
4311 uris = uris->next;
4312 }
4313
4314 objs = g_list_reverse (objs);
4315
4316 res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
4317 objs,
4318 launch_context,
4319 G_SPAWN_SEARCH_PATH,
4320 error);
4321
4322 g_list_free_full (objs, free_file_or_uri);
4323
4324 return res;
4325 }
4326
4327 static gboolean
g_win32_app_info_launch(GAppInfo * appinfo,GList * files,GAppLaunchContext * launch_context,GError ** error)4328 g_win32_app_info_launch (GAppInfo *appinfo,
4329 GList *files,
4330 GAppLaunchContext *launch_context,
4331 GError **error)
4332 {
4333 gboolean res = FALSE;
4334 gboolean do_uris;
4335 GList *objs;
4336
4337 do_uris = g_win32_app_info_supports_uris (appinfo);
4338
4339 objs = NULL;
4340 while (files)
4341 {
4342 file_or_uri *obj;
4343 obj = g_new0 (file_or_uri, 1);
4344 obj->file = g_file_get_path (G_FILE (files->data));
4345
4346 if (do_uris)
4347 obj->uri = g_file_get_uri (G_FILE (files->data));
4348
4349 objs = g_list_prepend (objs, obj);
4350 files = files->next;
4351 }
4352
4353 objs = g_list_reverse (objs);
4354
4355 res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
4356 objs,
4357 launch_context,
4358 G_SPAWN_SEARCH_PATH,
4359 error);
4360
4361 g_list_free_full (objs, free_file_or_uri);
4362
4363 return res;
4364 }
4365
4366 static const char **
g_win32_app_info_get_supported_types(GAppInfo * appinfo)4367 g_win32_app_info_get_supported_types (GAppInfo *appinfo)
4368 {
4369 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4370
4371 return (const char**) info->supported_types;
4372 }
4373
4374 GAppInfo *
g_app_info_create_from_commandline(const char * commandline,const char * application_name,GAppInfoCreateFlags flags,GError ** error)4375 g_app_info_create_from_commandline (const char *commandline,
4376 const char *application_name,
4377 GAppInfoCreateFlags flags,
4378 GError **error)
4379 {
4380 GWin32AppInfo *info;
4381 GWin32AppInfoApplication *app;
4382
4383 g_return_val_if_fail (commandline, NULL);
4384
4385 info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
4386
4387 app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
4388
4389 if (application_name)
4390 {
4391 app->canonical_name = g_utf8_to_utf16 (application_name,
4392 -1,
4393 NULL,
4394 NULL,
4395 NULL);
4396 app->canonical_name_u8 = g_strdup (application_name);
4397 app->canonical_name_folded = g_utf8_casefold (application_name, -1);
4398 }
4399
4400 app->command = g_utf8_to_utf16 (commandline, -1, NULL, NULL, NULL);
4401 app->command_u8 = g_strdup (commandline);
4402
4403 extract_executable (app->command,
4404 &app->executable,
4405 &app->executable_basename,
4406 &app->executable_folded,
4407 NULL);
4408
4409 app->no_open_with = FALSE;
4410 app->user_specific = FALSE;
4411 app->default_app = FALSE;
4412
4413 info->app = app;
4414 info->handler = NULL;
4415
4416 return G_APP_INFO (info);
4417 }
4418
4419 /* GAppInfo interface init */
4420
4421 static void
g_win32_app_info_iface_init(GAppInfoIface * iface)4422 g_win32_app_info_iface_init (GAppInfoIface *iface)
4423 {
4424 iface->dup = g_win32_app_info_dup;
4425 iface->equal = g_win32_app_info_equal;
4426 iface->get_id = g_win32_app_info_get_id;
4427 iface->get_name = g_win32_app_info_get_name;
4428 iface->get_description = g_win32_app_info_get_description;
4429 iface->get_executable = g_win32_app_info_get_executable;
4430 iface->get_icon = g_win32_app_info_get_icon;
4431 iface->launch = g_win32_app_info_launch;
4432 iface->supports_uris = g_win32_app_info_supports_uris;
4433 iface->supports_files = g_win32_app_info_supports_files;
4434 iface->launch_uris = g_win32_app_info_launch_uris;
4435 /* iface->should_show = g_win32_app_info_should_show;*/
4436 /* iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
4437 /* iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
4438 /* iface->add_supports_type = g_win32_app_info_add_supports_type;*/
4439 /* iface->can_remove_supports_type = g_win32_app_info_can_remove_supports_type;*/
4440 /* iface->remove_supports_type = g_win32_app_info_remove_supports_type;*/
4441 /* iface->can_delete = g_win32_app_info_can_delete;*/
4442 /* iface->do_delete = g_win32_app_info_delete;*/
4443 iface->get_commandline = g_win32_app_info_get_commandline;
4444 iface->get_display_name = g_win32_app_info_get_display_name;
4445 /* iface->set_as_last_used_for_type = g_win32_app_info_set_as_last_used_for_type;*/
4446 iface->get_supported_types = g_win32_app_info_get_supported_types;
4447 }
4448
4449 GAppInfo *
g_app_info_get_default_for_uri_scheme(const char * uri_scheme)4450 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
4451 {
4452 GWin32AppInfoURLSchema *scheme;
4453 char *scheme_down;
4454 GAppInfo *result;
4455
4456 scheme_down = g_utf8_casefold (uri_scheme, -1);
4457
4458 if (!scheme_down)
4459 return NULL;
4460
4461 if (strcmp (scheme_down, "file") == 0)
4462 {
4463 g_free (scheme_down);
4464 return NULL;
4465 }
4466
4467 g_win32_appinfo_init ();
4468 G_LOCK (gio_win32_appinfo);
4469
4470 scheme = g_hash_table_lookup (urls, scheme_down);
4471 g_free (scheme_down);
4472
4473 if (scheme)
4474 g_object_ref (scheme);
4475
4476 G_UNLOCK (gio_win32_appinfo);
4477
4478 result = NULL;
4479
4480 if (scheme != NULL &&
4481 scheme->chosen_handler != NULL &&
4482 scheme->chosen_handler->app != NULL)
4483 result = g_win32_app_info_new_from_app (scheme->chosen_handler->app,
4484 scheme->chosen_handler);
4485
4486 if (scheme)
4487 g_object_unref (scheme);
4488
4489 return result;
4490 }
4491
4492 GAppInfo *
g_app_info_get_default_for_type(const char * content_type,gboolean must_support_uris)4493 g_app_info_get_default_for_type (const char *content_type,
4494 gboolean must_support_uris)
4495 {
4496 GWin32AppInfoFileExtension *ext;
4497 char *ext_down;
4498 GWin32AppInfoHandler *handler;
4499 GAppInfo *result;
4500 GWin32AppInfoApplication *app;
4501 GHashTableIter iter;
4502
4503 ext_down = g_utf8_casefold (content_type, -1);
4504
4505 if (!ext_down)
4506 return NULL;
4507
4508 g_win32_appinfo_init ();
4509 G_LOCK (gio_win32_appinfo);
4510
4511 /* Assuming that "content_type" is a file extension, not a MIME type */
4512 ext = g_hash_table_lookup (extensions, ext_down);
4513 g_free (ext_down);
4514
4515 result = NULL;
4516
4517 if (ext != NULL)
4518 g_object_ref (ext);
4519
4520 G_UNLOCK (gio_win32_appinfo);
4521
4522 if (ext != NULL)
4523 {
4524 if (ext->chosen_handler != NULL &&
4525 ext->chosen_handler->app != NULL &&
4526 (!must_support_uris ||
4527 g_win32_app_supports_uris (ext->chosen_handler->app)))
4528 result = g_win32_app_info_new_from_app (ext->chosen_handler->app,
4529 ext->chosen_handler);
4530 else
4531 {
4532 g_hash_table_iter_init (&iter, ext->handlers);
4533
4534 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
4535 {
4536 if (handler->app &&
4537 (!must_support_uris ||
4538 g_win32_app_supports_uris (ext->chosen_handler->app)))
4539 {
4540 result = g_win32_app_info_new_from_app (handler->app, handler);
4541 break;
4542 }
4543 }
4544
4545 if (result == NULL)
4546 {
4547 g_hash_table_iter_init (&iter, ext->other_apps);
4548 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
4549 {
4550 if (!must_support_uris ||
4551 g_win32_app_supports_uris (ext->chosen_handler->app))
4552 {
4553 result = g_win32_app_info_new_from_app (app, NULL);
4554 break;
4555 }
4556 }
4557 }
4558 }
4559 g_object_unref (ext);
4560 }
4561
4562 return result;
4563 }
4564
4565 GList *
g_app_info_get_all(void)4566 g_app_info_get_all (void)
4567 {
4568 GHashTableIter iter;
4569 gpointer value;
4570 GList *infos;
4571 GList *apps;
4572 GList *apps_i;
4573
4574 g_win32_appinfo_init ();
4575 G_LOCK (gio_win32_appinfo);
4576
4577 apps = NULL;
4578 g_hash_table_iter_init (&iter, apps_by_id);
4579 while (g_hash_table_iter_next (&iter, NULL, &value))
4580 apps = g_list_prepend (apps, g_object_ref (G_OBJECT (value)));
4581
4582 G_UNLOCK (gio_win32_appinfo);
4583
4584 infos = NULL;
4585 for (apps_i = apps; apps_i; apps_i = apps_i->next)
4586 infos = g_list_prepend (infos,
4587 g_win32_app_info_new_from_app (apps_i->data, NULL));
4588
4589 g_list_free_full (apps, g_object_unref);
4590
4591 return infos;
4592 }
4593
4594 GList *
g_app_info_get_all_for_type(const char * content_type)4595 g_app_info_get_all_for_type (const char *content_type)
4596 {
4597 GWin32AppInfoFileExtension *ext;
4598 char *ext_down;
4599 GWin32AppInfoHandler *handler;
4600 GWin32AppInfoApplication *app;
4601 GHashTableIter iter;
4602 GList *result;
4603
4604 ext_down = g_utf8_casefold (content_type, -1);
4605
4606 if (!ext_down)
4607 return NULL;
4608
4609 g_win32_appinfo_init ();
4610 G_LOCK (gio_win32_appinfo);
4611
4612 /* Assuming that "content_type" is a file extension, not a MIME type */
4613 ext = g_hash_table_lookup (extensions, ext_down);
4614 g_free (ext_down);
4615
4616 result = NULL;
4617
4618 if (ext != NULL)
4619 g_object_ref (ext);
4620
4621 G_UNLOCK (gio_win32_appinfo);
4622
4623 if (ext == NULL)
4624 return NULL;
4625
4626 if (ext->chosen_handler != NULL &&
4627 ext->chosen_handler->app != NULL)
4628 result = g_list_prepend (result,
4629 g_win32_app_info_new_from_app (ext->chosen_handler->app,
4630 ext->chosen_handler));
4631
4632 g_hash_table_iter_init (&iter, ext->handlers);
4633
4634 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
4635 {
4636 if (handler->app &&
4637 (ext->chosen_handler == NULL || ext->chosen_handler->app != handler->app))
4638 result = g_list_prepend (result,
4639 g_win32_app_info_new_from_app (handler->app,
4640 handler));
4641 }
4642
4643 g_hash_table_iter_init (&iter, ext->other_apps);
4644
4645 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
4646 {
4647 result = g_list_prepend (result, g_win32_app_info_new_from_app (app, NULL));
4648 }
4649
4650 g_object_unref (ext);
4651
4652 result = g_list_reverse (result);
4653
4654 return result;
4655 }
4656
4657 GList *
g_app_info_get_fallback_for_type(const gchar * content_type)4658 g_app_info_get_fallback_for_type (const gchar *content_type)
4659 {
4660 /* TODO: fix this once gcontenttype support is improved */
4661 return g_app_info_get_all_for_type (content_type);
4662 }
4663
4664 GList *
g_app_info_get_recommended_for_type(const gchar * content_type)4665 g_app_info_get_recommended_for_type (const gchar *content_type)
4666 {
4667 /* TODO: fix this once gcontenttype support is improved */
4668 return g_app_info_get_all_for_type (content_type);
4669 }
4670
4671 void
g_app_info_reset_type_associations(const char * content_type)4672 g_app_info_reset_type_associations (const char *content_type)
4673 {
4674 /* nothing to do */
4675 }
4676