* Image Uploader - an inline images implementation for protocols without * support for such feature. * Copyright (C) 2014, Tomasz Wasilczyk <twasilczyk@pidgin.im> * 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 of the License, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA #include "gtkwebviewtoolbar.h" #include <json-glib/json-glib.h> #include <libsoup/soup.h> #define IMGUP_IMGUR_CLIENT_ID "b6d33c6bb80e1b6" #define IMGUP_PREF_PREFIX "/plugins/gtk/imgupload/" static PurplePlugin *plugin_handle = NULL; static SoupSession *session = NULL; imgup_upload_done(PidginWebView *webview, const gchar *url, const gchar *title); imgup_upload_failed(PidginWebView *webview); /****************************************************************************** ******************************************************************************/ imgup_conn_is_hooked(PurpleConnection *gc) return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gc), "imgupload-set")); /****************************************************************************** ******************************************************************************/ imgup_imgur_uploaded(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg, PidginWebView *webview = PIDGIN_WEBVIEW(_webview); const gchar *url, *title; if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { imgup_upload_failed(webview); parser = json_parser_new(); if (!json_parser_load_from_data(parser, msg->response_body->data, msg->response_body->length, NULL)) { purple_debug_warning("imgupload", "Invalid json got from imgur"); imgup_upload_failed(webview); result = json_node_get_object(json_parser_get_root(parser)); if (!json_object_get_boolean_member(result, "success")) { purple_debug_warning("imgupload", "imgur - not a success"); imgup_upload_failed(webview); result = json_object_get_object_member(result, "data"); url = json_object_get_string_member(result, "link"); title = g_object_get_data(G_OBJECT(msg), "imgupload-imgur-name"); imgup_upload_done(webview, url, title); g_object_set_data(G_OBJECT(msg), "imgupload-imgur-name", NULL); static PurpleHttpConnection * imgup_imgur_upload(PidginWebView *webview, PurpleImage *image) gchar *req_data, *img_data, *img_data_e; msg = soup_message_new("POST", "https://api.imgur.com/3/image"); soup_message_headers_replace(msg, "Authorization", "Client-ID " IMGUP_IMGUR_CLIENT_ID); /* TODO: make it a plain, multipart/form-data request */ img_data = g_base64_encode(purple_image_get_data(image), purple_image_get_data_size(image)); img_data_e = g_uri_escape_string(img_data, NULL, FALSE); req_data = g_strdup_printf("type=base64&image=%s", img_data_e); soup_message_set_request(msg, "application/x-www-form-urlencoded", SOUP_MESSAGE_TAKE, req_data, strlen(req_data)); g_object_set_data_full(G_OBJECT(msg), "imgupload-imgur-name", g_strdup(purple_image_get_friendly_filename(image)), soup_session_queue_message(session, msg, imgup_imgur_uploaded, webview); /****************************************************************************** * Image/link upload and insertion ******************************************************************************/ imgup_upload_finish(PidginWebView *webview) g_object_steal_data(G_OBJECT(webview), "imgupload-msg"); plswait = g_object_get_data(G_OBJECT(webview), "imgupload-plswait"); g_object_set_data(G_OBJECT(webview), "imgupload-plswait", NULL); purple_request_close(PURPLE_REQUEST_WAIT, plswait); imgup_upload_done(PidginWebView *webview, const gchar *url, const gchar *title) imgup_upload_finish(webview); if (!purple_prefs_get_bool(IMGUP_PREF_PREFIX "use_url_desc")) PidginWebViewButtons format; format = pidgin_webview_get_format_functions(webview); url_desc = format & PIDGIN_WEBVIEW_LINKDESC; pidgin_webview_insert_link(webview, url, url_desc ? title : NULL); imgup_upload_failed(PidginWebView *webview) imgup_upload_finish(webview); is_cancelled = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(webview), g_object_set_data(G_OBJECT(webview), "imgupload-cancelled", NULL); purple_debug_error("imgupload", "Failed uploading image"); imgup_upload_cancel_message(SoupMessage *msg) soup_session_cancel_message(session, msg, SOUP_STATUS_CANCELLED); imgup_upload_cancel(gpointer _webview) PidginWebView *webview = PIDGIN_WEBVIEW(_webview); g_object_set_data(G_OBJECT(webview), "imgupload-plswait", NULL); g_object_set_data(G_OBJECT(webview), "imgupload-cancelled", msg = g_object_steal_data(G_OBJECT(webview), "imgupload-msg"); soup_session_cancel_message(session, msg, SOUP_STATUS_CANCELLED); imgup_upload_start(PidginWebView *webview, PurpleImage *image, gpointer _gtkconv) PidginConversation *gtkconv = _gtkconv; PurpleConversation *conv = gtkconv->active_conv; if (!imgup_conn_is_hooked(purple_conversation_get_connection(conv))) msg = imgup_imgur_upload(webview, image); g_object_set_data_full(G_OBJECT(webview), "imgupload-msg", msg, (GDestroyNotify)imgup_upload_cancel_message); plswait = purple_request_wait(plugin_handle, _("Uploading image"), _("Please wait for image URL being retrieved..."), NULL, FALSE, imgup_upload_cancel, purple_request_cpar_from_conversation(conv), webview); g_object_set_data(G_OBJECT(webview), "imgupload-plswait", plswait); /****************************************************************************** ******************************************************************************/ imgup_pidconv_init(PidginConversation *gtkconv) webview = PIDGIN_WEBVIEW(gtkconv->entry); g_signal_connect(G_OBJECT(webview), "insert-image", G_CALLBACK(imgup_upload_start), gtkconv); imgup_pidconv_uninit(PidginConversation *gtkconv) webview = PIDGIN_WEBVIEW(gtkconv->entry); g_signal_handlers_disconnect_by_func(G_OBJECT(webview), G_CALLBACK(imgup_upload_start), gtkconv); imgup_conv_init(PurpleConversation *conv) gc = purple_conversation_get_connection(conv); if (!imgup_conn_is_hooked(gc)) purple_conversation_set_features(conv, purple_conversation_get_features(conv) & ~PURPLE_CONNECTION_FLAG_NO_IMAGES); g_object_set_data(G_OBJECT(conv), "imgupload-set", GINT_TO_POINTER(TRUE)); imgup_conv_uninit(PurpleConversation *conv) gc = purple_conversation_get_connection(conv); if (!imgup_conn_is_hooked(gc)) if (!g_object_get_data(G_OBJECT(conv), "imgupload-set")) purple_conversation_set_features(conv, purple_conversation_get_features(conv) | PURPLE_CONNECTION_FLAG_NO_IMAGES); g_object_set_data(G_OBJECT(conv), "imgupload-set", NULL); imgup_conn_init(PurpleConnection *gc) PurpleConnectionFlags flags; flags = purple_connection_get_flags(gc); if (!(flags & PURPLE_CONNECTION_FLAG_NO_IMAGES)) flags &= ~PURPLE_CONNECTION_FLAG_NO_IMAGES; purple_connection_set_flags(gc, flags); g_object_set_data(G_OBJECT(gc), "imgupload-set", GINT_TO_POINTER(TRUE)); imgup_conn_uninit(PurpleConnection *gc) if (!imgup_conn_is_hooked(gc)) purple_connection_set_flags(gc, purple_connection_get_flags(gc) | PURPLE_CONNECTION_FLAG_NO_IMAGES); g_object_set_data(G_OBJECT(gc), "imgupload-set", NULL); /****************************************************************************** ******************************************************************************/ imgup_prefs_ok(gpointer _unused, PurpleRequestFields *fields) use_url_desc = purple_request_fields_get_bool(fields, "use_url_desc"); purple_prefs_set_bool(IMGUP_PREF_PREFIX "use_url_desc", use_url_desc); imgup_prefs_get(PurplePlugin *plugin) PurpleRequestCommonParameters *cpar; PurpleRequestFields *fields; PurpleRequestFieldGroup *group; PurpleRequestField *field; fields = purple_request_fields_new(); group = purple_request_field_group_new(NULL); purple_request_fields_add_group(fields, group); field = purple_request_field_bool_new("use_url_desc", _("Use image filename as link description"), purple_prefs_get_bool(IMGUP_PREF_PREFIX "use_url_desc")); purple_request_field_group_add_field(group, field); cpar = purple_request_cpar_new(); purple_request_cpar_set_icon(cpar, PURPLE_REQUEST_ICON_DIALOG); handle = purple_request_fields(plugin, _("Image Uploader"), NULL, NULL, fields, _("OK"), (GCallback)imgup_prefs_ok, /****************************************************************************** ******************************************************************************/ static PidginPluginInfo * plugin_query(GError **error) const gchar * const authors[] = { "Tomasz Wasilczyk <twasilczyk@pidgin.im>", return pidgin_plugin_info_new( "name", N_("Image Uploader"), "version", DISPLAY_VERSION, "category", N_("Utility"), "summary", N_("Inline images implementation for protocols " "without such feature."), "description", N_("Adds inline images support for protocols " "lacking this feature by uploading them to the " "website", PURPLE_WEBSITE, "abi-version", PURPLE_ABI_VERSION, "pref-request-cb", imgup_prefs_get, plugin_load(PurplePlugin *plugin, GError **error) purple_prefs_add_none("/plugins"); purple_prefs_add_none("/plugins/gtk"); purple_prefs_add_none("/plugins/gtk/imgupload"); purple_prefs_add_bool(IMGUP_PREF_PREFIX "use_url_desc", TRUE); session = soup_session_new(); it = purple_connections_get_all(); for (; it; it = g_list_next(it)) { PurpleConnection *gc = it->data; it = purple_conversations_get_all(); for (; it; it = g_list_next(it)) { PurpleConversation *conv = it->data; if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) imgup_pidconv_init(PIDGIN_CONVERSATION(conv)); purple_signal_connect(purple_connections_get_handle(), PURPLE_CALLBACK(imgup_conn_init), NULL); purple_signal_connect(purple_connections_get_handle(), PURPLE_CALLBACK(imgup_conn_uninit), NULL); purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed", plugin, PURPLE_CALLBACK(imgup_pidconv_init), NULL); plugin_unload(PurplePlugin *plugin, GError **error) soup_session_abort(session); it = purple_conversations_get_all(); for (; it; it = g_list_next(it)) { PurpleConversation *conv = it->data; if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) imgup_pidconv_uninit(PIDGIN_CONVERSATION(conv)); it = purple_connections_get_all(); for (; it; it = g_list_next(it)) { PurpleConnection *gc = it->data; g_clear_object(&session); PURPLE_PLUGIN_INIT(imgupload, plugin_query, plugin_load, plugin_unload);