(root)/
glib-2.79.0/
gio/
tests/
win32-appinfo.c
       1  /* GLib testing framework examples and tests
       2   * Copyright (C) 2019 Руслан Ижбулатов <lrn1986@gmail.com>
       3   *
       4   * SPDX-License-Identifier: LicenseRef-old-glib-tests
       5   *
       6   * This work is provided "as is"; redistribution and modification
       7   * in whole or in part, in any medium, physical or electronic is
       8   * permitted without restriction.
       9   *
      10   * This work is distributed in the hope that it will be useful,
      11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      13   *
      14   * In no event shall the authors or contributors be liable for any
      15   * direct, indirect, incidental, special, exemplary, or consequential
      16   * damages (including, but not limited to, procurement of substitute
      17   * goods or services; loss of use, data, or profits; or business
      18   * interruption) however caused and on any theory of liability, whether
      19   * in contract, strict liability, or tort (including negligence or
      20   * otherwise) arising in any way out of the use of this software, even
      21   * if advised of the possibility of such damage.
      22   */
      23  
      24  #include <glib/glib.h>
      25  #include <gio/gio.h>
      26  #include <stdlib.h>
      27  
      28  #include "../giowin32-private.c"
      29  
      30  static int
      31  g_utf16_cmp0 (const gunichar2 *str1,
      32                const gunichar2 *str2)
      33  {
      34    if (!str1)
      35      return -(str1 != str2);
      36    if (!str2)
      37      return str1 != str2;
      38  
      39    while (TRUE)
      40      {
      41        if (str1[0] > str2[0])
      42          return 1;
      43        else if (str1[0] < str2[0])
      44          return -1;
      45        else if (str1[0] == 0 && str2[0] == 0)
      46          return 0;
      47  
      48        str1++;
      49        str2++;
      50      }
      51  }
      52  
      53  #define g_assert_cmputf16(s1, cmp, s2, s1u8, s2u8) \
      54  G_STMT_START { \
      55    const gunichar2 *__s1 = (s1), *__s2 = (s2); \
      56    if (g_utf16_cmp0 (__s1, __s2) cmp 0) ; else \
      57      g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
      58                                  #s1u8 " " #cmp " " #s2u8, s1u8, #cmp, s2u8); \
      59  } G_STMT_END
      60  
      61  static void
      62  test_utf16_strfuncs (void)
      63  {
      64    gsize i;
      65  
      66    struct {
      67      gsize len;
      68      const gunichar2 utf16[10];
      69      const gchar *utf8;
      70      const gchar *utf8_folded;
      71    } string_cases[] = {
      72      {
      73        0,
      74        { 0x0000 },
      75        "",
      76        "",
      77      },
      78      {
      79        1,
      80        { 0x0020, 0x0000 },
      81        " ",
      82        " ",
      83      },
      84      {
      85        2,
      86        { 0x0020, 0xd800, 0x0000 },
      87        NULL,
      88        NULL,
      89      },
      90    };
      91  
      92    for (i = 0; i < G_N_ELEMENTS (string_cases); i++)
      93      {
      94        gsize len;
      95        gunichar2 *str;
      96        gboolean success;
      97        gchar *utf8;
      98        gchar *utf8_folded;
      99  
     100        len = g_utf16_len (string_cases[i].utf16);
     101        g_assert_cmpuint (len, ==, string_cases[i].len);
     102  
     103        str = (gunichar2 *) g_utf16_find_basename (string_cases[i].utf16, -1);
     104        /* This only works because all testcases lack separators */
     105        g_assert_true (string_cases[i].utf16 == str);
     106  
     107        str = g_wcsdup (string_cases[i].utf16, string_cases[i].len);
     108        g_assert_cmpmem (string_cases[i].utf16, len, str, len);
     109        g_free (str);
     110  
     111        str = g_wcsdup (string_cases[i].utf16, -1);
     112        g_assert_cmpmem (string_cases[i].utf16, len, str, len);
     113        g_free (str);
     114  
     115        success = g_utf16_to_utf8_and_fold (string_cases[i].utf16, -1, NULL, NULL);
     116  
     117        if (string_cases[i].utf8 == NULL)
     118          g_assert_false (success);
     119        else
     120          g_assert_true (success);
     121  
     122        utf8 = NULL;
     123        success = g_utf16_to_utf8_and_fold (string_cases[i].utf16, -1, &utf8, NULL);
     124  
     125        if (string_cases[i].utf8 != NULL)
     126          {
     127            g_assert_true (success);
     128            g_assert_cmpstr (string_cases[i].utf8, ==, utf8);
     129            /* This only works because all testcases lack separators */
     130            g_assert_true (utf8 == g_utf8_find_basename (utf8, len));
     131          }
     132  
     133        g_free (utf8);
     134  
     135        utf8 = NULL;
     136        utf8_folded = NULL;
     137        success = g_utf16_to_utf8_and_fold (string_cases[i].utf16, -1, &utf8, &utf8_folded);
     138  
     139        if (string_cases[i].utf8 != NULL)
     140          {
     141            g_assert_true (success);
     142            g_assert_cmpstr (string_cases[i].utf8_folded, ==, utf8_folded);
     143          }
     144  
     145        g_free (utf8);
     146        g_free (utf8_folded);
     147      }
     148  }
     149  
     150  struct {
     151    const char *orig;
     152    const char *executable;
     153    const char *executable_basename;
     154    gboolean    is_rundll32;
     155    const char *fixed;
     156  } rundll32_commandlines[] = {
     157    {
     158      "%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen %1",
     159      "%SystemRoot%\\System32\\rundll32.exe",
     160      "rundll32.exe",
     161      TRUE,
     162      "%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\"  ImageView_Fullscreen %1",
     163    },
     164    {
     165      "%SystemRoot%/System32/rundll32.exe \"%ProgramFiles%/Windows Photo Viewer/PhotoViewer.dll\", ImageView_Fullscreen %1",
     166      "%SystemRoot%/System32/rundll32.exe",
     167      "rundll32.exe",
     168      TRUE,
     169      "%SystemRoot%/System32/rundll32.exe \"%ProgramFiles%/Windows Photo Viewer/PhotoViewer.dll\"  ImageView_Fullscreen %1",
     170    },
     171    {
     172      "%SystemRoot%\\System32/rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen %1",
     173      "%SystemRoot%\\System32/rundll32.exe",
     174      "rundll32.exe",
     175      TRUE,
     176      "%SystemRoot%\\System32/rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\"  ImageView_Fullscreen %1",
     177    },
     178    {
     179      "\"some path with spaces\\rundll32.exe\" \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen %1",
     180      "some path with spaces\\rundll32.exe",
     181      "rundll32.exe",
     182      TRUE,
     183      "\"some path with spaces\\rundll32.exe\" \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\"  ImageView_Fullscreen %1",
     184    },
     185    {
     186      "    \"some path with spaces\\rundll32.exe\"\"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\",ImageView_Fullscreen %1",
     187      "some path with spaces\\rundll32.exe",
     188      "rundll32.exe",
     189      TRUE,
     190      "    \"some path with spaces\\rundll32.exe\"\"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\" ImageView_Fullscreen %1",
     191    },
     192    {
     193      "rundll32.exe foo.bar,baz",
     194      "rundll32.exe",
     195      "rundll32.exe",
     196      TRUE,
     197      "rundll32.exe foo.bar baz",
     198    },
     199    {
     200      "  rundll32.exe foo.bar,baz",
     201      "rundll32.exe",
     202      "rundll32.exe",
     203      TRUE,
     204      "  rundll32.exe foo.bar baz",
     205    },
     206    {
     207      "rundll32.exe",
     208      "rundll32.exe",
     209      "rundll32.exe",
     210      FALSE,
     211      NULL,
     212    },
     213    {
     214      "rundll32.exe ,foobar",
     215      "rundll32.exe",
     216      "rundll32.exe",
     217      FALSE,
     218      NULL,
     219    },
     220    {
     221      "rundll32.exe   ,foobar",
     222      "rundll32.exe",
     223      "rundll32.exe",
     224      FALSE,
     225      NULL,
     226    },
     227    {
     228      "rundll32.exe foo.dll",
     229      "rundll32.exe",
     230      "rundll32.exe",
     231      FALSE,
     232      NULL,
     233    },
     234    {
     235      "rundll32.exe \"foo bar\",baz",
     236      "rundll32.exe",
     237      "rundll32.exe",
     238      TRUE,
     239      "rundll32.exe \"foo bar\" baz",
     240    },
     241    {
     242      "\"rundll32.exe\" \"foo bar\",baz",
     243      "rundll32.exe",
     244      "rundll32.exe",
     245      TRUE,
     246      "\"rundll32.exe\" \"foo bar\" baz",
     247    },
     248    {
     249      "\"rundll32.exe\" \"foo bar\",, , ,,, , ,,baz",
     250      "rundll32.exe",
     251      "rundll32.exe",
     252      TRUE,
     253      "\"rundll32.exe\" \"foo bar\" , , ,,, , ,,baz",
     254    },
     255    {
     256      "\"rundll32.exe\" foo.bar,,,,,,,,,baz",
     257      "rundll32.exe",
     258      "rundll32.exe",
     259      TRUE,
     260      "\"rundll32.exe\" foo.bar ,,,,,,,,baz",
     261    },
     262    {
     263      "\"rundll32.exe\" foo.bar baz",
     264      "rundll32.exe",
     265      "rundll32.exe",
     266      TRUE,
     267      "\"rundll32.exe\" foo.bar baz",
     268    },
     269    {
     270      "\"RuNdlL32.exe\" foo.bar baz",
     271      "RuNdlL32.exe",
     272      "RuNdlL32.exe",
     273      TRUE,
     274      "\"RuNdlL32.exe\" foo.bar baz",
     275    },
     276    {
     277      "%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll,\" ImageView_Fullscreen %1",
     278      "%SystemRoot%\\System32\\rundll32.exe",
     279      "rundll32.exe",
     280      TRUE,
     281      "%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll,\" ImageView_Fullscreen %1",
     282    },
     283    {
     284      "\"rundll32.exe\" \"foo bar,\"baz",
     285      "rundll32.exe",
     286      "rundll32.exe",
     287      TRUE,
     288      "\"rundll32.exe\" \"foo bar,\"baz",
     289    },
     290    {
     291      "\"rundll32.exe\" some,thing",
     292      "rundll32.exe",
     293      "rundll32.exe",
     294      TRUE,
     295      "\"rundll32.exe\" some thing",
     296    },
     297    {
     298      "\"rundll32.exe\" some,",
     299      "rundll32.exe",
     300      "rundll32.exe",
     301      FALSE,
     302      "\"rundll32.exe\" some,",
     303    },
     304    /* These filenames are not allowed on Windows, but our function doesn't care about that */
     305    {
     306      "run\"dll32.exe foo\".bar,baz",
     307      "run\"dll32.exe",
     308      "run\"dll32.exe",
     309      FALSE,
     310      NULL,
     311    },
     312    {
     313      "run,dll32.exe foo.bar,baz",
     314      "run,dll32.exe",
     315      "run,dll32.exe",
     316      FALSE,
     317      NULL,
     318    },
     319    {
     320      "\"rundll32.exe\" some, thing",
     321      "rundll32.exe",
     322      "rundll32.exe",
     323      TRUE,
     324      "\"rundll32.exe\" some  thing",
     325    },
     326    /* Commands with "rundll32" (without the .exe suffix) do exist,
     327     * but GLib currently does not recognize them, so there's no point
     328     * in testing these.
     329     */
     330  };
     331  
     332  static void
     333  test_win32_rundll32_fixup (void)
     334  {
     335    gsize i;
     336  
     337    for (i = 0; i < G_N_ELEMENTS (rundll32_commandlines); i++)
     338      {
     339        gunichar2 *argument;
     340        gunichar2 *expected;
     341  
     342        if (!rundll32_commandlines[i].is_rundll32)
     343          continue;
     344  
     345        argument = g_utf8_to_utf16 (rundll32_commandlines[i].orig, -1, NULL, NULL, NULL);
     346        expected = g_utf8_to_utf16 (rundll32_commandlines[i].fixed, -1, NULL, NULL, NULL);
     347  
     348        g_assert_nonnull (argument);
     349        g_assert_nonnull (expected);
     350        _g_win32_fixup_broken_microsoft_rundll_commandline (argument);
     351  
     352        g_assert_cmputf16 (argument, ==, expected, rundll32_commandlines[i].orig, rundll32_commandlines[i].fixed);
     353  
     354        g_free (argument);
     355        g_free (expected);
     356      }
     357  }
     358  
     359  static void
     360  test_win32_extract_executable (void)
     361  {
     362    gsize i;
     363  
     364    for (i = 0; i < G_N_ELEMENTS (rundll32_commandlines); i++)
     365      {
     366        gunichar2 *argument;
     367        gchar *dll_function;
     368        gchar *executable;
     369        gchar *executable_basename;
     370        gchar *executable_folded;
     371        gchar *executable_folded_basename;
     372  
     373        argument = g_utf8_to_utf16 (rundll32_commandlines[i].orig, -1, NULL, NULL, NULL);
     374  
     375        _g_win32_extract_executable (argument, NULL, NULL, NULL, NULL, &dll_function);
     376  
     377        if (rundll32_commandlines[i].is_rundll32)
     378          g_assert_nonnull (dll_function);
     379        else
     380          g_assert_null (dll_function);
     381  
     382        g_free (dll_function);
     383  
     384        executable = NULL;
     385        executable_basename = NULL;
     386        executable_folded = NULL;
     387        executable_folded_basename = NULL;
     388        _g_win32_extract_executable (argument, &executable, &executable_basename, &executable_folded, &executable_folded_basename, NULL);
     389  
     390        g_assert_cmpstr (rundll32_commandlines[i].executable, ==, executable);
     391        g_assert_cmpstr (rundll32_commandlines[i].executable_basename, ==, executable_basename);
     392        g_assert_nonnull (executable_folded);
     393  
     394        g_free (executable);
     395        g_free (executable_folded);
     396  
     397        /* Check the corner-case where we don't want to know where basename is */
     398        executable = NULL;
     399        executable_folded = NULL;
     400        _g_win32_extract_executable (argument, &executable, NULL, &executable_folded, NULL, NULL);
     401  
     402        g_assert_cmpstr (rundll32_commandlines[i].executable, ==, executable);
     403        g_assert_nonnull (executable_folded);
     404  
     405        g_free (executable);
     406        g_free (executable_folded);
     407  
     408        g_free (argument);
     409      }
     410  }
     411  
     412  static void
     413  test_win32_parse_filename (void)
     414  {
     415    gsize i;
     416  
     417    for (i = 0; i < G_N_ELEMENTS (rundll32_commandlines); i++)
     418      {
     419        gunichar2 *argument;
     420        argument = g_utf8_to_utf16 (rundll32_commandlines[i].orig, -1, NULL, NULL, NULL);
     421        /* Just checking that it doesn't blow up on various (sometimes incorrect) strings */
     422        _g_win32_parse_filename (argument, FALSE, NULL, NULL, NULL, NULL);
     423        g_free (argument);
     424      }
     425  }
     426  
     427  static void
     428  do_fail_on_broken_utf16_1 (void)
     429  {
     430    const gunichar2 utf16[] = { 0xd800, 0x0000 };
     431    _g_win32_extract_executable (utf16, NULL, NULL, NULL, NULL, NULL);
     432  }
     433  
     434  static void
     435  do_fail_on_broken_utf16_2 (void)
     436  {
     437    /* "rundll32.exe <invalid utf16> r" */
     438    gchar *dll_function;
     439    const gunichar2 utf16[] = { 0x0072, 0x0075, 0x006E, 0x0064, 0x006C, 0x006C, 0x0033, 0x0032,
     440                                0x002E, 0x0065, 0x0078, 0x0065, 0x0020, 0xd800, 0x0020, 0x0072, 0x0000 };
     441    _g_win32_extract_executable (utf16, NULL, NULL, NULL, NULL, &dll_function);
     442  }
     443  
     444  static void
     445  test_fail_on_broken_utf16 (void)
     446  {
     447    g_test_trap_subprocess ("/appinfo/subprocess/win32-assert-broken-utf16_1", 0,
     448                            G_TEST_SUBPROCESS_DEFAULT);
     449    g_test_trap_assert_failed ();
     450    g_test_trap_assert_stderr ("*GLib-GIO:ERROR:*giowin32-private.c:*:_g_win32_extract_executable: assertion failed: (folded)*");
     451    g_test_trap_subprocess ("/appinfo/subprocess/win32-assert-broken-utf16_2", 0,
     452                            G_TEST_SUBPROCESS_DEFAULT);
     453    g_test_trap_assert_failed ();
     454    g_test_trap_assert_stderr ("*GLib-GIO:ERROR:*giowin32-private.c:*:_g_win32_extract_executable: assertion failed: (folded)*");
     455  }
     456  
     457  int
     458  main (int   argc,
     459        char *argv[])
     460  {
     461    g_test_init (&argc, &argv, NULL);
     462  
     463    g_test_add_func ("/appinfo/utf16-strfuncs", test_utf16_strfuncs);
     464    g_test_add_func ("/appinfo/win32-extract-executable", test_win32_extract_executable);
     465    g_test_add_func ("/appinfo/win32-rundll32-fixup", test_win32_rundll32_fixup);
     466    g_test_add_func ("/appinfo/win32-parse-filename", test_win32_parse_filename);
     467    g_test_add_func ("/appinfo/win32-utf16-conversion-fail", test_fail_on_broken_utf16);
     468  
     469    g_test_add_func ("/appinfo/subprocess/win32-assert-broken-utf16_1", do_fail_on_broken_utf16_1);
     470    g_test_add_func ("/appinfo/subprocess/win32-assert-broken-utf16_2", do_fail_on_broken_utf16_2);
     471  
     472    return g_test_run ();
     473  }