pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Don't leak simple watcher structs.
2019-10-25, Elliott Sales de Andrade
3f61b0cd94de
Don't leak simple watcher structs.
/*
* 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
"internal.h"
#include
"debug.h"
#include
"glibcompat.h"
#include
"version.h"
#include
"gtk3compat.h"
#include
"gtkconv.h"
#include
"gtkplugin.h"
#include
"gtkutils.h"
#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
;
static
void
imgup_upload_done
(
PidginWebView
*
webview
,
const
gchar
*
url
,
const
gchar
*
title
);
static
void
imgup_upload_failed
(
PidginWebView
*
webview
);
/******************************************************************************
* Helper functions
******************************************************************************/
static
gboolean
imgup_conn_is_hooked
(
PurpleConnection
*
gc
)
{
return
GPOINTER_TO_INT
(
g_object_get_data
(
G_OBJECT
(
gc
),
"imgupload-set"
));
}
/******************************************************************************
* Imgur implementation
******************************************************************************/
static
void
imgup_imgur_uploaded
(
G_GNUC_UNUSED
SoupSession
*
session
,
SoupMessage
*
msg
,
gpointer
_webview
)
{
JsonParser
*
parser
;
JsonObject
*
result
;
PidginWebView
*
webview
=
PIDGIN_WEBVIEW
(
_webview
);
const
gchar
*
url
,
*
title
;
if
(
!
SOUP_STATUS_IS_SUCCESSFUL
(
msg
->
status_code
))
{
imgup_upload_failed
(
webview
);
return
;
}
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
);
return
;
}
result
=
json_node_get_object
(
json_parser_get_root
(
parser
));
if
(
!
json_object_get_boolean_member
(
result
,
"success"
))
{
g_object_unref
(
parser
);
purple_debug_warning
(
"imgupload"
,
"imgur - not a success"
);
imgup_upload_failed
(
webview
);
return
;
}
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_unref
(
parser
);
g_object_set_data
(
G_OBJECT
(
msg
),
"imgupload-imgur-name"
,
NULL
);
}
static
PurpleHttpConnection
*
imgup_imgur_upload
(
PidginWebView
*
webview
,
PurpleImage
*
image
)
{
SoupMessage
*
msg
;
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
);
g_free
(
img_data
);
req_data
=
g_strdup_printf
(
"type=base64&image=%s"
,
img_data_e
);
g_free
(
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
)),
g_free
);
soup_session_queue_message
(
session
,
msg
,
imgup_imgur_uploaded
,
webview
);
return
msg
;
}
/******************************************************************************
* Image/link upload and insertion
******************************************************************************/
static
void
imgup_upload_finish
(
PidginWebView
*
webview
)
{
gpointer
plswait
;
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
);
if
(
plswait
)
purple_request_close
(
PURPLE_REQUEST_WAIT
,
plswait
);
}
static
void
imgup_upload_done
(
PidginWebView
*
webview
,
const
gchar
*
url
,
const
gchar
*
title
)
{
gboolean
url_desc
;
imgup_upload_finish
(
webview
);
if
(
!
purple_prefs_get_bool
(
IMGUP_PREF_PREFIX
"use_url_desc"
))
url_desc
=
FALSE
;
else
{
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
);
}
static
void
imgup_upload_failed
(
PidginWebView
*
webview
)
{
gboolean
is_cancelled
;
imgup_upload_finish
(
webview
);
is_cancelled
=
GPOINTER_TO_INT
(
g_object_get_data
(
G_OBJECT
(
webview
),
"imgupload-cancelled"
));
g_object_set_data
(
G_OBJECT
(
webview
),
"imgupload-cancelled"
,
NULL
);
if
(
!
is_cancelled
)
purple_debug_error
(
"imgupload"
,
"Failed uploading image"
);
}
static
void
imgup_upload_cancel_message
(
SoupMessage
*
msg
)
{
soup_session_cancel_message
(
session
,
msg
,
SOUP_STATUS_CANCELLED
);
}
static
void
imgup_upload_cancel
(
gpointer
_webview
)
{
SoupMessage
*
msg
;
PidginWebView
*
webview
=
PIDGIN_WEBVIEW
(
_webview
);
g_object_set_data
(
G_OBJECT
(
webview
),
"imgupload-plswait"
,
NULL
);
g_object_set_data
(
G_OBJECT
(
webview
),
"imgupload-cancelled"
,
GINT_TO_POINTER
(
TRUE
));
msg
=
g_object_steal_data
(
G_OBJECT
(
webview
),
"imgupload-msg"
);
if
(
msg
)
{
soup_session_cancel_message
(
session
,
msg
,
SOUP_STATUS_CANCELLED
);
}
}
static
gboolean
imgup_upload_start
(
PidginWebView
*
webview
,
PurpleImage
*
image
,
gpointer
_gtkconv
)
{
PidginConversation
*
gtkconv
=
_gtkconv
;
PurpleConversation
*
conv
=
gtkconv
->
active_conv
;
SoupMessage
*
msg
;
gpointer
plswait
;
if
(
!
imgup_conn_is_hooked
(
purple_conversation_get_connection
(
conv
)))
return
FALSE
;
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
);
return
TRUE
;
}
/******************************************************************************
* Setup/cleanup
******************************************************************************/
static
void
imgup_pidconv_init
(
PidginConversation
*
gtkconv
)
{
PidginWebView
*
webview
;
webview
=
PIDGIN_WEBVIEW
(
gtkconv
->
entry
);
g_signal_connect
(
G_OBJECT
(
webview
),
"insert-image"
,
G_CALLBACK
(
imgup_upload_start
),
gtkconv
);
}
static
void
imgup_pidconv_uninit
(
PidginConversation
*
gtkconv
)
{
PidginWebView
*
webview
;
webview
=
PIDGIN_WEBVIEW
(
gtkconv
->
entry
);
g_signal_handlers_disconnect_by_func
(
G_OBJECT
(
webview
),
G_CALLBACK
(
imgup_upload_start
),
gtkconv
);
}
static
void
imgup_conv_init
(
PurpleConversation
*
conv
)
{
PurpleConnection
*
gc
;
gc
=
purple_conversation_get_connection
(
conv
);
if
(
!
gc
)
return
;
if
(
!
imgup_conn_is_hooked
(
gc
))
return
;
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
));
}
static
void
imgup_conv_uninit
(
PurpleConversation
*
conv
)
{
PurpleConnection
*
gc
;
gc
=
purple_conversation_get_connection
(
conv
);
if
(
gc
)
{
if
(
!
imgup_conn_is_hooked
(
gc
))
return
;
}
else
{
if
(
!
g_object_get_data
(
G_OBJECT
(
conv
),
"imgupload-set"
))
return
;
}
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
);
}
static
void
imgup_conn_init
(
PurpleConnection
*
gc
)
{
PurpleConnectionFlags
flags
;
flags
=
purple_connection_get_flags
(
gc
);
if
(
!
(
flags
&
PURPLE_CONNECTION_FLAG_NO_IMAGES
))
return
;
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
));
}
static
void
imgup_conn_uninit
(
PurpleConnection
*
gc
)
{
if
(
!
imgup_conn_is_hooked
(
gc
))
return
;
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
);
}
/******************************************************************************
* Prefs
******************************************************************************/
static
void
imgup_prefs_ok
(
gpointer
_unused
,
PurpleRequestFields
*
fields
)
{
gboolean
use_url_desc
;
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
);
}
static
gpointer
imgup_prefs_get
(
PurplePlugin
*
plugin
)
{
PurpleRequestCommonParameters
*
cpar
;
PurpleRequestFields
*
fields
;
PurpleRequestFieldGroup
*
group
;
PurpleRequestField
*
field
;
gpointer
handle
;
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
,
_
(
"Cancel"
),
NULL
,
cpar
,
NULL
);
return
handle
;
}
/******************************************************************************
* Plugin stuff
******************************************************************************/
static
PidginPluginInfo
*
plugin_query
(
GError
**
error
)
{
const
gchar
*
const
authors
[]
=
{
"Tomasz Wasilczyk <twasilczyk@pidgin.im>"
,
NULL
};
return
pidgin_plugin_info_new
(
"id"
,
"gtk-imgupload"
,
"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 "
"external service."
),
"authors"
,
authors
,
"website"
,
PURPLE_WEBSITE
,
"abi-version"
,
PURPLE_ABI_VERSION
,
"pref-request-cb"
,
imgup_prefs_get
,
NULL
);
}
static
gboolean
plugin_load
(
PurplePlugin
*
plugin
,
GError
**
error
)
{
GList
*
it
;
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
);
plugin_handle
=
plugin
;
session
=
soup_session_new
();
it
=
purple_connections_get_all
();
for
(;
it
;
it
=
g_list_next
(
it
))
{
PurpleConnection
*
gc
=
it
->
data
;
imgup_conn_init
(
gc
);
}
it
=
purple_conversations_get_all
();
for
(;
it
;
it
=
g_list_next
(
it
))
{
PurpleConversation
*
conv
=
it
->
data
;
imgup_conv_init
(
conv
);
if
(
PIDGIN_IS_PIDGIN_CONVERSATION
(
conv
))
imgup_pidconv_init
(
PIDGIN_CONVERSATION
(
conv
));
}
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-on"
,
plugin
,
PURPLE_CALLBACK
(
imgup_conn_init
),
NULL
);
purple_signal_connect
(
purple_connections_get_handle
(),
"signing-off"
,
plugin
,
PURPLE_CALLBACK
(
imgup_conn_uninit
),
NULL
);
purple_signal_connect
(
pidgin_conversations_get_handle
(),
"conversation-displayed"
,
plugin
,
PURPLE_CALLBACK
(
imgup_pidconv_init
),
NULL
);
return
TRUE
;
}
static
gboolean
plugin_unload
(
PurplePlugin
*
plugin
,
GError
**
error
)
{
GList
*
it
;
soup_session_abort
(
session
);
it
=
purple_conversations_get_all
();
for
(;
it
;
it
=
g_list_next
(
it
))
{
PurpleConversation
*
conv
=
it
->
data
;
imgup_conv_uninit
(
conv
);
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
;
imgup_conn_uninit
(
gc
);
}
g_clear_object
(
&
session
);
plugin_handle
=
NULL
;
return
TRUE
;
}
PURPLE_PLUGIN_INIT
(
imgupload
,
plugin_query
,
plugin_load
,
plugin_unload
);