diff --git a/src/common/dbus/dbus-client.c b/src/common/dbus/dbus-client.c index 417d70c0..d3f1ac23 100644 --- a/src/common/dbus/dbus-client.c +++ b/src/common/dbus/dbus-client.c @@ -53,6 +53,33 @@ new_param_variant (const char *arg) return g_variant_new_tuple (args, 1); } +static gboolean +has_theme_argument (void) +{ + char *theme_path = NULL; + guint i; + + if (arg_url && zoitechat_theme_path_from_arg (arg_url, &theme_path)) + { + g_free (theme_path); + return TRUE; + } + + if (arg_urls) + { + for (i = 0; i < g_strv_length (arg_urls); i++) + { + if (zoitechat_theme_path_from_arg (arg_urls[i], &theme_path)) + { + g_free (theme_path); + return TRUE; + } + } + } + + return FALSE; +} + void zoitechat_remote (void) /* TODO: dbus_g_connection_unref (connection) are commented because it makes @@ -68,12 +95,17 @@ zoitechat_remote (void) GError *error = NULL; char *command = NULL; guint i; + gboolean allow_remote; /* if there is nothing to do, return now. */ - if (!arg_existing || !(arg_url || arg_urls || arg_command)) { + if (!(arg_url || arg_urls || arg_command)) { return; } + allow_remote = arg_existing || has_theme_argument (); + if (!allow_remote) + return; + arg_dont_autoconnect = TRUE; connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); diff --git a/src/common/outbound.c b/src/common/outbound.c index ac16dc23..aae7c830 100644 --- a/src/common/outbound.c +++ b/src/common/outbound.c @@ -3770,6 +3770,35 @@ cmd_url (struct session *sess, char *tbuf, char *word[], char *word_eol[]) { if (word[2][0]) { + char *theme_path = NULL; + if (zoitechat_theme_path_from_arg (word[2], &theme_path)) + { + GError *error = NULL; + char *basename = g_path_get_basename (theme_path); + char *dot = strrchr (basename, '.'); + char *message; + + if (dot) + *dot = '\0'; + + if (zoitechat_import_theme (theme_path, &error)) + { + message = g_strdup_printf (_("Theme \"%s\" imported."), basename); + fe_message (message, FE_MSG_INFO); + g_free (message); + } + else + { + fe_message (error ? error->message : _("Failed to import theme."), + FE_MSG_ERROR); + g_clear_error (&error); + } + + g_free (basename); + g_free (theme_path); + return TRUE; + } + char *server_name = NULL; char *port = NULL; char *channel = NULL; diff --git a/src/common/zoitechat.c b/src/common/zoitechat.c index adff6f7b..3cb494f8 100644 --- a/src/common/zoitechat.c +++ b/src/common/zoitechat.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,293 @@ struct session *current_tab; struct session *current_sess = 0; struct zoitechatprefs prefs; +gboolean +zoitechat_theme_path_from_arg (const char *arg, char **path_out) +{ + char *path = NULL; + const char *ext; + + if (!arg) + return FALSE; + + if (g_str_has_prefix (arg, "file://")) + path = g_filename_from_uri (arg, NULL, NULL); + else + path = g_strdup (arg); + + if (!path) + return FALSE; + + ext = strrchr (path, '.'); + if (!g_file_test (path, G_FILE_TEST_IS_REGULAR) || + !ext || + (g_ascii_strcasecmp (ext, ".zct") != 0 && + g_ascii_strcasecmp (ext, ".hct") != 0)) + { + g_free (path); + return FALSE; + } + + if (path_out) + *path_out = path; + else + g_free (path); + + return TRUE; +} + +#ifdef WIN32 +static gboolean +zoitechat_has_theme_argument (void) +{ + char *theme_path = NULL; + guint i; + + if (arg_url && zoitechat_theme_path_from_arg (arg_url, &theme_path)) + { + g_free (theme_path); + return TRUE; + } + + if (arg_urls) + { + for (i = 0; i < g_strv_length (arg_urls); i++) + { + if (zoitechat_theme_path_from_arg (arg_urls[i], &theme_path)) + { + g_free (theme_path); + return TRUE; + } + } + } + + return FALSE; +} + +static HWND +zoitechat_find_running_window (void) +{ + HWND hwnd = FindWindowA ("ZoiteChat", NULL); + + if (!hwnd) + hwnd = FindWindowA ("zoitechat", NULL); + if (!hwnd) + hwnd = FindWindowA (NULL, "ZoiteChat"); + + return hwnd; +} + +static gboolean +zoitechat_send_command_to_existing (HWND hwnd, const char *command) +{ + COPYDATASTRUCT copy_data; + DWORD_PTR send_result = 0; + + if (!hwnd || !command || !*command) + return FALSE; + + copy_data.dwData = 0; + copy_data.cbData = (DWORD)strlen (command) + 1; + copy_data.lpData = (void *)command; + + return SendMessageTimeoutA (hwnd, WM_COPYDATA, (WPARAM)NULL, + (LPARAM)©_data, + SMTO_ABORTIFHUNG | SMTO_BLOCK, + 5000, &send_result) != 0; +} + +static gboolean +zoitechat_remote_win32 (void) +{ + HWND hwnd; + gboolean allow_remote; + gboolean sent = FALSE; + + allow_remote = arg_existing || zoitechat_has_theme_argument (); + if (!allow_remote) + return FALSE; + + hwnd = zoitechat_find_running_window (); + if (!hwnd) + return FALSE; + + if (arg_url) + { + char *command = g_strdup_printf ("url %s", arg_url); + sent = zoitechat_send_command_to_existing (hwnd, command) || sent; + g_free (command); + } + else if (arg_command) + { + sent = zoitechat_send_command_to_existing (hwnd, arg_command) || sent; + } + + if (arg_urls) + { + guint i; + for (i = 0; i < g_strv_length (arg_urls); i++) + { + char *command = g_strdup_printf ("url %s", arg_urls[i]); + sent = zoitechat_send_command_to_existing (hwnd, command) || sent; + g_free (command); + } + g_strfreev (arg_urls); + arg_urls = NULL; + } + + return sent; +} +#endif + +gboolean +zoitechat_import_theme (const char *path, GError **error) +{ + char *themes_dir; + char *basename; + char *dot; + char *theme_dir; + char *argv[] = {"unzip", "-o", (char *)path, "-d", NULL, NULL}; + int status = 0; + gboolean ok; +#ifdef WIN32 + char *command = NULL; + char *powershell = NULL; + char *extractor = NULL; +#endif + + if (!path) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + _("No theme file specified.")); + return FALSE; + } + + themes_dir = g_build_filename (get_xdir (), "themes", NULL); + basename = g_path_get_basename (path); + if (!basename || basename[0] == '\0') + { + g_free (themes_dir); + g_free (basename); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + _("Failed to determine theme name.")); + return FALSE; + } + + dot = strrchr (basename, '.'); + if (dot) + *dot = '\0'; + + theme_dir = g_build_filename (themes_dir, basename, NULL); + if (g_mkdir_with_parents (theme_dir, 0700) != 0) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Failed to create theme directory.")); + g_free (theme_dir); + g_free (basename); + g_free (themes_dir); + return FALSE; + } + + argv[4] = theme_dir; +#ifdef WIN32 + extractor = g_find_program_in_path ("unzip"); + if (extractor) + { + argv[0] = extractor; + ok = g_spawn_sync (NULL, argv, NULL, 0, NULL, NULL, + NULL, NULL, &status, error); + } + else + { + powershell = g_find_program_in_path ("powershell.exe"); + if (!powershell) + powershell = g_find_program_in_path ("powershell"); + + if (!powershell) + { + g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT, + _("No archive extractor was found.")); + ok = FALSE; + } + else + { + GString *escaped_path = g_string_new ("'"); + GString *escaped_dir = g_string_new ("'"); + const char *cursor; + + for (cursor = path; *cursor != '\0'; cursor++) + { + if (*cursor == '\'') + g_string_append (escaped_path, "''"); + else + g_string_append_c (escaped_path, *cursor); + } + g_string_append_c (escaped_path, '\''); + + for (cursor = theme_dir; *cursor != '\0'; cursor++) + { + if (*cursor == '\'') + g_string_append (escaped_dir, "''"); + else + g_string_append_c (escaped_dir, *cursor); + } + g_string_append_c (escaped_dir, '\''); + + command = g_strdup_printf ("Expand-Archive -LiteralPath %s -DestinationPath %s -Force", + escaped_path->str, + escaped_dir->str); + g_string_free (escaped_path, TRUE); + g_string_free (escaped_dir, TRUE); + + { + char *ps_argv[] = {powershell, "-NoProfile", "-NonInteractive", "-Command", command, NULL}; + ok = g_spawn_sync (NULL, ps_argv, NULL, 0, NULL, NULL, + NULL, NULL, &status, error); + } + } + } +#else + ok = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, + NULL, NULL, &status, error); +#endif + if (!ok) + { +#ifdef WIN32 + g_free (command); + g_free (powershell); + g_free (extractor); +#endif + g_free (theme_dir); + g_free (basename); + g_free (themes_dir); + return FALSE; + } + + if (!g_spawn_check_exit_status (status, error)) + { +#ifdef WIN32 + g_free (command); + g_free (powershell); + g_free (extractor); +#endif + g_free (theme_dir); + g_free (basename); + g_free (themes_dir); + return FALSE; + } + +#ifdef WIN32 + g_free (command); + g_free (powershell); + g_free (extractor); +#endif + + g_free (theme_dir); + g_free (basename); + g_free (themes_dir); + return TRUE; +} + /* * Update the priority queue of the "interesting sessions" * (sess_list_by_lastact). @@ -448,41 +736,36 @@ irc_init (session *sess) if (arg_url != NULL) { theme_path = NULL; - if (g_str_has_prefix (arg_url, "file://")) - theme_path = g_filename_from_uri (arg_url, NULL, NULL); - else - theme_path = g_strdup (arg_url); - - if (theme_path != NULL) + if (zoitechat_theme_path_from_arg (arg_url, &theme_path)) { - const char *ext = strrchr (theme_path, '.'); + GError *error = NULL; + char *basename = g_path_get_basename (theme_path); + char *dot = strrchr (basename, '.'); + char *message; - if (g_file_test (theme_path, G_FILE_TEST_IS_REGULAR) && - ext != NULL && - (g_ascii_strcasecmp (ext, ".zct") == 0 || - g_ascii_strcasecmp (ext, ".hct") == 0)) + if (dot) + *dot = '\0'; + + if (zoitechat_import_theme (theme_path, &error)) { - char *themes_dir = g_build_filename (get_xdir (), "themes", NULL); - char *basename = g_path_get_basename (theme_path); - char *message = g_strdup_printf (_("Theme file \"%s\" opened.\n" - "To install it, move it into:\n%s\n" - "Then apply it from Settings → Themes."), - basename, themes_dir); - - g_mkdir_with_parents (themes_dir, 0700); + message = g_strdup_printf (_("Theme \"%s\" imported."), basename); fe_message (message, FE_MSG_INFO); - fe_open_url (themes_dir); - g_free (message); - g_free (basename); - g_free (themes_dir); } else { - buf = g_strdup_printf ("server %s", arg_url); - handle_command (sess, buf, FALSE); - g_free (buf); + fe_message (error ? error->message : _("Failed to import theme."), + FE_MSG_ERROR); + g_clear_error (&error); } + + g_free (basename); + } + else + { + buf = g_strdup_printf ("server %s", arg_url); + handle_command (sess, buf, FALSE); + g_free (buf); } g_free (theme_path); @@ -495,41 +778,36 @@ irc_init (session *sess) for (i = 0; i < g_strv_length (arg_urls); i++) { theme_path = NULL; - if (g_str_has_prefix (arg_urls[i], "file://")) - theme_path = g_filename_from_uri (arg_urls[i], NULL, NULL); - else - theme_path = g_strdup (arg_urls[i]); - - if (theme_path != NULL) + if (zoitechat_theme_path_from_arg (arg_urls[i], &theme_path)) { - const char *ext = strrchr (theme_path, '.'); + GError *error = NULL; + char *basename = g_path_get_basename (theme_path); + char *dot = strrchr (basename, '.'); + char *message; - if (g_file_test (theme_path, G_FILE_TEST_IS_REGULAR) && - ext != NULL && - (g_ascii_strcasecmp (ext, ".zct") == 0 || - g_ascii_strcasecmp (ext, ".hct") == 0)) + if (dot) + *dot = '\0'; + + if (zoitechat_import_theme (theme_path, &error)) { - char *themes_dir = g_build_filename (get_xdir (), "themes", NULL); - char *basename = g_path_get_basename (theme_path); - char *message = g_strdup_printf (_("Theme file \"%s\" opened.\n" - "To install it, move it into:\n%s\n" - "Then apply it from Settings → Themes."), - basename, themes_dir); - - g_mkdir_with_parents (themes_dir, 0700); + message = g_strdup_printf (_("Theme \"%s\" imported."), basename); fe_message (message, FE_MSG_INFO); - fe_open_url (themes_dir); - g_free (message); - g_free (basename); - g_free (themes_dir); } else { - buf = g_strdup_printf ("%s %s", i==0? "server" : "newserver", arg_urls[i]); - handle_command (sess, buf, FALSE); - g_free (buf); + fe_message (error ? error->message : _("Failed to import theme."), + FE_MSG_ERROR); + g_clear_error (&error); } + + g_free (basename); + } + else + { + buf = g_strdup_printf ("%s %s", i==0? "server" : "newserver", arg_urls[i]); + handle_command (sess, buf, FALSE); + g_free (buf); } g_free (theme_path); @@ -1162,7 +1440,12 @@ main (int argc, char *argv[]) ret = fe_args (argc, argv); if (ret != -1) return ret; - + +#ifdef WIN32 + if (zoitechat_remote_win32 ()) + return 0; +#endif + #ifdef USE_DBUS zoitechat_remote (); #endif diff --git a/src/common/zoitechat.h b/src/common/zoitechat.h index 41eb98ac..893beea5 100644 --- a/src/common/zoitechat.h +++ b/src/common/zoitechat.h @@ -29,6 +29,9 @@ #ifndef HEXCHAT_H #define HEXCHAT_H +gboolean zoitechat_theme_path_from_arg (const char *arg, char **path_out); +gboolean zoitechat_import_theme (const char *path, GError **error); + #ifdef USE_OPENSSL #ifdef __APPLE__ #define __AVAILABILITYMACROS__ diff --git a/src/fe-gtk/maingui.c b/src/fe-gtk/maingui.c index ce367149..2dbba560 100644 --- a/src/fe-gtk/maingui.c +++ b/src/fe-gtk/maingui.c @@ -78,6 +78,9 @@ enum static void mg_create_entry (session *sess, GtkWidget *box); static void mg_create_search (session *sess, GtkWidget *box); +#ifdef G_OS_WIN32 +static GdkFilterReturn mg_win32_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data); +#endif static void mg_link_irctab (session *sess, int focus); static session_gui static_mg_gui; @@ -3143,6 +3146,9 @@ mg_create_topwindow (session *sess) { GtkWidget *win; GtkWidget *table; +#ifdef G_OS_WIN32 + GdkWindow *parent_win; +#endif if (sess->type == SESS_DIALOG) win = gtkutil_window_new ("ZoiteChat", NULL, @@ -3218,6 +3224,11 @@ mg_create_topwindow (session *sess) mg_place_userlist_and_chanview (sess->gui); gtk_widget_show (win); + +#ifdef G_OS_WIN32 + parent_win = gtk_widget_get_window (win); + gdk_window_add_filter (parent_win, mg_win32_filter, NULL); +#endif } static gboolean @@ -3246,13 +3257,34 @@ mg_tabwindow_de_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) #ifdef G_OS_WIN32 static GdkFilterReturn -mg_time_change (GdkXEvent *xevent, GdkEvent *event, gpointer data) +mg_win32_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data) { MSG *msg = (MSG*)xevent; + if (!msg) + return GDK_FILTER_CONTINUE; + if (msg->message == WM_TIMECHANGE) { _tzset(); + return GDK_FILTER_CONTINUE; + } + + if (msg->message == WM_COPYDATA) + { + COPYDATASTRUCT *copy_data = (COPYDATASTRUCT *)msg->lParam; + + if (copy_data && copy_data->lpData && copy_data->cbData > 0 && current_sess) + { + char *command = g_strndup ((const char *)copy_data->lpData, copy_data->cbData); + + if (command) + { + handle_command (current_sess, command, FALSE); + g_free (command); + return GDK_FILTER_REMOVE; + } + } } return GDK_FILTER_CONTINUE; @@ -3334,7 +3366,7 @@ mg_create_tabwindow (session *sess) #ifdef G_OS_WIN32 parent_win = gtk_widget_get_window (win); - gdk_window_add_filter (parent_win, mg_time_change, NULL); + gdk_window_add_filter (parent_win, mg_win32_filter, NULL); #endif }