diff options
Diffstat (limited to 'plugins/checkupdate/gedit-check-update-plugin.c')
-rwxr-xr-x | plugins/checkupdate/gedit-check-update-plugin.c | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/plugins/checkupdate/gedit-check-update-plugin.c b/plugins/checkupdate/gedit-check-update-plugin.c new file mode 100755 index 00000000..aa9f7a5e --- /dev/null +++ b/plugins/checkupdate/gedit-check-update-plugin.c @@ -0,0 +1,697 @@ +/* + * Copyright (C) 2009 - Ignacio Casal Quinteiro <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gedit-check-update-plugin.h" + +#include <glib/gi18n-lib.h> +#include <gedit/gedit-debug.h> +#include <gedit/gedit-utils.h> +#include <libsoup/soup.h> +#include <gtk/gtk.h> +#include <stdlib.h> + +#include <mateconf/mateconf-client.h> + +#if !GTK_CHECK_VERSION(2, 17, 1) +#include <gedit/gedit-message-area.h> +#endif + +#define MATECONF_KEY_BASE "/apps/gedit-2/plugins/checkupdate" +#define MATECONF_KEY_IGNORE_VERSION MATECONF_KEY_BASE "/ignore_version" + +#define WINDOW_DATA_KEY "GeditCheckUpdatePluginWindowData" + +#define VERSION_PLACE "<a href=\"[0-9]\\.[0-9]+/\">" + +#ifdef G_OS_WIN32 +#define GEDIT_URL "http://ftp.acc.umu.se/pub/mate/binaries/win32/gedit/" +#define FILE_REGEX "gedit\\-setup\\-[0-9]+\\.[0-9]+\\.[0-9]+(\\-[0-9]+)?\\.exe" +#else +#define GEDIT_URL "http://ftp.acc.umu.se/pub/mate/binaries/mac/gedit/" +#define FILE_REGEX "gedit\\-[0-9]+\\.[0-9]+\\.[0-9]+(\\-[0-9]+)?\\.dmg" +#endif + +#ifdef OS_OSX +#include "gedit/osx/gedit-osx.h" +#endif + +#define GEDIT_CHECK_UPDATE_PLUGIN_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), \ + GEDIT_TYPE_CHECK_UPDATE_PLUGIN, \ + GeditCheckUpdatePluginPrivate)) + +GEDIT_PLUGIN_REGISTER_TYPE (GeditCheckUpdatePlugin, gedit_check_update_plugin) + +struct _GeditCheckUpdatePluginPrivate +{ + SoupSession *session; + + MateConfClient *mateconf_client; +}; + +typedef struct +{ + GeditCheckUpdatePlugin *plugin; + + gchar *url; + gchar *version; +} WindowData; + +static void +free_window_data (gpointer data) +{ + WindowData *window_data; + + if (data == NULL) + return; + + window_data = (WindowData *)data; + + g_free (window_data->url); + g_free (window_data->version); + g_slice_free (WindowData, data); +} + +static void +gedit_check_update_plugin_init (GeditCheckUpdatePlugin *plugin) +{ + plugin->priv = GEDIT_CHECK_UPDATE_PLUGIN_GET_PRIVATE (plugin); + + gedit_debug_message (DEBUG_PLUGINS, + "GeditCheckUpdatePlugin initializing"); + + plugin->priv->session = soup_session_async_new (); + + plugin->priv->mateconf_client = mateconf_client_get_default (); + + mateconf_client_add_dir (plugin->priv->mateconf_client, + MATECONF_KEY_BASE, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + NULL); +} + +static void +gedit_check_update_plugin_dispose (GObject *object) +{ + GeditCheckUpdatePlugin *plugin = GEDIT_CHECK_UPDATE_PLUGIN (object); + + if (plugin->priv->session != NULL) + { + g_object_unref (plugin->priv->session); + plugin->priv->session = NULL; + } + + if (plugin->priv->mateconf_client != NULL) + { + mateconf_client_suggest_sync (plugin->priv->mateconf_client, NULL); + + g_object_unref (G_OBJECT (plugin->priv->mateconf_client)); + + plugin->priv->mateconf_client = NULL; + } + + gedit_debug_message (DEBUG_PLUGINS, + "GeditCheckUpdatePlugin disposing"); + + G_OBJECT_CLASS (gedit_check_update_plugin_parent_class)->dispose (object); +} + +static void +gedit_check_update_plugin_finalize (GObject *object) +{ + gedit_debug_message (DEBUG_PLUGINS, + "GeditCheckUpdatePlugin finalizing"); + + G_OBJECT_CLASS (gedit_check_update_plugin_parent_class)->finalize (object); +} + +static void +set_contents (GtkWidget *infobar, + GtkWidget *contents) +{ +#if !GTK_CHECK_VERSION (2, 17, 1) + gedit_message_area_set_contents (GEDIT_MESSAGE_AREA (infobar), + contents); +#else + GtkWidget *content_area; + + content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar)); + gtk_container_add (GTK_CONTAINER (content_area), contents); +#endif +} + +static void +set_message_area_text_and_icon (GtkWidget *message_area, + const gchar *icon_stock_id, + const gchar *primary_text, + const gchar *secondary_text) +{ + GtkWidget *hbox_content; + GtkWidget *image; + GtkWidget *vbox; + gchar *primary_markup; + gchar *secondary_markup; + GtkWidget *primary_label; + GtkWidget *secondary_label; + + hbox_content = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox_content); + + image = gtk_image_new_from_stock (icon_stock_id, GTK_ICON_SIZE_DIALOG); + gtk_widget_show (image); + gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_widget_show (vbox); + gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); + + primary_markup = g_strdup_printf ("<b>%s</b>", primary_text); + primary_label = gtk_label_new (primary_markup); + g_free (primary_markup); + gtk_widget_show (primary_label); + gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); + gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5); + GTK_WIDGET_SET_FLAGS (primary_label, GTK_CAN_FOCUS); + gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + + if (secondary_text != NULL) + { + secondary_markup = g_strdup_printf ("<small>%s</small>", + secondary_text); + secondary_label = gtk_label_new (secondary_markup); + g_free (secondary_markup); + gtk_widget_show (secondary_label); + gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (secondary_label, GTK_CAN_FOCUS); + gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); + gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5); + } + + set_contents (message_area, hbox_content); +} + +static void +on_response_cb (GtkWidget *infobar, + gint response_id, + GeditWindow *window) +{ + if (response_id == GTK_RESPONSE_YES) + { + GError *error = NULL; + WindowData *data; + + data = g_object_get_data (G_OBJECT (window), + WINDOW_DATA_KEY); + +#ifdef OS_OSX + gedit_osx_show_url (data->url); +#else + gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (window)), + data->url, + GDK_CURRENT_TIME, + &error); +#endif + if (error != NULL) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("There was an error displaying the URI.")); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", error->message); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + gtk_widget_show (dialog); + + g_error_free (error); + } + } + else if (response_id == GTK_RESPONSE_NO) + { + WindowData *data; + + data = g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY); + + mateconf_client_set_string (data->plugin->priv->mateconf_client, + MATECONF_KEY_IGNORE_VERSION, + data->version, + NULL); + } + + g_object_set_data (G_OBJECT (window), + WINDOW_DATA_KEY, + NULL); + + gtk_widget_destroy (infobar); +} + +static GtkWidget * +create_infobar (GeditWindow *window, + const gchar *version) +{ + GtkWidget *infobar; + gchar *message; + +#if !GTK_CHECK_VERSION (2, 17, 1) + infobar = gedit_message_area_new (); + + gedit_message_area_add_stock_button_with_text (GEDIT_MESSAGE_AREA (infobar), + _("_Download"), + GTK_STOCK_SAVE, + GTK_RESPONSE_YES); + gedit_message_area_add_stock_button_with_text (GEDIT_MESSAGE_AREA (infobar), + _("_Ignore Version"), + GTK_STOCK_DISCARD, + GTK_RESPONSE_NO); + gedit_message_area_add_button (GEDIT_MESSAGE_AREA (infobar), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); +#else + GtkWidget *button; + + infobar = gtk_info_bar_new (); + + button = gedit_gtk_button_new_with_stock_icon (_("_Download"), + GTK_STOCK_SAVE); + gtk_widget_show (button); + + gtk_info_bar_add_action_widget (GTK_INFO_BAR (infobar), + button, + GTK_RESPONSE_YES); + + button = gedit_gtk_button_new_with_stock_icon (_("_Ignore Version"), + GTK_STOCK_DISCARD); + gtk_widget_show (button); + + gtk_info_bar_add_action_widget (GTK_INFO_BAR (infobar), + button, + GTK_RESPONSE_NO); + + gtk_info_bar_add_button (GTK_INFO_BAR (infobar), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + + gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), + GTK_MESSAGE_INFO); +#endif + + message = g_strdup_printf ("%s (%s)", _("There is a new version of gedit"), version); + set_message_area_text_and_icon (infobar, + "gtk-dialog-info", + message, + _("You can download the new version of gedit" + " by clicking on the download button or" + " ignore that version and wait for a new one")); + + g_free (message); + + g_signal_connect (infobar, "response", + G_CALLBACK (on_response_cb), + window); + + return infobar; +} + +static void +pack_infobar (GtkWidget *window, + GtkWidget *infobar) +{ + GtkWidget *vbox; + + vbox = gtk_bin_get_child (GTK_BIN (window)); + + gtk_box_pack_start (GTK_BOX (vbox), infobar, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (vbox), infobar, 2); +} + +static gchar * +get_file (const gchar *text, + const gchar *regex_place) +{ + GRegex *regex; + GMatchInfo *match_info; + gchar *word = NULL; + + regex = g_regex_new (regex_place, 0, 0, NULL); + g_regex_match (regex, text, 0, &match_info); + while (g_match_info_matches (match_info)) + { + g_free (word); + + word = g_match_info_fetch (match_info, 0); + + g_match_info_next (match_info, NULL); + } + g_match_info_free (match_info); + g_regex_unref (regex); + + return word; +} + +static void +get_numbers (const gchar *version, + gint *major, + gint *minor, + gint *micro) +{ + gchar **split; + gint num = 2; + + if (micro != NULL) + num = 3; + + split = g_strsplit (version, ".", num); + *major = atoi (split[0]); + *minor = atoi (split[1]); + if (micro != NULL) + *micro = atoi (split[2]); + + g_strfreev (split); +} + +static gboolean +newer_version (const gchar *v1, + const gchar *v2, + gboolean with_micro) +{ + gboolean newer = FALSE; + gint major1, minor1, micro1; + gint major2, minor2, micro2; + + if (v1 == NULL || v2 == NULL) + return FALSE; + + if (with_micro) + { + get_numbers (v1, &major1, &minor1, µ1); + get_numbers (v2, &major2, &minor2, µ2); + } + else + { + get_numbers (v1, &major1, &minor1, NULL); + get_numbers (v2, &major2, &minor2, NULL); + } + + if (major1 > major2) + { + newer = TRUE; + } + else if (minor1 > minor2 && major1 == major2) + { + newer = TRUE; + } + else if (with_micro && micro1 > micro2 && minor1 == minor2) + { + newer = TRUE; + } + + return newer; +} + +static gchar * +parse_file_version (const gchar *file) +{ + gchar *p, *aux; + + p = (gchar *)file; + + while (*p != '\0' && !g_ascii_isdigit (*p)) + { + p++; + } + + if (*p == '\0') + return NULL; + + aux = g_strrstr (p, "-"); + if (aux == NULL) + aux = g_strrstr (p, "."); + + return g_strndup (p, aux - p); +} + +static gchar * +get_ignore_version (GeditCheckUpdatePlugin *plugin) +{ + return mateconf_client_get_string (plugin->priv->mateconf_client, + MATECONF_KEY_IGNORE_VERSION, + NULL); +} + +static void +parse_page_file (SoupSession *session, + SoupMessage *msg, + GeditWindow *window) +{ + if (msg->status_code == SOUP_STATUS_OK) + { + gchar *file; + gchar *file_version; + gchar *ignore_version; + WindowData *data; + + data = g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY); + + file = get_file (msg->response_body->data, FILE_REGEX); + file_version = parse_file_version (file); + ignore_version = get_ignore_version (data->plugin); + + if (newer_version (file_version, VERSION, TRUE) && + (ignore_version == NULL || *ignore_version == '\0' || + newer_version (file_version, ignore_version, TRUE))) + { + GtkWidget *infobar; + WindowData *data; + gchar *file_url; + + data = g_object_get_data (G_OBJECT (window), + WINDOW_DATA_KEY); + + file_url = g_strconcat (data->url, file, NULL); + + g_free (data->url); + data->url = file_url; + data->version = g_strdup (file_version); + + infobar = create_infobar (window, file_version); + pack_infobar (GTK_WIDGET (window), infobar); + gtk_widget_show (infobar); + } + + g_free (ignore_version); + g_free (file_version); + g_free (file); + } + else + { + g_object_set_data (G_OBJECT (window), + WINDOW_DATA_KEY, + NULL); + } +} + +static gboolean +is_unstable (const gchar *version) +{ + gchar **split; + gint minor; + gboolean unstable = TRUE;; + + split = g_strsplit (version, ".", 2); + minor = atoi (split[1]); + g_strfreev (split); + + if ((minor % 2) == 0) + unstable = FALSE; + + return unstable; +} + +static gchar * +get_file_page_version (const gchar *text, + const gchar *regex_place) +{ + GRegex *regex; + GMatchInfo *match_info; + GString *string = NULL; + gchar *unstable = NULL; + gchar *stable = NULL; + + regex = g_regex_new (regex_place, 0, 0, NULL); + g_regex_match (regex, text, 0, &match_info); + while (g_match_info_matches (match_info)) + { + gint end; + gint i; + + g_match_info_fetch_pos (match_info, 0, NULL, &end); + + string = g_string_new (""); + + i = end; + while (text[i] != '/') + { + string = g_string_append_c (string, text[i]); + i++; + } + + if (is_unstable (string->str)) + { + g_free (unstable); + unstable = g_string_free (string, FALSE); + } + else + { + g_free (stable); + stable = g_string_free (string, FALSE); + } + + g_match_info_next (match_info, NULL); + } + g_match_info_free (match_info); + g_regex_unref (regex); + + if ((GEDIT_MINOR_VERSION % 2) == 0) + { + g_free (unstable); + + return stable; + } + else + { + /* We need to check that stable isn't newer than unstable */ + if (newer_version (stable, unstable, FALSE)) + { + g_free (unstable); + + return stable; + } + else + { + g_free (stable); + + return unstable; + } + } +} + +static void +parse_page_version (SoupSession *session, + SoupMessage *msg, + GeditWindow *window) +{ + if (msg->status_code == SOUP_STATUS_OK) + { + gchar *version; + SoupMessage *msg2; + WindowData *data; + + data = g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY); + + version = get_file_page_version (msg->response_body->data, + VERSION_PLACE); + + data->url = g_strconcat (GEDIT_URL, version, "/", NULL); + g_free (version); + msg2 = soup_message_new ("GET", data->url); + + soup_session_queue_message (session, msg2, + (SoupSessionCallback)parse_page_file, + window); + } + else + { + g_object_set_data (G_OBJECT (window), + WINDOW_DATA_KEY, + NULL); + } +} + +static void +impl_activate (GeditPlugin *plugin, + GeditWindow *window) +{ + SoupMessage *msg; + WindowData *data; + + gedit_debug (DEBUG_PLUGINS); + + data = g_slice_new (WindowData); + data->plugin = GEDIT_CHECK_UPDATE_PLUGIN (plugin); + data->url = NULL; + data->version = NULL; + + g_object_set_data_full (G_OBJECT (window), + WINDOW_DATA_KEY, + data, + free_window_data); + + msg = soup_message_new ("GET", GEDIT_URL); + + soup_session_queue_message (GEDIT_CHECK_UPDATE_PLUGIN (plugin)->priv->session, msg, + (SoupSessionCallback)parse_page_version, + window); +} + +static void +impl_deactivate (GeditPlugin *plugin, + GeditWindow *window) +{ + + gedit_debug (DEBUG_PLUGINS); + + soup_session_abort (GEDIT_CHECK_UPDATE_PLUGIN (plugin)->priv->session); + + g_object_set_data (G_OBJECT (window), + WINDOW_DATA_KEY, + NULL); +} + +static void +gedit_check_update_plugin_class_init (GeditCheckUpdatePluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeditPluginClass *plugin_class = GEDIT_PLUGIN_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (GeditCheckUpdatePluginPrivate)); + + object_class->finalize = gedit_check_update_plugin_finalize; + object_class->dispose = gedit_check_update_plugin_dispose; + + plugin_class->activate = impl_activate; + plugin_class->deactivate = impl_deactivate; +} |