pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Route GLib debug logging directly to the Finch debug window
2021-10-18, Elliott Sales de Andrade
1896a80ff8e3
Route GLib debug logging directly to the Finch debug window
Instead of flowing through purple debug, this merges some bits of the existing GLib log handler, and the purple debug printer.
Testing Done:
Open the Debug window an see some `GLib-*` outputs.
Reviewed at https://reviews.imfreedom.org/r/1057/
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* 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
<libsoup/soup.h>
#include
<string.h>
#include
<purple.h>
#include
"libpurple/glibcompat.h"
#include
"api.h"
#include
"data.h"
typedef
struct
{
FbApi
*
api
;
SoupSession
*
cons
;
PurpleConnection
*
gc
;
PurpleRoomlist
*
roomlist
;
GQueue
*
msgs
;
GHashTable
*
imgs
;
GHashTable
*
unread
;
GHashTable
*
evs
;
}
FbDataPrivate
;
/**
* FbData:
*
* Represents the connection data used by #FacebookProtocol.
*/
struct
_FbData
{
GObject
parent
;
FbDataPrivate
*
priv
;
};
typedef
struct
{
FbData
*
fata
;
gchar
*
url
;
FbDataImageFunc
func
;
gpointer
data
;
GDestroyNotify
dunc
;
gboolean
active
;
const
guint8
*
image
;
gsize
size
;
}
FbDataImagePrivate
;
/**
* FbDataImage:
*
* Represents the data used for fetching images.
*/
struct
_FbDataImage
{
GObject
parent
;
FbDataImagePrivate
*
priv
;
};
static
const
gchar
*
fb_props_strs
[]
=
{
"cid"
,
"did"
,
"stoken"
,
"token"
};
G_DEFINE_TYPE_WITH_PRIVATE
(
FbData
,
fb_data
,
G_TYPE_OBJECT
);
G_DEFINE_TYPE_WITH_PRIVATE
(
FbDataImage
,
fb_data_image
,
G_TYPE_OBJECT
);
static
void
fb_data_dispose
(
GObject
*
obj
)
{
FbDataPrivate
*
priv
=
FB_DATA
(
obj
)
->
priv
;
GHashTableIter
iter
;
gpointer
ptr
;
soup_session_abort
(
priv
->
cons
);
g_hash_table_iter_init
(
&
iter
,
priv
->
evs
);
while
(
g_hash_table_iter_next
(
&
iter
,
NULL
,
&
ptr
))
{
g_source_remove
(
GPOINTER_TO_UINT
(
ptr
));
}
if
(
G_LIKELY
(
priv
->
api
!=
NULL
))
{
g_object_unref
(
priv
->
api
);
}
g_object_unref
(
priv
->
cons
);
g_queue_free_full
(
priv
->
msgs
,
(
GDestroyNotify
)
fb_api_message_free
);
g_hash_table_destroy
(
priv
->
imgs
);
g_hash_table_destroy
(
priv
->
unread
);
g_hash_table_destroy
(
priv
->
evs
);
}
static
void
fb_data_class_init
(
FbDataClass
*
klass
)
{
GObjectClass
*
gklass
=
G_OBJECT_CLASS
(
klass
);
gklass
->
dispose
=
fb_data_dispose
;
}
static
void
fb_data_init
(
FbData
*
fata
)
{
FbDataPrivate
*
priv
=
fb_data_get_instance_private
(
fata
);
fata
->
priv
=
priv
;
priv
->
msgs
=
g_queue_new
();
priv
->
imgs
=
g_hash_table_new_full
(
g_direct_hash
,
g_direct_equal
,
g_object_unref
,
NULL
);
priv
->
unread
=
g_hash_table_new_full
(
fb_id_hash
,
fb_id_equal
,
g_free
,
NULL
);
priv
->
evs
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
NULL
);
}
static
void
fb_data_image_dispose
(
GObject
*
obj
)
{
FbDataImage
*
img
=
FB_DATA_IMAGE
(
obj
);
FbDataImagePrivate
*
priv
=
img
->
priv
;
FbData
*
fata
=
priv
->
fata
;
if
((
priv
->
dunc
!=
NULL
)
&&
(
priv
->
data
!=
NULL
))
{
priv
->
dunc
(
priv
->
data
);
}
g_free
(
priv
->
url
);
g_hash_table_steal
(
fata
->
priv
->
imgs
,
img
);
}
static
void
fb_data_image_class_init
(
FbDataImageClass
*
klass
)
{
GObjectClass
*
gklass
=
G_OBJECT_CLASS
(
klass
);
gklass
->
dispose
=
fb_data_image_dispose
;
}
static
void
fb_data_image_init
(
FbDataImage
*
img
)
{
FbDataImagePrivate
*
priv
=
fb_data_image_get_instance_private
(
img
);
img
->
priv
=
priv
;
}
FbData
*
fb_data_new
(
PurpleConnection
*
gc
,
GProxyResolver
*
resolver
)
{
FbData
*
fata
;
FbDataPrivate
*
priv
;
fata
=
g_object_new
(
FB_TYPE_DATA
,
NULL
);
priv
=
fata
->
priv
;
priv
->
cons
=
soup_session_new_with_options
(
"proxy-resolver"
,
resolver
,
NULL
);
priv
->
api
=
fb_api_new
(
gc
,
resolver
);
priv
->
gc
=
gc
;
return
fata
;
}
gboolean
fb_data_load
(
FbData
*
fata
)
{
const
gchar
*
str
;
FbDataPrivate
*
priv
;
FbId
id
;
gboolean
ret
=
TRUE
;
guint
i
;
guint64
uint
;
GValue
val
=
G_VALUE_INIT
;
PurpleAccount
*
acct
;
g_return_val_if_fail
(
FB_IS_DATA
(
fata
),
FALSE
);
priv
=
fata
->
priv
;
acct
=
purple_connection_get_account
(
priv
->
gc
);
for
(
i
=
0
;
i
<
G_N_ELEMENTS
(
fb_props_strs
);
i
++
)
{
str
=
purple_account_get_string
(
acct
,
fb_props_strs
[
i
],
NULL
);
if
(
str
==
NULL
)
{
ret
=
FALSE
;
}
g_value_init
(
&
val
,
G_TYPE_STRING
);
g_value_set_string
(
&
val
,
str
);
g_object_set_property
(
G_OBJECT
(
priv
->
api
),
fb_props_strs
[
i
],
&
val
);
g_value_unset
(
&
val
);
}
str
=
purple_account_get_string
(
acct
,
"mid"
,
NULL
);
if
(
str
!=
NULL
)
{
uint
=
g_ascii_strtoull
(
str
,
NULL
,
10
);
g_value_init
(
&
val
,
G_TYPE_UINT64
);
g_value_set_uint64
(
&
val
,
uint
);
g_object_set_property
(
G_OBJECT
(
priv
->
api
),
"mid"
,
&
val
);
g_value_unset
(
&
val
);
}
else
{
ret
=
FALSE
;
}
str
=
purple_account_get_string
(
acct
,
"uid"
,
NULL
);
if
(
str
!=
NULL
)
{
id
=
FB_ID_FROM_STR
(
str
);
g_value_init
(
&
val
,
FB_TYPE_ID
);
g_value_set_int64
(
&
val
,
id
);
g_object_set_property
(
G_OBJECT
(
priv
->
api
),
"uid"
,
&
val
);
g_value_unset
(
&
val
);
}
else
{
ret
=
FALSE
;
}
fb_api_rehash
(
priv
->
api
);
return
ret
;
}
void
fb_data_save
(
FbData
*
fata
)
{
const
gchar
*
str
;
FbDataPrivate
*
priv
;
gchar
*
dup
;
guint
i
;
guint64
uint
;
GValue
val
=
G_VALUE_INIT
;
PurpleAccount
*
acct
;
g_return_if_fail
(
FB_IS_DATA
(
fata
));
priv
=
fata
->
priv
;
acct
=
purple_connection_get_account
(
priv
->
gc
);
for
(
i
=
0
;
i
<
G_N_ELEMENTS
(
fb_props_strs
);
i
++
)
{
g_value_init
(
&
val
,
G_TYPE_STRING
);
g_object_get_property
(
G_OBJECT
(
priv
->
api
),
fb_props_strs
[
i
],
&
val
);
str
=
g_value_get_string
(
&
val
);
if
(
purple_strequal
(
fb_props_strs
[
i
],
"token"
)
&&
!
purple_account_get_remember_password
(
acct
))
{
str
=
""
;
}
purple_account_set_string
(
acct
,
fb_props_strs
[
i
],
str
);
g_value_unset
(
&
val
);
}
g_value_init
(
&
val
,
G_TYPE_UINT64
);
g_object_get_property
(
G_OBJECT
(
priv
->
api
),
"mid"
,
&
val
);
uint
=
g_value_get_uint64
(
&
val
);
g_value_unset
(
&
val
);
dup
=
g_strdup_printf
(
"%"
G_GINT64_FORMAT
,
uint
);
purple_account_set_string
(
acct
,
"mid"
,
dup
);
g_free
(
dup
);
g_value_init
(
&
val
,
G_TYPE_INT64
);
g_object_get_property
(
G_OBJECT
(
priv
->
api
),
"uid"
,
&
val
);
uint
=
g_value_get_int64
(
&
val
);
g_value_unset
(
&
val
);
dup
=
g_strdup_printf
(
"%"
FB_ID_FORMAT
,
uint
);
purple_account_set_string
(
acct
,
"uid"
,
dup
);
g_free
(
dup
);
}
void
fb_data_add_timeout
(
FbData
*
fata
,
const
gchar
*
name
,
guint
interval
,
GSourceFunc
func
,
gpointer
data
)
{
FbDataPrivate
*
priv
;
gchar
*
key
;
guint
id
;
g_return_if_fail
(
FB_IS_DATA
(
fata
));
priv
=
fata
->
priv
;
fb_data_clear_timeout
(
fata
,
name
,
TRUE
);
key
=
g_strdup
(
name
);
id
=
g_timeout_add_seconds
(
interval
,
func
,
data
);
g_hash_table_replace
(
priv
->
evs
,
key
,
GUINT_TO_POINTER
(
id
));
}
void
fb_data_clear_timeout
(
FbData
*
fata
,
const
gchar
*
name
,
gboolean
remove
)
{
FbDataPrivate
*
priv
;
gpointer
ptr
;
guint
id
;
g_return_if_fail
(
FB_IS_DATA
(
fata
));
priv
=
fata
->
priv
;
ptr
=
g_hash_table_lookup
(
priv
->
evs
,
name
);
id
=
GPOINTER_TO_UINT
(
ptr
);
if
((
id
>
0
)
&&
remove
)
{
g_source_remove
(
id
);
}
g_hash_table_remove
(
priv
->
evs
,
name
);
}
FbApi
*
fb_data_get_api
(
FbData
*
fata
)
{
FbDataPrivate
*
priv
;
g_return_val_if_fail
(
FB_IS_DATA
(
fata
),
NULL
);
priv
=
fata
->
priv
;
return
priv
->
api
;
}
PurpleConnection
*
fb_data_get_connection
(
FbData
*
fata
)
{
FbDataPrivate
*
priv
;
g_return_val_if_fail
(
FB_IS_DATA
(
fata
),
NULL
);
priv
=
fata
->
priv
;
return
priv
->
gc
;
}
PurpleRoomlist
*
fb_data_get_roomlist
(
FbData
*
fata
)
{
FbDataPrivate
*
priv
;
g_return_val_if_fail
(
FB_IS_DATA
(
fata
),
NULL
);
priv
=
fata
->
priv
;
return
priv
->
roomlist
;
}
gboolean
fb_data_get_unread
(
FbData
*
fata
,
FbId
id
)
{
FbDataPrivate
*
priv
;
gpointer
*
ptr
;
g_return_val_if_fail
(
FB_IS_DATA
(
fata
),
FALSE
);
g_return_val_if_fail
(
id
!=
0
,
FALSE
);
priv
=
fata
->
priv
;
ptr
=
g_hash_table_lookup
(
priv
->
unread
,
&
id
);
return
GPOINTER_TO_INT
(
ptr
);
}
void
fb_data_set_roomlist
(
FbData
*
fata
,
PurpleRoomlist
*
list
)
{
FbDataPrivate
*
priv
;
g_return_if_fail
(
FB_IS_DATA
(
fata
));
priv
=
fata
->
priv
;
priv
->
roomlist
=
list
;
}
void
fb_data_set_unread
(
FbData
*
fata
,
FbId
id
,
gboolean
unread
)
{
FbDataPrivate
*
priv
;
gpointer
key
;
g_return_if_fail
(
FB_IS_DATA
(
fata
));
g_return_if_fail
(
id
!=
0
);
priv
=
fata
->
priv
;
if
(
!
unread
)
{
g_hash_table_remove
(
priv
->
unread
,
&
id
);
return
;
}
key
=
g_memdup2
(
&
id
,
sizeof
id
);
g_hash_table_replace
(
priv
->
unread
,
key
,
GINT_TO_POINTER
(
unread
));
}
void
fb_data_add_message
(
FbData
*
fata
,
FbApiMessage
*
msg
)
{
FbDataPrivate
*
priv
;
g_return_if_fail
(
FB_IS_DATA
(
fata
));
priv
=
fata
->
priv
;
g_queue_push_tail
(
priv
->
msgs
,
msg
);
}
void
fb_data_remove_message
(
FbData
*
fata
,
FbApiMessage
*
msg
)
{
FbDataPrivate
*
priv
;
g_return_if_fail
(
FB_IS_DATA
(
fata
));
priv
=
fata
->
priv
;
g_queue_remove
(
priv
->
msgs
,
msg
);
}
GSList
*
fb_data_take_messages
(
FbData
*
fata
,
FbId
uid
)
{
FbApiMessage
*
msg
;
FbDataPrivate
*
priv
;
GList
*
l
;
GList
*
prev
;
GSList
*
msgs
=
NULL
;
g_return_val_if_fail
(
FB_IS_DATA
(
fata
),
NULL
);
priv
=
fata
->
priv
;
l
=
priv
->
msgs
->
tail
;
while
(
l
!=
NULL
)
{
msg
=
l
->
data
;
prev
=
l
->
prev
;
if
(
msg
->
uid
==
uid
)
{
msgs
=
g_slist_prepend
(
msgs
,
msg
);
g_queue_delete_link
(
priv
->
msgs
,
l
);
}
l
=
prev
;
}
return
msgs
;
}
FbDataImage
*
fb_data_image_add
(
FbData
*
fata
,
const
gchar
*
url
,
FbDataImageFunc
func
,
gpointer
data
,
GDestroyNotify
dunc
)
{
FbDataImage
*
img
;
FbDataImagePrivate
*
priv
;
g_return_val_if_fail
(
FB_IS_DATA
(
fata
),
NULL
);
g_return_val_if_fail
(
url
!=
NULL
,
NULL
);
g_return_val_if_fail
(
func
!=
NULL
,
NULL
);
img
=
g_object_new
(
FB_TYPE_DATA_IMAGE
,
NULL
);
priv
=
img
->
priv
;
priv
->
fata
=
fata
;
priv
->
url
=
g_strdup
(
url
);
priv
->
func
=
func
;
priv
->
data
=
data
;
priv
->
dunc
=
dunc
;
g_hash_table_insert
(
fata
->
priv
->
imgs
,
img
,
img
);
return
img
;
}
gboolean
fb_data_image_get_active
(
FbDataImage
*
img
)
{
FbDataImagePrivate
*
priv
;
g_return_val_if_fail
(
FB_IS_DATA_IMAGE
(
img
),
FALSE
);
priv
=
img
->
priv
;
return
priv
->
active
;
}
gpointer
fb_data_image_get_data
(
FbDataImage
*
img
)
{
FbDataImagePrivate
*
priv
;
g_return_val_if_fail
(
FB_IS_DATA_IMAGE
(
img
),
NULL
);
priv
=
img
->
priv
;
return
priv
->
data
;
}
FbData
*
fb_data_image_get_fata
(
FbDataImage
*
img
)
{
FbDataImagePrivate
*
priv
;
g_return_val_if_fail
(
FB_IS_DATA_IMAGE
(
img
),
NULL
);
priv
=
img
->
priv
;
return
priv
->
fata
;
}
const
guint8
*
fb_data_image_get_image
(
FbDataImage
*
img
,
gsize
*
size
)
{
FbDataImagePrivate
*
priv
;
g_return_val_if_fail
(
FB_IS_DATA_IMAGE
(
img
),
NULL
);
priv
=
img
->
priv
;
if
(
size
!=
NULL
)
{
*
size
=
priv
->
size
;
}
return
priv
->
image
;
}
guint8
*
fb_data_image_dup_image
(
FbDataImage
*
img
,
gsize
*
size
)
{
FbDataImagePrivate
*
priv
;
g_return_val_if_fail
(
FB_IS_DATA_IMAGE
(
img
),
NULL
);
priv
=
img
->
priv
;
if
(
size
!=
NULL
)
{
*
size
=
priv
->
size
;
}
if
(
priv
->
size
<
1
)
{
return
NULL
;
}
return
g_memdup2
(
priv
->
image
,
priv
->
size
);
}
const
gchar
*
fb_data_image_get_url
(
FbDataImage
*
img
)
{
FbDataImagePrivate
*
priv
;
g_return_val_if_fail
(
FB_IS_DATA_IMAGE
(
img
),
NULL
);
priv
=
img
->
priv
;
return
priv
->
url
;
}
static
void
fb_data_image_cb
(
G_GNUC_UNUSED
SoupSession
*
session
,
SoupMessage
*
res
,
gpointer
data
)
{
FbDataImage
*
img
=
data
;
FbDataImagePrivate
*
priv
=
img
->
priv
;
GError
*
err
=
NULL
;
fb_http_error_chk
(
res
,
&
err
);
priv
->
image
=
(
guint8
*
)
res
->
response_body
->
data
;
priv
->
size
=
res
->
response_body
->
length
;
priv
->
func
(
img
,
err
);
if
(
G_LIKELY
(
err
==
NULL
))
{
fb_data_image_queue
(
priv
->
fata
);
}
else
{
g_error_free
(
err
);
}
g_object_unref
(
img
);
}
void
fb_data_image_queue
(
FbData
*
fata
)
{
const
gchar
*
url
;
FbDataImage
*
img
;
FbDataPrivate
*
priv
;
GHashTableIter
iter
;
guint
active
=
0
;
g_return_if_fail
(
FB_IS_DATA
(
fata
));
priv
=
fata
->
priv
;
g_hash_table_iter_init
(
&
iter
,
priv
->
imgs
);
while
(
g_hash_table_iter_next
(
&
iter
,
(
gpointer
*
)
&
img
,
NULL
))
{
if
(
fb_data_image_get_active
(
img
))
{
active
++
;
}
}
if
(
active
>=
FB_DATA_ICON_MAX
)
{
return
;
}
g_hash_table_iter_init
(
&
iter
,
priv
->
imgs
);
while
(
g_hash_table_iter_next
(
&
iter
,
(
gpointer
*
)
&
img
,
NULL
))
{
SoupMessage
*
msg
;
if
(
fb_data_image_get_active
(
img
))
{
continue
;
}
img
->
priv
->
active
=
TRUE
;
url
=
fb_data_image_get_url
(
img
);
msg
=
soup_message_new
(
"GET"
,
url
);
// purple_http_request_set_max_len(req, FB_DATA_ICON_SIZE_MAX);
soup_session_queue_message
(
priv
->
cons
,
msg
,
fb_data_image_cb
,
img
);
if
(
++
active
>=
FB_DATA_ICON_MAX
)
{
break
;
}
}
}