#include #include #include #include #include #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; }