Migrate certificates, icons, logs to XDG dirs
[pidgin-git.git] / pidgin / plugins / imgupload.c
bloba91f15a8334dfb91a6988c873897f4d4a1f70537
1 /*
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
22 #include "internal.h"
24 #include "debug.h"
25 #include "glibcompat.h"
26 #include "http.h"
27 #include "version.h"
29 #include "gtk3compat.h"
30 #include "gtkconv.h"
31 #include "gtkplugin.h"
32 #include "gtkutils.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;
43 static void
44 imgup_upload_done(PidginWebView *webview, const gchar *url, const gchar *title);
45 static void
46 imgup_upload_failed(PidginWebView *webview);
49 /******************************************************************************
50 * Helper functions
51 ******************************************************************************/
53 static gboolean
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 ******************************************************************************/
64 static void
65 imgup_imgur_uploaded(PurpleHttpConnection *hc, PurpleHttpResponse *resp,
66 gpointer _webview)
68 JsonParser *parser;
69 JsonObject *result;
70 const gchar *data;
71 gsize data_len;
72 PidginWebView *webview = PIDGIN_WEBVIEW(_webview);
73 const gchar *url, *title;
75 if (!purple_http_response_is_successful(resp)) {
76 imgup_upload_failed(webview);
77 return;
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);
86 return;
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);
97 return;
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);
127 g_free(img_data);
128 req_data = g_strdup_printf("type=base64&image=%s", img_data_e);
129 g_free(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);
134 g_free(req_data);
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);
143 return hc;
146 /******************************************************************************
147 * Image/link upload and insertion
148 ******************************************************************************/
150 static void
151 imgup_upload_finish(PidginWebView *webview)
153 gpointer plswait;
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);
159 if (plswait)
160 purple_request_close(PURPLE_REQUEST_WAIT, plswait);
163 static void
164 imgup_upload_done(PidginWebView *webview, const gchar *url, const gchar *title)
166 gboolean url_desc;
168 imgup_upload_finish(webview);
170 if (!purple_prefs_get_bool(IMGUP_PREF_PREFIX "use_url_desc"))
171 url_desc = FALSE;
172 else {
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);
182 static void
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);
193 if (!is_cancelled)
194 purple_debug_error("imgupload", "Failed uploading image");
197 static void
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");
207 if (hc)
208 purple_http_conn_cancel(hc);
211 static gboolean
212 imgup_upload_start(PidginWebView *webview, PurpleImage *image, gpointer _gtkconv)
214 PidginConversation *gtkconv = _gtkconv;
215 PurpleConversation *conv = gtkconv->active_conv;
216 PurpleHttpConnection *hc;
217 gpointer plswait;
219 if (!imgup_conn_is_hooked(purple_conversation_get_connection(conv)))
220 return FALSE;
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);
232 return TRUE;
236 /******************************************************************************
237 * Setup/cleanup
238 ******************************************************************************/
240 static void
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);
251 static void
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);
262 static void
263 imgup_conv_init(PurpleConversation *conv)
265 PurpleConnection *gc;
267 gc = purple_conversation_get_connection(conv);
268 if (!gc)
269 return;
270 if (!imgup_conn_is_hooked(gc))
271 return;
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));
280 static void
281 imgup_conv_uninit(PurpleConversation *conv)
283 PurpleConnection *gc;
285 gc = purple_conversation_get_connection(conv);
286 if (gc) {
287 if (!imgup_conn_is_hooked(gc))
288 return;
289 } else {
290 if (!g_object_get_data(G_OBJECT(conv), "imgupload-set"))
291 return;
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);
301 static void
302 imgup_conn_init(PurpleConnection *gc)
304 PurpleConnectionFlags flags;
306 flags = purple_connection_get_flags(gc);
308 if (!(flags & PURPLE_CONNECTION_FLAG_NO_IMAGES))
309 return;
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));
317 static void
318 imgup_conn_uninit(PurpleConnection *gc)
320 if (!imgup_conn_is_hooked(gc))
321 return;
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 /******************************************************************************
330 * Prefs
331 ******************************************************************************/
333 static void
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);
343 static gpointer
344 imgup_prefs_get(PurplePlugin *plugin)
346 PurpleRequestCommonParameters *cpar;
347 PurpleRequestFields *fields;
348 PurpleRequestFieldGroup *group;
349 PurpleRequestField *field;
350 gpointer handle;
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,
367 _("Cancel"), NULL,
368 cpar, NULL);
370 return handle;
373 /******************************************************************************
374 * Plugin stuff
375 ******************************************************************************/
377 static PidginPluginInfo *
378 plugin_query(GError **error)
380 const gchar * const authors[] = {
381 "Tomasz Wasilczyk <twasilczyk@pidgin.im>",
382 NULL
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."),
395 "authors", authors,
396 "website", PURPLE_WEBSITE,
397 "abi-version", PURPLE_ABI_VERSION,
398 "pref-request-cb", imgup_prefs_get,
399 NULL
403 static gboolean
404 plugin_load(PurplePlugin *plugin, GError **error)
406 GList *it;
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;
419 imgup_conn_init(gc);
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(),
431 "signed-on", plugin,
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);
440 return TRUE;
443 static gboolean
444 plugin_unload(PurplePlugin *plugin, GError **error)
446 GList *it;
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;
464 return TRUE;
467 PURPLE_PLUGIN_INIT(imgupload, plugin_query, plugin_load, plugin_unload);