pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Bump the versions for the next round of development
release-2.x.y
2021-05-13, Gary Kramlich
47988aaeaf57
Bump the versions for the next round of development
Testing Done:
Configured and built.
Reviewed at https://reviews.imfreedom.org/r/642/
/**
* @file gg.c Gadu-Gadu protocol plugin
*
* purple
*
* Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
*
* Some parts of the code are adapted or taken from the previous implementation
* of this plugin written by Arkadiusz Miskiewicz <misiek@pld.org.pl>
* Some parts Copyright (C) 2009 Krzysztof Klinikowski <grommasher@gmail.com>
*
* Thanks to Google's Summer of Code Program.
*
* 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
"plugin.h"
#include
"version.h"
#include
"notify.h"
#include
"status.h"
#include
"blist.h"
#include
"accountopt.h"
#include
"debug.h"
#include
"glibcompat.h"
#include
"util.h"
#include
"request.h"
#include
"xmlnode.h"
#include
<libgadu.h>
#include
"gg.h"
#include
"confer.h"
#include
"search.h"
#include
"buddylist.h"
#include
"gg-utils.h"
#define DISABLE_AVATARS 1
static
PurplePlugin
*
my_protocol
=
NULL
;
/* Prototypes */
static
void
ggp_set_status
(
PurpleAccount
*
account
,
PurpleStatus
*
status
);
static
int
ggp_to_gg_status
(
PurpleStatus
*
status
,
char
**
msg
);
/* ---------------------------------------------------------------------- */
/* ----- EXTERNAL CALLBACKS --------------------------------------------- */
/* ---------------------------------------------------------------------- */
/* ----- HELPERS -------------------------------------------------------- */
static
PurpleInputCondition
ggp_tcpsocket_inputcond_gg_to_purple
(
enum
gg_check_t
check
)
{
PurpleInputCondition
cond
=
0
;
if
(
check
&
GG_CHECK_READ
)
cond
|=
PURPLE_INPUT_READ
;
if
(
check
&
GG_CHECK_WRITE
)
cond
|=
PURPLE_INPUT_WRITE
;
return
cond
;
}
/**
* Set up libgadu's proxy.
*
* @param account Account for which to set up the proxy.
*
* @return Zero if proxy setup is valid, otherwise -1.
*/
static
int
ggp_setup_proxy
(
PurpleAccount
*
account
)
{
PurpleProxyInfo
*
gpi
;
gpi
=
purple_proxy_get_setup
(
account
);
if
((
purple_proxy_info_get_type
(
gpi
)
!=
PURPLE_PROXY_NONE
)
&&
(
purple_proxy_info_get_host
(
gpi
)
==
NULL
||
purple_proxy_info_get_port
(
gpi
)
<=
0
))
{
gg_proxy_enabled
=
0
;
purple_notify_error
(
NULL
,
NULL
,
_
(
"Invalid proxy settings"
),
_
(
"Either the host name or port number specified for your given proxy type is invalid."
));
return
-1
;
}
else
if
(
purple_proxy_info_get_type
(
gpi
)
!=
PURPLE_PROXY_NONE
)
{
gg_proxy_enabled
=
1
;
gg_proxy_host
=
g_strdup
(
purple_proxy_info_get_host
(
gpi
));
gg_proxy_port
=
purple_proxy_info_get_port
(
gpi
);
gg_proxy_username
=
g_strdup
(
purple_proxy_info_get_username
(
gpi
));
gg_proxy_password
=
g_strdup
(
purple_proxy_info_get_password
(
gpi
));
}
else
{
gg_proxy_enabled
=
0
;
}
return
0
;
}
/* }}} */
/* ---------------------------------------------------------------------- */
static
void
ggp_callback_buddylist_save_ok
(
PurpleConnection
*
gc
,
const
char
*
filename
)
{
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
char
*
buddylist
=
ggp_buddylist_dump
(
account
);
purple_debug_info
(
"gg"
,
"Saving...
\n
"
);
purple_debug_info
(
"gg"
,
"file = %s
\n
"
,
filename
);
if
(
buddylist
==
NULL
)
{
purple_notify_info
(
account
,
_
(
"Save Buddylist..."
),
_
(
"Your buddylist is empty, nothing was written to the file."
),
NULL
);
return
;
}
if
(
purple_util_write_data_to_file_absolute
(
filename
,
buddylist
,
-1
))
{
purple_notify_info
(
account
,
_
(
"Save Buddylist..."
),
_
(
"Buddylist saved successfully!"
),
NULL
);
}
else
{
gchar
*
primary
=
g_strdup_printf
(
_
(
"Couldn't write buddy list for %s to %s"
),
purple_account_get_username
(
account
),
filename
);
purple_notify_error
(
account
,
_
(
"Save Buddylist..."
),
primary
,
NULL
);
g_free
(
primary
);
}
g_free
(
buddylist
);
}
static
void
ggp_callback_buddylist_load_ok
(
PurpleConnection
*
gc
,
gchar
*
file
)
{
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
GError
*
error
=
NULL
;
char
*
buddylist
=
NULL
;
gsize
length
;
purple_debug_info
(
"gg"
,
"file_name = %s
\n
"
,
file
);
if
(
!
g_file_get_contents
(
file
,
&
buddylist
,
&
length
,
&
error
))
{
purple_notify_error
(
account
,
_
(
"Couldn't load buddylist"
),
_
(
"Couldn't load buddylist"
),
error
->
message
);
purple_debug_error
(
"gg"
,
"Couldn't load buddylist. file = %s; error = %s
\n
"
,
file
,
error
->
message
);
g_error_free
(
error
);
return
;
}
ggp_buddylist_load
(
gc
,
buddylist
);
g_free
(
buddylist
);
purple_notify_info
(
account
,
_
(
"Load Buddylist..."
),
_
(
"Buddylist loaded successfully!"
),
NULL
);
}
/* }}} */
/*
*/
/* static void ggp_action_buddylist_save(PurplePluginAction *action) {{{ */
static
void
ggp_action_buddylist_save
(
PurplePluginAction
*
action
)
{
PurpleConnection
*
gc
=
(
PurpleConnection
*
)
action
->
context
;
purple_request_file
(
action
,
_
(
"Save buddylist..."
),
NULL
,
TRUE
,
G_CALLBACK
(
ggp_callback_buddylist_save_ok
),
NULL
,
purple_connection_get_account
(
gc
),
NULL
,
NULL
,
gc
);
}
static
void
ggp_action_buddylist_load
(
PurplePluginAction
*
action
)
{
PurpleConnection
*
gc
=
(
PurpleConnection
*
)
action
->
context
;
purple_request_file
(
action
,
_
(
"Load buddylist from file..."
),
NULL
,
FALSE
,
G_CALLBACK
(
ggp_callback_buddylist_load_ok
),
NULL
,
purple_connection_get_account
(
gc
),
NULL
,
NULL
,
gc
);
}
/* ----- PUBLIC DIRECTORY SEARCH ---------------------------------------- */
static
void
ggp_callback_show_next
(
PurpleConnection
*
gc
,
GList
*
row
,
gpointer
user_data
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
GGPSearchForm
*
form
=
user_data
;
guint32
seq
;
form
->
page_number
++
;
ggp_search_remove
(
info
->
searches
,
form
->
seq
);
purple_debug_info
(
"gg"
,
"ggp_callback_show_next(): Removed seq %u
\n
"
,
form
->
seq
);
seq
=
ggp_search_start
(
gc
,
form
);
ggp_search_add
(
info
->
searches
,
seq
,
form
);
purple_debug_info
(
"gg"
,
"ggp_callback_show_next(): Added seq %u
\n
"
,
seq
);
}
static
void
ggp_callback_add_buddy
(
PurpleConnection
*
gc
,
GList
*
row
,
gpointer
user_data
)
{
purple_blist_request_add_buddy
(
purple_connection_get_account
(
gc
),
g_list_nth_data
(
row
,
0
),
NULL
,
NULL
);
}
static
void
ggp_callback_im
(
PurpleConnection
*
gc
,
GList
*
row
,
gpointer
user_data
)
{
PurpleAccount
*
account
;
PurpleConversation
*
conv
;
char
*
name
;
account
=
purple_connection_get_account
(
gc
);
name
=
g_list_nth_data
(
row
,
0
);
conv
=
purple_conversation_new
(
PURPLE_CONV_TYPE_IM
,
account
,
name
);
purple_conversation_present
(
conv
);
}
static
void
ggp_callback_find_buddies
(
PurpleConnection
*
gc
,
PurpleRequestFields
*
fields
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
GGPSearchForm
*
form
;
guint32
seq
;
form
=
ggp_search_form_new
(
GGP_SEARCH_TYPE_FULL
);
form
->
user_data
=
info
;
form
->
lastname
=
g_strdup
(
purple_request_fields_get_string
(
fields
,
"lastname"
));
form
->
firstname
=
g_strdup
(
purple_request_fields_get_string
(
fields
,
"firstname"
));
form
->
nickname
=
g_strdup
(
purple_request_fields_get_string
(
fields
,
"nickname"
));
form
->
city
=
g_strdup
(
purple_request_fields_get_string
(
fields
,
"city"
));
form
->
birthyear
=
g_strdup
(
purple_request_fields_get_string
(
fields
,
"year"
));
switch
(
purple_request_fields_get_choice
(
fields
,
"gender"
))
{
case
1
:
form
->
gender
=
g_strdup
(
GG_PUBDIR50_GENDER_MALE
);
break
;
case
2
:
form
->
gender
=
g_strdup
(
GG_PUBDIR50_GENDER_FEMALE
);
break
;
default
:
form
->
gender
=
NULL
;
break
;
}
form
->
active
=
purple_request_fields_get_bool
(
fields
,
"active"
)
?
g_strdup
(
GG_PUBDIR50_ACTIVE_TRUE
)
:
NULL
;
seq
=
ggp_search_start
(
gc
,
form
);
ggp_search_add
(
info
->
searches
,
seq
,
form
);
purple_debug_info
(
"gg"
,
"ggp_callback_find_buddies(): Added seq %u
\n
"
,
seq
);
}
static
void
ggp_find_buddies
(
PurplePluginAction
*
action
)
{
PurpleConnection
*
gc
=
(
PurpleConnection
*
)
action
->
context
;
PurpleRequestFields
*
fields
;
PurpleRequestFieldGroup
*
group
;
PurpleRequestField
*
field
;
fields
=
purple_request_fields_new
();
group
=
purple_request_field_group_new
(
NULL
);
purple_request_fields_add_group
(
fields
,
group
);
field
=
purple_request_field_string_new
(
"lastname"
,
_
(
"Last name"
),
NULL
,
FALSE
);
purple_request_field_string_set_masked
(
field
,
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"firstname"
,
_
(
"First name"
),
NULL
,
FALSE
);
purple_request_field_string_set_masked
(
field
,
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"nickname"
,
_
(
"Nickname"
),
NULL
,
FALSE
);
purple_request_field_string_set_masked
(
field
,
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"city"
,
_
(
"City"
),
NULL
,
FALSE
);
purple_request_field_string_set_masked
(
field
,
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"year"
,
_
(
"Year of birth"
),
NULL
,
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_choice_new
(
"gender"
,
_
(
"Gender"
),
0
);
purple_request_field_choice_add
(
field
,
_
(
"Male or female"
));
purple_request_field_choice_add
(
field
,
_
(
"Male"
));
purple_request_field_choice_add
(
field
,
_
(
"Female"
));
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_bool_new
(
"active"
,
_
(
"Only online"
),
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
purple_request_fields
(
gc
,
_
(
"Find buddies"
),
_
(
"Find buddies"
),
_
(
"Please, enter your search criteria below"
),
fields
,
_
(
"OK"
),
G_CALLBACK
(
ggp_callback_find_buddies
),
_
(
"Cancel"
),
NULL
,
purple_connection_get_account
(
gc
),
NULL
,
NULL
,
gc
);
}
/* ----- CHANGE STATUS BROADCASTING ------------------------------------------------ */
static
void
ggp_action_change_status_broadcasting_ok
(
PurpleConnection
*
gc
,
PurpleRequestFields
*
fields
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
int
selected_field
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
PurpleStatus
*
status
;
selected_field
=
purple_request_fields_get_choice
(
fields
,
"status_broadcasting"
);
if
(
selected_field
==
0
)
info
->
status_broadcasting
=
TRUE
;
else
info
->
status_broadcasting
=
FALSE
;
status
=
purple_account_get_active_status
(
account
);
ggp_set_status
(
account
,
status
);
}
static
void
ggp_action_change_status_broadcasting
(
PurplePluginAction
*
action
)
{
PurpleConnection
*
gc
=
(
PurpleConnection
*
)
action
->
context
;
GGPInfo
*
info
=
gc
->
proto_data
;
PurpleRequestFields
*
fields
;
PurpleRequestFieldGroup
*
group
;
PurpleRequestField
*
field
;
fields
=
purple_request_fields_new
();
group
=
purple_request_field_group_new
(
NULL
);
purple_request_fields_add_group
(
fields
,
group
);
field
=
purple_request_field_choice_new
(
"status_broadcasting"
,
_
(
"Show status to:"
),
0
);
purple_request_field_choice_add
(
field
,
_
(
"All people"
));
purple_request_field_choice_add
(
field
,
_
(
"Only buddies"
));
purple_request_field_group_add_field
(
group
,
field
);
if
(
info
->
status_broadcasting
)
purple_request_field_choice_set_default_value
(
field
,
0
);
else
purple_request_field_choice_set_default_value
(
field
,
1
);
purple_request_fields
(
gc
,
_
(
"Change status broadcasting"
),
_
(
"Change status broadcasting"
),
_
(
"Please, select who can see your status"
),
fields
,
_
(
"OK"
),
G_CALLBACK
(
ggp_action_change_status_broadcasting_ok
),
_
(
"Cancel"
),
NULL
,
purple_connection_get_account
(
gc
),
NULL
,
NULL
,
gc
);
}
/* ----- CONFERENCES ---------------------------------------------------- */
static
void
ggp_callback_add_to_chat_ok
(
PurpleBuddy
*
buddy
,
PurpleRequestFields
*
fields
)
{
PurpleConnection
*
conn
;
PurpleRequestField
*
field
;
GList
*
sel
;
conn
=
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
));
g_return_if_fail
(
conn
!=
NULL
);
field
=
purple_request_fields_get_field
(
fields
,
"name"
);
sel
=
purple_request_field_list_get_selected
(
field
);
if
(
sel
==
NULL
)
{
purple_debug_error
(
"gg"
,
"No chat selected
\n
"
);
return
;
}
ggp_confer_participants_add_uin
(
conn
,
sel
->
data
,
ggp_str_to_uin
(
purple_buddy_get_name
(
buddy
)));
}
static
void
ggp_bmenu_add_to_chat
(
PurpleBlistNode
*
node
,
gpointer
ignored
)
{
PurpleBuddy
*
buddy
;
PurpleConnection
*
gc
;
GGPInfo
*
info
;
PurpleRequestFields
*
fields
;
PurpleRequestFieldGroup
*
group
;
PurpleRequestField
*
field
;
GList
*
l
;
gchar
*
msg
;
buddy
=
(
PurpleBuddy
*
)
node
;
gc
=
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
));
info
=
gc
->
proto_data
;
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
(
"name"
,
"Chat name"
);
for
(
l
=
info
->
chats
;
l
!=
NULL
;
l
=
l
->
next
)
{
GGPChat
*
chat
=
l
->
data
;
purple_request_field_list_add
(
field
,
chat
->
name
,
chat
->
name
);
}
purple_request_field_group_add_field
(
group
,
field
);
msg
=
g_strdup_printf
(
_
(
"Select a chat for buddy: %s"
),
purple_buddy_get_alias
(
buddy
));
purple_request_fields
(
gc
,
_
(
"Add to chat..."
),
_
(
"Add to chat..."
),
msg
,
fields
,
_
(
"Add"
),
G_CALLBACK
(
ggp_callback_add_to_chat_ok
),
_
(
"Cancel"
),
NULL
,
purple_connection_get_account
(
gc
),
NULL
,
NULL
,
buddy
);
g_free
(
msg
);
}
/* ----- BLOCK BUDDIES -------------------------------------------------- */
static
void
ggp_add_deny
(
PurpleConnection
*
gc
,
const
char
*
who
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
uin_t
uin
=
ggp_str_to_uin
(
who
);
purple_debug_info
(
"gg"
,
"ggp_add_deny: %u
\n
"
,
uin
);
gg_remove_notify_ex
(
info
->
session
,
uin
,
GG_USER_NORMAL
);
gg_add_notify_ex
(
info
->
session
,
uin
,
GG_USER_BLOCKED
);
}
static
void
ggp_rem_deny
(
PurpleConnection
*
gc
,
const
char
*
who
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
uin_t
uin
=
ggp_str_to_uin
(
who
);
purple_debug_info
(
"gg"
,
"ggp_rem_deny: %u
\n
"
,
uin
);
gg_remove_notify_ex
(
info
->
session
,
uin
,
GG_USER_BLOCKED
);
gg_add_notify_ex
(
info
->
session
,
uin
,
GG_USER_NORMAL
);
}
/* ---------------------------------------------------------------------- */
/* ----- INTERNAL CALLBACKS --------------------------------------------- */
/* ---------------------------------------------------------------------- */
#if !DISABLE_AVATARS
struct
gg_fetch_avatar_data
{
PurpleConnection
*
gc
;
gchar
*
uin
;
gchar
*
avatar_url
;
};
static
void
gg_fetch_avatar_cb
(
PurpleUtilFetchUrlData
*
url_data
,
gpointer
user_data
,
const
gchar
*
data
,
size_t
len
,
const
gchar
*
error_message
)
{
struct
gg_fetch_avatar_data
*
d
=
user_data
;
PurpleAccount
*
account
;
PurpleBuddy
*
buddy
;
gpointer
buddy_icon_data
;
purple_debug_info
(
"gg"
,
"gg_fetch_avatar_cb: got avatar image for %s
\n
"
,
d
->
uin
);
/* FIXME: This shouldn't be necessary */
if
(
!
PURPLE_CONNECTION_IS_VALID
(
d
->
gc
))
{
g_free
(
d
->
uin
);
g_free
(
d
->
avatar_url
);
g_free
(
d
);
g_return_if_reached
();
}
account
=
purple_connection_get_account
(
d
->
gc
);
buddy
=
purple_find_buddy
(
account
,
d
->
uin
);
if
(
buddy
==
NULL
)
goto
out
;
buddy_icon_data
=
g_memdup2
(
data
,
len
);
purple_buddy_icons_set_for_user
(
account
,
purple_buddy_get_name
(
buddy
),
buddy_icon_data
,
len
,
d
->
avatar_url
);
purple_debug_info
(
"gg"
,
"gg_fetch_avatar_cb: UIN %s should have avatar "
"now
\n
"
,
d
->
uin
);
out
:
g_free
(
d
->
uin
);
g_free
(
d
->
avatar_url
);
g_free
(
d
);
}
static
void
gg_get_avatar_url_cb
(
PurpleUtilFetchUrlData
*
url_data
,
gpointer
user_data
,
const
gchar
*
url_text
,
size_t
len
,
const
gchar
*
error_message
)
{
struct
gg_fetch_avatar_data
*
data
;
PurpleConnection
*
gc
=
user_data
;
PurpleAccount
*
account
;
PurpleBuddy
*
buddy
;
const
char
*
uin
;
const
char
*
is_blank
;
const
char
*
checksum
;
gchar
*
bigavatar
=
NULL
;
xmlnode
*
xml
=
NULL
;
xmlnode
*
xmlnode_users
;
xmlnode
*
xmlnode_user
;
xmlnode
*
xmlnode_avatars
;
xmlnode
*
xmlnode_avatar
;
xmlnode
*
xmlnode_bigavatar
;
g_return_if_fail
(
PURPLE_CONNECTION_IS_VALID
(
gc
));
account
=
purple_connection_get_account
(
gc
);
if
(
error_message
!=
NULL
)
purple_debug_error
(
"gg"
,
"gg_get_avatars_cb error: %s
\n
"
,
error_message
);
else
if
(
len
>
0
&&
url_text
&&
*
url_text
)
{
xml
=
xmlnode_from_str
(
url_text
,
-1
);
if
(
xml
==
NULL
)
goto
out
;
xmlnode_users
=
xmlnode_get_child
(
xml
,
"users"
);
if
(
xmlnode_users
==
NULL
)
goto
out
;
xmlnode_user
=
xmlnode_get_child
(
xmlnode_users
,
"user"
);
if
(
xmlnode_user
==
NULL
)
goto
out
;
uin
=
xmlnode_get_attrib
(
xmlnode_user
,
"uin"
);
xmlnode_avatars
=
xmlnode_get_child
(
xmlnode_user
,
"avatars"
);
if
(
xmlnode_avatars
==
NULL
)
goto
out
;
xmlnode_avatar
=
xmlnode_get_child
(
xmlnode_avatars
,
"avatar"
);
if
(
xmlnode_avatar
==
NULL
)
goto
out
;
xmlnode_bigavatar
=
xmlnode_get_child
(
xmlnode_avatar
,
"originBigAvatar"
);
if
(
xmlnode_bigavatar
==
NULL
)
goto
out
;
is_blank
=
xmlnode_get_attrib
(
xmlnode_avatar
,
"blank"
);
bigavatar
=
xmlnode_get_data
(
xmlnode_bigavatar
);
purple_debug_info
(
"gg"
,
"gg_get_avatar_url_cb: UIN %s, IS_BLANK %s, "
"URL %s
\n
"
,
uin
?
uin
:
"(null)"
,
is_blank
?
is_blank
:
"(null)"
,
bigavatar
?
bigavatar
:
"(null)"
);
if
(
uin
!=
NULL
&&
bigavatar
!=
NULL
)
{
buddy
=
purple_find_buddy
(
account
,
uin
);
if
(
buddy
==
NULL
)
goto
out
;
checksum
=
purple_buddy_icons_get_checksum_for_user
(
buddy
);
if
(
purple_strequal
(
is_blank
,
"1"
))
{
purple_buddy_icons_set_for_user
(
account
,
purple_buddy_get_name
(
buddy
),
NULL
,
0
,
NULL
);
}
else
if
(
!
purple_strequal
(
checksum
,
bigavatar
))
{
data
=
g_new0
(
struct
gg_fetch_avatar_data
,
1
);
data
->
gc
=
gc
;
data
->
uin
=
g_strdup
(
uin
);
data
->
avatar_url
=
g_strdup
(
bigavatar
);
purple_debug_info
(
"gg"
,
"gg_get_avatar_url_cb: "
"requesting avatar for %s
\n
"
,
uin
);
url_data
=
purple_util_fetch_url_request_len_with_account
(
account
,
bigavatar
,
TRUE
,
"Mozilla/4.0 (compatible; MSIE 5.0)"
,
FALSE
,
NULL
,
FALSE
,
-1
,
gg_fetch_avatar_cb
,
data
);
}
}
}
out
:
if
(
xml
)
xmlnode_free
(
xml
);
g_free
(
bigavatar
);
}
#endif
/**
* Try to update avatar of the buddy.
*
* @param gc PurpleConnection
* @param uin UIN of the buddy.
*/
static
void
ggp_update_buddy_avatar
(
PurpleConnection
*
gc
,
uin_t
uin
)
{
#if DISABLE_AVATARS
purple_debug_warning
(
"gg"
,
"ggp_update_buddy_avatar: disabled, please "
"update to 3.0.0, when available
\n
"
);
#else
gchar
*
avatarurl
;
PurpleUtilFetchUrlData
*
url_data
;
purple_debug_info
(
"gg"
,
"ggp_update_buddy_avatar(gc, %u)
\n
"
,
uin
);
avatarurl
=
g_strdup_printf
(
"http://api.gadu-gadu.pl/avatars/%u/0.xml"
,
uin
);
url_data
=
purple_util_fetch_url_request_len_with_account
(
purple_connection_get_account
(
gc
),
avatarurl
,
TRUE
,
"Mozilla/4.0 (compatible; MSIE 5.5)"
,
FALSE
,
NULL
,
FALSE
,
-1
,
gg_get_avatar_url_cb
,
gc
);
g_free
(
avatarurl
);
#endif
}
/**
* Handle change of the status of the buddy.
*
* @param gc PurpleConnection
* @param uin UIN of the buddy.
* @param status ID of the status.
* @param descr Description.
*/
static
void
ggp_generic_status_handler
(
PurpleConnection
*
gc
,
uin_t
uin
,
int
status
,
const
char
*
descr
)
{
gchar
*
from
;
const
char
*
st
;
char
*
status_msg
=
NULL
;
ggp_update_buddy_avatar
(
gc
,
uin
);
from
=
g_strdup_printf
(
"%u"
,
uin
);
switch
(
status
)
{
case
GG_STATUS_NOT_AVAIL
:
case
GG_STATUS_NOT_AVAIL_DESCR
:
st
=
purple_primitive_get_id_from_type
(
PURPLE_STATUS_OFFLINE
);
break
;
case
GG_STATUS_FFC
:
case
GG_STATUS_FFC_DESCR
:
st
=
purple_primitive_get_id_from_type
(
PURPLE_STATUS_AVAILABLE
);
break
;
case
GG_STATUS_AVAIL
:
case
GG_STATUS_AVAIL_DESCR
:
st
=
purple_primitive_get_id_from_type
(
PURPLE_STATUS_AVAILABLE
);
break
;
case
GG_STATUS_BUSY
:
case
GG_STATUS_BUSY_DESCR
:
st
=
purple_primitive_get_id_from_type
(
PURPLE_STATUS_AWAY
);
break
;
case
GG_STATUS_DND
:
case
GG_STATUS_DND_DESCR
:
st
=
purple_primitive_get_id_from_type
(
PURPLE_STATUS_UNAVAILABLE
);
break
;
case
GG_STATUS_BLOCKED
:
/* user is blocking us.... */
st
=
"blocked"
;
break
;
default
:
st
=
purple_primitive_get_id_from_type
(
PURPLE_STATUS_AVAILABLE
);
purple_debug_info
(
"gg"
,
"GG_EVENT_NOTIFY: Unknown status: %d
\n
"
,
status
);
break
;
}
if
(
descr
!=
NULL
)
{
status_msg
=
g_strdup
(
descr
);
g_strstrip
(
status_msg
);
if
(
status_msg
[
0
]
==
'\0'
)
{
g_free
(
status_msg
);
status_msg
=
NULL
;
}
}
purple_debug_info
(
"gg"
,
"status of %u is %s [%s]
\n
"
,
uin
,
st
,
status_msg
?
status_msg
:
""
);
if
(
status_msg
==
NULL
)
{
purple_prpl_got_user_status
(
purple_connection_get_account
(
gc
),
from
,
st
,
NULL
);
}
else
{
purple_prpl_got_user_status
(
purple_connection_get_account
(
gc
),
from
,
st
,
"message"
,
status_msg
,
NULL
);
g_free
(
status_msg
);
}
g_free
(
from
);
}
static
void
ggp_sr_close_cb
(
gpointer
user_data
)
{
GGPSearchForm
*
form
=
user_data
;
GGPInfo
*
info
=
form
->
user_data
;
ggp_search_remove
(
info
->
searches
,
form
->
seq
);
purple_debug_info
(
"gg"
,
"ggp_sr_close_cb(): Removed seq %u
\n
"
,
form
->
seq
);
ggp_search_form_destroy
(
form
);
}
/**
* Translate a status' ID to a more user-friendly name.
*
* @param id The ID of the status.
*
* @return The user-friendly name of the status.
*/
static
const
char
*
ggp_status_by_id
(
unsigned
int
id
)
{
const
char
*
st
;
purple_debug_info
(
"gg"
,
"ggp_status_by_id: %d
\n
"
,
id
);
switch
(
id
)
{
case
GG_STATUS_NOT_AVAIL
:
case
GG_STATUS_NOT_AVAIL_DESCR
:
st
=
_
(
"Offline"
);
break
;
case
GG_STATUS_AVAIL
:
case
GG_STATUS_AVAIL_DESCR
:
st
=
_
(
"Available"
);
break
;
case
GG_STATUS_FFC
:
case
GG_STATUS_FFC_DESCR
:
return
_
(
"Chatty"
);
case
GG_STATUS_DND
:
case
GG_STATUS_DND_DESCR
:
return
_
(
"Do Not Disturb"
);
case
GG_STATUS_BUSY
:
case
GG_STATUS_BUSY_DESCR
:
st
=
_
(
"Away"
);
break
;
default
:
st
=
_
(
"Unknown"
);
break
;
}
return
st
;
}
static
void
ggp_pubdir_handle_info
(
PurpleConnection
*
gc
,
gg_pubdir50_t
req
,
GGPSearchForm
*
form
)
{
PurpleNotifyUserInfo
*
user_info
;
PurpleBuddy
*
buddy
;
char
*
val
,
*
who
;
user_info
=
purple_notify_user_info_new
();
val
=
ggp_search_get_result
(
req
,
0
,
GG_PUBDIR50_STATUS
);
/* XXX: Use of ggp_str_to_uin() is an ugly hack! */
purple_notify_user_info_add_pair
(
user_info
,
_
(
"Status"
),
ggp_status_by_id
(
ggp_str_to_uin
(
val
)));
g_free
(
val
);
who
=
ggp_search_get_result
(
req
,
0
,
GG_PUBDIR50_UIN
);
purple_notify_user_info_add_pair
(
user_info
,
_
(
"UIN"
),
who
);
val
=
ggp_search_get_result
(
req
,
0
,
GG_PUBDIR50_FIRSTNAME
);
purple_notify_user_info_add_pair
(
user_info
,
_
(
"First Name"
),
val
);
g_free
(
val
);
val
=
ggp_search_get_result
(
req
,
0
,
GG_PUBDIR50_NICKNAME
);
purple_notify_user_info_add_pair
(
user_info
,
_
(
"Nickname"
),
val
);
g_free
(
val
);
val
=
ggp_search_get_result
(
req
,
0
,
GG_PUBDIR50_CITY
);
purple_notify_user_info_add_pair
(
user_info
,
_
(
"City"
),
val
);
g_free
(
val
);
val
=
ggp_search_get_result
(
req
,
0
,
GG_PUBDIR50_BIRTHYEAR
);
if
(
strncmp
(
val
,
"0"
,
1
))
{
purple_notify_user_info_add_pair
(
user_info
,
_
(
"Birth Year"
),
val
);
}
g_free
(
val
);
/*
* Include a status message, if exists and buddy is in the blist.
*/
buddy
=
purple_find_buddy
(
purple_connection_get_account
(
gc
),
who
);
if
(
NULL
!=
buddy
)
{
PurpleStatus
*
status
;
const
char
*
msg
;
char
*
text
;
status
=
purple_presence_get_active_status
(
purple_buddy_get_presence
(
buddy
));
msg
=
purple_status_get_attr_string
(
status
,
"message"
);
if
(
msg
!=
NULL
)
{
text
=
g_markup_escape_text
(
msg
,
-1
);
purple_notify_user_info_add_pair
(
user_info
,
_
(
"Message"
),
text
);
g_free
(
text
);
}
}
purple_notify_userinfo
(
gc
,
who
,
user_info
,
ggp_sr_close_cb
,
form
);
g_free
(
who
);
purple_notify_user_info_destroy
(
user_info
);
}
static
void
ggp_pubdir_handle_full
(
PurpleConnection
*
gc
,
gg_pubdir50_t
req
,
GGPSearchForm
*
form
)
{
PurpleNotifySearchResults
*
results
;
PurpleNotifySearchColumn
*
column
;
int
res_count
;
int
start
;
int
i
;
g_return_if_fail
(
form
!=
NULL
);
res_count
=
gg_pubdir50_count
(
req
);
res_count
=
(
res_count
>
PUBDIR_RESULTS_MAX
)
?
PUBDIR_RESULTS_MAX
:
res_count
;
if
(
form
->
page_size
==
0
)
form
->
page_size
=
res_count
;
results
=
purple_notify_searchresults_new
();
if
(
results
==
NULL
)
{
purple_debug_error
(
"gg"
,
"ggp_pubdir_reply_handler: "
"Unable to display the search results.
\n
"
);
purple_notify_error
(
gc
,
NULL
,
_
(
"Unable to display the search results."
),
NULL
);
if
(
form
->
window
==
NULL
)
ggp_sr_close_cb
(
form
);
return
;
}
column
=
purple_notify_searchresults_column_new
(
_
(
"UIN"
));
purple_notify_searchresults_column_add
(
results
,
column
);
column
=
purple_notify_searchresults_column_new
(
_
(
"First Name"
));
purple_notify_searchresults_column_add
(
results
,
column
);
column
=
purple_notify_searchresults_column_new
(
_
(
"Nickname"
));
purple_notify_searchresults_column_add
(
results
,
column
);
column
=
purple_notify_searchresults_column_new
(
_
(
"City"
));
purple_notify_searchresults_column_add
(
results
,
column
);
column
=
purple_notify_searchresults_column_new
(
_
(
"Birth Year"
));
purple_notify_searchresults_column_add
(
results
,
column
);
purple_debug_info
(
"gg"
,
"Going with %d entries
\n
"
,
res_count
);
start
=
(
int
)
ggp_str_to_uin
(
gg_pubdir50_get
(
req
,
0
,
GG_PUBDIR50_START
));
purple_debug_info
(
"gg"
,
"start = %d
\n
"
,
start
);
for
(
i
=
0
;
i
<
res_count
;
i
++
)
{
GList
*
row
=
NULL
;
char
*
birth
=
ggp_search_get_result
(
req
,
i
,
GG_PUBDIR50_BIRTHYEAR
);
/* TODO: Status will be displayed as an icon. */
/* row = g_list_append(row, ggp_search_get_result(req, i, GG_PUBDIR50_STATUS)); */
row
=
g_list_append
(
row
,
ggp_search_get_result
(
req
,
i
,
GG_PUBDIR50_UIN
));
row
=
g_list_append
(
row
,
ggp_search_get_result
(
req
,
i
,
GG_PUBDIR50_FIRSTNAME
));
row
=
g_list_append
(
row
,
ggp_search_get_result
(
req
,
i
,
GG_PUBDIR50_NICKNAME
));
row
=
g_list_append
(
row
,
ggp_search_get_result
(
req
,
i
,
GG_PUBDIR50_CITY
));
row
=
g_list_append
(
row
,
(
birth
&&
strncmp
(
birth
,
"0"
,
1
))
?
birth
:
g_strdup
(
"-"
));
purple_notify_searchresults_row_add
(
results
,
row
);
}
purple_notify_searchresults_button_add
(
results
,
PURPLE_NOTIFY_BUTTON_CONTINUE
,
ggp_callback_show_next
);
purple_notify_searchresults_button_add
(
results
,
PURPLE_NOTIFY_BUTTON_ADD
,
ggp_callback_add_buddy
);
purple_notify_searchresults_button_add
(
results
,
PURPLE_NOTIFY_BUTTON_IM
,
ggp_callback_im
);
if
(
form
->
window
==
NULL
)
{
void
*
h
=
purple_notify_searchresults
(
gc
,
_
(
"Gadu-Gadu Public Directory"
),
_
(
"Search results"
),
NULL
,
results
,
(
PurpleNotifyCloseCallback
)
ggp_sr_close_cb
,
form
);
if
(
h
==
NULL
)
{
purple_debug_error
(
"gg"
,
"ggp_pubdir_reply_handler: "
"Unable to display the search results.
\n
"
);
purple_notify_error
(
gc
,
NULL
,
_
(
"Unable to display the search results."
),
NULL
);
return
;
}
form
->
window
=
h
;
}
else
{
purple_notify_searchresults_new_rows
(
gc
,
results
,
form
->
window
);
}
}
static
void
ggp_pubdir_reply_handler
(
PurpleConnection
*
gc
,
gg_pubdir50_t
req
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
GGPSearchForm
*
form
;
int
res_count
;
guint32
seq
;
seq
=
gg_pubdir50_seq
(
req
);
form
=
ggp_search_get
(
info
->
searches
,
seq
);
purple_debug_info
(
"gg"
,
"ggp_pubdir_reply_handler(): seq %u --> form %p
\n
"
,
seq
,
form
);
/*
* this can happen when user will request more results
* and close the results window before they arrive.
*/
g_return_if_fail
(
form
!=
NULL
);
res_count
=
gg_pubdir50_count
(
req
);
if
(
res_count
<
1
)
{
purple_debug_info
(
"gg"
,
"GG_EVENT_PUBDIR50_SEARCH_REPLY: Nothing found
\n
"
);
purple_notify_error
(
gc
,
NULL
,
_
(
"No matching users found"
),
_
(
"There are no users matching your search criteria."
));
if
(
form
->
window
==
NULL
)
ggp_sr_close_cb
(
form
);
return
;
}
switch
(
form
->
search_type
)
{
case
GGP_SEARCH_TYPE_INFO
:
ggp_pubdir_handle_info
(
gc
,
req
,
form
);
break
;
case
GGP_SEARCH_TYPE_FULL
:
ggp_pubdir_handle_full
(
gc
,
req
,
form
);
break
;
default
:
purple_debug_warning
(
"gg"
,
"Unknown search_type!
\n
"
);
break
;
}
}
static
void
ggp_recv_image_handler
(
PurpleConnection
*
gc
,
const
struct
gg_event
*
ev
)
{
gint
imgid
=
0
;
GGPInfo
*
info
=
gc
->
proto_data
;
GList
*
entry
=
g_list_first
(
info
->
pending_richtext_messages
);
gchar
*
handlerid
=
g_strdup_printf
(
"IMGID_HANDLER-%i"
,
ev
->
event
.
image_reply
.
crc32
);
imgid
=
purple_imgstore_add_with_id
(
g_memdup2
(
ev
->
event
.
image_reply
.
image
,
ev
->
event
.
image_reply
.
size
),
ev
->
event
.
image_reply
.
size
,
ev
->
event
.
image_reply
.
filename
);
purple_debug_info
(
"gg"
,
"ggp_recv_image_handler: got image with crc32: %u
\n
"
,
ev
->
event
.
image_reply
.
crc32
);
while
(
entry
)
{
if
(
strstr
((
gchar
*
)
entry
->
data
,
handlerid
)
!=
NULL
)
{
gchar
**
split
=
g_strsplit
((
gchar
*
)
entry
->
data
,
handlerid
,
3
);
gchar
*
text
=
g_strdup_printf
(
"%s%i%s"
,
split
[
0
],
imgid
,
split
[
1
]);
purple_debug_info
(
"gg"
,
"ggp_recv_image_handler: found message matching crc32: %s
\n
"
,
(
gchar
*
)
entry
->
data
);
g_strfreev
(
split
);
info
->
pending_richtext_messages
=
g_list_remove
(
info
->
pending_richtext_messages
,
entry
->
data
);
/* We don't have any more images to download */
if
(
strstr
(
text
,
"<IMG ID=
\"
IMGID_HANDLER"
)
==
NULL
)
{
gchar
*
buf
=
g_strdup_printf
(
"%lu"
,
(
unsigned
long
int
)
ev
->
event
.
image_reply
.
sender
);
serv_got_im
(
gc
,
buf
,
text
,
PURPLE_MESSAGE_IMAGES
,
time
(
NULL
));
g_free
(
buf
);
purple_debug_info
(
"gg"
,
"ggp_recv_image_handler: richtext message: %s
\n
"
,
text
);
g_free
(
text
);
break
;
}
info
->
pending_richtext_messages
=
g_list_append
(
info
->
pending_richtext_messages
,
text
);
break
;
}
entry
=
g_list_next
(
entry
);
}
g_free
(
handlerid
);
return
;
}
/**
* Dispatch a message received from a buddy.
*
* @param gc PurpleConnection.
* @param ev Gadu-Gadu event structure.
*
* Image receiving, some code borrowed from Kadu http://www.kadu.net
*/
static
void
ggp_recv_message_handler
(
PurpleConnection
*
gc
,
const
struct
gg_event
*
ev
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
PurpleConversation
*
conv
;
gchar
*
from
;
gchar
*
msg
;
gchar
*
tmp
;
if
(
ev
->
event
.
msg
.
message
==
NULL
)
{
purple_debug_warning
(
"gg"
,
"ggp_recv_message_handler: NULL as message pointer
\n
"
);
return
;
}
from
=
g_strdup_printf
(
"%lu"
,
(
unsigned
long
int
)
ev
->
event
.
msg
.
sender
);
/*
tmp = charset_convert((const char *)ev->event.msg.message,
"CP1250", "UTF-8");
*/
tmp
=
g_strdup_printf
(
"%s"
,
ev
->
event
.
msg
.
message
);
purple_str_strip_char
(
tmp
,
'\r'
);
msg
=
g_markup_escape_text
(
tmp
,
-1
);
g_free
(
tmp
);
/* We got richtext message */
if
(
ev
->
event
.
msg
.
formats_length
)
{
gboolean
got_image
=
FALSE
,
bold
=
FALSE
,
italic
=
FALSE
,
under
=
FALSE
;
char
*
cformats
=
(
char
*
)
ev
->
event
.
msg
.
formats
;
char
*
cformats_end
=
cformats
+
ev
->
event
.
msg
.
formats_length
;
gint
increased_len
=
0
;
struct
gg_msg_richtext_format
*
actformat
;
struct
gg_msg_richtext_image
*
actimage
;
GString
*
message
=
g_string_new
(
msg
);
gchar
*
handlerid
;
purple_debug_info
(
"gg"
,
"ggp_recv_message_handler: richtext msg from (%s): %s %i formats
\n
"
,
from
,
msg
,
ev
->
event
.
msg
.
formats_length
);
while
(
cformats
<
cformats_end
)
{
gint
byteoffset
;
actformat
=
(
struct
gg_msg_richtext_format
*
)
cformats
;
cformats
+=
sizeof
(
struct
gg_msg_richtext_format
);
byteoffset
=
g_utf8_offset_to_pointer
(
message
->
str
,
actformat
->
position
+
increased_len
)
-
message
->
str
;
if
(
actformat
->
position
==
0
&&
actformat
->
font
==
0
)
{
purple_debug_warning
(
"gg"
,
"ggp_recv_message_handler: bogus formatting (inc: %i)
\n
"
,
increased_len
);
continue
;
}
purple_debug_info
(
"gg"
,
"ggp_recv_message_handler: format at pos: %i, image:%i, bold:%i, italic: %i, under:%i (inc: %i)
\n
"
,
actformat
->
position
,
(
actformat
->
font
&
GG_FONT_IMAGE
)
!=
0
,
(
actformat
->
font
&
GG_FONT_BOLD
)
!=
0
,
(
actformat
->
font
&
GG_FONT_ITALIC
)
!=
0
,
(
actformat
->
font
&
GG_FONT_UNDERLINE
)
!=
0
,
increased_len
);
if
(
actformat
->
font
&
GG_FONT_IMAGE
)
{
got_image
=
TRUE
;
actimage
=
(
struct
gg_msg_richtext_image
*
)(
cformats
);
cformats
+=
sizeof
(
struct
gg_msg_richtext_image
);
purple_debug_info
(
"gg"
,
"ggp_recv_message_handler: image received, size: %d, crc32: %i
\n
"
,
actimage
->
size
,
actimage
->
crc32
);
/* Checking for errors, image size shouldn't be
* larger than 255.000 bytes */
if
(
actimage
->
size
>
255000
)
{
purple_debug_warning
(
"gg"
,
"ggp_recv_message_handler: received image large than 255 kb
\n
"
);
continue
;
}
gg_image_request
(
info
->
session
,
ev
->
event
.
msg
.
sender
,
actimage
->
size
,
actimage
->
crc32
);
handlerid
=
g_strdup_printf
(
"<IMG ID=
\"
IMGID_HANDLER-%i
\"
>"
,
actimage
->
crc32
);
g_string_insert
(
message
,
byteoffset
,
handlerid
);
increased_len
+=
strlen
(
handlerid
);
g_free
(
handlerid
);
continue
;
}
if
(
actformat
->
font
&
GG_FONT_BOLD
)
{
if
(
bold
==
FALSE
)
{
g_string_insert
(
message
,
byteoffset
,
"<b>"
);
increased_len
+=
3
;
bold
=
TRUE
;
}
}
else
if
(
bold
)
{
g_string_insert
(
message
,
byteoffset
,
"</b>"
);
increased_len
+=
4
;
bold
=
FALSE
;
}
if
(
actformat
->
font
&
GG_FONT_ITALIC
)
{
if
(
italic
==
FALSE
)
{
g_string_insert
(
message
,
byteoffset
,
"<i>"
);
increased_len
+=
3
;
italic
=
TRUE
;
}
}
else
if
(
italic
)
{
g_string_insert
(
message
,
byteoffset
,
"</i>"
);
increased_len
+=
4
;
italic
=
FALSE
;
}
if
(
actformat
->
font
&
GG_FONT_UNDERLINE
)
{
if
(
under
==
FALSE
)
{
g_string_insert
(
message
,
byteoffset
,
"<u>"
);
increased_len
+=
3
;
under
=
TRUE
;
}
}
else
if
(
under
)
{
g_string_insert
(
message
,
byteoffset
,
"</u>"
);
increased_len
+=
4
;
under
=
FALSE
;
}
if
(
actformat
->
font
&
GG_FONT_COLOR
)
{
cformats
+=
sizeof
(
struct
gg_msg_richtext_color
);
}
}
msg
=
message
->
str
;
g_string_free
(
message
,
FALSE
);
if
(
got_image
)
{
info
->
pending_richtext_messages
=
g_list_append
(
info
->
pending_richtext_messages
,
msg
);
return
;
}
}
purple_debug_info
(
"gg"
,
"ggp_recv_message_handler: msg from (%s): %s (class = %d; rcpt_count = %d)
\n
"
,
from
,
msg
,
ev
->
event
.
msg
.
msgclass
,
ev
->
event
.
msg
.
recipients_count
);
if
(
ev
->
event
.
msg
.
recipients_count
==
0
)
{
serv_got_im
(
gc
,
from
,
msg
,
0
,
ev
->
event
.
msg
.
time
);
}
else
{
const
char
*
chat_name
;
int
chat_id
;
char
*
buddy_name
;
chat_name
=
ggp_confer_find_by_participants
(
gc
,
ev
->
event
.
msg
.
recipients
,
ev
->
event
.
msg
.
recipients_count
);
if
(
chat_name
==
NULL
)
{
chat_name
=
ggp_confer_add_new
(
gc
,
NULL
);
serv_got_joined_chat
(
gc
,
info
->
chats_count
,
chat_name
);
ggp_confer_participants_add_uin
(
gc
,
chat_name
,
ev
->
event
.
msg
.
sender
);
ggp_confer_participants_add
(
gc
,
chat_name
,
ev
->
event
.
msg
.
recipients
,
ev
->
event
.
msg
.
recipients_count
);
}
conv
=
ggp_confer_find_by_name
(
gc
,
chat_name
);
chat_id
=
purple_conv_chat_get_id
(
PURPLE_CONV_CHAT
(
conv
));
buddy_name
=
ggp_buddy_get_name
(
gc
,
ev
->
event
.
msg
.
sender
);
serv_got_chat_in
(
gc
,
chat_id
,
buddy_name
,
PURPLE_MESSAGE_RECV
,
msg
,
ev
->
event
.
msg
.
time
);
g_free
(
buddy_name
);
}
g_free
(
msg
);
g_free
(
from
);
}
static
void
ggp_send_image_handler
(
PurpleConnection
*
gc
,
const
struct
gg_event
*
ev
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
PurpleStoredImage
*
image
;
gint
imgid
=
GPOINTER_TO_INT
(
g_hash_table_lookup
(
info
->
pending_images
,
GINT_TO_POINTER
(
ev
->
event
.
image_request
.
crc32
)));
purple_debug_info
(
"gg"
,
"ggp_send_image_handler: image request received, crc32: %u, imgid: %d
\n
"
,
ev
->
event
.
image_request
.
crc32
,
imgid
);
if
(
imgid
)
{
if
((
image
=
purple_imgstore_find_by_id
(
imgid
)))
{
gint
image_size
=
purple_imgstore_get_size
(
image
);
gconstpointer
image_bin
=
purple_imgstore_get_data
(
image
);
const
char
*
image_filename
=
purple_imgstore_get_filename
(
image
);
purple_debug_info
(
"gg"
,
"ggp_send_image_handler: sending image imgid: %i, crc: %u
\n
"
,
imgid
,
ev
->
event
.
image_request
.
crc32
);
gg_image_reply
(
info
->
session
,
(
unsigned
long
int
)
ev
->
event
.
image_request
.
sender
,
image_filename
,
image_bin
,
image_size
);
purple_imgstore_unref
(
image
);
}
else
{
purple_debug_error
(
"gg"
,
"ggp_send_image_handler: image imgid: %i, crc: %u in hash but not found in imgstore!
\n
"
,
imgid
,
ev
->
event
.
image_request
.
crc32
);
}
g_hash_table_remove
(
info
->
pending_images
,
GINT_TO_POINTER
(
ev
->
event
.
image_request
.
crc32
));
}
}
static
void
ggp_typing_notification_handler
(
PurpleConnection
*
gc
,
uin_t
uin
,
int
length
)
{
gchar
*
from
;
from
=
g_strdup_printf
(
"%u"
,
uin
);
if
(
length
)
serv_got_typing
(
gc
,
from
,
0
,
PURPLE_TYPING
);
else
serv_got_typing_stopped
(
gc
,
from
);
g_free
(
from
);
}
/**
* Handling of XML events.
*
* @param gc PurpleConnection.
* @param data Raw XML contents.
*
* @see http://toxygen.net/libgadu/protocol/#ch1.13
*/
static
void
ggp_xml_event_handler
(
PurpleConnection
*
gc
,
char
*
data
)
{
xmlnode
*
xml
=
NULL
;
xmlnode
*
xmlnode_next_event
;
xml
=
xmlnode_from_str
(
data
,
-1
);
if
(
xml
==
NULL
)
goto
out
;
xmlnode_next_event
=
xmlnode_get_child
(
xml
,
"event"
);
while
(
xmlnode_next_event
!=
NULL
)
{
xmlnode
*
xmlnode_current_event
=
xmlnode_next_event
;
xmlnode
*
xmlnode_type
;
char
*
event_type_raw
;
int
event_type
=
0
;
xmlnode
*
xmlnode_sender
;
char
*
event_sender_raw
;
uin_t
event_sender
=
0
;
xmlnode_next_event
=
xmlnode_get_next_twin
(
xmlnode_next_event
);
xmlnode_type
=
xmlnode_get_child
(
xmlnode_current_event
,
"type"
);
if
(
xmlnode_type
==
NULL
)
continue
;
event_type_raw
=
xmlnode_get_data
(
xmlnode_type
);
if
(
event_type_raw
!=
NULL
)
event_type
=
atoi
(
event_type_raw
);
g_free
(
event_type_raw
);
xmlnode_sender
=
xmlnode_get_child
(
xmlnode_current_event
,
"sender"
);
if
(
xmlnode_sender
!=
NULL
)
{
event_sender_raw
=
xmlnode_get_data
(
xmlnode_sender
);
if
(
event_sender_raw
!=
NULL
)
event_sender
=
ggp_str_to_uin
(
event_sender_raw
);
g_free
(
event_sender_raw
);
}
switch
(
event_type
)
{
case
28
:
/* avatar update */
purple_debug_info
(
"gg"
,
"ggp_xml_event_handler: avatar updated (uid: %u)
\n
"
,
event_sender
);
ggp_update_buddy_avatar
(
gc
,
event_sender
);
break
;
default
:
purple_debug_error
(
"gg"
,
"ggp_xml_event_handler: unsupported event type=%d from=%u
\n
"
,
event_type
,
event_sender
);
}
}
out
:
if
(
xml
)
xmlnode_free
(
xml
);
}
static
void
ggp_callback_recv
(
gpointer
_gc
,
gint
fd
,
PurpleInputCondition
cond
)
{
PurpleConnection
*
gc
=
_gc
;
GGPInfo
*
info
=
gc
->
proto_data
;
struct
gg_event
*
ev
;
int
i
;
if
(
!
(
ev
=
gg_watch_fd
(
info
->
session
)))
{
purple_debug_error
(
"gg"
,
"ggp_callback_recv: gg_watch_fd failed -- CRITICAL!
\n
"
);
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to read from socket"
));
return
;
}
purple_input_remove
(
gc
->
inpa
);
gc
->
inpa
=
purple_input_add
(
info
->
session
->
fd
,
ggp_tcpsocket_inputcond_gg_to_purple
(
info
->
session
->
check
),
ggp_callback_recv
,
gc
);
switch
(
ev
->
type
)
{
case
GG_EVENT_NONE
:
/* Nothing happened. */
break
;
case
GG_EVENT_MSG
:
ggp_recv_message_handler
(
gc
,
ev
);
break
;
case
GG_EVENT_ACK
:
/* Changing %u to %i fixes compiler warning */
purple_debug_info
(
"gg"
,
"ggp_callback_recv: message sent to: %i, delivery status=%d, seq=%d
\n
"
,
ev
->
event
.
ack
.
recipient
,
ev
->
event
.
ack
.
status
,
ev
->
event
.
ack
.
seq
);
break
;
case
GG_EVENT_IMAGE_REPLY
:
ggp_recv_image_handler
(
gc
,
ev
);
break
;
case
GG_EVENT_IMAGE_REQUEST
:
ggp_send_image_handler
(
gc
,
ev
);
break
;
case
GG_EVENT_NOTIFY
:
case
GG_EVENT_NOTIFY_DESCR
:
{
struct
gg_notify_reply
*
n
;
char
*
descr
;
purple_debug_info
(
"gg"
,
"notify_pre: (%d) status: %d
\n
"
,
ev
->
event
.
notify
->
uin
,
GG_S
(
ev
->
event
.
notify
->
status
));
n
=
(
ev
->
type
==
GG_EVENT_NOTIFY
)
?
ev
->
event
.
notify
:
ev
->
event
.
notify_descr
.
notify
;
for
(;
n
->
uin
;
n
++
)
{
descr
=
(
ev
->
type
==
GG_EVENT_NOTIFY
)
?
NULL
:
ev
->
event
.
notify_descr
.
descr
;
purple_debug_info
(
"gg"
,
"notify: (%d) status: %d; descr: %s
\n
"
,
n
->
uin
,
GG_S
(
n
->
status
),
descr
?
descr
:
"(null)"
);
ggp_generic_status_handler
(
gc
,
n
->
uin
,
GG_S
(
n
->
status
),
descr
);
}
}
break
;
case
GG_EVENT_NOTIFY60
:
for
(
i
=
0
;
ev
->
event
.
notify60
[
i
].
uin
;
i
++
)
{
purple_debug_info
(
"gg"
,
"notify60: (%d) status=%d; version=%d; descr=%s
\n
"
,
ev
->
event
.
notify60
[
i
].
uin
,
GG_S
(
ev
->
event
.
notify60
[
i
].
status
),
ev
->
event
.
notify60
[
i
].
version
,
ev
->
event
.
notify60
[
i
].
descr
?
ev
->
event
.
notify60
[
i
].
descr
:
"(null)"
);
ggp_generic_status_handler
(
gc
,
ev
->
event
.
notify60
[
i
].
uin
,
GG_S
(
ev
->
event
.
notify60
[
i
].
status
),
ev
->
event
.
notify60
[
i
].
descr
);
}
break
;
case
GG_EVENT_STATUS
:
purple_debug_info
(
"gg"
,
"status: (%d) status=%d; descr=%s
\n
"
,
ev
->
event
.
status
.
uin
,
GG_S
(
ev
->
event
.
status
.
status
),
ev
->
event
.
status
.
descr
?
ev
->
event
.
status
.
descr
:
"(null)"
);
ggp_generic_status_handler
(
gc
,
ev
->
event
.
status
.
uin
,
GG_S
(
ev
->
event
.
status
.
status
),
ev
->
event
.
status
.
descr
);
break
;
case
GG_EVENT_STATUS60
:
purple_debug_info
(
"gg"
,
"status60: (%d) status=%d; version=%d; descr=%s
\n
"
,
ev
->
event
.
status60
.
uin
,
GG_S
(
ev
->
event
.
status60
.
status
),
ev
->
event
.
status60
.
version
,
ev
->
event
.
status60
.
descr
?
ev
->
event
.
status60
.
descr
:
"(null)"
);
ggp_generic_status_handler
(
gc
,
ev
->
event
.
status60
.
uin
,
GG_S
(
ev
->
event
.
status60
.
status
),
ev
->
event
.
status60
.
descr
);
break
;
case
GG_EVENT_PUBDIR50_SEARCH_REPLY
:
ggp_pubdir_reply_handler
(
gc
,
ev
->
event
.
pubdir50
);
break
;
case
GG_EVENT_TYPING_NOTIFICATION
:
ggp_typing_notification_handler
(
gc
,
ev
->
event
.
typing_notification
.
uin
,
ev
->
event
.
typing_notification
.
length
);
break
;
case
GG_EVENT_XML_EVENT
:
purple_debug_info
(
"gg"
,
"GG_EVENT_XML_EVENT
\n
"
);
ggp_xml_event_handler
(
gc
,
ev
->
event
.
xml_event
.
data
);
break
;
default
:
purple_debug_error
(
"gg"
,
"unsupported event type=%d
\n
"
,
ev
->
type
);
break
;
}
gg_free_event
(
ev
);
}
static
void
ggp_async_login_handler
(
gpointer
_gc
,
gint
fd
,
PurpleInputCondition
cond
)
{
PurpleConnection
*
gc
=
_gc
;
GGPInfo
*
info
;
struct
gg_event
*
ev
;
g_return_if_fail
(
PURPLE_CONNECTION_IS_VALID
(
gc
));
info
=
gc
->
proto_data
;
purple_debug_info
(
"gg"
,
"login_handler: session: check = %d; state = %d;
\n
"
,
info
->
session
->
check
,
info
->
session
->
state
);
switch
(
info
->
session
->
state
)
{
case
GG_STATE_RESOLVING
:
purple_debug_info
(
"gg"
,
"GG_STATE_RESOLVING
\n
"
);
break
;
case
GG_STATE_RESOLVING_GG
:
purple_debug_info
(
"gg"
,
"GG_STATE_RESOLVING_GG
\n
"
);
break
;
case
GG_STATE_CONNECTING_HUB
:
purple_debug_info
(
"gg"
,
"GG_STATE_CONNECTING_HUB
\n
"
);
break
;
case
GG_STATE_READING_DATA
:
purple_debug_info
(
"gg"
,
"GG_STATE_READING_DATA
\n
"
);
break
;
case
GG_STATE_CONNECTING_GG
:
purple_debug_info
(
"gg"
,
"GG_STATE_CONNECTING_GG
\n
"
);
break
;
case
GG_STATE_READING_KEY
:
purple_debug_info
(
"gg"
,
"GG_STATE_READING_KEY
\n
"
);
break
;
case
GG_STATE_READING_REPLY
:
purple_debug_info
(
"gg"
,
"GG_STATE_READING_REPLY
\n
"
);
break
;
case
GG_STATE_TLS_NEGOTIATION
:
purple_debug_info
(
"gg"
,
"GG_STATE_TLS_NEGOTIATION
\n
"
);
break
;
default
:
purple_debug_error
(
"gg"
,
"unknown state = %d
\n
"
,
info
->
session
->
state
);
break
;
}
if
(
!
(
ev
=
gg_watch_fd
(
info
->
session
)))
{
purple_debug_error
(
"gg"
,
"login_handler: gg_watch_fd failed!
\n
"
);
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to read from socket"
));
return
;
}
purple_debug_info
(
"gg"
,
"login_handler: session->fd = %d
\n
"
,
info
->
session
->
fd
);
purple_debug_info
(
"gg"
,
"login_handler: session: check = %d; state = %d;
\n
"
,
info
->
session
->
check
,
info
->
session
->
state
);
purple_input_remove
(
gc
->
inpa
);
/** XXX I think that this shouldn't be done if ev->type is GG_EVENT_CONN_FAILED or GG_EVENT_CONN_SUCCESS -datallah */
if
(
info
->
session
->
fd
>=
0
)
gc
->
inpa
=
purple_input_add
(
info
->
session
->
fd
,
ggp_tcpsocket_inputcond_gg_to_purple
(
info
->
session
->
check
),
ggp_async_login_handler
,
gc
);
switch
(
ev
->
type
)
{
case
GG_EVENT_NONE
:
/* Nothing happened. */
purple_debug_info
(
"gg"
,
"GG_EVENT_NONE
\n
"
);
break
;
case
GG_EVENT_CONN_SUCCESS
:
{
purple_debug_info
(
"gg"
,
"GG_EVENT_CONN_SUCCESS
\n
"
);
purple_input_remove
(
gc
->
inpa
);
gc
->
inpa
=
purple_input_add
(
info
->
session
->
fd
,
ggp_tcpsocket_inputcond_gg_to_purple
(
info
->
session
->
check
),
ggp_callback_recv
,
gc
);
ggp_buddylist_send
(
gc
);
purple_connection_update_progress
(
gc
,
_
(
"Connected"
),
1
,
2
);
purple_connection_set_state
(
gc
,
PURPLE_CONNECTED
);
}
break
;
case
GG_EVENT_CONN_FAILED
:
purple_input_remove
(
gc
->
inpa
);
gc
->
inpa
=
0
;
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Connection failed"
));
break
;
case
GG_EVENT_MSG
:
if
(
ev
->
event
.
msg
.
sender
==
0
)
/* system messages are mostly ads */
purple_debug_info
(
"gg"
,
"System message:
\n
%s
\n
"
,
ev
->
event
.
msg
.
message
);
else
purple_debug_warning
(
"gg"
,
"GG_EVENT_MSG: message from user %u "
"unexpected while connecting:
\n
%s
\n
"
,
ev
->
event
.
msg
.
sender
,
ev
->
event
.
msg
.
message
);
break
;
default
:
purple_debug_error
(
"gg"
,
"strange event: %d
\n
"
,
ev
->
type
);
break
;
}
gg_free_event
(
ev
);
}
/* ---------------------------------------------------------------------- */
/* ----- PurplePluginProtocolInfo ----------------------------------------- */
/* ---------------------------------------------------------------------- */
static
const
char
*
ggp_list_icon
(
PurpleAccount
*
account
,
PurpleBuddy
*
buddy
)
{
return
"gadu-gadu"
;
}
static
char
*
ggp_status_text
(
PurpleBuddy
*
b
)
{
PurpleStatus
*
status
;
const
char
*
msg
;
char
*
text
;
char
*
tmp
;
status
=
purple_presence_get_active_status
(
purple_buddy_get_presence
(
b
));
msg
=
purple_status_get_attr_string
(
status
,
"message"
);
if
(
msg
==
NULL
)
return
NULL
;
tmp
=
purple_markup_strip_html
(
msg
);
text
=
g_markup_escape_text
(
tmp
,
-1
);
g_free
(
tmp
);
return
text
;
}
static
void
ggp_tooltip_text
(
PurpleBuddy
*
b
,
PurpleNotifyUserInfo
*
user_info
,
gboolean
full
)
{
PurpleStatus
*
status
;
char
*
text
,
*
tmp
;
const
char
*
msg
,
*
name
,
*
alias
;
g_return_if_fail
(
b
!=
NULL
);
status
=
purple_presence_get_active_status
(
purple_buddy_get_presence
(
b
));
msg
=
purple_status_get_attr_string
(
status
,
"message"
);
name
=
purple_status_get_name
(
status
);
alias
=
purple_buddy_get_alias
(
b
);
purple_notify_user_info_add_pair
(
user_info
,
_
(
"Alias"
),
alias
);
if
(
msg
!=
NULL
)
{
text
=
g_markup_escape_text
(
msg
,
-1
);
if
(
PURPLE_BUDDY_IS_ONLINE
(
b
))
{
tmp
=
g_strdup_printf
(
"%s: %s"
,
name
,
text
);
purple_notify_user_info_add_pair
(
user_info
,
_
(
"Status"
),
tmp
);
g_free
(
tmp
);
}
else
{
purple_notify_user_info_add_pair
(
user_info
,
_
(
"Message"
),
text
);
}
g_free
(
text
);
/* We don't want to duplicate 'Status: Offline'. */
}
else
if
(
PURPLE_BUDDY_IS_ONLINE
(
b
))
{
purple_notify_user_info_add_pair
(
user_info
,
_
(
"Status"
),
name
);
}
}
static
GList
*
ggp_status_types
(
PurpleAccount
*
account
)
{
PurpleStatusType
*
type
;
GList
*
types
=
NULL
;
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_AVAILABLE
,
NULL
,
NULL
,
TRUE
,
TRUE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
PURPLE_TYPE_STRING
),
NULL
);
types
=
g_list_append
(
types
,
type
);
/*
* Without this selecting Invisible as own status doesn't
* work. It's not used and not needed to show status of buddies.
*/
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_INVISIBLE
,
NULL
,
NULL
,
TRUE
,
TRUE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
PURPLE_TYPE_STRING
),
NULL
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_AWAY
,
NULL
,
NULL
,
TRUE
,
TRUE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
PURPLE_TYPE_STRING
),
NULL
);
types
=
g_list_append
(
types
,
type
);
/*
* New statuses for GG 8.0 like PoGGadaj ze mna (not yet because
* libpurple can't support Chatty status) and Nie przeszkadzac
*/
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_UNAVAILABLE
,
NULL
,
NULL
,
TRUE
,
TRUE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
PURPLE_TYPE_STRING
),
NULL
);
types
=
g_list_append
(
types
,
type
);
/*
* This status is necessary to display guys who are blocking *us*.
*/
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_INVISIBLE
,
"blocked"
,
_
(
"Blocked"
),
TRUE
,
FALSE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
PURPLE_TYPE_STRING
),
NULL
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_OFFLINE
,
NULL
,
NULL
,
TRUE
,
TRUE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
PURPLE_TYPE_STRING
),
NULL
);
types
=
g_list_append
(
types
,
type
);
return
types
;
}
static
GList
*
ggp_blist_node_menu
(
PurpleBlistNode
*
node
)
{
PurpleMenuAction
*
act
;
GList
*
m
=
NULL
;
PurpleAccount
*
account
;
GGPInfo
*
info
;
if
(
!
PURPLE_BLIST_NODE_IS_BUDDY
(
node
))
return
NULL
;
account
=
purple_buddy_get_account
((
PurpleBuddy
*
)
node
);
info
=
purple_account_get_connection
(
account
)
->
proto_data
;
if
(
info
->
chats
)
{
act
=
purple_menu_action_new
(
_
(
"Add to chat"
),
PURPLE_CALLBACK
(
ggp_bmenu_add_to_chat
),
NULL
,
NULL
);
m
=
g_list_append
(
m
,
act
);
}
return
m
;
}
static
GList
*
ggp_chat_info
(
PurpleConnection
*
gc
)
{
GList
*
m
=
NULL
;
struct
proto_chat_entry
*
pce
;
pce
=
g_new0
(
struct
proto_chat_entry
,
1
);
pce
->
label
=
_
(
"Chat _name:"
);
pce
->
identifier
=
"name"
;
pce
->
required
=
TRUE
;
m
=
g_list_append
(
m
,
pce
);
return
m
;
}
static
void
ggp_login_to
(
PurpleAccount
*
account
,
uint32_t
server
)
{
PurpleConnection
*
gc
;
PurplePresence
*
presence
;
PurpleStatus
*
status
;
struct
gg_login_params
*
glp
;
GGPInfo
*
info
;
const
gchar
*
encryption_type
;
if
(
ggp_setup_proxy
(
account
)
==
-1
)
return
;
gc
=
purple_account_get_connection
(
account
);
glp
=
g_new0
(
struct
gg_login_params
,
1
);
info
=
gc
->
proto_data
;
g_return_if_fail
(
info
);
/* Probably this should be moved to *_new() function. */
info
->
session
=
NULL
;
info
->
chats
=
NULL
;
info
->
chats_count
=
0
;
info
->
token
=
NULL
;
info
->
searches
=
ggp_search_new
();
info
->
pending_richtext_messages
=
NULL
;
info
->
pending_images
=
g_hash_table_new
(
g_direct_hash
,
g_direct_equal
);
info
->
status_broadcasting
=
purple_account_get_bool
(
account
,
"status_broadcasting"
,
TRUE
);
glp
->
uin
=
ggp_get_uin
(
account
);
glp
->
password
=
(
char
*
)
purple_account_get_password
(
account
);
glp
->
image_size
=
255
;
presence
=
purple_account_get_presence
(
account
);
status
=
purple_presence_get_active_status
(
presence
);
glp
->
encoding
=
GG_ENCODING_UTF8
;
glp
->
protocol_features
=
(
GG_FEATURE_STATUS80
|
GG_FEATURE_DND_FFC
|
GG_FEATURE_TYPING_NOTIFICATION
);
glp
->
async
=
1
;
glp
->
status
=
ggp_to_gg_status
(
status
,
&
glp
->
status_descr
);
encryption_type
=
purple_account_get_string
(
account
,
"encryption"
,
"none"
);
purple_debug_info
(
"gg"
,
"Requested encryption type: %s
\n
"
,
encryption_type
);
if
(
purple_strequal
(
encryption_type
,
"opportunistic_tls"
))
glp
->
tls
=
1
;
else
glp
->
tls
=
0
;
purple_debug_info
(
"gg"
,
"TLS enabled: %d
\n
"
,
glp
->
tls
);
if
(
!
info
->
status_broadcasting
)
glp
->
status
=
glp
->
status
|
GG_STATUS_FRIENDS_MASK
;
glp
->
server_addr
=
server
;
info
->
session
=
gg_login
(
glp
);
purple_connection_update_progress
(
gc
,
_
(
"Connecting"
),
0
,
2
);
if
(
info
->
session
==
NULL
)
{
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Connection failed"
));
g_free
(
glp
);
return
;
}
gc
->
inpa
=
purple_input_add
(
info
->
session
->
fd
,
ggp_tcpsocket_inputcond_gg_to_purple
(
info
->
session
->
check
),
ggp_async_login_handler
,
gc
);
}
static
void
ggp_login_resolved
(
GSList
*
hosts
,
gpointer
_account
,
const
char
*
error_message
)
{
PurpleAccount
*
account
=
_account
;
PurpleConnection
*
gc
;
GGPInfo
*
info
;
uint32_t
server_addr
=
0
;
gc
=
purple_account_get_connection
(
account
);
info
=
gc
->
proto_data
;
g_return_if_fail
(
info
);
info
->
dns_query
=
NULL
;
while
(
hosts
&&
(
hosts
=
g_slist_delete_link
(
hosts
,
hosts
)))
{
struct
sockaddr
*
addr
=
hosts
->
data
;
if
(
addr
->
sa_family
==
AF_INET
&&
server_addr
==
0
)
{
struct
sockaddr_in
*
addrv4
=
(
struct
sockaddr_in
*
)
addr
;
server_addr
=
addrv4
->
sin_addr
.
s_addr
;
}
g_free
(
hosts
->
data
);
hosts
=
g_slist_delete_link
(
hosts
,
hosts
);
}
if
(
server_addr
==
0
)
{
gchar
*
tmp
=
g_strdup_printf
(
_
(
"Unable to resolve hostname: %s"
),
error_message
);
purple_connection_error_reason
(
gc
,
/* should this be a settings error? */
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
tmp
);
g_free
(
tmp
);
return
;
}
ggp_login_to
(
account
,
server_addr
);
}
static
void
ggp_login
(
PurpleAccount
*
account
)
{
PurpleConnection
*
gc
;
GGPInfo
*
info
;
const
char
*
address
;
gc
=
purple_account_get_connection
(
account
);
info
=
g_new0
(
GGPInfo
,
1
);
gc
->
proto_data
=
info
;
address
=
purple_account_get_string
(
account
,
"gg_server"
,
""
);
if
(
address
==
NULL
||
address
[
0
]
==
'\0'
)
{
purple_debug_info
(
"gg"
,
"Trying to retrieve address from gg appmsg service
\n
"
);
ggp_login_to
(
account
,
0
);
return
;
}
purple_debug_info
(
"gg"
,
"Using gg server given by user (%s)
\n
"
,
address
);
info
->
dns_query
=
purple_dnsquery_a_account
(
account
,
address
,
8074
,
ggp_login_resolved
,
account
);
}
static
void
ggp_close
(
PurpleConnection
*
gc
)
{
if
(
gc
==
NULL
)
{
purple_debug_info
(
"gg"
,
"gc == NULL
\n
"
);
return
;
}
if
(
gc
->
proto_data
)
{
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
PurpleStatus
*
status
;
GGPInfo
*
info
=
gc
->
proto_data
;
if
(
info
->
dns_query
)
purple_dnsquery_destroy
(
info
->
dns_query
);
status
=
purple_account_get_active_status
(
account
);
if
(
info
->
session
!=
NULL
)
{
ggp_set_status
(
account
,
status
);
gg_logoff
(
info
->
session
);
gg_free_session
(
info
->
session
);
}
purple_account_set_bool
(
account
,
"status_broadcasting"
,
info
->
status_broadcasting
);
/* Immediately close any notifications on this handle since that process depends
* upon the contents of info->searches, which we are about to destroy.
*/
purple_notify_close_with_handle
(
gc
);
ggp_search_destroy
(
info
->
searches
);
g_list_free
(
info
->
pending_richtext_messages
);
g_hash_table_destroy
(
info
->
pending_images
);
g_free
(
info
);
gc
->
proto_data
=
NULL
;
}
if
(
gc
->
inpa
>
0
)
purple_input_remove
(
gc
->
inpa
);
purple_debug_info
(
"gg"
,
"Connection closed.
\n
"
);
}
static
int
ggp_send_im
(
PurpleConnection
*
gc
,
const
char
*
who
,
const
char
*
msg
,
PurpleMessageFlags
flags
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
char
*
tmp
,
*
plain
;
int
ret
=
1
;
unsigned
char
format
[
1024
];
unsigned
int
format_length
=
sizeof
(
struct
gg_msg_richtext
);
gint
pos
=
0
;
GData
*
attribs
;
const
char
*
start
,
*
end
=
NULL
,
*
last
;
if
(
msg
==
NULL
||
*
msg
==
'\0'
)
{
return
0
;
}
last
=
msg
;
/* Check if the message is richtext */
/* TODO: Check formatting, too */
if
(
purple_markup_find_tag
(
"img"
,
last
,
&
start
,
&
end
,
&
attribs
))
{
GString
*
string_buffer
=
g_string_new
(
NULL
);
struct
gg_msg_richtext
fmt
;
do
{
PurpleStoredImage
*
image
;
const
char
*
id
;
/* Add text before the image */
if
(
start
-
last
)
{
pos
=
pos
+
g_utf8_strlen
(
last
,
start
-
last
);
g_string_append_len
(
string_buffer
,
last
,
start
-
last
);
}
if
((
id
=
g_datalist_get_data
(
&
attribs
,
"id"
))
&&
(
image
=
purple_imgstore_find_by_id
(
atoi
(
id
))))
{
struct
gg_msg_richtext_format
actformat
;
struct
gg_msg_richtext_image
actimage
;
gint
image_size
=
purple_imgstore_get_size
(
image
);
gconstpointer
image_bin
=
purple_imgstore_get_data
(
image
);
const
char
*
image_filename
=
purple_imgstore_get_filename
(
image
);
uint32_t
crc32
=
gg_crc32
(
0
,
image_bin
,
image_size
);
g_hash_table_insert
(
info
->
pending_images
,
GINT_TO_POINTER
(
crc32
),
GINT_TO_POINTER
(
atoi
(
id
)));
purple_imgstore_ref
(
image
);
purple_debug_info
(
"gg"
,
"ggp_send_im_richtext: got crc: %u for imgid: %i
\n
"
,
crc32
,
atoi
(
id
));
actformat
.
font
=
GG_FONT_IMAGE
;
actformat
.
position
=
pos
;
actimage
.
unknown1
=
0x0109
;
actimage
.
size
=
gg_fix32
(
image_size
);
actimage
.
crc32
=
gg_fix32
(
crc32
);
if
(
actimage
.
size
>
255000
)
{
purple_debug_warning
(
"gg"
,
"ggp_send_im_richtext: image over 255kb!
\n
"
);
}
else
{
purple_debug_info
(
"gg"
,
"ggp_send_im_richtext: adding images to richtext, size: %i, crc32: %u, name: %s
\n
"
,
actimage
.
size
,
actimage
.
crc32
,
image_filename
);
memcpy
(
format
+
format_length
,
&
actformat
,
sizeof
(
actformat
));
format_length
+=
sizeof
(
actformat
);
memcpy
(
format
+
format_length
,
&
actimage
,
sizeof
(
actimage
));
format_length
+=
sizeof
(
actimage
);
}
}
else
{
purple_debug_error
(
"gg"
,
"ggp_send_im_richtext: image not found in the image store!"
);
}
last
=
end
+
1
;
g_datalist_clear
(
&
attribs
);
}
while
(
purple_markup_find_tag
(
"img"
,
last
,
&
start
,
&
end
,
&
attribs
));
/* Add text after the images */
if
(
last
&&
*
last
)
{
pos
=
pos
+
g_utf8_strlen
(
last
,
-1
);
g_string_append
(
string_buffer
,
last
);
}
fmt
.
flag
=
2
;
fmt
.
length
=
format_length
-
sizeof
(
fmt
);
memcpy
(
format
,
&
fmt
,
sizeof
(
fmt
));
purple_debug_info
(
"gg"
,
"ggp_send_im: richtext msg = %s
\n
"
,
string_buffer
->
str
);
plain
=
purple_unescape_html
(
string_buffer
->
str
);
g_string_free
(
string_buffer
,
TRUE
);
}
else
{
purple_debug_info
(
"gg"
,
"ggp_send_im: msg = %s
\n
"
,
msg
);
plain
=
purple_unescape_html
(
msg
);
}
/*
tmp = charset_convert(plain, "UTF-8", "CP1250");
*/
tmp
=
g_strdup_printf
(
"%s"
,
plain
);
if
(
tmp
&&
(
format_length
-
sizeof
(
struct
gg_msg_richtext
)))
{
if
(
gg_send_message_richtext
(
info
->
session
,
GG_CLASS_CHAT
,
ggp_str_to_uin
(
who
),
(
unsigned
char
*
)
tmp
,
format
,
format_length
)
<
0
)
{
ret
=
-1
;
}
else
{
ret
=
1
;
}
}
else
if
(
NULL
==
tmp
||
*
tmp
==
0
)
{
ret
=
0
;
}
else
if
(
strlen
(
tmp
)
>
GG_MSG_MAXSIZE
)
{
ret
=
-
E2BIG
;
}
else
if
(
gg_send_message
(
info
->
session
,
GG_CLASS_CHAT
,
ggp_str_to_uin
(
who
),
(
unsigned
char
*
)
tmp
)
<
0
)
{
ret
=
-1
;
}
else
{
ret
=
1
;
}
g_free
(
plain
);
g_free
(
tmp
);
return
ret
;
}
static
unsigned
int
ggp_send_typing
(
PurpleConnection
*
gc
,
const
char
*
name
,
PurpleTypingState
state
)
{
int
dummy_length
;
// we don't send real length of typed message
if
(
state
==
PURPLE_TYPED
)
// not supported
return
1
;
if
(
state
==
PURPLE_TYPING
)
dummy_length
=
(
int
)
g_random_int
();
else
// PURPLE_NOT_TYPING
dummy_length
=
0
;
gg_typing_notification
(
((
GGPInfo
*
)
gc
->
proto_data
)
->
session
,
ggp_str_to_uin
(
name
),
dummy_length
);
return
1
;
// wait 1 second before another notification
}
static
void
ggp_get_info
(
PurpleConnection
*
gc
,
const
char
*
name
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
GGPSearchForm
*
form
;
guint32
seq
;
form
=
ggp_search_form_new
(
GGP_SEARCH_TYPE_INFO
);
form
->
user_data
=
info
;
form
->
uin
=
g_strdup
(
name
);
seq
=
ggp_search_start
(
gc
,
form
);
ggp_search_add
(
info
->
searches
,
seq
,
form
);
purple_debug_info
(
"gg"
,
"ggp_get_info(): Added seq %u"
,
seq
);
}
static
int
ggp_to_gg_status
(
PurpleStatus
*
status
,
char
**
msg
)
{
const
char
*
status_id
=
purple_status_get_id
(
status
);
int
new_status
,
new_status_descr
;
const
char
*
new_msg
;
g_return_val_if_fail
(
msg
!=
NULL
,
0
);
purple_debug_info
(
"gg"
,
"ggp_to_gg_status: Requested status = %s
\n
"
,
status_id
);
if
(
purple_strequal
(
status_id
,
"available"
))
{
new_status
=
GG_STATUS_AVAIL
;
new_status_descr
=
GG_STATUS_AVAIL_DESCR
;
}
else
if
(
purple_strequal
(
status_id
,
"away"
))
{
new_status
=
GG_STATUS_BUSY
;
new_status_descr
=
GG_STATUS_BUSY_DESCR
;
}
else
if
(
purple_strequal
(
status_id
,
"unavailable"
))
{
new_status
=
GG_STATUS_DND
;
new_status_descr
=
GG_STATUS_DND_DESCR
;
}
else
if
(
purple_strequal
(
status_id
,
"invisible"
))
{
new_status
=
GG_STATUS_INVISIBLE
;
new_status_descr
=
GG_STATUS_INVISIBLE_DESCR
;
}
else
if
(
purple_strequal
(
status_id
,
"offline"
))
{
new_status
=
GG_STATUS_NOT_AVAIL
;
new_status_descr
=
GG_STATUS_NOT_AVAIL_DESCR
;
}
else
{
new_status
=
GG_STATUS_AVAIL
;
new_status_descr
=
GG_STATUS_AVAIL_DESCR
;
purple_debug_info
(
"gg"
,
"ggp_set_status: unknown status requested (status_id=%s)
\n
"
,
status_id
);
}
new_msg
=
purple_status_get_attr_string
(
status
,
"message"
);
if
(
new_msg
)
{
/*
char *tmp = purple_markup_strip_html(new_msg);
*msg = charset_convert(tmp, "UTF-8", "CP1250");
g_free(tmp);
*/
*
msg
=
purple_markup_strip_html
(
new_msg
);
return
new_status_descr
;
}
else
{
*
msg
=
NULL
;
return
new_status
;
}
}
static
void
ggp_set_status
(
PurpleAccount
*
account
,
PurpleStatus
*
status
)
{
PurpleConnection
*
gc
;
GGPInfo
*
info
;
int
new_status
;
char
*
new_msg
=
NULL
;
if
(
!
purple_status_is_active
(
status
))
return
;
gc
=
purple_account_get_connection
(
account
);
info
=
gc
->
proto_data
;
new_status
=
ggp_to_gg_status
(
status
,
&
new_msg
);
if
(
!
info
->
status_broadcasting
)
new_status
=
new_status
|
GG_STATUS_FRIENDS_MASK
;
if
(
new_msg
==
NULL
)
{
gg_change_status
(
info
->
session
,
new_status
);
}
else
{
gg_change_status_descr
(
info
->
session
,
new_status
,
new_msg
);
g_free
(
new_msg
);
}
ggp_status_fake_to_self
(
account
);
}
static
void
ggp_add_buddy
(
PurpleConnection
*
gc
,
PurpleBuddy
*
buddy
,
PurpleGroup
*
group
)
{
PurpleAccount
*
account
;
GGPInfo
*
info
=
gc
->
proto_data
;
const
gchar
*
name
=
purple_buddy_get_name
(
buddy
);
gg_add_notify
(
info
->
session
,
ggp_str_to_uin
(
name
));
account
=
purple_connection_get_account
(
gc
);
if
(
purple_strequal
(
purple_account_get_username
(
account
),
name
))
{
ggp_status_fake_to_self
(
account
);
}
}
static
void
ggp_remove_buddy
(
PurpleConnection
*
gc
,
PurpleBuddy
*
buddy
,
PurpleGroup
*
group
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
gg_remove_notify
(
info
->
session
,
ggp_str_to_uin
(
purple_buddy_get_name
(
buddy
)));
}
static
void
ggp_join_chat
(
PurpleConnection
*
gc
,
GHashTable
*
data
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
GGPChat
*
chat
;
char
*
chat_name
;
GList
*
l
;
PurpleConversation
*
conv
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
chat_name
=
g_hash_table_lookup
(
data
,
"name"
);
if
(
chat_name
==
NULL
)
return
;
purple_debug_info
(
"gg"
,
"joined %s chat
\n
"
,
chat_name
);
for
(
l
=
info
->
chats
;
l
!=
NULL
;
l
=
l
->
next
)
{
chat
=
l
->
data
;
if
(
chat
!=
NULL
&&
g_utf8_collate
(
chat
->
name
,
chat_name
)
==
0
)
{
purple_notify_error
(
gc
,
_
(
"Chat error"
),
_
(
"This chat name is already in use"
),
NULL
);
return
;
}
}
ggp_confer_add_new
(
gc
,
chat_name
);
conv
=
serv_got_joined_chat
(
gc
,
info
->
chats_count
,
chat_name
);
purple_conv_chat_add_user
(
PURPLE_CONV_CHAT
(
conv
),
purple_account_get_username
(
account
),
NULL
,
PURPLE_CBFLAGS_NONE
,
TRUE
);
}
static
char
*
ggp_get_chat_name
(
GHashTable
*
data
)
{
return
g_strdup
(
g_hash_table_lookup
(
data
,
"name"
));
}
static
int
ggp_chat_send
(
PurpleConnection
*
gc
,
int
id
,
const
char
*
message
,
PurpleMessageFlags
flags
)
{
PurpleConversation
*
conv
;
GGPInfo
*
info
=
gc
->
proto_data
;
GGPChat
*
chat
=
NULL
;
GList
*
l
;
/* char *msg, *plain; */
gchar
*
msg
;
uin_t
*
uins
;
int
count
=
0
;
if
((
conv
=
purple_find_chat
(
gc
,
id
))
==
NULL
)
return
-
EINVAL
;
for
(
l
=
info
->
chats
;
l
!=
NULL
;
l
=
l
->
next
)
{
chat
=
l
->
data
;
if
(
g_utf8_collate
(
chat
->
name
,
conv
->
name
)
==
0
)
{
break
;
}
chat
=
NULL
;
}
if
(
chat
==
NULL
)
{
purple_debug_error
(
"gg"
,
"ggp_chat_send: Hm... that's strange. No such chat?
\n
"
);
return
-
EINVAL
;
}
uins
=
g_new0
(
uin_t
,
g_list_length
(
chat
->
participants
));
for
(
l
=
chat
->
participants
;
l
!=
NULL
;
l
=
l
->
next
)
{
uin_t
uin
=
GPOINTER_TO_INT
(
l
->
data
);
uins
[
count
++
]
=
uin
;
}
/*
plain = purple_unescape_html(message);
msg = charset_convert(plain, "UTF-8", "CP1250");
g_free(plain);
*/
msg
=
purple_unescape_html
(
message
);
gg_send_message_confer
(
info
->
session
,
GG_CLASS_CHAT
,
count
,
uins
,
(
unsigned
char
*
)
msg
);
g_free
(
msg
);
g_free
(
uins
);
serv_got_chat_in
(
gc
,
id
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
flags
,
message
,
time
(
NULL
));
return
0
;
}
static
void
ggp_keepalive
(
PurpleConnection
*
gc
)
{
GGPInfo
*
info
=
gc
->
proto_data
;
/* purple_debug_info("gg", "Keeping connection alive....\n"); */
if
(
gg_ping
(
info
->
session
)
<
0
)
{
purple_debug_info
(
"gg"
,
"Not connected to the server "
"or gg_session is not correct
\n
"
);
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Not connected to the server"
));
}
}
static
GList
*
ggp_actions
(
PurplePlugin
*
plugin
,
gpointer
context
)
{
GList
*
m
=
NULL
;
PurplePluginAction
*
act
;
act
=
purple_plugin_action_new
(
_
(
"Find buddies..."
),
ggp_find_buddies
);
m
=
g_list_append
(
m
,
act
);
act
=
purple_plugin_action_new
(
_
(
"Change status broadcasting"
),
ggp_action_change_status_broadcasting
);
m
=
g_list_append
(
m
,
act
);
m
=
g_list_append
(
m
,
NULL
);
act
=
purple_plugin_action_new
(
_
(
"Save buddylist to file..."
),
ggp_action_buddylist_save
);
m
=
g_list_append
(
m
,
act
);
act
=
purple_plugin_action_new
(
_
(
"Load buddylist from file..."
),
ggp_action_buddylist_load
);
m
=
g_list_append
(
m
,
act
);
return
m
;
}
static
gboolean
ggp_offline_message
(
const
PurpleBuddy
*
buddy
)
{
return
TRUE
;
}
static
gboolean
ggp_load
(
PurplePlugin
*
plugin
)
{
purple_debug_info
(
"gg"
,
"Loading Gadu-Gadu protocol plugin with "
"libgadu %s...
\n
"
,
gg_libgadu_version
());
gg_is_gpl_compliant
();
return
TRUE
;
}
static
PurplePluginProtocolInfo
prpl_info
=
{
OPT_PROTO_IM_IMAGE
,
NULL
,
/* user_splits */
NULL
,
/* protocol_options */
{
"png"
,
32
,
32
,
96
,
96
,
0
,
PURPLE_ICON_SCALE_DISPLAY
},
/* icon_spec */
ggp_list_icon
,
/* list_icon */
NULL
,
/* list_emblem */
ggp_status_text
,
/* status_text */
ggp_tooltip_text
,
/* tooltip_text */
ggp_status_types
,
/* status_types */
ggp_blist_node_menu
,
/* blist_node_menu */
ggp_chat_info
,
/* chat_info */
NULL
,
/* chat_info_defaults */
ggp_login
,
/* login */
ggp_close
,
/* close */
ggp_send_im
,
/* send_im */
NULL
,
/* set_info */
ggp_send_typing
,
/* send_typing */
ggp_get_info
,
/* get_info */
ggp_set_status
,
/* set_away */
NULL
,
/* set_idle */
NULL
,
/* change_passwd */
ggp_add_buddy
,
/* add_buddy */
NULL
,
/* add_buddies */
ggp_remove_buddy
,
/* remove_buddy */
NULL
,
/* remove_buddies */
NULL
,
/* add_permit */
ggp_add_deny
,
/* add_deny */
NULL
,
/* rem_permit */
ggp_rem_deny
,
/* rem_deny */
NULL
,
/* set_permit_deny */
ggp_join_chat
,
/* join_chat */
NULL
,
/* reject_chat */
ggp_get_chat_name
,
/* get_chat_name */
NULL
,
/* chat_invite */
NULL
,
/* chat_leave */
NULL
,
/* chat_whisper */
ggp_chat_send
,
/* chat_send */
ggp_keepalive
,
/* keepalive */
NULL
,
/* register_user */
NULL
,
/* get_cb_info */
NULL
,
/* get_cb_away */
NULL
,
/* alias_buddy */
NULL
,
/* group_buddy */
NULL
,
/* rename_group */
NULL
,
/* buddy_free */
NULL
,
/* convo_closed */
NULL
,
/* normalize */
NULL
,
/* set_buddy_icon */
NULL
,
/* remove_group */
NULL
,
/* get_cb_real_name */
NULL
,
/* set_chat_topic */
NULL
,
/* find_blist_chat */
NULL
,
/* roomlist_get_list */
NULL
,
/* roomlist_cancel */
NULL
,
/* roomlist_expand_category */
NULL
,
/* can_receive_file */
NULL
,
/* send_file */
NULL
,
/* new_xfer */
ggp_offline_message
,
/* offline_message */
NULL
,
/* whiteboard_prpl_ops */
NULL
,
/* send_raw */
NULL
,
/* roomlist_room_serialize */
NULL
,
/* unregister_user */
NULL
,
/* send_attention */
NULL
,
/* get_attention_types */
sizeof
(
PurplePluginProtocolInfo
),
/* struct_size */
NULL
,
/* get_account_text_table */
NULL
,
/* initiate_media */
NULL
,
/* can_do_media */
NULL
,
/* get_moods */
NULL
,
/* set_public_alias */
NULL
,
/* get_public_alias */
NULL
,
/* add_buddy_with_invite */
NULL
,
/* add_buddies_with_invite */
NULL
,
/* get_cb_alias */
NULL
,
/* chat_can_receive_file */
NULL
,
/* chat_send_file */
};
static
PurplePluginInfo
info
=
{
PURPLE_PLUGIN_MAGIC
,
/* magic */
PURPLE_MAJOR_VERSION
,
/* major_version */
PURPLE_MINOR_VERSION
,
/* minor_version */
PURPLE_PLUGIN_PROTOCOL
,
/* plugin type */
NULL
,
/* ui_requirement */
0
,
/* flags */
NULL
,
/* dependencies */
PURPLE_PRIORITY_DEFAULT
,
/* priority */
"prpl-gg"
,
/* id */
"Gadu-Gadu"
,
/* name */
DISPLAY_VERSION
,
/* version */
N_
(
"Gadu-Gadu Protocol Plugin"
),
/* summary */
N_
(
"Polish popular IM"
),
/* description */
"boler@sourceforge.net"
,
/* author */
PURPLE_WEBSITE
,
/* homepage */
ggp_load
,
/* load */
NULL
,
/* unload */
NULL
,
/* destroy */
NULL
,
/* ui_info */
&
prpl_info
,
/* extra_info */
NULL
,
/* prefs_info */
ggp_actions
,
/* actions */
/* padding */
NULL
,
NULL
,
NULL
,
NULL
};
static
void
purple_gg_debug_handler
(
int
level
,
const
char
*
format
,
va_list
args
)
{
PurpleDebugLevel
purple_level
;
char
msgbuff
[
1000
];
int
ret
;
/* Don't use glib's printf family, since it might not support
* system-specific formatting modifiers (like %Iu for size on win32). */
ret
=
vsnprintf
(
msgbuff
,
sizeof
(
msgbuff
)
/
sizeof
(
char
),
format
,
args
);
if
(
ret
<=
0
)
{
purple_debug_fatal
(
"gg"
,
"failed to printf the following message: %s"
,
format
?
format
:
"(null)
\n
"
);
return
;
}
/* This is pretty pointless since the GG_DEBUG levels don't correspond to
* the purple ones */
switch
(
level
)
{
case
GG_DEBUG_FUNCTION
:
purple_level
=
PURPLE_DEBUG_INFO
;
break
;
case
GG_DEBUG_MISC
:
case
GG_DEBUG_NET
:
case
GG_DEBUG_DUMP
:
case
GG_DEBUG_TRAFFIC
:
default
:
purple_level
=
PURPLE_DEBUG_MISC
;
break
;
}
purple_debug
(
purple_level
,
"gg"
,
"%s"
,
msgbuff
);
}
static
void
init_plugin
(
PurplePlugin
*
plugin
)
{
PurpleAccountOption
*
option
;
GList
*
encryption_options
=
NULL
;
option
=
purple_account_option_string_new
(
_
(
"Nickname"
),
"nick"
,
_
(
"Gadu-Gadu User"
));
prpl_info
.
protocol_options
=
g_list_append
(
prpl_info
.
protocol_options
,
option
);
option
=
purple_account_option_string_new
(
_
(
"GG server"
),
"gg_server"
,
""
);
prpl_info
.
protocol_options
=
g_list_append
(
prpl_info
.
protocol_options
,
option
);
#define ADD_VALUE(list, desc, v) { \
PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \
kvp->key = g_strdup((desc)); \
kvp->value = g_strdup((v)); \
list = g_list_append(list, kvp); \
}
ADD_VALUE
(
encryption_options
,
_
(
"Don't use encryption"
),
"none"
);
ADD_VALUE
(
encryption_options
,
_
(
"Use encryption if available"
),
"opportunistic_tls"
);
#if 0
/* TODO */
ADD_VALUE(encryption_options, _("Require encryption"), "require_tls");
#endif
option
=
purple_account_option_list_new
(
_
(
"Connection security"
),
"encryption"
,
encryption_options
);
prpl_info
.
protocol_options
=
g_list_append
(
prpl_info
.
protocol_options
,
option
);
my_protocol
=
plugin
;
gg_debug_handler
=
purple_gg_debug_handler
;
}
PURPLE_INIT_PLUGIN
(
gg
,
init_plugin
,
info
);
/* vim: set ts=8 sts=0 sw=8 noet: */