Merge branch 'appinfo-rundll32' into 'master'

Add rundll32 support to GAppInfo

Closes #1932

See merge request GNOME/glib!1259
This commit is contained in:
Philip Withnall 2020-01-20 10:36:29 +00:00
commit adee3b31dc
4 changed files with 1110 additions and 246 deletions

447
gio/giowin32-private.c Normal file
View 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).
*/
}

View File

@ -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;

View File

@ -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
View 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 ();
}