| 
									
										
										
										
											2019-11-29 16:48:32 +00:00
										 |  |  |  | /* giowin32-private.c - private glib-gio functions for W32 GAppInfo
 | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * Copyright 2019 Руслан Ижбулатов | 
					
						
							|  |  |  |  |  * | 
					
						
							| 
									
										
										
										
											2022-05-18 09:12:45 +01:00
										 |  |  |  |  * SPDX-License-Identifier: LGPL-2.1-or-later | 
					
						
							|  |  |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-11-29 16:48:32 +00:00
										 |  |  |  |  * 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/>.
 | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 13:50:37 +00:00
										 |  |  |  | static gsize | 
					
						
							| 
									
										
										
										
											2019-11-29 16:48:32 +00:00
										 |  |  |  | g_utf16_len (const gunichar2 *str) | 
					
						
							|  |  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-02-04 13:50:37 +00:00
										 |  |  |  |   gsize result; | 
					
						
							| 
									
										
										
										
											2019-11-29 16:48:32 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   for (result = 0; str[0] != 0; str++, result++) | 
					
						
							|  |  |  |  |     ; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return result; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gunichar2 * | 
					
						
							|  |  |  |  | g_wcsdup (const gunichar2 *str, gssize str_len) | 
					
						
							|  |  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-02-04 13:50:37 +00:00
										 |  |  |  |   gsize str_len_unsigned; | 
					
						
							|  |  |  |  |   gsize str_size; | 
					
						
							| 
									
										
										
										
											2019-11-29 16:48:32 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_return_val_if_fail (str != NULL, NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 13:50:37 +00:00
										 |  |  |  |   if (str_len < 0) | 
					
						
							|  |  |  |  |     str_len_unsigned = g_utf16_len (str); | 
					
						
							|  |  |  |  |   else | 
					
						
							|  |  |  |  |     str_len_unsigned = (gsize) str_len; | 
					
						
							| 
									
										
										
										
											2019-11-29 16:48:32 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 13:50:37 +00:00
										 |  |  |  |   g_assert (str_len_unsigned <= G_MAXSIZE / sizeof (gunichar2) - 1); | 
					
						
							|  |  |  |  |   str_size = (str_len_unsigned + 1) * sizeof (gunichar2); | 
					
						
							| 
									
										
										
										
											2019-11-29 16:48:32 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 13:50:37 +00:00
										 |  |  |  |   return g_memdup2 (str, str_size); | 
					
						
							| 
									
										
										
										
											2019-11-29 16:48:32 +00:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 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; | 
					
						
							|  |  |  |  |               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). | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  | } |