pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
closing merged branch
memory-leaks
2020-02-03, Gary Kramlich
208e387899a5
closing merged branch
/* 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
"internal.h"
#include
<gio/gio.h>
#include
<stdarg.h>
#include
<string.h>
#include
"buddylist.h"
#include
"conversations.h"
#include
"message.h"
#include
"request.h"
#include
"server.h"
#include
"util.h"
GQuark
fb_util_error_quark
(
void
)
{
static
GQuark
q
=
0
;
if
(
G_UNLIKELY
(
q
==
0
))
{
q
=
g_quark_from_static_string
(
"fb-util-error-quark"
);
}
return
q
;
}
PurpleBuddy
*
fb_util_account_find_buddy
(
PurpleAccount
*
acct
,
PurpleChatConversation
*
chat
,
const
gchar
*
search
,
GError
**
error
)
{
const
gchar
*
alias
;
const
gchar
*
name
;
GSList
*
buddies
;
GSList
*
l
;
guint
retc
;
PurpleBuddy
*
ret
=
NULL
;
g_return_val_if_fail
(
PURPLE_IS_ACCOUNT
(
acct
),
NULL
);
g_return_val_if_fail
(
search
!=
NULL
,
NULL
);
buddies
=
purple_blist_find_buddies
(
acct
,
NULL
);
for
(
retc
=
0
,
l
=
buddies
;
l
!=
NULL
;
l
=
l
->
next
)
{
name
=
purple_buddy_get_name
(
l
->
data
);
alias
=
purple_buddy_get_alias
(
l
->
data
);
if
((
chat
!=
NULL
)
&&
!
purple_chat_conversation_has_user
(
chat
,
name
))
{
continue
;
}
if
(
g_ascii_strcasecmp
(
name
,
search
)
==
0
)
{
ret
=
l
->
data
;
retc
++
;
}
if
(
g_ascii_strcasecmp
(
alias
,
search
)
==
0
)
{
ret
=
l
->
data
;
retc
++
;
}
}
if
(
retc
==
0
)
{
g_set_error
(
error
,
FB_UTIL_ERROR
,
FB_UTIL_ERROR_GENERAL
,
_
(
"Buddy %s not found"
),
search
);
}
else
if
(
retc
>
1
)
{
g_set_error
(
error
,
FB_UTIL_ERROR
,
FB_UTIL_ERROR_GENERAL
,
_
(
"Buddy name %s is ambiguous"
),
search
);
ret
=
NULL
;
}
g_slist_free
(
buddies
);
return
ret
;
}
void
fb_util_debug
(
PurpleDebugLevel
level
,
const
gchar
*
format
,
...)
{
va_list
ap
;
va_start
(
ap
,
format
);
fb_util_vdebug
(
level
,
format
,
ap
);
va_end
(
ap
);
}
void
fb_util_vdebug
(
PurpleDebugLevel
level
,
const
gchar
*
format
,
va_list
ap
)
{
gboolean
unsafe
;
gboolean
verbose
;
gchar
*
str
;
g_return_if_fail
(
format
!=
NULL
);
unsafe
=
(
level
&
FB_UTIL_DEBUG_FLAG_UNSAFE
)
!=
0
;
verbose
=
(
level
&
FB_UTIL_DEBUG_FLAG_VERBOSE
)
!=
0
;
if
((
unsafe
&&
!
purple_debug_is_unsafe
())
||
(
verbose
&&
!
purple_debug_is_verbose
()))
{
return
;
}
/* Ensure all local flags are removed */
level
&=
~
FB_UTIL_DEBUG_FLAG_ALL
;
str
=
g_strdup_vprintf
(
format
,
ap
);
purple_debug
(
level
,
"facebook"
,
"%s
\n
"
,
str
);
g_free
(
str
);
}
void
fb_util_debug_misc
(
const
gchar
*
format
,
...)
{
va_list
ap
;
va_start
(
ap
,
format
);
fb_util_vdebug
(
PURPLE_DEBUG_MISC
,
format
,
ap
);
va_end
(
ap
);
}
void
fb_util_debug_info
(
const
gchar
*
format
,
...)
{
va_list
ap
;
va_start
(
ap
,
format
);
fb_util_vdebug
(
PURPLE_DEBUG_INFO
,
format
,
ap
);
va_end
(
ap
);
}
void
fb_util_debug_warning
(
const
gchar
*
format
,
...)
{
va_list
ap
;
va_start
(
ap
,
format
);
fb_util_vdebug
(
PURPLE_DEBUG_WARNING
,
format
,
ap
);
va_end
(
ap
);
}
void
fb_util_debug_error
(
const
gchar
*
format
,
...)
{
va_list
ap
;
va_start
(
ap
,
format
);
fb_util_vdebug
(
PURPLE_DEBUG_ERROR
,
format
,
ap
);
va_end
(
ap
);
}
void
fb_util_debug_fatal
(
const
gchar
*
format
,
...)
{
va_list
ap
;
va_start
(
ap
,
format
);
fb_util_vdebug
(
PURPLE_DEBUG_FATAL
,
format
,
ap
);
va_end
(
ap
);
}
void
fb_util_debug_hexdump
(
PurpleDebugLevel
level
,
const
GByteArray
*
bytes
,
const
gchar
*
format
,
...)
{
gchar
c
;
guint
i
;
guint
j
;
GString
*
gstr
;
va_list
ap
;
static
const
gchar
*
indent
=
" "
;
g_return_if_fail
(
bytes
!=
NULL
);
if
(
format
!=
NULL
)
{
va_start
(
ap
,
format
);
fb_util_vdebug
(
level
,
format
,
ap
);
va_end
(
ap
);
}
gstr
=
g_string_sized_new
(
80
);
for
(
i
=
0
;
i
<
bytes
->
len
;
i
+=
16
)
{
g_string_append_printf
(
gstr
,
"%s%08x "
,
indent
,
i
);
for
(
j
=
0
;
j
<
16
;
j
++
)
{
if
((
i
+
j
)
<
bytes
->
len
)
{
g_string_append_printf
(
gstr
,
"%02x "
,
bytes
->
data
[
i
+
j
]);
}
else
{
g_string_append
(
gstr
,
" "
);
}
if
(
j
==
7
)
{
g_string_append_c
(
gstr
,
' '
);
}
}
g_string_append
(
gstr
,
" |"
);
for
(
j
=
0
;
(
j
<
16
)
&&
((
i
+
j
)
<
bytes
->
len
);
j
++
)
{
c
=
bytes
->
data
[
i
+
j
];
if
(
!
g_ascii_isprint
(
c
)
||
g_ascii_isspace
(
c
))
{
c
=
'.'
;
}
g_string_append_c
(
gstr
,
c
);
}
g_string_append_c
(
gstr
,
'|'
);
fb_util_debug
(
level
,
"%s"
,
gstr
->
str
);
g_string_erase
(
gstr
,
0
,
-1
);
}
g_string_append_printf
(
gstr
,
"%s%08x"
,
indent
,
i
);
fb_util_debug
(
level
,
"%s"
,
gstr
->
str
);
g_string_free
(
gstr
,
TRUE
);
}
gchar
*
fb_util_get_locale
(
void
)
{
const
gchar
*
const
*
langs
;
const
gchar
*
lang
;
gchar
*
chr
;
guint
i
;
static
const
gchar
chrs
[]
=
{
'.'
,
'@'
};
langs
=
g_get_language_names
();
lang
=
langs
[
0
];
if
(
purple_strequal
(
lang
,
"C"
))
{
return
g_strdup
(
"en_US"
);
}
for
(
i
=
0
;
i
<
G_N_ELEMENTS
(
chrs
);
i
++
)
{
chr
=
strchr
(
lang
,
chrs
[
i
]);
if
(
chr
!=
NULL
)
{
return
g_strndup
(
lang
,
chr
-
lang
);
}
}
return
g_strdup
(
lang
);
}
gchar
*
fb_util_rand_alnum
(
guint
len
)
{
gchar
*
ret
;
GRand
*
rand
;
guint
i
;
guint
j
;
static
const
gchar
chars
[]
=
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
;
static
const
gsize
charc
=
G_N_ELEMENTS
(
chars
)
-
1
;
g_return_val_if_fail
(
len
>
0
,
NULL
);
rand
=
g_rand_new
();
ret
=
g_new
(
gchar
,
len
+
1
);
for
(
i
=
0
;
i
<
len
;
i
++
)
{
j
=
g_rand_int_range
(
rand
,
0
,
charc
);
ret
[
i
]
=
chars
[
j
];
}
ret
[
len
]
=
0
;
g_rand_free
(
rand
);
return
ret
;
}
static
void
fb_util_request_buddy_ok
(
gpointer
*
mata
,
PurpleRequestFields
*
fields
)
{
FbUtilRequestBuddyFunc
func
=
mata
[
0
];
GList
*
l
;
GList
*
select
;
gpointer
data
=
mata
[
2
];
GSList
*
ret
=
NULL
;
PurpleBuddy
*
bdy
;
PurpleRequestField
*
field
;
if
(
func
==
NULL
)
{
g_free
(
mata
);
return
;
}
field
=
purple_request_fields_get_field
(
fields
,
"buddy"
);
select
=
purple_request_field_list_get_selected
(
field
);
for
(
l
=
select
;
l
!=
NULL
;
l
=
l
->
next
)
{
bdy
=
purple_request_field_list_get_data
(
field
,
l
->
data
);
ret
=
g_slist_prepend
(
ret
,
bdy
);
}
ret
=
g_slist_reverse
(
ret
);
func
(
ret
,
data
);
g_slist_free
(
ret
);
g_free
(
mata
);
}
static
void
fb_util_request_buddy_cancel
(
gpointer
*
mata
,
PurpleRequestFields
*
fields
)
{
FbUtilRequestBuddyFunc
func
=
mata
[
1
];
gpointer
data
=
mata
[
2
];
if
(
func
!=
NULL
)
{
func
(
NULL
,
data
);
}
g_free
(
mata
);
}
static
gint
fb_buddy_cmp
(
gconstpointer
a
,
gconstpointer
b
)
{
PurpleBuddy
*
pba
,
*
pbb
;
gint
alias_verdict
,
name_verdict
;
gchar
*
astr
,
*
bstr
;
pba
=
PURPLE_BUDDY
(
a
);
pbb
=
PURPLE_BUDDY
(
b
);
astr
=
g_utf8_casefold
(
purple_buddy_get_alias
(
pba
),
-1
);
bstr
=
g_utf8_casefold
(
purple_buddy_get_alias
(
pbb
),
-1
);
alias_verdict
=
g_utf8_collate
(
astr
,
bstr
);
g_free
(
astr
);
g_free
(
bstr
);
if
(
alias_verdict
!=
0
)
{
return
alias_verdict
;
}
astr
=
g_utf8_casefold
(
purple_buddy_get_name
(
pba
),
-1
);
bstr
=
g_utf8_casefold
(
purple_buddy_get_name
(
pbb
),
-1
);
name_verdict
=
g_utf8_collate
(
astr
,
bstr
);
g_free
(
astr
);
g_free
(
bstr
);
return
name_verdict
;
}
gpointer
fb_util_request_buddy
(
PurpleConnection
*
gc
,
const
gchar
*
title
,
const
gchar
*
primary
,
const
gchar
*
secondary
,
GSList
*
select
,
gboolean
multi
,
GCallback
ok_cb
,
GCallback
cancel_cb
,
gpointer
data
)
{
const
gchar
*
alias
;
const
gchar
*
name
;
gchar
*
str
;
GList
*
items
=
NULL
;
gpointer
*
mata
;
GSList
*
buddies
;
GSList
*
l
;
PurpleAccount
*
acct
;
PurpleRequestCommonParameters
*
cpar
;
PurpleRequestField
*
field
;
PurpleRequestFieldGroup
*
group
;
PurpleRequestFields
*
fields
;
mata
=
g_new0
(
gpointer
,
3
);
mata
[
0
]
=
ok_cb
;
mata
[
1
]
=
cancel_cb
;
mata
[
2
]
=
data
;
acct
=
purple_connection_get_account
(
gc
);
buddies
=
purple_blist_find_buddies
(
acct
,
NULL
);
buddies
=
g_slist_sort
(
buddies
,
fb_buddy_cmp
);
fields
=
purple_request_fields_new
();
group
=
purple_request_field_group_new
(
NULL
);
purple_request_fields_add_group
(
fields
,
group
);
field
=
purple_request_field_list_new
(
"buddy"
,
NULL
);
purple_request_field_list_set_multi_select
(
field
,
multi
);
purple_request_field_set_required
(
field
,
TRUE
);
purple_request_field_group_add_field
(
group
,
field
);
for
(
l
=
buddies
;
l
!=
NULL
;
l
=
l
->
next
)
{
name
=
purple_buddy_get_name
(
l
->
data
);
alias
=
purple_buddy_get_alias
(
l
->
data
);
str
=
g_strdup_printf
(
"%s (%s)"
,
alias
,
name
);
purple_request_field_list_add_icon
(
field
,
str
,
NULL
,
l
->
data
);
g_free
(
str
);
}
for
(
l
=
select
;
l
!=
NULL
;
l
=
l
->
next
)
{
name
=
purple_buddy_get_name
(
l
->
data
);
alias
=
purple_buddy_get_alias
(
l
->
data
);
str
=
g_strdup_printf
(
"%s (%s)"
,
alias
,
name
);
items
=
g_list_append
(
items
,
str
);
}
purple_request_field_list_set_selected
(
field
,
items
);
g_slist_free
(
buddies
);
g_list_free_full
(
items
,
g_free
);
cpar
=
purple_request_cpar_from_connection
(
gc
);
return
purple_request_fields
(
gc
,
title
,
primary
,
secondary
,
fields
,
_
(
"Ok"
),
G_CALLBACK
(
fb_util_request_buddy_ok
),
_
(
"Cancel"
),
G_CALLBACK
(
fb_util_request_buddy_cancel
),
cpar
,
mata
);
}
void
fb_util_serv_got_im
(
PurpleConnection
*
gc
,
const
gchar
*
who
,
const
gchar
*
text
,
PurpleMessageFlags
flags
,
guint64
timestamp
)
{
const
gchar
*
name
;
PurpleAccount
*
acct
;
PurpleIMConversation
*
conv
;
PurpleMessage
*
msg
;
if
(
!
(
flags
&
PURPLE_MESSAGE_SEND
))
{
purple_serv_got_im
(
gc
,
who
,
text
,
flags
,
timestamp
);
return
;
}
acct
=
purple_connection_get_account
(
gc
);
conv
=
purple_conversations_find_im_with_account
(
who
,
acct
);
if
(
conv
==
NULL
)
{
conv
=
purple_im_conversation_new
(
acct
,
who
);
}
name
=
purple_account_get_username
(
acct
);
msg
=
purple_message_new_outgoing
(
name
,
text
,
flags
);
purple_message_set_time
(
msg
,
timestamp
);
purple_conversation_write_message
(
PURPLE_CONVERSATION
(
conv
),
msg
);
}
void
fb_util_serv_got_chat_in
(
PurpleConnection
*
gc
,
gint
id
,
const
gchar
*
who
,
const
gchar
*
text
,
PurpleMessageFlags
flags
,
guint64
timestamp
)
{
const
gchar
*
name
;
PurpleAccount
*
acct
;
PurpleChatConversation
*
conv
;
PurpleMessage
*
msg
;
if
(
!
(
flags
&
PURPLE_MESSAGE_SEND
))
{
purple_serv_got_chat_in
(
gc
,
id
,
who
,
flags
,
text
,
timestamp
);
return
;
}
acct
=
purple_connection_get_account
(
gc
);
conv
=
purple_conversations_find_chat
(
gc
,
id
);
name
=
purple_account_get_username
(
acct
);
msg
=
purple_message_new_outgoing
(
name
,
text
,
flags
);
purple_message_set_time
(
msg
,
timestamp
);
purple_conversation_write_message
(
PURPLE_CONVERSATION
(
conv
),
msg
);
}
gboolean
fb_util_strtest
(
const
gchar
*
str
,
GAsciiType
type
)
{
gsize
i
;
gsize
size
;
guchar
c
;
g_return_val_if_fail
(
str
!=
NULL
,
FALSE
);
size
=
strlen
(
str
);
for
(
i
=
0
;
i
<
size
;
i
++
)
{
c
=
(
guchar
)
str
[
i
];
if
((
g_ascii_table
[
c
]
&
type
)
==
0
)
{
return
FALSE
;
}
}
return
TRUE
;
}
gboolean
fb_util_zlib_test
(
const
GByteArray
*
bytes
)
{
guint8
b0
;
guint8
b1
;
g_return_val_if_fail
(
bytes
!=
NULL
,
FALSE
);
if
(
bytes
->
len
<
2
)
{
return
FALSE
;
}
b0
=
*
(
bytes
->
data
+
0
);
b1
=
*
(
bytes
->
data
+
1
);
return
((((
b0
<<
8
)
|
b1
)
%
31
)
==
0
)
&&
/* Check the header */
((
b0
&
0x0F
)
==
8
/* Z_DEFLATED */
);
/* Check the method */
}
static
GByteArray
*
fb_util_zlib_conv
(
GConverter
*
conv
,
const
GByteArray
*
bytes
,
GError
**
error
)
{
GByteArray
*
ret
;
GConverterResult
res
;
gsize
cize
=
0
;
gsize
rize
;
gsize
wize
;
guint8
data
[
1024
];
ret
=
g_byte_array_new
();
while
(
TRUE
)
{
rize
=
0
;
wize
=
0
;
res
=
g_converter_convert
(
conv
,
bytes
->
data
+
cize
,
bytes
->
len
-
cize
,
data
,
sizeof
data
,
G_CONVERTER_INPUT_AT_END
,
&
rize
,
&
wize
,
error
);
switch
(
res
)
{
case
G_CONVERTER_CONVERTED
:
g_byte_array_append
(
ret
,
data
,
wize
);
cize
+=
rize
;
break
;
case
G_CONVERTER_ERROR
:
g_byte_array_free
(
ret
,
TRUE
);
return
NULL
;
case
G_CONVERTER_FINISHED
:
g_byte_array_append
(
ret
,
data
,
wize
);
return
ret
;
default
:
break
;
}
}
}
GByteArray
*
fb_util_zlib_deflate
(
const
GByteArray
*
bytes
,
GError
**
error
)
{
GByteArray
*
ret
;
GZlibCompressor
*
conv
;
conv
=
g_zlib_compressor_new
(
G_ZLIB_COMPRESSOR_FORMAT_ZLIB
,
-1
);
ret
=
fb_util_zlib_conv
(
G_CONVERTER
(
conv
),
bytes
,
error
);
g_object_unref
(
conv
);
return
ret
;
}
GByteArray
*
fb_util_zlib_inflate
(
const
GByteArray
*
bytes
,
GError
**
error
)
{
GByteArray
*
ret
;
GZlibDecompressor
*
conv
;
conv
=
g_zlib_decompressor_new
(
G_ZLIB_COMPRESSOR_FORMAT_ZLIB
);
ret
=
fb_util_zlib_conv
(
G_CONVERTER
(
conv
),
bytes
,
error
);
g_object_unref
(
conv
);
return
ret
;
}