mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-28 02:20:04 +01:00
Merge branch 'appinfo-rundll32' into 'master'
Add rundll32 support to GAppInfo Closes #1932 See merge request GNOME/glib!1259
This commit is contained in:
commit
adee3b31dc
447
gio/giowin32-private.c
Normal file
447
gio/giowin32-private.c
Normal file
@ -0,0 +1,447 @@
|
||||
/* giowin32-private.c - private glib-gio functions for W32 GAppInfo
|
||||
*
|
||||
* Copyright 2019 Руслан Ижбулатов
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
static gssize
|
||||
g_utf16_len (const gunichar2 *str)
|
||||
{
|
||||
gssize result;
|
||||
|
||||
for (result = 0; str[0] != 0; str++, result++)
|
||||
;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gunichar2 *
|
||||
g_wcsdup (const gunichar2 *str, gssize str_len)
|
||||
{
|
||||
gssize str_size;
|
||||
|
||||
g_return_val_if_fail (str != NULL, NULL);
|
||||
|
||||
if (str_len == -1)
|
||||
str_len = g_utf16_len (str);
|
||||
|
||||
g_assert (str_len <= G_MAXSIZE / sizeof (gunichar2) - 1);
|
||||
str_size = (str_len + 1) * sizeof (gunichar2);
|
||||
|
||||
return g_memdup (str, str_size);
|
||||
}
|
||||
|
||||
static const gunichar2 *
|
||||
g_utf16_wchr (const gunichar2 *str, const wchar_t wchr)
|
||||
{
|
||||
for (; str != NULL && str[0] != 0; str++)
|
||||
if ((wchar_t) str[0] == wchr)
|
||||
return str;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_utf16_to_utf8_and_fold (const gunichar2 *str,
|
||||
gssize length,
|
||||
gchar **str_u8,
|
||||
gchar **str_u8_folded)
|
||||
{
|
||||
gchar *u8;
|
||||
gchar *folded;
|
||||
u8 = g_utf16_to_utf8 (str, length, NULL, NULL, NULL);
|
||||
|
||||
if (u8 == NULL)
|
||||
return FALSE;
|
||||
|
||||
folded = g_utf8_casefold (u8, -1);
|
||||
|
||||
if (str_u8)
|
||||
*str_u8 = g_steal_pointer (&u8);
|
||||
|
||||
g_free (u8);
|
||||
|
||||
if (str_u8_folded)
|
||||
*str_u8_folded = g_steal_pointer (&folded);
|
||||
|
||||
g_free (folded);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Finds the last directory separator in @filename,
|
||||
* returns a pointer to the position after that separator.
|
||||
* If the string ends with a separator, returned value
|
||||
* will be pointing at the NUL terminator.
|
||||
* If the string does not contain separators, returns the
|
||||
* string itself.
|
||||
*/
|
||||
static const gunichar2 *
|
||||
g_utf16_find_basename (const gunichar2 *filename,
|
||||
gssize len)
|
||||
{
|
||||
const gunichar2 *result;
|
||||
|
||||
if (len < 0)
|
||||
len = g_utf16_len (filename);
|
||||
if (len == 0)
|
||||
return filename;
|
||||
|
||||
result = &filename[len - 1];
|
||||
|
||||
while (result > filename)
|
||||
{
|
||||
if ((wchar_t) result[0] == L'/' ||
|
||||
(wchar_t) result[0] == L'\\')
|
||||
{
|
||||
result += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
result -= 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Finds the last directory separator in @filename,
|
||||
* returns a pointer to the position after that separator.
|
||||
* If the string ends with a separator, returned value
|
||||
* will be pointing at the NUL terminator.
|
||||
* If the string does not contain separators, returns the
|
||||
* string itself.
|
||||
*/
|
||||
static const gchar *
|
||||
g_utf8_find_basename (const gchar *filename,
|
||||
gssize len)
|
||||
{
|
||||
const gchar *result;
|
||||
|
||||
if (len < 0)
|
||||
len = strlen (filename);
|
||||
if (len == 0)
|
||||
return filename;
|
||||
|
||||
result = &filename[len - 1];
|
||||
|
||||
while (result > filename)
|
||||
{
|
||||
if (result[0] == '/' ||
|
||||
result[0] == '\\')
|
||||
{
|
||||
result += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
result -= 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses @commandline, figuring out what the filename being invoked
|
||||
* is. All returned strings are pointers into @commandline.
|
||||
* @commandline must be a valid UTF-16 string and not be NULL.
|
||||
* @after_executable is the first character after executable
|
||||
* (usually a space, but not always).
|
||||
* If @comma_separator is TRUE, accepts ',' as a separator between
|
||||
* the filename and the following argument.
|
||||
*/
|
||||
static void
|
||||
_g_win32_parse_filename (const gunichar2 *commandline,
|
||||
gboolean comma_separator,
|
||||
const gunichar2 **executable_start,
|
||||
gssize *executable_len,
|
||||
const gunichar2 **executable_basename,
|
||||
const gunichar2 **after_executable)
|
||||
{
|
||||
const gunichar2 *p;
|
||||
const gunichar2 *first_argument;
|
||||
gboolean quoted;
|
||||
gssize len;
|
||||
gssize execlen;
|
||||
gboolean found;
|
||||
|
||||
while ((wchar_t) commandline[0] == L' ')
|
||||
commandline++;
|
||||
|
||||
quoted = FALSE;
|
||||
execlen = 0;
|
||||
found = FALSE;
|
||||
first_argument = NULL;
|
||||
|
||||
if ((wchar_t) commandline[0] == L'"')
|
||||
{
|
||||
quoted = TRUE;
|
||||
commandline += 1;
|
||||
}
|
||||
|
||||
len = g_utf16_len (commandline);
|
||||
p = commandline;
|
||||
|
||||
while (p < &commandline[len])
|
||||
{
|
||||
switch ((wchar_t) p[0])
|
||||
{
|
||||
case L'"':
|
||||
if (quoted)
|
||||
{
|
||||
first_argument = p + 1;
|
||||
/* Note: this is a valid commandline for opening "c:/file.txt":
|
||||
* > "notepad"c:/file.txt
|
||||
*/
|
||||
p = &commandline[len];
|
||||
found = TRUE;
|
||||
}
|
||||
else
|
||||
execlen += 1;
|
||||
break;
|
||||
case L' ':
|
||||
if (!quoted)
|
||||
{
|
||||
first_argument = p;
|
||||
p = &commandline[len];
|
||||
found = TRUE;
|
||||
}
|
||||
else
|
||||
execlen += 1;
|
||||
break;
|
||||
case L',':
|
||||
if (!quoted && comma_separator)
|
||||
{
|
||||
first_argument = p;
|
||||
p = &commandline[len];
|
||||
found = TRUE;
|
||||
}
|
||||
else
|
||||
execlen += 1;
|
||||
break;
|
||||
default:
|
||||
execlen += 1;
|
||||
break;
|
||||
}
|
||||
p += 1;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
first_argument = &commandline[len];
|
||||
|
||||
if (executable_start)
|
||||
*executable_start = commandline;
|
||||
|
||||
if (executable_len)
|
||||
*executable_len = execlen;
|
||||
|
||||
if (executable_basename)
|
||||
*executable_basename = g_utf16_find_basename (commandline, execlen);
|
||||
|
||||
if (after_executable)
|
||||
*after_executable = first_argument;
|
||||
}
|
||||
|
||||
/* Make sure @commandline is a valid UTF-16 string before
|
||||
* calling this function!
|
||||
* follow_class_chain_to_handler() does perform such validation.
|
||||
*/
|
||||
static void
|
||||
_g_win32_extract_executable (const gunichar2 *commandline,
|
||||
gchar **ex_out,
|
||||
gchar **ex_basename_out,
|
||||
gchar **ex_folded_out,
|
||||
gchar **ex_folded_basename_out,
|
||||
gchar **dll_function_out)
|
||||
{
|
||||
gchar *ex;
|
||||
gchar *ex_folded;
|
||||
const gunichar2 *first_argument;
|
||||
const gunichar2 *executable;
|
||||
const gunichar2 *executable_basename;
|
||||
gboolean quoted;
|
||||
gboolean folded;
|
||||
gssize execlen;
|
||||
|
||||
_g_win32_parse_filename (commandline, FALSE, &executable, &execlen, &executable_basename, &first_argument);
|
||||
|
||||
commandline = executable;
|
||||
|
||||
while ((wchar_t) first_argument[0] == L' ')
|
||||
first_argument++;
|
||||
|
||||
folded = g_utf16_to_utf8_and_fold (executable, (gssize) execlen, &ex, &ex_folded);
|
||||
/* This should never fail as @executable has to be valid UTF-16. */
|
||||
g_assert (folded);
|
||||
|
||||
if (dll_function_out)
|
||||
*dll_function_out = NULL;
|
||||
|
||||
/* See if the executable basename is "rundll32.exe". If so, then
|
||||
* parse the rest of the commandline as r'"?path-to-dll"?[ ]*,*[ ]*dll_function_to_invoke'
|
||||
*/
|
||||
/* Using just "rundll32.exe", without an absolute path, seems
|
||||
* very exploitable, but MS does that sometimes, so we have
|
||||
* to accept that.
|
||||
*/
|
||||
if ((g_strcmp0 (ex_folded, "rundll32.exe") == 0 ||
|
||||
g_str_has_suffix (ex_folded, "\\rundll32.exe") ||
|
||||
g_str_has_suffix (ex_folded, "/rundll32.exe")) &&
|
||||
first_argument[0] != 0 &&
|
||||
dll_function_out != NULL)
|
||||
{
|
||||
/* Corner cases:
|
||||
* > rundll32.exe c:\some,file,with,commas.dll,some_function
|
||||
* is treated by rundll32 as:
|
||||
* dll=c:\some
|
||||
* function=file,with,commas.dll,some_function
|
||||
* unless the dll name is surrounded by double quotation marks:
|
||||
* > rundll32.exe "c:\some,file,with,commas.dll",some_function
|
||||
* in which case everything works normally.
|
||||
* Also, quoting only works if it surrounds the file name, i.e:
|
||||
* > rundll32.exe "c:\some,file"",with,commas.dll",some_function
|
||||
* will not work.
|
||||
* Also, comma is optional when filename is quoted or when function
|
||||
* name is separated from the filename by space(s):
|
||||
* > rundll32.exe "c:\some,file,with,commas.dll"some_function
|
||||
* will work,
|
||||
* > rundll32.exe c:\some_dll_without_commas_or_spaces.dll some_function
|
||||
* will work too.
|
||||
* Also, any number of commas is accepted:
|
||||
* > rundll32.exe c:\some_dll_without_commas_or_spaces.dll , , ,,, , some_function
|
||||
* works just fine.
|
||||
* And the ultimate example is:
|
||||
* > "rundll32.exe""c:\some,file,with,commas.dll"some_function
|
||||
* and it also works.
|
||||
* Good job, Microsoft!
|
||||
*/
|
||||
const gunichar2 *filename_end = NULL;
|
||||
gssize filename_len = 0;
|
||||
gssize function_len = 0;
|
||||
const gunichar2 *dllpart;
|
||||
|
||||
quoted = FALSE;
|
||||
|
||||
if ((wchar_t) first_argument[0] == L'"')
|
||||
quoted = TRUE;
|
||||
|
||||
_g_win32_parse_filename (first_argument, TRUE, &dllpart, &filename_len, NULL, &filename_end);
|
||||
|
||||
if (filename_end[0] != 0 && filename_len > 0)
|
||||
{
|
||||
const gunichar2 *function_begin = filename_end;
|
||||
|
||||
while ((wchar_t) function_begin[0] == L',' || (wchar_t) function_begin[0] == L' ')
|
||||
function_begin += 1;
|
||||
|
||||
if (function_begin[0] != 0)
|
||||
{
|
||||
gchar *dllpart_utf8;
|
||||
gchar *dllpart_utf8_folded;
|
||||
gchar *function_utf8;
|
||||
gboolean folded;
|
||||
const gunichar2 *space = g_utf16_wchr (function_begin, L' ');
|
||||
|
||||
if (space)
|
||||
function_len = space - function_begin;
|
||||
else
|
||||
function_len = g_utf16_len (function_begin);
|
||||
|
||||
if (quoted)
|
||||
first_argument += 1;
|
||||
|
||||
folded = g_utf16_to_utf8_and_fold (first_argument, filename_len, &dllpart_utf8, &dllpart_utf8_folded);
|
||||
g_assert (folded);
|
||||
|
||||
function_utf8 = g_utf16_to_utf8 (function_begin, function_len, NULL, NULL, NULL);
|
||||
|
||||
/* We only take this branch when dll_function_out is not NULL */
|
||||
*dll_function_out = g_steal_pointer (&function_utf8);
|
||||
|
||||
g_free (function_utf8);
|
||||
|
||||
/*
|
||||
* Free our previous output candidate (rundll32) and replace it with the DLL path,
|
||||
* then proceed forward as if nothing has changed.
|
||||
*/
|
||||
g_free (ex);
|
||||
g_free (ex_folded);
|
||||
|
||||
ex = dllpart_utf8;
|
||||
ex_folded = dllpart_utf8_folded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ex_out)
|
||||
{
|
||||
if (ex_basename_out)
|
||||
*ex_basename_out = (gchar *) g_utf8_find_basename (ex, -1);
|
||||
|
||||
*ex_out = g_steal_pointer (&ex);
|
||||
}
|
||||
|
||||
g_free (ex);
|
||||
|
||||
if (ex_folded_out)
|
||||
{
|
||||
if (ex_folded_basename_out)
|
||||
*ex_folded_basename_out = (gchar *) g_utf8_find_basename (ex_folded, -1);
|
||||
|
||||
*ex_folded_out = g_steal_pointer (&ex_folded);
|
||||
}
|
||||
|
||||
g_free (ex_folded);
|
||||
}
|
||||
|
||||
/**
|
||||
* rundll32 accepts many different commandlines. Among them is this:
|
||||
* > rundll32.exe "c:/program files/foo/bar.dll",,, , ,,,, , function_name %1
|
||||
* rundll32 just reads the first argument as a potentially quoted
|
||||
* filename until the quotation ends (if quoted) or until a comma,
|
||||
* or until a space. Then ignores all subsequent spaces (if any) and commas (if any;
|
||||
* at least one comma is mandatory only if the filename is not quoted),
|
||||
* and then interprets the rest of the commandline (until a space or a NUL-byte)
|
||||
* as a name of a function.
|
||||
* When GLib tries to run a program, it attempts to correctly re-quote the arguments,
|
||||
* turning the first argument into "c:/program files/foo/bar.dll,,,".
|
||||
* This breaks rundll32 parsing logic.
|
||||
* Try to work around this by ensuring that the syntax is like this:
|
||||
* > rundll32.exe "c:/program files/foo/bar.dll" function_name
|
||||
* This syntax is valid for rundll32 *and* GLib spawn routines won't break it.
|
||||
*
|
||||
* @commandline must have at least 2 arguments, and the second argument
|
||||
* must contain a (possibly quoted) filename, followed by a space or
|
||||
* a comma. This can be checked for with an extract_executable() call -
|
||||
* it should return a non-null dll_function.
|
||||
*/
|
||||
static void
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (gunichar2 *commandline)
|
||||
{
|
||||
const gunichar2 *first_argument;
|
||||
gunichar2 *after_first_argument;
|
||||
|
||||
_g_win32_parse_filename (commandline, FALSE, NULL, NULL, NULL, &first_argument);
|
||||
|
||||
while ((wchar_t) first_argument[0] == L' ')
|
||||
first_argument++;
|
||||
|
||||
_g_win32_parse_filename (first_argument, TRUE, NULL, NULL, NULL, (const gunichar2 **) &after_first_argument);
|
||||
|
||||
if ((wchar_t) after_first_argument[0] == L',')
|
||||
after_first_argument[0] = 0x0020;
|
||||
/* Else everything is ok (first char after filename is ' ' or the first char
|
||||
* of the function name - either way this will work).
|
||||
*/
|
||||
}
|
@ -139,6 +139,13 @@ struct _GWin32AppInfoHandler {
|
||||
/* Pointer to a location within @executable */
|
||||
gchar *executable_basename;
|
||||
|
||||
/* If not NULL, then @executable and its derived fields contain the name
|
||||
* of a DLL file (without the name of the function that rundll32.exe should
|
||||
* invoke), and this field contains the name of the function to be invoked.
|
||||
* The application is then invoked as 'rundll32.exe "dll_path",dll_function other_arguments...'.
|
||||
*/
|
||||
gchar *dll_function;
|
||||
|
||||
/* Icon of the application for this handler */
|
||||
GIcon *icon;
|
||||
|
||||
@ -213,6 +220,13 @@ struct _GWin32AppInfoApplication {
|
||||
/* Pointer to a location within @executable */
|
||||
gchar *executable_basename;
|
||||
|
||||
/* If not NULL, then @executable and its derived fields contain the name
|
||||
* of a DLL file (without the name of the function that rundll32.exe should
|
||||
* invoke), and this field contains the name of the function to be invoked.
|
||||
* The application is then invoked as 'rundll32.exe "dll_path",dll_function other_arguments...'.
|
||||
*/
|
||||
gchar *dll_function;
|
||||
|
||||
/* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema,
|
||||
* UTF-8, folded) -> a GWin32AppInfoHandler
|
||||
* Schema can be used as a key in the urls hashmap.
|
||||
@ -294,6 +308,7 @@ g_win32_appinfo_handler_dispose (GObject *object)
|
||||
g_clear_pointer (&handler->proxy_command, g_free);
|
||||
g_clear_pointer (&handler->executable_folded, g_free);
|
||||
g_clear_pointer (&handler->executable, g_free);
|
||||
g_clear_pointer (&handler->dll_function, g_free);
|
||||
g_clear_object (&handler->key);
|
||||
g_clear_object (&handler->proxy_key);
|
||||
g_clear_object (&handler->icon);
|
||||
@ -332,6 +347,7 @@ g_win32_appinfo_application_dispose (GObject *object)
|
||||
g_clear_pointer (&app->command_u8, g_free);
|
||||
g_clear_pointer (&app->executable_folded, g_free);
|
||||
g_clear_pointer (&app->executable, g_free);
|
||||
g_clear_pointer (&app->dll_function, g_free);
|
||||
g_clear_pointer (&app->supported_urls, g_hash_table_destroy);
|
||||
g_clear_pointer (&app->supported_exts, g_hash_table_destroy);
|
||||
g_clear_object (&app->icon);
|
||||
@ -464,17 +480,6 @@ static GWin32RegistryKey *applications_key;
|
||||
/* Watch this key */
|
||||
static GWin32RegistryKey *classes_root_key;
|
||||
|
||||
static gunichar2 *
|
||||
g_wcsdup (const gunichar2 *str, gssize str_size)
|
||||
{
|
||||
if (str_size == -1)
|
||||
{
|
||||
str_size = wcslen (str) + 1;
|
||||
str_size *= sizeof (gunichar2);
|
||||
}
|
||||
return g_memdup (str, str_size);
|
||||
}
|
||||
|
||||
#define URL_ASSOCIATIONS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
|
||||
#define USER_CHOICE L"\\UserChoice"
|
||||
#define OPEN_WITH_PROGIDS L"\\OpenWithProgids"
|
||||
@ -486,6 +491,12 @@ g_wcsdup (const gunichar2 *str, gssize str_size)
|
||||
#define REG_PATH_MAX 256
|
||||
#define REG_PATH_MAX_SIZE (REG_PATH_MAX * sizeof (gunichar2))
|
||||
|
||||
/* for g_wcsdup(),
|
||||
* _g_win32_extract_executable(),
|
||||
* _g_win32_fixup_broken_microsoft_rundll_commandline()
|
||||
*/
|
||||
#include "giowin32-private.c"
|
||||
|
||||
static gunichar2 *
|
||||
read_resource_string (gunichar2 *res)
|
||||
{
|
||||
@ -694,41 +705,29 @@ _g_win32_registry_key_build_and_new_w (GError **error, ...)
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
/* Slow and dirty validator for UTF-16 strings */
|
||||
static gboolean
|
||||
utf8_and_fold (const gunichar2 *str,
|
||||
gchar **str_u8,
|
||||
gchar **str_u8_folded)
|
||||
g_utf16_validate (const gunichar2 *str,
|
||||
glong len)
|
||||
{
|
||||
gchar *u8;
|
||||
gchar *folded;
|
||||
u8 = g_utf16_to_utf8 (str, -1, NULL, NULL, NULL);
|
||||
gchar *tmp;
|
||||
|
||||
if (u8 == NULL)
|
||||
if (str == NULL)
|
||||
return FALSE;
|
||||
|
||||
folded = g_utf8_casefold (u8, -1);
|
||||
tmp = g_utf16_to_utf8 (str, len, NULL, NULL, NULL);
|
||||
|
||||
if (folded == NULL)
|
||||
{
|
||||
g_free (u8);
|
||||
return FALSE;
|
||||
}
|
||||
if (tmp == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (str_u8)
|
||||
*str_u8 = u8;
|
||||
else
|
||||
g_free (u8);
|
||||
|
||||
if (str_u8_folded)
|
||||
*str_u8_folded = folded;
|
||||
else
|
||||
g_free (folded);
|
||||
g_free (tmp);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Does a UTF-16 validity check on *proxy_command and/or *program_command.
|
||||
* Fails if that check doesn't pass.
|
||||
*/
|
||||
static gboolean
|
||||
follow_class_chain_to_handler (const gunichar2 *program_id,
|
||||
gsize program_id_size,
|
||||
@ -772,8 +771,9 @@ follow_class_chain_to_handler (const gunichar2 *program_id,
|
||||
NULL);
|
||||
if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR)
|
||||
{
|
||||
if ((program_id_u8 != NULL || program_id_folded != NULL) &&
|
||||
!utf8_and_fold (program_id, program_id_u8, program_id_folded))
|
||||
if (((program_id_u8 != NULL || program_id_folded != NULL) &&
|
||||
!g_utf16_to_utf8_and_fold (program_id, -1, program_id_u8, program_id_folded)) ||
|
||||
!g_utf16_validate (*program_command, -1))
|
||||
{
|
||||
g_object_unref (key);
|
||||
g_free (program_command);
|
||||
@ -805,27 +805,23 @@ follow_class_chain_to_handler (const gunichar2 *program_id,
|
||||
(void **) proxy_id,
|
||||
&proxy_id_size,
|
||||
NULL);
|
||||
g_object_unref (key);
|
||||
|
||||
if (!got_value ||
|
||||
(val_type != G_WIN32_REGISTRY_VALUE_STR))
|
||||
{
|
||||
g_object_unref (key);
|
||||
g_clear_pointer (proxy_id, g_free);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (proxy_key)
|
||||
*proxy_key = key;
|
||||
else
|
||||
g_object_unref (key);
|
||||
|
||||
key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, *proxy_id,
|
||||
SHELL_OPEN_COMMAND, NULL);
|
||||
|
||||
if (key == NULL)
|
||||
{
|
||||
g_clear_pointer (proxy_id, g_free);
|
||||
if (proxy_key)
|
||||
g_clear_object (proxy_key);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -836,12 +832,17 @@ follow_class_chain_to_handler (const gunichar2 *program_id,
|
||||
(void **) proxy_command,
|
||||
NULL,
|
||||
NULL);
|
||||
g_object_unref (key);
|
||||
|
||||
if (proxy_key)
|
||||
*proxy_key = key;
|
||||
else
|
||||
g_object_unref (key);
|
||||
|
||||
if (!got_value ||
|
||||
val_type != G_WIN32_REGISTRY_VALUE_STR ||
|
||||
((program_id_u8 != NULL || program_id_folded != NULL) &&
|
||||
!utf8_and_fold (program_id, program_id_u8, program_id_folded)))
|
||||
!g_utf16_to_utf8_and_fold (program_id, -1, program_id_u8, program_id_folded)) ||
|
||||
!g_utf16_validate (*proxy_command, -1))
|
||||
{
|
||||
g_clear_pointer (proxy_id, g_free);
|
||||
g_clear_pointer (proxy_command, g_free);
|
||||
@ -853,124 +854,6 @@ follow_class_chain_to_handler (const gunichar2 *program_id,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
extract_executable (gunichar2 *commandline,
|
||||
gchar **ex_out,
|
||||
gchar **ex_basename_out,
|
||||
gchar **ex_folded_out,
|
||||
gchar **ex_folded_basename_out)
|
||||
{
|
||||
gchar *ex;
|
||||
gchar *ex_folded;
|
||||
gunichar2 *p;
|
||||
gboolean quoted;
|
||||
size_t len;
|
||||
size_t execlen;
|
||||
gunichar2 *exepart;
|
||||
gboolean found;
|
||||
|
||||
quoted = FALSE;
|
||||
execlen = 0;
|
||||
found = FALSE;
|
||||
len = wcslen (commandline);
|
||||
p = commandline;
|
||||
|
||||
while (p < &commandline[len])
|
||||
{
|
||||
switch (p[0])
|
||||
{
|
||||
case L'"':
|
||||
quoted = !quoted;
|
||||
break;
|
||||
case L' ':
|
||||
if (!quoted)
|
||||
{
|
||||
execlen = p - commandline;
|
||||
p = &commandline[len];
|
||||
found = TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
p += 1;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
execlen = len;
|
||||
|
||||
exepart = g_wcsdup (commandline, (execlen + 1) * sizeof (gunichar2));
|
||||
exepart[execlen] = L'\0';
|
||||
|
||||
p = &exepart[0];
|
||||
|
||||
while (execlen > 0 && exepart[0] == L'"' && exepart[execlen - 1] == L'"')
|
||||
{
|
||||
p = &exepart[1];
|
||||
exepart[execlen - 1] = L'\0';
|
||||
execlen -= 2;
|
||||
}
|
||||
|
||||
if (!utf8_and_fold (p, &ex, &ex_folded))
|
||||
/* Currently no code to handle this case. It shouldn't happen though... */
|
||||
g_assert_not_reached ();
|
||||
|
||||
g_free (exepart);
|
||||
|
||||
if (ex_out)
|
||||
{
|
||||
*ex_out = ex;
|
||||
|
||||
if (ex_basename_out)
|
||||
{
|
||||
*ex_basename_out = &ex[strlen (ex) - 1];
|
||||
|
||||
while (*ex_basename_out > ex)
|
||||
{
|
||||
if ((*ex_basename_out)[0] == '/' ||
|
||||
(*ex_basename_out)[0] == '\\')
|
||||
{
|
||||
*ex_basename_out += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
*ex_basename_out -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_free (ex);
|
||||
}
|
||||
|
||||
if (ex_folded_out)
|
||||
{
|
||||
*ex_folded_out = ex_folded;
|
||||
|
||||
if (ex_folded_basename_out)
|
||||
{
|
||||
*ex_folded_basename_out = &ex_folded[strlen (ex_folded) - 1];
|
||||
|
||||
while (*ex_folded_basename_out > ex_folded)
|
||||
{
|
||||
if ((*ex_folded_basename_out)[0] == '/' ||
|
||||
(*ex_folded_basename_out)[0] == '\\')
|
||||
{
|
||||
*ex_folded_basename_out += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
*ex_folded_basename_out -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_free (ex_folded);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_url_association (const gunichar2 *schema)
|
||||
{
|
||||
@ -998,7 +881,7 @@ get_url_association (const gunichar2 *schema)
|
||||
if (user_choice == NULL)
|
||||
return;
|
||||
|
||||
if (!utf8_and_fold (schema, &schema_u8, &schema_folded))
|
||||
if (!g_utf16_to_utf8_and_fold (schema, -1, &schema_u8, &schema_folded))
|
||||
{
|
||||
g_object_unref (user_choice);
|
||||
return;
|
||||
@ -1065,11 +948,14 @@ get_url_association (const gunichar2 *schema)
|
||||
handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
|
||||
handler_rec->proxy_command =
|
||||
proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
|
||||
extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL);
|
||||
_g_win32_extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL,
|
||||
&handler_rec->dll_function);
|
||||
if (handler_rec->dll_function != NULL)
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
|
||||
read_handler_icon (proxy_key, program_key, &handler_rec->icon);
|
||||
g_hash_table_insert (handlers,
|
||||
g_strdup (program_id_folded),
|
||||
@ -1147,7 +1033,7 @@ get_file_ext (const gunichar2 *ext)
|
||||
if (user_choice == NULL && open_with_progids == NULL)
|
||||
return;
|
||||
|
||||
if (!utf8_and_fold (ext, &ext_u8, &ext_folded))
|
||||
if (!g_utf16_to_utf8_and_fold (ext, -1, &ext_u8, &ext_folded))
|
||||
{
|
||||
g_clear_object (&user_choice);
|
||||
g_clear_object (&open_with_progids);
|
||||
@ -1207,11 +1093,14 @@ get_file_ext (const gunichar2 *ext)
|
||||
proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
|
||||
handler_rec->proxy_command =
|
||||
proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
|
||||
extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL);
|
||||
_g_win32_extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL,
|
||||
&handler_rec->dll_function);
|
||||
if (handler_rec->dll_function != NULL)
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
|
||||
read_handler_icon (proxy_key,
|
||||
program_key,
|
||||
&handler_rec->icon);
|
||||
@ -1325,11 +1214,14 @@ get_file_ext (const gunichar2 *ext)
|
||||
proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
|
||||
handler_rec->proxy_command =
|
||||
proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
|
||||
extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL);
|
||||
_g_win32_extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL,
|
||||
&handler_rec->dll_function);
|
||||
if (handler_rec->dll_function != NULL)
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
|
||||
read_handler_icon (proxy_key,
|
||||
program_key,
|
||||
&handler_rec->icon);
|
||||
@ -1637,6 +1529,7 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
|
||||
gchar *app_executable_basename;
|
||||
gchar *app_executable_folded;
|
||||
gchar *app_executable_folded_basename;
|
||||
gchar *app_dll_function;
|
||||
GWin32RegistryKey *associations;
|
||||
|
||||
app_key_path = g_wcsdup (input_app_key_path, -1);
|
||||
@ -1652,7 +1545,7 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
|
||||
|
||||
canonical_name += 1;
|
||||
|
||||
if (!utf8_and_fold (canonical_name, &canonical_name_u8, &canonical_name_folded))
|
||||
if (!g_utf16_to_utf8_and_fold (canonical_name, -1, &canonical_name_u8, &canonical_name_folded))
|
||||
{
|
||||
g_free (app_key_path);
|
||||
return;
|
||||
@ -1705,7 +1598,9 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
|
||||
if (success &&
|
||||
(vtype != G_WIN32_REGISTRY_VALUE_STR ||
|
||||
!g_utf16_validate (shell_open_command, -1)))
|
||||
{
|
||||
/* Must have a command */
|
||||
g_clear_pointer (&shell_open_command, g_free);
|
||||
@ -1717,11 +1612,14 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
|
||||
return;
|
||||
}
|
||||
|
||||
extract_executable (shell_open_command,
|
||||
&app_executable,
|
||||
&app_executable_basename,
|
||||
&app_executable_folded,
|
||||
&app_executable_folded_basename);
|
||||
_g_win32_extract_executable (shell_open_command,
|
||||
&app_executable,
|
||||
&app_executable_basename,
|
||||
&app_executable_folded,
|
||||
&app_executable_folded_basename,
|
||||
&app_dll_function);
|
||||
if (app_dll_function != NULL)
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (shell_open_command);
|
||||
|
||||
app = g_hash_table_lookup (apps_by_id, canonical_name_folded);
|
||||
|
||||
@ -1748,6 +1646,8 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
|
||||
app->user_specific = user_specific;
|
||||
app->default_app = default_app;
|
||||
|
||||
app->dll_function = g_strdup (app_dll_function);
|
||||
|
||||
g_hash_table_insert (apps_by_id,
|
||||
g_strdup (canonical_name_folded),
|
||||
app);
|
||||
@ -1966,11 +1866,14 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
|
||||
proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
|
||||
handler_rec->proxy_command =
|
||||
proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
|
||||
extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL);
|
||||
_g_win32_extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL,
|
||||
&handler_rec->dll_function);
|
||||
if (handler_rec->dll_function != NULL)
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
|
||||
read_handler_icon (proxy_key,
|
||||
program_key,
|
||||
&handler_rec->icon);
|
||||
@ -1984,9 +1887,10 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
|
||||
g_clear_object (&proxy_key);
|
||||
}
|
||||
|
||||
if (utf8_and_fold (file_extension,
|
||||
&file_extension_u8,
|
||||
&file_extension_folded))
|
||||
if (g_utf16_to_utf8_and_fold (file_extension,
|
||||
-1,
|
||||
&file_extension_u8,
|
||||
&file_extension_folded))
|
||||
{
|
||||
ext = g_hash_table_lookup (extensions,
|
||||
file_extension_folded);
|
||||
@ -2122,11 +2026,14 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
|
||||
proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
|
||||
handler_rec->proxy_command =
|
||||
proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
|
||||
extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL);
|
||||
_g_win32_extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL,
|
||||
&handler_rec->dll_function);
|
||||
if (handler_rec->dll_function != NULL)
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
|
||||
read_handler_icon (proxy_key,
|
||||
program_key,
|
||||
&handler_rec->icon);
|
||||
@ -2140,9 +2047,10 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
|
||||
g_clear_object (&proxy_key);
|
||||
}
|
||||
|
||||
if (utf8_and_fold (url_schema,
|
||||
&schema_u8,
|
||||
&schema_folded))
|
||||
if (g_utf16_to_utf8_and_fold (url_schema,
|
||||
-1,
|
||||
&schema_u8,
|
||||
&schema_folded))
|
||||
{
|
||||
schema = g_hash_table_lookup (urls,
|
||||
schema_folded);
|
||||
@ -2197,6 +2105,7 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
|
||||
|
||||
g_clear_pointer (&app_executable, g_free);
|
||||
g_clear_pointer (&app_executable_folded, g_free);
|
||||
g_clear_pointer (&app_dll_function, g_free);
|
||||
g_clear_pointer (&fallback_friendly_name, g_free);
|
||||
g_clear_pointer (&description, g_free);
|
||||
g_clear_pointer (&icon_source, g_free);
|
||||
@ -2243,8 +2152,6 @@ read_exeapps (void)
|
||||
{
|
||||
GWin32RegistryKey *applications_key;
|
||||
GWin32RegistrySubkeyIter app_iter;
|
||||
gunichar2 *app_exe_basename;
|
||||
gsize app_exe_basename_len;
|
||||
|
||||
applications_key =
|
||||
g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
|
||||
@ -2260,6 +2167,8 @@ read_exeapps (void)
|
||||
|
||||
while (g_win32_registry_subkey_iter_next (&app_iter, TRUE, NULL))
|
||||
{
|
||||
gunichar2 *app_exe_basename;
|
||||
gsize app_exe_basename_len;
|
||||
GWin32RegistryKey *incapable_app;
|
||||
gunichar2 *friendly_app_name;
|
||||
gboolean success;
|
||||
@ -2280,7 +2189,8 @@ read_exeapps (void)
|
||||
if (!g_win32_registry_subkey_iter_get_name_w (&app_iter,
|
||||
&app_exe_basename,
|
||||
&app_exe_basename_len,
|
||||
NULL))
|
||||
NULL) ||
|
||||
!g_utf16_validate (app_exe_basename, app_exe_basename_len))
|
||||
continue;
|
||||
|
||||
incapable_app =
|
||||
@ -2291,11 +2201,12 @@ read_exeapps (void)
|
||||
if (incapable_app == NULL)
|
||||
continue;
|
||||
|
||||
extract_executable (app_exe_basename,
|
||||
&appexe,
|
||||
&appexe_basename,
|
||||
&appexe_folded,
|
||||
&appexe_folded_basename);
|
||||
_g_win32_extract_executable (app_exe_basename,
|
||||
&appexe,
|
||||
&appexe_basename,
|
||||
&appexe_folded,
|
||||
&appexe_folded_basename,
|
||||
NULL);
|
||||
|
||||
shell_open_command_key =
|
||||
g_win32_registry_key_get_child_w (incapable_app,
|
||||
@ -2314,7 +2225,9 @@ read_exeapps (void)
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
|
||||
if (success &&
|
||||
(vtype != G_WIN32_REGISTRY_VALUE_STR ||
|
||||
!g_utf16_validate (shell_open_command, -1)))
|
||||
{
|
||||
g_clear_pointer (&shell_open_command, g_free);
|
||||
}
|
||||
@ -2385,6 +2298,21 @@ read_exeapps (void)
|
||||
{
|
||||
app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
|
||||
|
||||
if (shell_open_command)
|
||||
{
|
||||
gchar *dll_function;
|
||||
|
||||
_g_win32_extract_executable (shell_open_command,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&dll_function);
|
||||
if (dll_function != NULL)
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (shell_open_command);
|
||||
g_clear_pointer (&dll_function, g_free);
|
||||
}
|
||||
|
||||
app->command =
|
||||
shell_open_command ? g_wcsdup (shell_open_command, -1) : NULL;
|
||||
|
||||
@ -2442,9 +2370,10 @@ read_exeapps (void)
|
||||
NULL)) ||
|
||||
(ext_name_len <= 0) ||
|
||||
(ext_name[0] != L'.') ||
|
||||
(!utf8_and_fold (ext_name,
|
||||
&ext_u8,
|
||||
&ext_folded)))
|
||||
(!g_utf16_to_utf8_and_fold (ext_name,
|
||||
-1,
|
||||
&ext_u8,
|
||||
&ext_folded)))
|
||||
continue;
|
||||
|
||||
file_extn = NULL;
|
||||
@ -2590,11 +2519,14 @@ read_class_extension (GWin32RegistryKey *classes_root,
|
||||
handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
|
||||
handler_rec->proxy_command =
|
||||
proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
|
||||
extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL);
|
||||
_g_win32_extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL,
|
||||
&handler_rec->dll_function);
|
||||
if (handler_rec->dll_function != NULL)
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
|
||||
read_handler_icon (proxy_key, program_key, &handler_rec->icon);
|
||||
g_hash_table_insert (handlers,
|
||||
g_strdup (ext_folded),
|
||||
@ -2708,11 +2640,14 @@ read_class_url (GWin32RegistryKey *classes_root,
|
||||
handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
|
||||
handler_rec->proxy_command =
|
||||
proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
|
||||
extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL);
|
||||
_g_win32_extract_executable (proxy_command ? proxy_command : program_command,
|
||||
&handler_rec->executable,
|
||||
&handler_rec->executable_basename,
|
||||
&handler_rec->executable_folded,
|
||||
NULL,
|
||||
&handler_rec->dll_function);
|
||||
if (handler_rec->dll_function != NULL)
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
|
||||
read_handler_icon (proxy_key, program_key, &handler_rec->icon);
|
||||
g_hash_table_insert (handlers,
|
||||
g_strdup (program_id_folded),
|
||||
@ -2832,9 +2767,10 @@ link_chosen_handlers (void)
|
||||
|
||||
if (handler->proxy_command &&
|
||||
handler->proxy_id &&
|
||||
utf8_and_fold (handler->proxy_id,
|
||||
NULL,
|
||||
&proxy_id_folded))
|
||||
g_utf16_to_utf8_and_fold (handler->proxy_id,
|
||||
-1,
|
||||
NULL,
|
||||
&proxy_id_folded))
|
||||
{
|
||||
GWin32AppInfoHandler *proxy;
|
||||
|
||||
@ -2890,9 +2826,10 @@ link_chosen_handlers (void)
|
||||
|
||||
if (handler->proxy_command &&
|
||||
handler->proxy_id &&
|
||||
utf8_and_fold (handler->proxy_id,
|
||||
NULL,
|
||||
&proxy_id_folded))
|
||||
g_utf16_to_utf8_and_fold (handler->proxy_id,
|
||||
-1,
|
||||
NULL,
|
||||
&proxy_id_folded))
|
||||
{
|
||||
GWin32AppInfoHandler *proxy;
|
||||
|
||||
@ -3134,11 +3071,6 @@ link_handlers_to_unregistered_apps (void)
|
||||
(handler->executable_folded == NULL))
|
||||
continue;
|
||||
|
||||
hndexe_fc_basename = g_utf8_casefold (handler->executable_basename, -1);
|
||||
|
||||
if (hndexe_fc_basename == NULL)
|
||||
continue;
|
||||
|
||||
g_hash_table_iter_init (&app_iter, apps_by_id);
|
||||
|
||||
while (g_hash_table_iter_next (&app_iter,
|
||||
@ -3159,6 +3091,11 @@ link_handlers_to_unregistered_apps (void)
|
||||
if (handler->app != NULL)
|
||||
continue;
|
||||
|
||||
hndexe_fc_basename = g_utf8_casefold (handler->executable_basename, -1);
|
||||
|
||||
if (hndexe_fc_basename == NULL)
|
||||
continue;
|
||||
|
||||
g_hash_table_iter_init (&app_iter, apps_by_exe);
|
||||
|
||||
while ((hndexe_fc_basename != NULL) &&
|
||||
@ -4379,13 +4316,20 @@ g_app_info_create_from_commandline (const char *commandline,
|
||||
{
|
||||
GWin32AppInfo *info;
|
||||
GWin32AppInfoApplication *app;
|
||||
gunichar2 *app_command;
|
||||
|
||||
g_return_val_if_fail (commandline, NULL);
|
||||
|
||||
info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
|
||||
app_command = g_utf8_to_utf16 (commandline, -1, NULL, NULL, NULL);
|
||||
|
||||
if (app_command == NULL)
|
||||
return NULL;
|
||||
|
||||
info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
|
||||
app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
|
||||
|
||||
app->command = g_steal_pointer (&app_command);
|
||||
|
||||
if (application_name)
|
||||
{
|
||||
app->canonical_name = g_utf8_to_utf16 (application_name,
|
||||
@ -4397,14 +4341,16 @@ g_app_info_create_from_commandline (const char *commandline,
|
||||
app->canonical_name_folded = g_utf8_casefold (application_name, -1);
|
||||
}
|
||||
|
||||
app->command = g_utf8_to_utf16 (commandline, -1, NULL, NULL, NULL);
|
||||
app->command_u8 = g_strdup (commandline);
|
||||
_g_win32_extract_executable (app->command,
|
||||
&app->executable,
|
||||
&app->executable_basename,
|
||||
&app->executable_folded,
|
||||
NULL,
|
||||
&app->dll_function);
|
||||
if (app->dll_function != NULL)
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (app->command);
|
||||
|
||||
extract_executable (app->command,
|
||||
&app->executable,
|
||||
&app->executable_basename,
|
||||
&app->executable_folded,
|
||||
NULL);
|
||||
app->command_u8 = g_utf16_to_utf8 (app->command, -1, NULL, NULL, NULL);
|
||||
|
||||
app->no_open_with = FALSE;
|
||||
app->user_specific = FALSE;
|
||||
|
@ -79,6 +79,7 @@ gio_tests = {
|
||||
'tls-interaction' : {'extra_sources' : ['gtesttlsbackend.c']},
|
||||
'tls-database' : {'extra_sources' : ['gtesttlsbackend.c']},
|
||||
'gdbus-address-get-session' : {},
|
||||
'win32-appinfo' : {},
|
||||
}
|
||||
|
||||
test_extra_programs = {
|
||||
|
470
gio/tests/win32-appinfo.c
Normal file
470
gio/tests/win32-appinfo.c
Normal file
@ -0,0 +1,470 @@
|
||||
/* GLib testing framework examples and tests
|
||||
* Copyright (C) 2019 Руслан Ижбулатов <lrn1986@gmail.com>
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
*
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include <glib/glib.h>
|
||||
#include <gio/gio.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "../giowin32-private.c"
|
||||
|
||||
static int
|
||||
g_utf16_cmp0 (const gunichar2 *str1,
|
||||
const gunichar2 *str2)
|
||||
{
|
||||
if (!str1)
|
||||
return -(str1 != str2);
|
||||
if (!str2)
|
||||
return str1 != str2;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
if (str1[0] > str2[0])
|
||||
return 1;
|
||||
else if (str1[0] < str2[0])
|
||||
return -1;
|
||||
else if (str1[0] == 0 && str2[0] == 0)
|
||||
return 0;
|
||||
|
||||
str1++;
|
||||
str2++;
|
||||
}
|
||||
}
|
||||
|
||||
#define g_assert_cmputf16(s1, cmp, s2, s1u8, s2u8) \
|
||||
G_STMT_START { \
|
||||
const gunichar2 *__s1 = (s1), *__s2 = (s2); \
|
||||
if (g_utf16_cmp0 (__s1, __s2) cmp 0) ; else \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#s1u8 " " #cmp " " #s2u8, s1u8, #cmp, s2u8); \
|
||||
} G_STMT_END
|
||||
|
||||
static void
|
||||
test_utf16_strfuncs (void)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
struct {
|
||||
gsize len;
|
||||
const gunichar2 utf16[10];
|
||||
const gchar *utf8;
|
||||
const gchar *utf8_folded;
|
||||
} string_cases[] = {
|
||||
{
|
||||
0,
|
||||
{ 0x0000 },
|
||||
"",
|
||||
"",
|
||||
},
|
||||
{
|
||||
1,
|
||||
{ 0x0020, 0x0000 },
|
||||
" ",
|
||||
" ",
|
||||
},
|
||||
{
|
||||
2,
|
||||
{ 0x0020, 0xd800, 0x0000 },
|
||||
NULL,
|
||||
NULL,
|
||||
},
|
||||
};
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (string_cases); i++)
|
||||
{
|
||||
gsize len;
|
||||
gunichar2 *str;
|
||||
gboolean success;
|
||||
gchar *utf8;
|
||||
gchar *utf8_folded;
|
||||
|
||||
len = g_utf16_len (string_cases[i].utf16);
|
||||
g_assert_cmpuint (len, ==, string_cases[i].len);
|
||||
|
||||
str = (gunichar2 *) g_utf16_find_basename (string_cases[i].utf16, -1);
|
||||
/* This only works because all testcases lack separators */
|
||||
g_assert_true (string_cases[i].utf16 == str);
|
||||
|
||||
str = g_wcsdup (string_cases[i].utf16, string_cases[i].len);
|
||||
g_assert_cmpmem (string_cases[i].utf16, len, str, len);
|
||||
g_free (str);
|
||||
|
||||
str = g_wcsdup (string_cases[i].utf16, -1);
|
||||
g_assert_cmpmem (string_cases[i].utf16, len, str, len);
|
||||
g_free (str);
|
||||
|
||||
success = g_utf16_to_utf8_and_fold (string_cases[i].utf16, -1, NULL, NULL);
|
||||
|
||||
if (string_cases[i].utf8 == NULL)
|
||||
g_assert_false (success);
|
||||
else
|
||||
g_assert_true (success);
|
||||
|
||||
utf8 = NULL;
|
||||
success = g_utf16_to_utf8_and_fold (string_cases[i].utf16, -1, &utf8, NULL);
|
||||
|
||||
if (string_cases[i].utf8 != NULL)
|
||||
{
|
||||
g_assert_true (success);
|
||||
g_assert_cmpstr (string_cases[i].utf8, ==, utf8);
|
||||
/* This only works because all testcases lack separators */
|
||||
g_assert_true (utf8 == g_utf8_find_basename (utf8, len));
|
||||
}
|
||||
|
||||
g_free (utf8);
|
||||
|
||||
utf8 = NULL;
|
||||
utf8_folded = NULL;
|
||||
success = g_utf16_to_utf8_and_fold (string_cases[i].utf16, -1, &utf8, &utf8_folded);
|
||||
|
||||
if (string_cases[i].utf8 != NULL)
|
||||
{
|
||||
g_assert_true (success);
|
||||
g_assert_cmpstr (string_cases[i].utf8_folded, ==, utf8_folded);
|
||||
}
|
||||
|
||||
g_free (utf8);
|
||||
g_free (utf8_folded);
|
||||
}
|
||||
}
|
||||
|
||||
struct {
|
||||
const char *orig;
|
||||
const char *executable;
|
||||
const char *executable_basename;
|
||||
gboolean is_rundll32;
|
||||
const char *fixed;
|
||||
} rundll32_commandlines[] = {
|
||||
{
|
||||
"%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen %1",
|
||||
"%SystemRoot%\\System32\\rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\" ImageView_Fullscreen %1",
|
||||
},
|
||||
{
|
||||
"%SystemRoot%/System32/rundll32.exe \"%ProgramFiles%/Windows Photo Viewer/PhotoViewer.dll\", ImageView_Fullscreen %1",
|
||||
"%SystemRoot%/System32/rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"%SystemRoot%/System32/rundll32.exe \"%ProgramFiles%/Windows Photo Viewer/PhotoViewer.dll\" ImageView_Fullscreen %1",
|
||||
},
|
||||
{
|
||||
"%SystemRoot%\\System32/rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen %1",
|
||||
"%SystemRoot%\\System32/rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"%SystemRoot%\\System32/rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\" ImageView_Fullscreen %1",
|
||||
},
|
||||
{
|
||||
"\"some path with spaces\\rundll32.exe\" \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen %1",
|
||||
"some path with spaces\\rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"\"some path with spaces\\rundll32.exe\" \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\" ImageView_Fullscreen %1",
|
||||
},
|
||||
{
|
||||
" \"some path with spaces\\rundll32.exe\"\"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\",ImageView_Fullscreen %1",
|
||||
"some path with spaces\\rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
" \"some path with spaces\\rundll32.exe\"\"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\" ImageView_Fullscreen %1",
|
||||
},
|
||||
{
|
||||
"rundll32.exe foo.bar,baz",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"rundll32.exe foo.bar baz",
|
||||
},
|
||||
{
|
||||
" rundll32.exe foo.bar,baz",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
" rundll32.exe foo.bar baz",
|
||||
},
|
||||
{
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
FALSE,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
"rundll32.exe ,foobar",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
FALSE,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
"rundll32.exe ,foobar",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
FALSE,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
"rundll32.exe foo.dll",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
FALSE,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
"rundll32.exe \"foo bar\",baz",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"rundll32.exe \"foo bar\" baz",
|
||||
},
|
||||
{
|
||||
"\"rundll32.exe\" \"foo bar\",baz",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"\"rundll32.exe\" \"foo bar\" baz",
|
||||
},
|
||||
{
|
||||
"\"rundll32.exe\" \"foo bar\",, , ,,, , ,,baz",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"\"rundll32.exe\" \"foo bar\" , , ,,, , ,,baz",
|
||||
},
|
||||
{
|
||||
"\"rundll32.exe\" foo.bar,,,,,,,,,baz",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"\"rundll32.exe\" foo.bar ,,,,,,,,baz",
|
||||
},
|
||||
{
|
||||
"\"rundll32.exe\" foo.bar baz",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"\"rundll32.exe\" foo.bar baz",
|
||||
},
|
||||
{
|
||||
"\"RuNdlL32.exe\" foo.bar baz",
|
||||
"RuNdlL32.exe",
|
||||
"RuNdlL32.exe",
|
||||
TRUE,
|
||||
"\"RuNdlL32.exe\" foo.bar baz",
|
||||
},
|
||||
{
|
||||
"%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll,\" ImageView_Fullscreen %1",
|
||||
"%SystemRoot%\\System32\\rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll,\" ImageView_Fullscreen %1",
|
||||
},
|
||||
{
|
||||
"\"rundll32.exe\" \"foo bar,\"baz",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"\"rundll32.exe\" \"foo bar,\"baz",
|
||||
},
|
||||
{
|
||||
"\"rundll32.exe\" some,thing",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"\"rundll32.exe\" some thing",
|
||||
},
|
||||
{
|
||||
"\"rundll32.exe\" some,",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
FALSE,
|
||||
"\"rundll32.exe\" some,",
|
||||
},
|
||||
/* These filenames are not allowed on Windows, but our function doesn't care about that */
|
||||
{
|
||||
"run\"dll32.exe foo\".bar,baz",
|
||||
"run\"dll32.exe",
|
||||
"run\"dll32.exe",
|
||||
FALSE,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
"run,dll32.exe foo.bar,baz",
|
||||
"run,dll32.exe",
|
||||
"run,dll32.exe",
|
||||
FALSE,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
"\"rundll32.exe\" some, thing",
|
||||
"rundll32.exe",
|
||||
"rundll32.exe",
|
||||
TRUE,
|
||||
"\"rundll32.exe\" some thing",
|
||||
},
|
||||
/* Commands with "rundll32" (without the .exe suffix) do exist,
|
||||
* but GLib currently does not recognize them, so there's no point
|
||||
* in testing these.
|
||||
*/
|
||||
};
|
||||
|
||||
static void
|
||||
test_win32_rundll32_fixup (void)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (rundll32_commandlines); i++)
|
||||
{
|
||||
gunichar2 *argument;
|
||||
gunichar2 *expected;
|
||||
|
||||
if (!rundll32_commandlines[i].is_rundll32)
|
||||
continue;
|
||||
|
||||
argument = g_utf8_to_utf16 (rundll32_commandlines[i].orig, -1, NULL, NULL, NULL);
|
||||
expected = g_utf8_to_utf16 (rundll32_commandlines[i].fixed, -1, NULL, NULL, NULL);
|
||||
|
||||
g_assert_nonnull (argument);
|
||||
g_assert_nonnull (expected);
|
||||
_g_win32_fixup_broken_microsoft_rundll_commandline (argument);
|
||||
|
||||
g_assert_cmputf16 (argument, ==, expected, rundll32_commandlines[i].orig, rundll32_commandlines[i].fixed);
|
||||
|
||||
g_free (argument);
|
||||
g_free (expected);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_win32_extract_executable (void)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (rundll32_commandlines); i++)
|
||||
{
|
||||
gunichar2 *argument;
|
||||
gchar *dll_function;
|
||||
gchar *executable;
|
||||
gchar *executable_basename;
|
||||
gchar *executable_folded;
|
||||
gchar *executable_folded_basename;
|
||||
|
||||
argument = g_utf8_to_utf16 (rundll32_commandlines[i].orig, -1, NULL, NULL, NULL);
|
||||
|
||||
_g_win32_extract_executable (argument, NULL, NULL, NULL, NULL, &dll_function);
|
||||
|
||||
if (rundll32_commandlines[i].is_rundll32)
|
||||
g_assert_nonnull (dll_function);
|
||||
else
|
||||
g_assert_null (dll_function);
|
||||
|
||||
g_free (dll_function);
|
||||
|
||||
executable = NULL;
|
||||
executable_basename = NULL;
|
||||
executable_folded = NULL;
|
||||
executable_folded_basename = NULL;
|
||||
_g_win32_extract_executable (argument, &executable, &executable_basename, &executable_folded, &executable_folded_basename, NULL);
|
||||
|
||||
g_assert_cmpstr (rundll32_commandlines[i].executable, ==, executable);
|
||||
g_assert_cmpstr (rundll32_commandlines[i].executable_basename, ==, executable_basename);
|
||||
g_assert_nonnull (executable_folded);
|
||||
|
||||
g_free (executable);
|
||||
g_free (executable_folded);
|
||||
|
||||
/* Check the corner-case where we don't want to know where basename is */
|
||||
executable = NULL;
|
||||
executable_folded = NULL;
|
||||
_g_win32_extract_executable (argument, &executable, NULL, &executable_folded, NULL, NULL);
|
||||
|
||||
g_assert_cmpstr (rundll32_commandlines[i].executable, ==, executable);
|
||||
g_assert_nonnull (executable_folded);
|
||||
|
||||
g_free (executable);
|
||||
g_free (executable_folded);
|
||||
|
||||
g_free (argument);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_win32_parse_filename (void)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (rundll32_commandlines); i++)
|
||||
{
|
||||
gunichar2 *argument;
|
||||
argument = g_utf8_to_utf16 (rundll32_commandlines[i].orig, -1, NULL, NULL, NULL);
|
||||
/* Just checking that it doesn't blow up on various (sometimes incorrect) strings */
|
||||
_g_win32_parse_filename (argument, FALSE, NULL, NULL, NULL, NULL);
|
||||
g_free (argument);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_fail_on_broken_utf16_1 (void)
|
||||
{
|
||||
const gunichar2 utf16[] = { 0xd800, 0x0000 };
|
||||
_g_win32_extract_executable (utf16, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
do_fail_on_broken_utf16_2 (void)
|
||||
{
|
||||
/* "rundll32.exe <invalid utf16> r" */
|
||||
gchar *dll_function;
|
||||
const gunichar2 utf16[] = { 0x0072, 0x0075, 0x006E, 0x0064, 0x006C, 0x006C, 0x0033, 0x0032,
|
||||
0x002E, 0x0065, 0x0078, 0x0065, 0x0020, 0xd800, 0x0020, 0x0072, 0x0000 };
|
||||
_g_win32_extract_executable (utf16, NULL, NULL, NULL, NULL, &dll_function);
|
||||
}
|
||||
|
||||
static void
|
||||
test_fail_on_broken_utf16 (void)
|
||||
{
|
||||
g_test_trap_subprocess ("/appinfo/subprocess/win32-assert-broken-utf16_1", 0, 0);
|
||||
g_test_trap_assert_failed ();
|
||||
g_test_trap_assert_stderr ("*GLib-GIO:ERROR:*giowin32-private.c:*:_g_win32_extract_executable: assertion failed: (folded)*");
|
||||
g_test_trap_subprocess ("/appinfo/subprocess/win32-assert-broken-utf16_2", 0, 0);
|
||||
g_test_trap_assert_failed ();
|
||||
g_test_trap_assert_stderr ("*GLib-GIO:ERROR:*giowin32-private.c:*:_g_win32_extract_executable: assertion failed: (folded)*");
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/appinfo/utf16-strfuncs", test_utf16_strfuncs);
|
||||
g_test_add_func ("/appinfo/win32-extract-executable", test_win32_extract_executable);
|
||||
g_test_add_func ("/appinfo/win32-rundll32-fixup", test_win32_rundll32_fixup);
|
||||
g_test_add_func ("/appinfo/win32-parse-filename", test_win32_parse_filename);
|
||||
g_test_add_func ("/appinfo/win32-utf16-conversion-fail", test_fail_on_broken_utf16);
|
||||
|
||||
g_test_add_func ("/appinfo/subprocess/win32-assert-broken-utf16_1", do_fail_on_broken_utf16_1);
|
||||
g_test_add_func ("/appinfo/subprocess/win32-assert-broken-utf16_2", do_fail_on_broken_utf16_2);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user