2 * Image Uploader - an inline images implementation for protocols without
3 * support for such feature.
5 * Copyright (C) 2014, Tomasz Wasilczyk <twasilczyk@pidgin.im>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 #include "glibcompat.h"
29 #include "gtk3compat.h"
31 #include "gtkplugin.h"
33 #include "gtkwebviewtoolbar.h"
34 #include "pidginstock.h"
36 #include <json-glib/json-glib.h>
38 #define IMGUP_IMGUR_CLIENT_ID "b6d33c6bb80e1b6"
39 #define IMGUP_PREF_PREFIX "/plugins/gtk/imgupload/"
41 static PurplePlugin
*plugin_handle
= NULL
;
44 imgup_upload_done(PidginWebView
*webview
, const gchar
*url
, const gchar
*title
);
46 imgup_upload_failed(PidginWebView
*webview
);
49 /******************************************************************************
51 ******************************************************************************/
54 imgup_conn_is_hooked(PurpleConnection
*gc
)
56 return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gc
), "imgupload-set"));
60 /******************************************************************************
61 * Imgur implementation
62 ******************************************************************************/
65 imgup_imgur_uploaded(PurpleHttpConnection
*hc
, PurpleHttpResponse
*resp
,
72 PidginWebView
*webview
= PIDGIN_WEBVIEW(_webview
);
73 const gchar
*url
, *title
;
75 if (!purple_http_response_is_successful(resp
)) {
76 imgup_upload_failed(webview
);
80 data
= purple_http_response_get_data(resp
, &data_len
);
81 parser
= json_parser_new();
82 if (!json_parser_load_from_data(parser
, data
, data_len
, NULL
)) {
83 purple_debug_warning("imgupload", "Invalid json got from imgur");
85 imgup_upload_failed(webview
);
89 result
= json_node_get_object(json_parser_get_root(parser
));
91 if (!json_object_get_boolean_member(result
, "success")) {
92 g_object_unref(parser
);
94 purple_debug_warning("imgupload", "imgur - not a success");
96 imgup_upload_failed(webview
);
100 result
= json_object_get_object_member(result
, "data");
101 url
= json_object_get_string_member(result
, "link");
103 title
= g_object_get_data(G_OBJECT(webview
), "imgupload-imgur-name");
105 imgup_upload_done(webview
, url
, title
);
107 g_object_unref(parser
);
108 g_object_set_data(G_OBJECT(webview
), "imgupload-imgur-name", NULL
);
111 static PurpleHttpConnection
*
112 imgup_imgur_upload(PidginWebView
*webview
, PurpleImage
*image
)
114 PurpleHttpRequest
*req
;
115 PurpleHttpConnection
*hc
;
116 gchar
*req_data
, *img_data
, *img_data_e
;
118 req
= purple_http_request_new("https://api.imgur.com/3/image");
119 purple_http_request_set_method(req
, "POST");
120 purple_http_request_header_set(req
, "Authorization",
121 "Client-ID " IMGUP_IMGUR_CLIENT_ID
);
123 /* TODO: make it a plain, multipart/form-data request */
124 img_data
= purple_base64_encode(purple_image_get_data(image
),
125 purple_image_get_size(image
));
126 img_data_e
= g_uri_escape_string(img_data
, NULL
, FALSE
);
128 req_data
= g_strdup_printf("type=base64&image=%s", img_data_e
);
131 purple_http_request_header_set(req
, "Content-Type",
132 "application/x-www-form-urlencoded");
133 purple_http_request_set_contents(req
, req_data
, -1);
136 /* TODO: set it to hc, not webview (after gobjectifying it) */
137 g_object_set_data_full(G_OBJECT(webview
), "imgupload-imgur-name",
138 g_strdup(purple_image_get_friendly_filename(image
)), g_free
);
140 hc
= purple_http_request(NULL
, req
, imgup_imgur_uploaded
, webview
);
141 purple_http_request_unref(req
);
146 /******************************************************************************
147 * Image/link upload and insertion
148 ******************************************************************************/
151 imgup_upload_finish(PidginWebView
*webview
)
155 g_object_steal_data(G_OBJECT(webview
), "imgupload-hc");
156 plswait
= g_object_get_data(G_OBJECT(webview
), "imgupload-plswait");
157 g_object_set_data(G_OBJECT(webview
), "imgupload-plswait", NULL
);
160 purple_request_close(PURPLE_REQUEST_WAIT
, plswait
);
164 imgup_upload_done(PidginWebView
*webview
, const gchar
*url
, const gchar
*title
)
168 imgup_upload_finish(webview
);
170 if (!purple_prefs_get_bool(IMGUP_PREF_PREFIX
"use_url_desc"))
173 PidginWebViewButtons format
;
175 format
= pidgin_webview_get_format_functions(webview
);
176 url_desc
= format
& PIDGIN_WEBVIEW_LINKDESC
;
179 pidgin_webview_insert_link(webview
, url
, url_desc
? title
: NULL
);
183 imgup_upload_failed(PidginWebView
*webview
)
185 gboolean is_cancelled
;
187 imgup_upload_finish(webview
);
189 is_cancelled
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(webview
),
190 "imgupload-cancelled"));
191 g_object_set_data(G_OBJECT(webview
), "imgupload-cancelled", NULL
);
194 purple_debug_error("imgupload", "Failed uploading image");
198 imgup_upload_cancel(gpointer _webview
)
200 PurpleHttpConnection
*hc
;
201 PidginWebView
*webview
= PIDGIN_WEBVIEW(_webview
);
203 g_object_set_data(G_OBJECT(webview
), "imgupload-plswait", NULL
);
204 g_object_set_data(G_OBJECT(webview
), "imgupload-cancelled",
205 GINT_TO_POINTER(TRUE
));
206 hc
= g_object_get_data(G_OBJECT(webview
), "imgupload-hc");
208 purple_http_conn_cancel(hc
);
212 imgup_upload_start(PidginWebView
*webview
, PurpleImage
*image
, gpointer _gtkconv
)
214 PidginConversation
*gtkconv
= _gtkconv
;
215 PurpleConversation
*conv
= gtkconv
->active_conv
;
216 PurpleHttpConnection
*hc
;
219 if (!imgup_conn_is_hooked(purple_conversation_get_connection(conv
)))
222 hc
= imgup_imgur_upload(webview
, image
);
223 g_object_set_data_full(G_OBJECT(webview
), "imgupload-hc",
224 hc
, (GDestroyNotify
)purple_http_conn_cancel
);
226 plswait
= purple_request_wait(plugin_handle
, _("Uploading image"),
227 _("Please wait for image URL being retrieved..."),
228 NULL
, FALSE
, imgup_upload_cancel
,
229 purple_request_cpar_from_conversation(conv
), webview
);
230 g_object_set_data(G_OBJECT(webview
), "imgupload-plswait", plswait
);
236 /******************************************************************************
238 ******************************************************************************/
241 imgup_pidconv_init(PidginConversation
*gtkconv
)
243 PidginWebView
*webview
;
245 webview
= PIDGIN_WEBVIEW(gtkconv
->entry
);
247 g_signal_connect(G_OBJECT(webview
), "insert-image",
248 G_CALLBACK(imgup_upload_start
), gtkconv
);
252 imgup_pidconv_uninit(PidginConversation
*gtkconv
)
254 PidginWebView
*webview
;
256 webview
= PIDGIN_WEBVIEW(gtkconv
->entry
);
258 g_signal_handlers_disconnect_by_func(G_OBJECT(webview
),
259 G_CALLBACK(imgup_upload_start
), gtkconv
);
263 imgup_conv_init(PurpleConversation
*conv
)
265 PurpleConnection
*gc
;
267 gc
= purple_conversation_get_connection(conv
);
270 if (!imgup_conn_is_hooked(gc
))
273 purple_conversation_set_features(conv
,
274 purple_conversation_get_features(conv
) &
275 ~PURPLE_CONNECTION_FLAG_NO_IMAGES
);
277 g_object_set_data(G_OBJECT(conv
), "imgupload-set", GINT_TO_POINTER(TRUE
));
281 imgup_conv_uninit(PurpleConversation
*conv
)
283 PurpleConnection
*gc
;
285 gc
= purple_conversation_get_connection(conv
);
287 if (!imgup_conn_is_hooked(gc
))
290 if (!g_object_get_data(G_OBJECT(conv
), "imgupload-set"))
294 purple_conversation_set_features(conv
,
295 purple_conversation_get_features(conv
) |
296 PURPLE_CONNECTION_FLAG_NO_IMAGES
);
298 g_object_set_data(G_OBJECT(conv
), "imgupload-set", NULL
);
302 imgup_conn_init(PurpleConnection
*gc
)
304 PurpleConnectionFlags flags
;
306 flags
= purple_connection_get_flags(gc
);
308 if (!(flags
& PURPLE_CONNECTION_FLAG_NO_IMAGES
))
311 flags
&= ~PURPLE_CONNECTION_FLAG_NO_IMAGES
;
312 purple_connection_set_flags(gc
, flags
);
314 g_object_set_data(G_OBJECT(gc
), "imgupload-set", GINT_TO_POINTER(TRUE
));
318 imgup_conn_uninit(PurpleConnection
*gc
)
320 if (!imgup_conn_is_hooked(gc
))
323 purple_connection_set_flags(gc
, purple_connection_get_flags(gc
) |
324 PURPLE_CONNECTION_FLAG_NO_IMAGES
);
326 g_object_set_data(G_OBJECT(gc
), "imgupload-set", NULL
);
329 /******************************************************************************
331 ******************************************************************************/
334 imgup_prefs_ok(gpointer _unused
, PurpleRequestFields
*fields
)
336 gboolean use_url_desc
;
338 use_url_desc
= purple_request_fields_get_bool(fields
, "use_url_desc");
340 purple_prefs_set_bool(IMGUP_PREF_PREFIX
"use_url_desc", use_url_desc
);
344 imgup_prefs_get(PurplePlugin
*plugin
)
346 PurpleRequestCommonParameters
*cpar
;
347 PurpleRequestFields
*fields
;
348 PurpleRequestFieldGroup
*group
;
349 PurpleRequestField
*field
;
352 fields
= purple_request_fields_new();
353 group
= purple_request_field_group_new(NULL
);
354 purple_request_fields_add_group(fields
, group
);
356 field
= purple_request_field_bool_new("use_url_desc",
357 _("Use image filename as link description"),
358 purple_prefs_get_bool(IMGUP_PREF_PREFIX
"use_url_desc"));
359 purple_request_field_group_add_field(group
, field
);
361 cpar
= purple_request_cpar_new();
362 purple_request_cpar_set_icon(cpar
, PURPLE_REQUEST_ICON_DIALOG
);
364 handle
= purple_request_fields(plugin
,
365 _("Image Uploader"), NULL
, NULL
, fields
,
366 _("OK"), (GCallback
)imgup_prefs_ok
,
373 /******************************************************************************
375 ******************************************************************************/
377 static PidginPluginInfo
*
378 plugin_query(GError
**error
)
380 const gchar
* const authors
[] = {
381 "Tomasz Wasilczyk <twasilczyk@pidgin.im>",
385 return pidgin_plugin_info_new(
386 "id", "gtk-imgupload",
387 "name", N_("Image Uploader"),
388 "version", DISPLAY_VERSION
,
389 "category", N_("Utility"),
390 "summary", N_("Inline images implementation for protocols "
391 "without such feature."),
392 "description", N_("Adds inline images support for protocols "
393 "lacking this feature by uploading them to the "
394 "external service."),
396 "website", PURPLE_WEBSITE
,
397 "abi-version", PURPLE_ABI_VERSION
,
398 "pref-request-cb", imgup_prefs_get
,
404 plugin_load(PurplePlugin
*plugin
, GError
**error
)
408 purple_prefs_add_none("/plugins");
409 purple_prefs_add_none("/plugins/gtk");
410 purple_prefs_add_none("/plugins/gtk/imgupload");
412 purple_prefs_add_bool(IMGUP_PREF_PREFIX
"use_url_desc", TRUE
);
414 plugin_handle
= plugin
;
416 it
= purple_connections_get_all();
417 for (; it
; it
= g_list_next(it
)) {
418 PurpleConnection
*gc
= it
->data
;
422 it
= purple_conversations_get_all();
423 for (; it
; it
= g_list_next(it
)) {
424 PurpleConversation
*conv
= it
->data
;
425 imgup_conv_init(conv
);
426 if (PIDGIN_IS_PIDGIN_CONVERSATION(conv
))
427 imgup_pidconv_init(PIDGIN_CONVERSATION(conv
));
430 purple_signal_connect(purple_connections_get_handle(),
432 PURPLE_CALLBACK(imgup_conn_init
), NULL
);
433 purple_signal_connect(purple_connections_get_handle(),
434 "signing-off", plugin
,
435 PURPLE_CALLBACK(imgup_conn_uninit
), NULL
);
436 purple_signal_connect(pidgin_conversations_get_handle(),
437 "conversation-displayed", plugin
,
438 PURPLE_CALLBACK(imgup_pidconv_init
), NULL
);
444 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
448 it
= purple_conversations_get_all();
449 for (; it
; it
= g_list_next(it
)) {
450 PurpleConversation
*conv
= it
->data
;
451 imgup_conv_uninit(conv
);
452 if (PIDGIN_IS_PIDGIN_CONVERSATION(conv
))
453 imgup_pidconv_uninit(PIDGIN_CONVERSATION(conv
));
456 it
= purple_connections_get_all();
457 for (; it
; it
= g_list_next(it
)) {
458 PurpleConnection
*gc
= it
->data
;
459 imgup_conn_uninit(gc
);
462 plugin_handle
= NULL
;
467 PURPLE_PLUGIN_INIT(imgupload
, plugin_query
, plugin_load
, plugin_unload
);