mirror of
https://github.com/rozniak/xfce-winxp-tc.git
synced 2026-01-26 19:49:44 +00:00
532 lines
11 KiB
C
532 lines
11 KiB
C
#include <gdk/gdk.h>
|
|
#include <gio/gdesktopappinfo.h>
|
|
#include <glib.h>
|
|
#include <sys/wait.h>
|
|
#include <wintc/comgtk.h>
|
|
|
|
#include "../public/desktop.h"
|
|
#include "../public/mime.h"
|
|
#include "../public/exec.h"
|
|
|
|
//
|
|
// LOCAL TYPEDEFS
|
|
//
|
|
typedef gboolean (*CmdParseFunc) (
|
|
const gchar* cmdline,
|
|
gchar** out_cmdline,
|
|
GError** out_error
|
|
);
|
|
|
|
//
|
|
// FORWARD DECLARATIONS
|
|
//
|
|
static gboolean parse_file_in_cmdline(
|
|
const gchar* cmdline,
|
|
gchar** out_cmdline,
|
|
GError** out_error
|
|
);
|
|
|
|
static gboolean parse_unc_path_in_cmdline(
|
|
const gchar* cmdline,
|
|
gchar** out_cmdline,
|
|
GError** out_error
|
|
);
|
|
|
|
static gboolean parse_url_in_cmdline(
|
|
const gchar* cmdline,
|
|
gchar** out_cmdline,
|
|
GError** out_error
|
|
);
|
|
|
|
static void set_display(
|
|
const gchar* display
|
|
);
|
|
|
|
static gchar** true_shell_parse_argv(
|
|
const gchar* cmdline,
|
|
GError** out_error
|
|
);
|
|
|
|
//
|
|
// FILE SCOPE VARS
|
|
//
|
|
static CmdParseFunc parsers[] = {
|
|
&parse_unc_path_in_cmdline,
|
|
&parse_url_in_cmdline,
|
|
&parse_file_in_cmdline,
|
|
NULL
|
|
};
|
|
|
|
//
|
|
// PUBLIC FUNCTIONS
|
|
//
|
|
gboolean wintc_launch_command(
|
|
const gchar* cmdline,
|
|
GError** out_error
|
|
)
|
|
{
|
|
gchar** argv;
|
|
gchar* display = NULL;
|
|
gboolean done_parsing = FALSE;
|
|
GError* error = NULL;
|
|
CmdParseFunc* pparser;
|
|
gchar* real_cmdline = NULL;
|
|
gchar* tmp_cmdline = g_strdup(cmdline);
|
|
gboolean success;
|
|
|
|
WINTC_SAFE_REF_CLEAR(out_error);
|
|
|
|
WINTC_LOG_USER_DEBUG("Launching %s", cmdline);
|
|
|
|
// Iterate through parsers
|
|
//
|
|
pparser = parsers;
|
|
|
|
while (!done_parsing && *pparser != NULL)
|
|
{
|
|
done_parsing =
|
|
(*pparser) (tmp_cmdline, &real_cmdline, &error);
|
|
|
|
if (error != NULL)
|
|
{
|
|
g_propagate_error(out_error, error);
|
|
return FALSE;
|
|
}
|
|
|
|
WINTC_LOG_USER_DEBUG("Parse result: %s", real_cmdline);
|
|
|
|
wintc_strsteal(&tmp_cmdline, &real_cmdline);
|
|
|
|
pparser++;
|
|
}
|
|
|
|
// Pre-processing complete, set real_cmdline
|
|
//
|
|
real_cmdline = tmp_cmdline;
|
|
|
|
WINTC_LOG_USER_DEBUG("Parsed command line: %s", real_cmdline);
|
|
|
|
// Parse the command line into an argument vector
|
|
//
|
|
argv = true_shell_parse_argv(real_cmdline, &error);
|
|
|
|
g_free(real_cmdline);
|
|
|
|
if (argv == NULL)
|
|
{
|
|
g_propagate_error(out_error, error);
|
|
return FALSE;
|
|
}
|
|
|
|
// Launch now!
|
|
//
|
|
display =
|
|
g_strdup(
|
|
gdk_display_get_name(gdk_display_get_default())
|
|
);
|
|
|
|
success =
|
|
g_spawn_async(
|
|
NULL,
|
|
argv,
|
|
NULL,
|
|
0,
|
|
(GSpawnChildSetupFunc) set_display,
|
|
display,
|
|
NULL,
|
|
&error
|
|
);
|
|
|
|
g_free(display);
|
|
g_strfreev(argv);
|
|
|
|
if (!success)
|
|
{
|
|
WINTC_LOG_USER_DEBUG("Failed to launch: %s", error->message);
|
|
|
|
g_propagate_error(out_error, error);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
WINTC_LOG_USER_DEBUG("Done.");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// PRIVATE FUNCTIONS
|
|
//
|
|
static gboolean parse_file_in_cmdline(
|
|
const gchar* cmdline,
|
|
gchar** out_cmdline,
|
|
GError** out_error
|
|
)
|
|
{
|
|
GError* error = NULL;
|
|
gchar* file_mime;
|
|
gchar* handler_cmdline;
|
|
GDesktopAppInfo* handler_entry;
|
|
|
|
WINTC_SAFE_REF_CLEAR(out_cmdline);
|
|
WINTC_SAFE_REF_CLEAR(out_error);
|
|
|
|
// See if we can query the MIME type
|
|
//
|
|
file_mime =
|
|
wintc_query_mime_for_file(
|
|
cmdline,
|
|
&error
|
|
);
|
|
|
|
if (file_mime == NULL)
|
|
{
|
|
// We don't consider 'file not found' an error here, since we may resolve
|
|
// it later
|
|
//
|
|
if (error->code != G_FILE_ERROR_NOENT)
|
|
{
|
|
g_propagate_error(out_error, error);
|
|
}
|
|
|
|
WINTC_SAFE_REF_SET(out_cmdline, g_strdup(cmdline));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// If it's not an executable, then parse a handler
|
|
//
|
|
if (
|
|
file_mime != NULL &&
|
|
g_strcmp0(file_mime, "application/x-sharedlib") != 0
|
|
)
|
|
{
|
|
WINTC_LOG_USER_DEBUG("Not an executable, will look for handler.");
|
|
|
|
handler_entry =
|
|
wintc_query_mime_handler(
|
|
file_mime,
|
|
&error
|
|
);
|
|
|
|
if (handler_entry == NULL)
|
|
{
|
|
WINTC_LOG_USER_DEBUG("I have nothing to handle the file!");
|
|
|
|
g_propagate_error(out_error, error);
|
|
WINTC_SAFE_REF_SET(out_cmdline, g_strdup(cmdline));
|
|
|
|
g_free(file_mime);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// We found a handler, build the cmdline now and return
|
|
//
|
|
handler_cmdline = wintc_desktop_app_info_get_command(handler_entry);
|
|
|
|
WINTC_SAFE_REF_SET(
|
|
out_cmdline,
|
|
g_strdup_printf(
|
|
"%s \"%s\"",
|
|
handler_cmdline,
|
|
cmdline
|
|
)
|
|
);
|
|
|
|
g_clear_object(&handler_entry);
|
|
g_free(handler_cmdline);
|
|
g_free(file_mime);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// It's an executable, pass on unchanged
|
|
//
|
|
WINTC_SAFE_REF_SET(out_cmdline, g_strdup(cmdline));
|
|
|
|
g_free(file_mime);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean parse_unc_path_in_cmdline(
|
|
const gchar* cmdline,
|
|
gchar** out_cmdline,
|
|
GError** out_error
|
|
)
|
|
{
|
|
static GRegex* unc_regex = NULL;
|
|
|
|
gchar* host;
|
|
GError* error = NULL;
|
|
gint match_count;
|
|
GMatchInfo* match_info;
|
|
gchar* path = NULL;
|
|
gchar* uri = NULL;
|
|
|
|
WINTC_SAFE_REF_CLEAR(out_cmdline);
|
|
WINTC_SAFE_REF_CLEAR(out_error);
|
|
|
|
// Create regex if it hasn't already been created
|
|
//
|
|
if (unc_regex == NULL)
|
|
{
|
|
unc_regex =
|
|
g_regex_new(
|
|
"^\\\\\\\\([0-9A-Za-z._-]+)(\\\\.+)?",
|
|
0,
|
|
0,
|
|
&error
|
|
);
|
|
|
|
if (unc_regex == NULL)
|
|
{
|
|
WINTC_LOG_USER_DEBUG("Failed to create the UNC path regex.");
|
|
|
|
g_propagate_error(out_error, error);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Examine command line
|
|
//
|
|
WINTC_LOG_USER_DEBUG("Checking if %s looks like a UNC path...", cmdline);
|
|
|
|
g_regex_match(unc_regex, cmdline, 0, &match_info);
|
|
|
|
match_count = g_match_info_get_match_count(match_info);
|
|
|
|
// Command line isn't a UNC path, return a duplicate of the original
|
|
//
|
|
if (match_count == 0)
|
|
{
|
|
WINTC_LOG_USER_DEBUG("Nope!");
|
|
|
|
g_match_info_free(match_info);
|
|
|
|
WINTC_SAFE_REF_SET(out_cmdline, g_strdup(cmdline));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Command line IS a UNC path, we need to retrieve the target host and path
|
|
//
|
|
WINTC_LOG_USER_DEBUG("Yeah, looks like a UNC path.");
|
|
|
|
host = g_strdup(g_match_info_fetch(match_info, 1));
|
|
|
|
if (match_count == 3) // We also have a path!
|
|
{
|
|
path =
|
|
wintc_strsubst(
|
|
g_match_info_fetch(match_info, 2),
|
|
"\\",
|
|
"/"
|
|
);
|
|
}
|
|
|
|
g_match_info_free(match_info);
|
|
|
|
// Construct URI (doesn't matter if path is NULL, the func stops at the
|
|
// first NULL anyway)
|
|
//
|
|
uri = g_strconcat("smb://", host, path, NULL);
|
|
|
|
// Clean up and return
|
|
//
|
|
g_free(host);
|
|
g_free(path);
|
|
|
|
WINTC_SAFE_REF_SET(out_cmdline, uri);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean parse_url_in_cmdline(
|
|
const gchar* cmdline,
|
|
gchar** out_cmdline,
|
|
GError** out_error
|
|
)
|
|
{
|
|
static GRegex* url_regex = NULL;
|
|
|
|
GError* error = NULL;
|
|
GDesktopAppInfo* handler_entry;
|
|
gchar* handler_cmdline;
|
|
GMatchInfo* match_info;
|
|
gchar* mime_type;
|
|
gchar* uri_scheme;
|
|
|
|
WINTC_SAFE_REF_CLEAR(out_cmdline);
|
|
WINTC_SAFE_REF_CLEAR(out_error);
|
|
|
|
// Create regex if it hasn't already been created
|
|
//
|
|
if (url_regex == NULL)
|
|
{
|
|
url_regex =
|
|
g_regex_new(
|
|
"^([A-Za-z-]+)://",
|
|
0,
|
|
0,
|
|
&error
|
|
);
|
|
|
|
if (url_regex == NULL)
|
|
{
|
|
WINTC_LOG_USER_DEBUG("Failed to create the URL regex.");
|
|
|
|
g_propagate_error(out_error, error);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Examine command line
|
|
//
|
|
WINTC_LOG_USER_DEBUG("Checking if %s looks like a URL...", cmdline);
|
|
|
|
g_regex_match(url_regex, cmdline, 0, &match_info);
|
|
|
|
// Command line isn't a URL, return a duplicate of the original
|
|
//
|
|
if (g_match_info_get_match_count(match_info) == 0)
|
|
{
|
|
WINTC_LOG_USER_DEBUG("Nope!");
|
|
|
|
g_match_info_free(match_info);
|
|
|
|
WINTC_SAFE_REF_SET(out_cmdline, g_strdup(cmdline));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Command line IS a URL, retrieve the scheme
|
|
//
|
|
WINTC_LOG_USER_DEBUG("Yeah, looks like a URL.");
|
|
|
|
uri_scheme = g_match_info_fetch(match_info, 1);
|
|
|
|
g_match_info_free(match_info);
|
|
|
|
// Special case: if the scheme is file:// then convert to just the file
|
|
// path
|
|
//
|
|
if (g_ascii_strcasecmp(uri_scheme, "file") == 0)
|
|
{
|
|
WINTC_SAFE_REF_SET(
|
|
out_cmdline,
|
|
g_uri_unescape_string(
|
|
cmdline + strlen("file://"),
|
|
NULL
|
|
)
|
|
);
|
|
|
|
g_free(uri_scheme);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Continue with normal handling - look up a handler for the scheme
|
|
//
|
|
mime_type = g_strdup_printf(
|
|
"x-scheme-handler/%s",
|
|
uri_scheme
|
|
);
|
|
|
|
handler_entry = wintc_query_mime_handler(mime_type, &error);
|
|
|
|
g_free(mime_type);
|
|
g_free(uri_scheme);
|
|
|
|
if (handler_entry == NULL)
|
|
{
|
|
WINTC_LOG_USER_DEBUG("I have nothing to handle the URL!");
|
|
|
|
g_propagate_error(out_error, error);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Output the constructed cmdline, the application cmdline with the URL as the
|
|
// argument
|
|
//
|
|
handler_cmdline = wintc_desktop_app_info_get_command(handler_entry);
|
|
|
|
WINTC_SAFE_REF_SET(
|
|
out_cmdline,
|
|
g_strdup_printf(
|
|
"%s %s",
|
|
handler_cmdline,
|
|
cmdline
|
|
)
|
|
);
|
|
|
|
g_clear_object(&handler_entry);
|
|
g_free(handler_cmdline);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void set_display(
|
|
const gchar* display
|
|
)
|
|
{
|
|
g_setenv("DISPLAY", display, TRUE);
|
|
}
|
|
|
|
static gchar** true_shell_parse_argv(
|
|
const gchar* cmdline,
|
|
GError** out_error
|
|
)
|
|
{
|
|
WINTC_SAFE_REF_CLEAR(out_error);
|
|
|
|
gchar** argv;
|
|
GError* error = NULL;
|
|
|
|
// Parse cmdline into argv
|
|
//
|
|
g_shell_parse_argv(
|
|
cmdline,
|
|
NULL,
|
|
&argv,
|
|
&error
|
|
);
|
|
|
|
if (error != NULL)
|
|
{
|
|
WINTC_LOG_USER_DEBUG("Failed to parse command line: %s", cmdline);
|
|
|
|
g_propagate_error(out_error, error);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Resolve path for executable (we might only have the name)
|
|
//
|
|
gchar* resolved_path = g_find_program_in_path(argv[0]);
|
|
|
|
if (resolved_path == NULL)
|
|
{
|
|
g_set_error(
|
|
out_error,
|
|
G_FILE_ERROR,
|
|
G_FILE_ERROR_NOENT,
|
|
"Cannot find executable '%s'.",
|
|
argv[0]
|
|
);
|
|
|
|
g_strfreev(argv);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
g_free(argv[0]);
|
|
argv[0] = resolved_path;
|
|
|
|
return argv;
|
|
}
|