Home | History | Annotate | Download | only in gio
      1 /* GIO - GLib Input, Output and Streaming Library
      2  *
      3  * Copyright (C) 2006-2007 Red Hat, Inc.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Lesser General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Lesser General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Lesser General
     16  * Public License along with this library; if not, write to the
     17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
     18  * Boston, MA 02111-1307, USA.
     19  *
     20  * Author: Alexander Larsson <alexl (at) redhat.com>
     21  */
     22 
     23 #include "config.h"
     24 
     25 #include <string.h>
     26 
     27 #include "gcontenttypeprivate.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 
     35 #include <windows.h>
     36 #include <shlwapi.h>
     37 
     38 #include "gioalias.h"
     39 
     40 #ifndef ASSOCF_INIT_BYEXENAME
     41 #define ASSOCF_INIT_BYEXENAME 0x00000002
     42 #endif
     43 
     44 /* These were wrong in MingW */
     45 #define REAL_ASSOCSTR_COMMAND 1
     46 #define REAL_ASSOCSTR_EXECUTABLE 2
     47 #define REAL_ASSOCSTR_FRIENDLYDOCNAME 3
     48 #define REAL_ASSOCSTR_FRIENDLYAPPNAME 4
     49 
     50 #ifndef AssocQueryString
     51 #pragma message("AssocQueryString not available with SDK used")
     52 #endif
     53 
     54 static void g_win32_app_info_iface_init (GAppInfoIface *iface);
     55 
     56 struct _GWin32AppInfo
     57 {
     58   GObject parent_instance;
     59   wchar_t *id;
     60   char *id_utf8;
     61   gboolean id_is_exename;
     62   char *executable;
     63   char *name;
     64   gboolean no_open_with;
     65 };
     66 
     67 G_DEFINE_TYPE_WITH_CODE (GWin32AppInfo, g_win32_app_info, G_TYPE_OBJECT,
     68 			 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
     69 						g_win32_app_info_iface_init))
     70 
     71 
     72 static void
     73 g_win32_app_info_finalize (GObject *object)
     74 {
     75   GWin32AppInfo *info;
     76 
     77   info = G_WIN32_APP_INFO (object);
     78 
     79   g_free (info->id);
     80   g_free (info->id_utf8);
     81   g_free (info->name);
     82   g_free (info->executable);
     83 
     84   G_OBJECT_CLASS (g_win32_app_info_parent_class)->finalize (object);
     85 }
     86 
     87 static void
     88 g_win32_app_info_class_init (GWin32AppInfoClass *klass)
     89 {
     90   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     91 
     92   gobject_class->finalize = g_win32_app_info_finalize;
     93 }
     94 
     95 static void
     96 g_win32_app_info_init (GWin32AppInfo *local)
     97 {
     98 }
     99 
    100 static GAppInfo *
    101 g_desktop_app_info_new_from_id (wchar_t *id /* takes ownership */,
    102 				gboolean id_is_exename)
    103 {
    104 #ifdef AssocQueryString
    105   ASSOCF flags;
    106 #endif
    107   wchar_t buffer[1024];
    108   DWORD buffer_size;
    109   GWin32AppInfo *info;
    110   HKEY app_key;
    111 
    112   info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
    113   info->id = id; /* Takes ownership */
    114   info->id_utf8 = g_utf16_to_utf8 (id, -1, NULL, NULL, NULL);
    115   info->id_is_exename = id_is_exename;
    116 
    117 #ifdef AssocQueryString
    118   flags = 0;
    119   if (id_is_exename)
    120     flags |= ASSOCF_INIT_BYEXENAME;
    121 
    122   buffer_size = 1024;
    123   if (AssocQueryStringW(flags,
    124 			REAL_ASSOCSTR_EXECUTABLE,
    125 			id,
    126 			NULL,
    127 			buffer,
    128 			&buffer_size) == S_OK)
    129     info->executable = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
    130 
    131   buffer_size = 1024;
    132   if (AssocQueryStringW(flags,
    133 			REAL_ASSOCSTR_FRIENDLYAPPNAME,
    134 			id,
    135 			NULL,
    136 			buffer,
    137 			&buffer_size) == S_OK)
    138     info->name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
    139 #endif
    140 
    141   if (info->name == NULL)
    142     {
    143       /* TODO: Should look up name from executable resources */
    144       if (info->executable)
    145 	info->name = g_path_get_basename (info->executable);
    146       else
    147 	info->name = g_strdup (info->id_utf8);
    148     }
    149 
    150 #ifdef AssocQueryString
    151   if (AssocQueryKeyW(flags,
    152 		     ASSOCKEY_APP,
    153 		     info->id,
    154 		     NULL,
    155 		     &app_key) == S_OK)
    156     {
    157       if (RegQueryValueExW (app_key, L"NoOpenWith", 0,
    158 			    NULL, NULL, NULL) == ERROR_SUCCESS)
    159 	info->no_open_with = TRUE;
    160       RegCloseKey (app_key);
    161     }
    162 #endif
    163 
    164   return G_APP_INFO (info);
    165 }
    166 
    167 static wchar_t *
    168 dup_wstring (wchar_t *str)
    169 {
    170   gsize len;
    171   for (len = 0; str[len] != 0; len++)
    172     ;
    173   return (wchar_t *)g_memdup (str, (len + 1) * 2);
    174 }
    175 
    176 static GAppInfo *
    177 g_win32_app_info_dup (GAppInfo *appinfo)
    178 {
    179   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
    180   GWin32AppInfo *new_info;
    181 
    182   new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
    183 
    184   new_info->id = dup_wstring (info->id);
    185   new_info->id_utf8 = g_strdup (info->id_utf8);
    186   new_info->id_is_exename = info->id_is_exename;
    187   new_info->name = g_strdup (info->name);
    188   new_info->executable = g_strdup (info->executable);
    189   new_info->no_open_with = info->no_open_with;
    190 
    191   return G_APP_INFO (new_info);
    192 }
    193 
    194 static gboolean
    195 g_win32_app_info_equal (GAppInfo *appinfo1,
    196                         GAppInfo *appinfo2)
    197 {
    198   GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
    199   GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
    200 
    201   if (info1->executable == NULL ||
    202       info2->executable == NULL)
    203     return FALSE;
    204 
    205   return strcmp (info1->executable, info2->executable) == 0;
    206 }
    207 
    208 static const char *
    209 g_win32_app_info_get_id (GAppInfo *appinfo)
    210 {
    211   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
    212 
    213   return info->id_utf8;
    214 }
    215 
    216 static const char *
    217 g_win32_app_info_get_name (GAppInfo *appinfo)
    218 {
    219   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
    220 
    221   if (info->name == NULL)
    222     return _("Unnamed");
    223 
    224   return info->name;
    225 }
    226 
    227 static const char *
    228 g_win32_app_info_get_description (GAppInfo *appinfo)
    229 {
    230   /* Win32 has no app descriptions */
    231   return NULL;
    232 }
    233 
    234 static const char *
    235 g_win32_app_info_get_executable (GAppInfo *appinfo)
    236 {
    237   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
    238 
    239   return info->executable;
    240 }
    241 
    242 static GIcon *
    243 g_win32_app_info_get_icon (GAppInfo *appinfo)
    244 {
    245   /* GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo); */
    246 
    247   /* TODO: How to handle icons */
    248   return NULL;
    249 }
    250 
    251 static gboolean
    252 g_win32_app_info_launch (GAppInfo           *appinfo,
    253 			 GList              *files,
    254 			 GAppLaunchContext  *launch_context,
    255 			 GError            **error)
    256 {
    257   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
    258 #ifdef AssocQueryString
    259   ASSOCF flags;
    260 #endif
    261   HKEY class_key;
    262   SHELLEXECUTEINFOW exec_info = {0};
    263   GList *l;
    264 
    265   /* TODO:  What might startup_id mean on win32? */
    266 #ifdef AssocQueryString
    267   flags = 0;
    268   if (info->id_is_exename)
    269     flags |= ASSOCF_INIT_BYEXENAME;
    270 
    271   if (AssocQueryKeyW (flags,
    272 		      ASSOCKEY_SHELLEXECCLASS,
    273 		      info->id,
    274 		      NULL,
    275 		      &class_key) != S_OK)
    276     {
    277       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Can't find application"));
    278       return FALSE;
    279     }
    280 #endif
    281 
    282   for (l = files; l != NULL; l = l->next)
    283     {
    284       char *path = g_file_get_path (l->data);
    285       wchar_t *wfilename = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
    286 
    287       g_free (path);
    288 
    289       memset (&exec_info, 0, sizeof (exec_info));
    290       exec_info.cbSize = sizeof (exec_info);
    291       exec_info.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_CLASSKEY;
    292       exec_info.lpFile = wfilename;
    293       exec_info.nShow = SW_SHOWNORMAL;
    294       exec_info.hkeyClass = class_key;
    295 
    296       if (!ShellExecuteExW (&exec_info))
    297 	{
    298 	  char *message_utf8 = g_win32_error_message (GetLastError ());
    299 
    300 	  g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Error launching application: %s"), message_utf8);
    301 	  g_free (message_utf8);
    302 
    303 	  g_free (wfilename);
    304 	  RegCloseKey (class_key);
    305 	  return FALSE;
    306 	}
    307 
    308       g_free (wfilename);
    309     }
    310 
    311   RegCloseKey (class_key);
    312 
    313   return TRUE;
    314 }
    315 
    316 static gboolean
    317 g_win32_app_info_supports_uris (GAppInfo *appinfo)
    318 {
    319   return FALSE;
    320 }
    321 
    322 static gboolean
    323 g_win32_app_info_supports_files (GAppInfo *appinfo)
    324 {
    325   return TRUE;
    326 }
    327 
    328 static gboolean
    329 g_win32_app_info_launch_uris (GAppInfo           *appinfo,
    330 			      GList              *uris,
    331 			      GAppLaunchContext  *launch_context,
    332 			      GError            **error)
    333 {
    334   g_set_error_literal (error, G_IO_ERROR,
    335                        G_IO_ERROR_NOT_SUPPORTED,
    336                        _("URIs not supported"));
    337   return FALSE;
    338 }
    339 
    340 static gboolean
    341 g_win32_app_info_should_show (GAppInfo *appinfo)
    342 {
    343   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
    344 
    345   if (info->no_open_with)
    346     return FALSE;
    347 
    348   return TRUE;
    349 }
    350 
    351 static gboolean
    352 g_win32_app_info_set_as_default_for_type (GAppInfo    *appinfo,
    353                                           const char  *content_type,
    354                                           GError     **error)
    355 {
    356   g_set_error_literal (error, G_IO_ERROR,
    357                        G_IO_ERROR_NOT_SUPPORTED,
    358                        _("association changes not supported on win32"));
    359   return FALSE;
    360 }
    361 
    362 GAppInfo *
    363 g_app_info_create_from_commandline (const char           *commandline,
    364 				    const char           *application_name,
    365 				    GAppInfoCreateFlags   flags,
    366 				    GError              **error)
    367 {
    368   g_set_error_literal (error, G_IO_ERROR,
    369                        G_IO_ERROR_NOT_SUPPORTED,
    370                        _("Association creation not supported on win32"));
    371   return NULL;
    372 }
    373 
    374 
    375 static void
    376 g_win32_app_info_iface_init (GAppInfoIface *iface)
    377 {
    378   iface->dup = g_win32_app_info_dup;
    379   iface->equal = g_win32_app_info_equal;
    380   iface->get_id = g_win32_app_info_get_id;
    381   iface->get_name = g_win32_app_info_get_name;
    382   iface->get_description = g_win32_app_info_get_description;
    383   iface->get_executable = g_win32_app_info_get_executable;
    384   iface->get_icon = g_win32_app_info_get_icon;
    385   iface->launch = g_win32_app_info_launch;
    386   iface->supports_uris = g_win32_app_info_supports_uris;
    387   iface->supports_files = g_win32_app_info_supports_files;
    388   iface->launch_uris = g_win32_app_info_launch_uris;
    389   iface->should_show = g_win32_app_info_should_show;
    390   iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;
    391 }
    392 
    393 static void
    394 enumerate_open_with_list (HKEY    dir_key,
    395 			  GList **prognames)
    396 {
    397   DWORD index;
    398   wchar_t name[256];
    399   DWORD name_len, nbytes;
    400   wchar_t data[256];
    401   wchar_t *data_alloc;
    402   DWORD type;
    403 
    404   /* Must also look inside for a,b,c, + MRUList */
    405   index = 0;
    406   name_len = 256;
    407   nbytes = sizeof (data) - 2;
    408   while (RegEnumValueW (dir_key,
    409 		        index,
    410 		        name,
    411 		        &name_len,
    412 		        0,
    413 		        &type,
    414 		        (LPBYTE)data,
    415 		        &nbytes) == ERROR_SUCCESS)
    416     {
    417       data[nbytes/2] = '\0';
    418       if (type == REG_SZ &&
    419 	  /* Ignore things like MRUList, just look at 'a', 'b', 'c', etc */
    420 	  name_len == 1)
    421 	{
    422 	  data_alloc = (wchar_t *)g_memdup (data, nbytes + 2);
    423 	  data_alloc[nbytes/2] = 0;
    424 	  *prognames = g_list_prepend (*prognames, data_alloc);
    425 	}
    426       index++;
    427       name_len = 256;
    428       nbytes = sizeof (data) - 2;
    429     }
    430 
    431   index = 0;
    432   name_len = 256;
    433   while (RegEnumKeyExW (dir_key,
    434 		        index,
    435 		        name,
    436 		        &name_len,
    437 		        NULL,
    438 		        NULL,
    439 		        NULL,
    440 		        NULL) == ERROR_SUCCESS)
    441     {
    442       *prognames = g_list_prepend (*prognames, g_memdup (name, (name_len + 1) * 2));
    443       index++;
    444       name_len = 256;
    445     }
    446 }
    447 
    448 static void
    449 enumerate_open_with_progids (HKEY dir_key,
    450 			     GList **progids)
    451 {
    452   DWORD index;
    453   wchar_t name[256];
    454   DWORD name_len, type;
    455 
    456   index = 0;
    457   name_len = 256;
    458   while (RegEnumValueW (dir_key,
    459 		        index,
    460 		        name,
    461 		        &name_len,
    462 		        0,
    463 		        &type,
    464 		        NULL,
    465 		        0) == ERROR_SUCCESS)
    466     {
    467       *progids = g_list_prepend (*progids, g_memdup (name, (name_len + 1) * 2));
    468       index++;
    469       name_len = 256;
    470     }
    471 }
    472 
    473 static void
    474 enumerate_open_with_root (HKEY    dir_key,
    475 			  GList **progids,
    476 			  GList **prognames)
    477 {
    478   HKEY reg_key = NULL;
    479 
    480   if (RegOpenKeyExW (dir_key, L"OpenWithList", 0,
    481 		     KEY_READ, &reg_key) == ERROR_SUCCESS)
    482     {
    483       enumerate_open_with_list (reg_key, prognames);
    484       RegCloseKey (reg_key);
    485     }
    486 
    487   if (RegOpenKeyExW (dir_key, L"OpenWithProgids", 0,
    488 		     KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS)
    489     {
    490       enumerate_open_with_progids (reg_key, progids);
    491       RegCloseKey (reg_key);
    492     }
    493 }
    494 
    495 static gboolean
    496 app_info_in_list (GAppInfo *info,
    497                   GList    *list)
    498 {
    499   while (list != NULL)
    500     {
    501       if (g_app_info_equal (info, list->data))
    502 	return TRUE;
    503       list = list->next;
    504     }
    505   return FALSE;
    506 }
    507 
    508 GList *
    509 g_app_info_get_all_for_type (const char *content_type)
    510 {
    511   GList *progids = NULL;
    512   GList *prognames = NULL;
    513   HKEY reg_key, sys_file_assoc_key, reg_key2;
    514   wchar_t percieved_type[128];
    515   DWORD nchars, key_type;
    516   wchar_t *wc_key;
    517   GList *l;
    518   GList *infos;
    519 
    520   wc_key = g_utf8_to_utf16 (content_type, -1, NULL, NULL, NULL);
    521   if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
    522 		     KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS)
    523     {
    524       enumerate_open_with_root (reg_key, &progids, &prognames);
    525 
    526       nchars = sizeof (percieved_type) / sizeof(wchar_t);
    527       if (RegQueryValueExW (reg_key, L"PerceivedType", 0,
    528 			    &key_type, (LPBYTE) percieved_type, &nchars) == ERROR_SUCCESS)
    529 	{
    530 	  if (key_type == REG_SZ &&
    531 	      RegOpenKeyExW (HKEY_CLASSES_ROOT, L"SystemFileAssociations", 0,
    532 			     KEY_QUERY_VALUE, &sys_file_assoc_key) == ERROR_SUCCESS)
    533 	    {
    534 	      if (RegOpenKeyExW (sys_file_assoc_key, percieved_type, 0,
    535 				 KEY_QUERY_VALUE, &reg_key2) == ERROR_SUCCESS)
    536 		{
    537 		  enumerate_open_with_root (reg_key2, &progids, &prognames);
    538 		  RegCloseKey (reg_key2);
    539 		}
    540 
    541 	      RegCloseKey (sys_file_assoc_key);
    542 	    }
    543 	}
    544       RegCloseKey (reg_key);
    545     }
    546 
    547   if (RegOpenKeyExW (HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts", 0,
    548 		     KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS)
    549     {
    550       if (RegOpenKeyExW (reg_key, wc_key, 0,
    551 			 KEY_QUERY_VALUE, &reg_key2) == ERROR_SUCCESS)
    552 	{
    553 	  enumerate_open_with_root (reg_key2, &progids, &prognames);
    554 	  RegCloseKey (reg_key2);
    555 	}
    556 
    557       RegCloseKey (reg_key);
    558     }
    559 
    560   infos = NULL;
    561   for (l = prognames; l != NULL; l = l->next)
    562     {
    563       GAppInfo *info;
    564 
    565       /* l->data ownership is taken */
    566       info = g_desktop_app_info_new_from_id ((wchar_t *)l->data, TRUE);
    567       if (app_info_in_list (info, infos))
    568 	g_object_unref (info);
    569       else
    570 	infos = g_list_prepend (infos, info);
    571     }
    572   g_list_free (prognames);
    573 
    574   for (l = progids; l != NULL; l = l->next)
    575     {
    576       GAppInfo *info;
    577 
    578       /* l->data ownership is taken */
    579       info = g_desktop_app_info_new_from_id ((wchar_t *)l->data, FALSE);
    580       if (app_info_in_list (info, infos))
    581 	g_object_unref (info);
    582       else
    583 	infos = g_list_prepend (infos, info);
    584     }
    585   g_list_free (progids);
    586 
    587   g_free (wc_key);
    588   return g_list_reverse (infos);
    589 }
    590 
    591 GAppInfo *
    592 g_app_info_get_default_for_type (const char *content_type,
    593 				 gboolean    must_support_uris)
    594 {
    595   wchar_t *wtype;
    596   wchar_t buffer[1024];
    597   DWORD buffer_size;
    598 
    599   wtype = g_utf8_to_utf16 (content_type, -1, NULL, NULL, NULL);
    600 
    601   /* Verify that we have some sort of app registered for this type */
    602 #ifdef AssocQueryString
    603   buffer_size = 1024;
    604   if (AssocQueryStringW (0,
    605 		  	 REAL_ASSOCSTR_COMMAND,
    606 			 wtype,
    607 			 NULL,
    608 			 buffer,
    609 			 &buffer_size) == S_OK)
    610     /* Takes ownership of wtype */
    611     return g_desktop_app_info_new_from_id (wtype, FALSE);
    612 #endif
    613 
    614   g_free (wtype);
    615   return NULL;
    616 }
    617 
    618 GAppInfo *
    619 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
    620 {
    621   /* TODO: Implement */
    622   return NULL;
    623 }
    624 
    625 GList *
    626 g_app_info_get_all (void)
    627 {
    628   DWORD index;
    629   wchar_t name[256];
    630   DWORD name_len;
    631   HKEY reg_key;
    632   GList *infos;
    633   GAppInfo *info;
    634 
    635   if (RegOpenKeyExW (HKEY_CLASSES_ROOT, L"Applications", 0,
    636 		     KEY_READ, &reg_key) != ERROR_SUCCESS)
    637     return NULL;
    638 
    639   infos = NULL;
    640   index = 0;
    641   name_len = 256;
    642   while (RegEnumKeyExW (reg_key,
    643 		        index,
    644 		        name,
    645 		        &name_len,
    646 		        NULL,
    647 		        NULL,
    648 		        NULL,
    649 		        NULL) == ERROR_SUCCESS)
    650     {
    651       wchar_t *name_dup = g_memdup (name, (name_len+1)*2);
    652       /* name_dup ownership is taken */
    653       info = g_desktop_app_info_new_from_id (name_dup, TRUE);
    654       infos = g_list_prepend (infos, info);
    655 
    656       index++;
    657       name_len = 256;
    658     }
    659 
    660   RegCloseKey (reg_key);
    661 
    662   return g_list_reverse (infos);
    663 }
    664 
    665 void
    666 g_app_info_reset_type_associations (const char *content_type)
    667 {
    668   /* nothing to do */
    669 }
    670