pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Fix some issues scanbuild found
22 months ago, Gary Kramlich
ba5964adbf7f
Fix some issues scanbuild found
Testing Done:
ran `ninja scan-build` and verified the issues were resolved.
Reviewed at https://reviews.imfreedom.org/r/1585/
/**
* @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
<glib/gi18n-lib.h>
#include
<gplugin.h>
#include
<gplugin-native.h>
#include
<purple.h>
#include
"gg.h"
#include
"chat.h"
#include
"search.h"
#include
"blist.h"
#include
"utils.h"
#include
"resolver-purple.h"
#include
"purplew.h"
#include
"libgadu-events.h"
#include
"multilogon.h"
#include
"status.h"
#include
"servconn.h"
#include
"tcpsocket.h"
#include
"pubdir-prpl.h"
#include
"message-prpl.h"
#include
"html.h"
#include
"libgaduw.h"
struct
_GGPProtocol
{
PurpleProtocol
parent
;
};
/* ---------------------------------------------------------------------- */
static
PurpleProtocol
*
my_protocol
=
NULL
;
/* ---------------------------------------------------------------------- */
ggp_buddy_data
*
ggp_buddy_get_data
(
PurpleBuddy
*
buddy
)
{
ggp_buddy_data
*
buddy_data
=
purple_buddy_get_protocol_data
(
buddy
);
if
(
buddy_data
)
return
buddy_data
;
buddy_data
=
g_new0
(
ggp_buddy_data
,
1
);
/* TODO: leak */
purple_buddy_set_protocol_data
(
buddy
,
buddy_data
);
return
buddy_data
;
}
static
void
ggp_buddy_free
(
PurpleProtocolClient
*
client
,
PurpleBuddy
*
buddy
)
{
ggp_buddy_data
*
buddy_data
=
purple_buddy_get_protocol_data
(
buddy
);
if
(
!
buddy_data
)
{
return
;
}
g_free
(
buddy_data
);
purple_buddy_set_protocol_data
(
buddy
,
NULL
);
}
const
gchar
*
ggp_get_imtoken
(
PurpleConnection
*
gc
)
{
GGPInfo
*
accdata
=
purple_connection_get_protocol_data
(
gc
);
if
(
accdata
->
imtoken
)
return
accdata
->
imtoken
;
if
(
accdata
->
imtoken_warned
)
return
NULL
;
accdata
->
imtoken_warned
=
TRUE
;
purple_notify_error
(
gc
,
_
(
"Authentication failed"
),
_
(
"IMToken value has not been received."
),
_
(
"Some features will be disabled. "
"You may try again after a while."
),
purple_request_cpar_from_connection
(
gc
));
return
NULL
;
}
uin_t
ggp_own_uin
(
PurpleConnection
*
gc
)
{
return
ggp_str_to_uin
(
purple_account_get_username
(
purple_connection_get_account
(
gc
)));
}
/* ---------------------------------------------------------------------- */
/* buddy list import/export from/to file */
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
,
purple_request_cpar_from_connection
(
gc
));
return
;
}
if
(
g_file_set_contents
(
filename
,
buddylist
,
-1
,
NULL
))
{
purple_notify_info
(
account
,
_
(
"Save Buddylist..."
),
_
(
"Buddylist saved successfully!"
),
NULL
,
purple_request_cpar_from_connection
(
gc
));
}
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
,
purple_request_cpar_from_connection
(
gc
));
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_request_cpar_from_connection
(
gc
));
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
,
purple_request_cpar_from_connection
(
gc
));
}
/* }}} */
static
void
ggp_action_buddylist_save
(
G_GNUC_UNUSED
GSimpleAction
*
action
,
GVariant
*
parameter
,
G_GNUC_UNUSED
gpointer
data
)
{
const
gchar
*
account_id
=
NULL
;
PurpleAccountManager
*
manager
=
NULL
;
PurpleAccount
*
account
=
NULL
;
PurpleConnection
*
connection
=
NULL
;
if
(
!
g_variant_is_of_type
(
parameter
,
G_VARIANT_TYPE_STRING
))
{
g_critical
(
"GG save buddylist action parameter is of incorrect type %s"
,
g_variant_get_type_string
(
parameter
));
}
account_id
=
g_variant_get_string
(
parameter
,
NULL
);
manager
=
purple_account_manager_get_default
();
account
=
purple_account_manager_find_by_id
(
manager
,
account_id
);
connection
=
purple_account_get_connection
(
account
);
purple_request_file
(
action
,
_
(
"Save buddylist..."
),
NULL
,
TRUE
,
G_CALLBACK
(
ggp_callback_buddylist_save_ok
),
NULL
,
purple_request_cpar_from_connection
(
connection
),
connection
);
}
static
void
ggp_action_buddylist_load
(
G_GNUC_UNUSED
GSimpleAction
*
action
,
GVariant
*
parameter
,
G_GNUC_UNUSED
gpointer
data
)
{
const
gchar
*
account_id
=
NULL
;
PurpleAccountManager
*
manager
=
NULL
;
PurpleAccount
*
account
=
NULL
;
PurpleConnection
*
connection
=
NULL
;
if
(
!
g_variant_is_of_type
(
parameter
,
G_VARIANT_TYPE_STRING
))
{
g_critical
(
"GG load buddylist action parameter is of incorrect type %s"
,
g_variant_get_type_string
(
parameter
));
}
account_id
=
g_variant_get_string
(
parameter
,
NULL
);
manager
=
purple_account_manager_get_default
();
account
=
purple_account_manager_find_by_id
(
manager
,
account_id
);
connection
=
purple_account_get_connection
(
account
);
purple_request_file
(
action
,
_
(
"Load buddylist from file..."
),
NULL
,
FALSE
,
G_CALLBACK
(
ggp_callback_buddylist_load_ok
),
NULL
,
purple_request_cpar_from_connection
(
connection
),
connection
);
}
/* ----- BLOCK BUDDIES -------------------------------------------------- */
static
void
ggp_add_deny
(
PurpleProtocolPrivacy
*
privacy
,
PurpleConnection
*
gc
,
const
char
*
who
)
{
GGPInfo
*
info
=
purple_connection_get_protocol_data
(
gc
);
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_remove_deny
(
PurpleProtocolPrivacy
*
privacy
,
PurpleConnection
*
gc
,
const
char
*
who
)
{
GGPInfo
*
info
=
purple_connection_get_protocol_data
(
gc
);
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 --------------------------------------------- */
/* ---------------------------------------------------------------------- */
static
void
ggp_typing_notification_handler
(
PurpleConnection
*
gc
,
uin_t
uin
,
int
length
)
{
gchar
*
from
;
from
=
g_strdup_printf
(
"%u"
,
uin
);
if
(
length
)
purple_serv_got_typing
(
gc
,
from
,
0
,
PURPLE_IM_TYPING
);
else
purple_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
* @todo: this may not be necessary anymore
*/
static
void
ggp_xml_event_handler
(
PurpleConnection
*
gc
,
char
*
data
)
{
PurpleXmlNode
*
xml
=
NULL
;
PurpleXmlNode
*
xmlnode_next_event
;
xml
=
purple_xmlnode_from_str
(
data
,
-1
);
if
(
xml
==
NULL
)
{
purple_debug_error
(
"gg"
,
"ggp_xml_event_handler: "
"invalid xml: [%s]
\n
"
,
data
);
goto
out
;
}
xmlnode_next_event
=
purple_xmlnode_get_child
(
xml
,
"event"
);
while
(
xmlnode_next_event
!=
NULL
)
{
PurpleXmlNode
*
xmlnode_current_event
=
xmlnode_next_event
;
PurpleXmlNode
*
xmlnode_type
;
char
*
event_type_raw
;
int
event_type
=
0
;
PurpleXmlNode
*
xmlnode_sender
;
char
*
event_sender_raw
;
uin_t
event_sender
=
0
;
xmlnode_next_event
=
purple_xmlnode_get_next_twin
(
xmlnode_next_event
);
xmlnode_type
=
purple_xmlnode_get_child
(
xmlnode_current_event
,
"type"
);
if
(
xmlnode_type
==
NULL
)
continue
;
event_type_raw
=
purple_xmlnode_get_data
(
xmlnode_type
);
if
(
event_type_raw
!=
NULL
)
event_type
=
atoi
(
event_type_raw
);
g_free
(
event_type_raw
);
xmlnode_sender
=
purple_xmlnode_get_child
(
xmlnode_current_event
,
"sender"
);
if
(
xmlnode_sender
!=
NULL
)
{
event_sender_raw
=
purple_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
);
break
;
default
:
purple_debug_error
(
"gg"
,
"ggp_xml_event_handler: unsupported event type=%d from=%u
\n
"
,
event_type
,
event_sender
);
}
}
out
:
if
(
xml
)
purple_xmlnode_free
(
xml
);
}
static
void
ggp_callback_recv
(
gpointer
_gc
,
gint
fd
,
PurpleInputCondition
cond
)
{
PurpleConnection
*
gc
=
_gc
;
GGPInfo
*
info
=
purple_connection_get_protocol_data
(
gc
);
struct
gg_event
*
ev
;
if
(
!
(
ev
=
gg_watch_fd
(
info
->
session
)))
{
purple_debug_error
(
"gg"
,
"ggp_callback_recv: gg_watch_fd failed -- CRITICAL!
\n
"
);
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to read from socket"
));
return
;
}
if
(
purple_debug_is_verbose
())
{
purple_debug_misc
(
"gg"
,
"ggp_callback_recv: got event %s"
,
gg_debug_event
(
ev
->
type
));
}
purple_input_remove
(
info
->
inpa
);
info
->
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_CONN_FAILED
:
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Server disconnected"
));
break
;
case
GG_EVENT_MSG
:
ggp_message_got
(
gc
,
&
ev
->
event
.
msg
);
break
;
case
GG_EVENT_ACK
:
case
GG_EVENT_ACK110
:
break
;
case
GG_EVENT_IMAGE_REPLY
:
ggp_image_recv
(
gc
,
&
ev
->
event
.
image_reply
);
break
;
case
GG_EVENT_IMAGE_REQUEST
:
ggp_image_send
(
gc
,
&
ev
->
event
.
image_request
);
break
;
case
GG_EVENT_NOTIFY60
:
case
GG_EVENT_STATUS60
:
ggp_status_got_others
(
gc
,
ev
);
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
;
case
GG_EVENT_USER_DATA
:
ggp_events_user_data
(
gc
,
&
ev
->
event
.
user_data
);
break
;
case
GG_EVENT_JSON_EVENT
:
ggp_events_json
(
gc
,
&
ev
->
event
.
json_event
);
break
;
case
GG_EVENT_USERLIST100_VERSION
:
ggp_roster_version
(
gc
,
&
ev
->
event
.
userlist100_version
);
break
;
case
GG_EVENT_USERLIST100_REPLY
:
ggp_roster_reply
(
gc
,
&
ev
->
event
.
userlist100_reply
);
break
;
case
GG_EVENT_MULTILOGON_MSG
:
ggp_message_got_multilogon
(
gc
,
&
ev
->
event
.
multilogon_msg
);
break
;
case
GG_EVENT_MULTILOGON_INFO
:
ggp_multilogon_info
(
gc
,
&
ev
->
event
.
multilogon_info
);
break
;
case
GG_EVENT_IMTOKEN
:
purple_debug_info
(
"gg"
,
"gg11: got IMTOKEN
\n
"
);
g_free
(
info
->
imtoken
);
info
->
imtoken
=
g_strdup
(
ev
->
event
.
imtoken
.
imtoken
);
break
;
case
GG_EVENT_PONG110
:
purple_debug_info
(
"gg"
,
"gg11: got PONG110 %lu
\n
"
,
(
long
unsigned
)
ev
->
event
.
pong110
.
time
);
break
;
case
GG_EVENT_CHAT_INFO
:
case
GG_EVENT_CHAT_INFO_GOT_ALL
:
case
GG_EVENT_CHAT_INFO_UPDATE
:
case
GG_EVENT_CHAT_CREATED
:
case
GG_EVENT_CHAT_INVITE_ACK
:
ggp_chat_got_event
(
gc
,
ev
);
break
;
case
GG_EVENT_DISCONNECT
:
ggp_servconn_remote_disconnect
(
gc
);
break
;
default
:
purple_debug_warning
(
"gg"
,
"unsupported event type=%d
\n
"
,
ev
->
type
);
break
;
}
gg_free_event
(
ev
);
}
void
ggp_async_login_handler
(
gpointer
_gc
,
gint
fd
,
PurpleInputCondition
cond
)
{
PurpleConnection
*
gc
=
_gc
;
GGPInfo
*
info
;
struct
gg_event
*
ev
;
PURPLE_ASSERT_CONNECTION_IS_VALID
(
gc
);
info
=
purple_connection_get_protocol_data
(
gc
);
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_ERROR
:
purple_debug_info
(
"gg"
,
"GG_STATE_ERROR
\n
"
);
break
;
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
;
case
GG_STATE_RESOLVING_HUB
:
purple_debug_info
(
"gg"
,
"GG_STATE_RESOLVING_HUB
\n
"
);
break
;
case
GG_STATE_READING_HUB
:
purple_debug_info
(
"gg"
,
"GG_STATE_READING_HUB
\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
(
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
(
info
->
inpa
);
info
->
inpa
=
0
;
/** 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
)
info
->
inpa
=
purple_input_add
(
info
->
session
->
fd
,
(
info
->
session
->
check
==
1
)
?
PURPLE_INPUT_WRITE
:
PURPLE_INPUT_READ
,
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:"
" successfully connected to %s
\n
"
,
info
->
session
->
connect_host
);
ggp_servconn_add_server
(
info
->
session
->
connect_host
);
purple_input_remove
(
info
->
inpa
);
info
->
inpa
=
purple_input_add
(
info
->
session
->
fd
,
PURPLE_INPUT_READ
,
ggp_callback_recv
,
gc
);
purple_connection_update_progress
(
gc
,
_
(
"Connected"
),
1
,
2
);
purple_connection_set_state
(
gc
,
PURPLE_CONNECTION_CONNECTED
);
ggp_buddylist_send
(
gc
);
ggp_roster_request_update
(
gc
);
}
break
;
case
GG_EVENT_CONN_FAILED
:
if
(
info
->
inpa
>
0
)
{
purple_input_remove
(
info
->
inpa
);
info
->
inpa
=
0
;
}
purple_debug_info
(
"gg"
,
"Connection failure: %d
\n
"
,
ev
->
event
.
failure
);
switch
(
ev
->
event
.
failure
)
{
case
GG_FAILURE_RESOLVING
:
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to resolve "
"hostname"
));
break
;
case
GG_FAILURE_PASSWORD
:
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
,
_
(
"Incorrect password"
));
break
;
case
GG_FAILURE_TLS
:
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR
,
_
(
"SSL Connection Failed"
));
break
;
case
GG_FAILURE_INTRUDER
:
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
,
_
(
"Your account has been "
"disabled because too many "
"incorrect passwords were "
"entered"
));
break
;
case
GG_FAILURE_UNAVAILABLE
:
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Service temporarily "
"unavailable"
));
break
;
case
GG_FAILURE_PROXY
:
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Error connecting to proxy "
"server"
));
break
;
case
GG_FAILURE_HUB
:
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Error connecting to master "
"server"
));
break
;
case
GG_FAILURE_INTERNAL
:
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_OTHER_ERROR
,
_
(
"Internal error"
));
break
;
default
:
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Connection failed"
));
}
break
;
case
GG_EVENT_MSG
:
if
(
ev
->
event
.
msg
.
sender
==
0
)
{
if
(
ev
->
event
.
msg
.
message
==
NULL
)
break
;
/* 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
);
}
static
gint
gg_uri_handler_find_account
(
PurpleAccount
*
account
,
G_GNUC_UNUSED
gconstpointer
data
)
{
const
gchar
*
protocol_id
;
protocol_id
=
purple_account_get_protocol_id
(
account
);
if
(
purple_strequal
(
protocol_id
,
"prpl-gg"
)
&&
purple_account_is_connected
(
account
))
{
return
0
;
}
else
{
return
-1
;
}
}
static
gboolean
gg_uri_handler
(
const
gchar
*
scheme
,
const
gchar
*
screenname
,
GHashTable
*
params
)
{
PurpleAccountManager
*
manager
=
NULL
;
GList
*
accounts
;
GList
*
account_node
;
PurpleConversation
*
im
;
g_return_val_if_fail
(
screenname
!=
NULL
,
FALSE
);
if
(
!
purple_strequal
(
scheme
,
"gg"
))
{
return
FALSE
;
}
if
(
screenname
[
0
]
==
'\0'
)
{
purple_debug_warning
(
"gg"
,
"Invalid empty screenname in URI"
);
return
FALSE
;
}
/* Find online Gadu-Gadu account */
manager
=
purple_account_manager_get_default
();
accounts
=
purple_account_manager_get_all
(
manager
);
account_node
=
g_list_find_custom
(
accounts
,
NULL
,
(
GCompareFunc
)
gg_uri_handler_find_account
);
if
(
account_node
==
NULL
)
{
return
FALSE
;
}
im
=
purple_im_conversation_new
(
account_node
->
data
,
screenname
);
purple_conversation_present
(
im
);
return
TRUE
;
}
/* ---------------------------------------------------------------------- */
/* ----- PurpleProtocol ----------------------------------------- */
/* ---------------------------------------------------------------------- */
static
const
char
*
ggp_list_icon
(
PurpleAccount
*
account
,
PurpleBuddy
*
buddy
)
{
return
"gadu-gadu"
;
}
static
PurpleBuddyIconSpec
*
ggp_protocol_get_buddy_icon_spec
(
PurpleProtocol
*
protocol
)
{
return
purple_buddy_icon_spec_new
(
"png"
,
1
,
1
,
200
,
200
,
0
,
PURPLE_ICON_SCALE_DISPLAY
|
PURPLE_ICON_SCALE_SEND
);
}
static
GList
*
ggp_protocol_get_account_options
(
PurpleProtocol
*
protocol
)
{
PurpleAccountOption
*
option
=
NULL
;
PurpleKeyValuePair
*
kvp
=
NULL
;
GList
*
encryption_options
=
NULL
;
GList
*
protocol_version
=
NULL
;
GList
*
opts
=
NULL
;
option
=
purple_account_option_string_new
(
_
(
"GG server"
),
"gg_server"
,
""
);
opts
=
g_list_append
(
opts
,
option
);
/* setup encryption options */
kvp
=
purple_key_value_pair_new
(
_
(
"Use encryption if available"
),
"opportunistic_tls"
);
encryption_options
=
g_list_append
(
encryption_options
,
kvp
);
kvp
=
purple_key_value_pair_new
(
_
(
"Require encryption"
),
"require_tls"
);
encryption_options
=
g_list_append
(
encryption_options
,
kvp
);
kvp
=
purple_key_value_pair_new
(
_
(
"Don't use encryption"
),
"none"
);
encryption_options
=
g_list_append
(
encryption_options
,
kvp
);
option
=
purple_account_option_list_new
(
_
(
"Connection security"
),
"encryption"
,
encryption_options
);
opts
=
g_list_append
(
opts
,
option
);
/* setup the protocol version */
kvp
=
purple_key_value_pair_new
(
_
(
"Default"
),
"default"
);
protocol_version
=
g_list_append
(
protocol_version
,
kvp
);
kvp
=
purple_key_value_pair_new
(
"GG 10"
,
"gg10"
);
protocol_version
=
g_list_append
(
protocol_version
,
kvp
);
kvp
=
purple_key_value_pair_new
(
"GG 11"
,
"gg11"
);
protocol_version
=
g_list_append
(
protocol_version
,
kvp
);
option
=
purple_account_option_list_new
(
_
(
"Protocol version"
),
"protocol_version"
,
protocol_version
);
opts
=
g_list_append
(
opts
,
option
);
option
=
purple_account_option_bool_new
(
_
(
"Show links from strangers"
),
"show_links_from_strangers"
,
1
);
opts
=
g_list_append
(
opts
,
option
);
return
opts
;
}
static
const
char
*
ggp_normalize
(
PurpleProtocolClient
*
client
,
PurpleAccount
*
account
,
const
char
*
who
)
{
static
char
normalized
[
21
];
/* maximum unsigned long long int size */
uin_t
uin
=
ggp_str_to_uin
(
who
);
if
(
uin
<=
0
)
{
return
NULL
;
}
g_snprintf
(
normalized
,
sizeof
(
normalized
),
"%u"
,
uin
);
return
normalized
;
}
/* TODO:
* - move to status.c ?
* - add information about not adding to his buddy list (not_a_friend)
*/
static
void
ggp_tooltip_text
(
PurpleProtocolClient
*
client
,
PurpleBuddy
*
b
,
PurpleNotifyUserInfo
*
user_info
,
gboolean
full
)
{
PurpleStatus
*
status
;
char
*
tmp
;
const
char
*
name
,
*
alias
;
gchar
*
msg
;
g_return_if_fail
(
b
!=
NULL
);
status
=
purple_presence_get_active_status
(
purple_buddy_get_presence
(
b
));
name
=
purple_status_get_name
(
status
);
alias
=
purple_buddy_get_alias
(
b
);
ggp_status_from_purplestatus
(
status
,
&
msg
);
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Alias"
),
alias
);
if
(
msg
!=
NULL
)
{
if
(
PURPLE_BUDDY_IS_ONLINE
(
b
))
{
tmp
=
g_strdup_printf
(
"%s: %s"
,
name
,
msg
);
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Status"
),
tmp
);
g_free
(
tmp
);
}
else
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Message"
),
msg
);
}
g_free
(
msg
);
/* We don't want to duplicate 'Status: Offline'. */
}
else
if
(
PURPLE_BUDDY_IS_ONLINE
(
b
))
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Status"
),
name
);
}
}
static
void
ggp_login
(
PurpleAccount
*
account
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
account
);
struct
gg_login_params
*
glp
;
GGPInfo
*
info
;
const
char
*
address
;
const
gchar
*
encryption_type
,
*
protocol_version
;
GProxyResolver
*
resolver
;
GError
*
error
=
NULL
;
purple_connection_set_flags
(
gc
,
PURPLE_CONNECTION_FLAG_HTML
|
PURPLE_CONNECTION_FLAG_NO_URLDESC
);
resolver
=
purple_proxy_get_proxy_resolver
(
account
,
&
error
);
if
(
resolver
==
NULL
)
{
purple_debug_error
(
"gg"
,
"Unable to get account proxy resolver: %s"
,
error
->
message
);
purple_connection_take_error
(
gc
,
error
);
return
;
}
glp
=
g_new0
(
struct
gg_login_params
,
1
);
glp
->
struct_size
=
sizeof
(
struct
gg_login_params
);
info
=
g_new0
(
GGPInfo
,
1
);
purple_connection_set_protocol_data
(
gc
,
info
);
info
->
http
=
soup_session_new_with_options
(
"proxy-resolver"
,
resolver
,
NULL
);
ggp_tcpsocket_setup
(
gc
,
glp
);
ggp_image_setup
(
gc
);
ggp_avatar_setup
(
gc
);
ggp_roster_setup
(
gc
);
ggp_multilogon_setup
(
gc
);
ggp_status_setup
(
gc
);
ggp_chat_setup
(
gc
);
ggp_message_setup
(
gc
);
ggp_edisc_setup
(
gc
,
resolver
);
g_object_unref
(
resolver
);
glp
->
uin
=
ggp_str_to_uin
(
purple_account_get_username
(
account
));
glp
->
password
=
ggp_convert_to_cp1250
(
purple_connection_get_password
(
gc
));
if
(
glp
->
uin
==
0
)
{
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_INVALID_USERNAME
,
_
(
"The username specified is invalid."
));
purple_str_wipe
(
glp
->
password
);
g_free
(
glp
);
return
;
}
glp
->
image_size
=
255
;
glp
->
status_flags
=
GG_STATUS_FLAG_UNKNOWN
;
if
(
purple_account_get_bool
(
account
,
"show_links_from_strangers"
,
1
))
glp
->
status_flags
|=
GG_STATUS_FLAG_SPAM
;
glp
->
encoding
=
GG_ENCODING_UTF8
;
glp
->
protocol_features
=
(
GG_FEATURE_DND_FFC
|
GG_FEATURE_TYPING_NOTIFICATION
|
GG_FEATURE_MULTILOGON
|
GG_FEATURE_USER_DATA
);
glp
->
async
=
1
;
encryption_type
=
purple_account_get_string
(
account
,
"encryption"
,
"opportunistic_tls"
);
purple_debug_info
(
"gg"
,
"Requested encryption type: %s
\n
"
,
encryption_type
);
if
(
purple_strequal
(
encryption_type
,
"opportunistic_tls"
))
glp
->
tls
=
GG_SSL_ENABLED
;
else
if
(
purple_strequal
(
encryption_type
,
"require_tls"
))
{
if
(
gg_libgadu_check_feature
(
GG_LIBGADU_FEATURE_SSL
))
glp
->
tls
=
GG_SSL_REQUIRED
;
else
{
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
,
_
(
"SSL support unavailable"
));
purple_str_wipe
(
glp
->
password
);
g_free
(
glp
);
return
;
}
}
else
/* encryption_type == "none" */
glp
->
tls
=
GG_SSL_DISABLED
;
purple_debug_misc
(
"gg"
,
"TLS mode: %d
\n
"
,
glp
->
tls
);
protocol_version
=
purple_account_get_string
(
account
,
"protocol_version"
,
"default"
);
purple_debug_info
(
"gg"
,
"Requested protocol version: %s
\n
"
,
protocol_version
);
if
(
purple_strequal
(
protocol_version
,
"gg10"
))
glp
->
protocol_version
=
GG_PROTOCOL_VERSION_100
;
else
if
(
purple_strequal
(
protocol_version
,
"gg11"
))
glp
->
protocol_version
=
GG_PROTOCOL_VERSION_110
;
glp
->
compatibility
=
GG_COMPAT_1_12_0
;
ggp_status_set_initial
(
gc
,
glp
);
address
=
purple_account_get_string
(
account
,
"gg_server"
,
""
);
if
(
address
&&
*
address
)
glp
->
connect_host
=
g_strdup
(
address
);
info
->
session
=
gg_login
(
glp
);
g_free
(
glp
->
connect_host
);
purple_str_wipe
(
glp
->
password
);
g_free
(
glp
);
purple_connection_update_progress
(
gc
,
_
(
"Connecting"
),
0
,
2
);
if
(
info
->
session
==
NULL
)
{
purple_connection_error
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Connection failed"
));
return
;
}
if
(
info
->
session
->
fd
>
0
)
{
info
->
inpa
=
purple_input_add
(
info
->
session
->
fd
,
PURPLE_INPUT_READ
,
ggp_async_login_handler
,
gc
);
}
}
static
void
ggp_close
(
PurpleConnection
*
gc
)
{
PurpleAccount
*
account
;
GGPInfo
*
info
;;
g_return_if_fail
(
gc
!=
NULL
);
account
=
purple_connection_get_account
(
gc
);
info
=
purple_connection_get_protocol_data
(
gc
);
purple_notify_close_with_handle
(
gc
);
if
(
info
)
{
if
(
info
->
session
!=
NULL
)
{
ggp_status_set_disconnected
(
account
);
gg_logoff
(
info
->
session
);
gg_free_session
(
info
->
session
);
}
ggp_image_cleanup
(
gc
);
ggp_avatar_cleanup
(
gc
);
ggp_roster_cleanup
(
gc
);
ggp_multilogon_cleanup
(
gc
);
ggp_status_cleanup
(
gc
);
ggp_chat_cleanup
(
gc
);
ggp_message_cleanup
(
gc
);
ggp_edisc_cleanup
(
gc
);
if
(
info
->
inpa
>
0
)
purple_input_remove
(
info
->
inpa
);
g_free
(
info
->
imtoken
);
if
(
info
->
http
)
{
soup_session_abort
(
info
->
http
);
g_object_unref
(
info
->
http
);
}
purple_connection_set_protocol_data
(
gc
,
NULL
);
g_free
(
info
);
}
purple_debug_info
(
"gg"
,
"Connection closed.
\n
"
);
}
static
unsigned
int
ggp_send_typing
(
PurpleProtocolIM
*
im
,
PurpleConnection
*
gc
,
const
char
*
name
,
PurpleIMTypingState
state
)
{
GGPInfo
*
info
=
purple_connection_get_protocol_data
(
gc
);
int
dummy_length
;
/* we don't send real length of typed message */
if
(
state
==
PURPLE_IM_TYPED
)
/* not supported */
return
1
;
if
(
state
==
PURPLE_IM_TYPING
)
dummy_length
=
(
int
)
g_random_int
();
else
/* PURPLE_IM_NOT_TYPING */
dummy_length
=
0
;
gg_typing_notification
(
info
->
session
,
ggp_str_to_uin
(
name
),
dummy_length
);
return
1
;
/* wait 1 second before another notification */
}
static
void
ggp_add_buddy
(
PurpleProtocolServer
*
protocol_server
,
PurpleConnection
*
gc
,
PurpleBuddy
*
buddy
,
PurpleGroup
*
group
,
const
gchar
*
message
)
{
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
GGPInfo
*
info
=
purple_connection_get_protocol_data
(
gc
);
const
gchar
*
name
=
purple_buddy_get_name
(
buddy
);
gg_add_notify
(
info
->
session
,
ggp_str_to_uin
(
name
));
/* gg server won't tell us our status here */
if
(
purple_strequal
(
purple_account_get_username
(
account
),
name
))
ggp_status_fake_to_self
(
gc
);
ggp_roster_add_buddy
(
protocol_server
,
gc
,
buddy
,
group
,
message
);
ggp_pubdir_request_buddy_alias
(
gc
,
buddy
);
}
static
void
ggp_remove_buddy
(
PurpleProtocolServer
*
protocol_server
,
PurpleConnection
*
gc
,
PurpleBuddy
*
buddy
,
PurpleGroup
*
group
)
{
GGPInfo
*
info
=
purple_connection_get_protocol_data
(
gc
);
gg_remove_notify
(
info
->
session
,
ggp_str_to_uin
(
purple_buddy_get_name
(
buddy
)));
ggp_roster_remove_buddy
(
protocol_server
,
gc
,
buddy
,
group
);
}
static
void
ggp_keepalive
(
PurpleProtocolServer
*
protocol_server
,
PurpleConnection
*
gc
)
{
GGPInfo
*
info
=
purple_connection_get_protocol_data
(
gc
);
/* 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
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Not connected to the server"
));
}
}
static
void
ggp_action_multilogon
(
G_GNUC_UNUSED
GSimpleAction
*
action
,
GVariant
*
parameter
,
G_GNUC_UNUSED
gpointer
data
)
{
const
gchar
*
account_id
=
NULL
;
PurpleAccountManager
*
manager
=
NULL
;
PurpleAccount
*
account
=
NULL
;
PurpleConnection
*
connection
=
NULL
;
if
(
!
g_variant_is_of_type
(
parameter
,
G_VARIANT_TYPE_STRING
))
{
g_critical
(
"GG multilogin action parameter is of incorrect type %s"
,
g_variant_get_type_string
(
parameter
));
}
account_id
=
g_variant_get_string
(
parameter
,
NULL
);
manager
=
purple_account_manager_get_default
();
account
=
purple_account_manager_find_by_id
(
manager
,
account_id
);
connection
=
purple_account_get_connection
(
account
);
ggp_multilogon_dialog
(
connection
);
}
static
void
ggp_action_status_broadcasting
(
G_GNUC_UNUSED
GSimpleAction
*
action
,
GVariant
*
parameter
,
G_GNUC_UNUSED
gpointer
data
)
{
const
gchar
*
account_id
=
NULL
;
PurpleAccountManager
*
manager
=
NULL
;
PurpleAccount
*
account
=
NULL
;
PurpleConnection
*
connection
=
NULL
;
if
(
!
g_variant_is_of_type
(
parameter
,
G_VARIANT_TYPE_STRING
))
{
g_critical
(
"GG broadcast action parameter is of incorrect type %s"
,
g_variant_get_type_string
(
parameter
));
}
account_id
=
g_variant_get_string
(
parameter
,
NULL
);
manager
=
purple_account_manager_get_default
();
account
=
purple_account_manager_find_by_id
(
manager
,
account_id
);
connection
=
purple_account_get_connection
(
account
);
ggp_status_broadcasting_dialog
(
connection
);
}
static
void
ggp_action_search
(
G_GNUC_UNUSED
GSimpleAction
*
action
,
GVariant
*
parameter
,
G_GNUC_UNUSED
gpointer
data
)
{
const
gchar
*
account_id
=
NULL
;
PurpleAccountManager
*
manager
=
NULL
;
PurpleAccount
*
account
=
NULL
;
PurpleConnection
*
connection
=
NULL
;
if
(
!
g_variant_is_of_type
(
parameter
,
G_VARIANT_TYPE_STRING
))
{
g_critical
(
"GG search action parameter is of incorrect type %s"
,
g_variant_get_type_string
(
parameter
));
}
account_id
=
g_variant_get_string
(
parameter
,
NULL
);
manager
=
purple_account_manager_get_default
();
account
=
purple_account_manager_find_by_id
(
manager
,
account_id
);
connection
=
purple_account_get_connection
(
account
);
ggp_pubdir_search
(
connection
,
NULL
);
}
static
void
ggp_action_set_info
(
G_GNUC_UNUSED
GSimpleAction
*
action
,
GVariant
*
parameter
,
G_GNUC_UNUSED
gpointer
data
)
{
const
gchar
*
account_id
=
NULL
;
PurpleAccountManager
*
manager
=
NULL
;
PurpleAccount
*
account
=
NULL
;
PurpleConnection
*
connection
=
NULL
;
if
(
!
g_variant_is_of_type
(
parameter
,
G_VARIANT_TYPE_STRING
))
{
g_critical
(
"GG set info action parameter is of incorrect type %s"
,
g_variant_get_type_string
(
parameter
));
}
account_id
=
g_variant_get_string
(
parameter
,
NULL
);
manager
=
purple_account_manager_get_default
();
account
=
purple_account_manager_find_by_id
(
manager
,
account_id
);
connection
=
purple_account_get_connection
(
account
);
ggp_pubdir_set_info
(
connection
);
}
static
const
gchar
*
ggp_protocol_actions_get_prefix
(
PurpleProtocolActions
*
actions
)
{
return
"prpl-gg"
;
}
static
GActionGroup
*
ggp_protocol_actions_get_action_group
(
PurpleProtocolActions
*
actions
,
G_GNUC_UNUSED
PurpleConnection
*
connection
)
{
GSimpleActionGroup
*
group
=
NULL
;
GActionEntry
entries
[]
=
{
{
.
name
=
"multilogon"
,
.
activate
=
ggp_action_multilogon
,
.
parameter_type
=
"s"
,
},
{
.
name
=
"broadcasting"
,
.
activate
=
ggp_action_status_broadcasting
,
.
parameter_type
=
"s"
,
},
{
.
name
=
"search"
,
.
activate
=
ggp_action_search
,
.
parameter_type
=
"s"
,
},
{
.
name
=
"set-info"
,
.
activate
=
ggp_action_set_info
,
.
parameter_type
=
"s"
,
},
{
.
name
=
"save-buddylist"
,
.
activate
=
ggp_action_buddylist_save
,
.
parameter_type
=
"s"
,
},
{
.
name
=
"load-buddylist"
,
.
activate
=
ggp_action_buddylist_load
,
.
parameter_type
=
"s"
,
},
};
gsize
nentries
=
G_N_ELEMENTS
(
entries
);
group
=
g_simple_action_group_new
();
g_action_map_add_action_entries
(
G_ACTION_MAP
(
group
),
entries
,
nentries
,
NULL
);
return
G_ACTION_GROUP
(
group
);
}
static
GMenu
*
ggp_protocol_actions_get_menu
(
PurpleProtocolActions
*
actions
)
{
GMenu
*
menu
=
NULL
,
*
submenu
=
NULL
;
GMenuItem
*
item
=
NULL
;
menu
=
g_menu_new
();
item
=
g_menu_item_new
(
_
(
"Show other sessions"
),
"prpl-gg.multilogon"
);
g_menu_item_set_attribute
(
item
,
PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET
,
"s"
,
"account"
);
g_menu_append_item
(
menu
,
item
);
g_object_unref
(
item
);
item
=
g_menu_item_new
(
_
(
"Show status only for buddies"
),
"prpl-gg.broadcasting"
);
g_menu_item_set_attribute
(
item
,
PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET
,
"s"
,
"account"
);
g_menu_append_item
(
menu
,
item
);
g_object_unref
(
item
);
item
=
g_menu_item_new
(
_
(
"Find buddies..."
),
"prpl-gg.search"
);
g_menu_item_set_attribute
(
item
,
PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET
,
"s"
,
"account"
);
g_menu_append_item
(
menu
,
item
);
g_object_unref
(
item
);
item
=
g_menu_item_new
(
_
(
"Set User Info"
),
"prpl-gg.set-info"
);
g_menu_item_set_attribute
(
item
,
PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET
,
"s"
,
"account"
);
g_menu_append_item
(
menu
,
item
);
g_object_unref
(
item
);
/* Buddy list management. */
submenu
=
g_menu_new
();
item
=
g_menu_item_new
(
_
(
"Save to file..."
),
"prpl-gg.save-buddylist"
);
g_menu_item_set_attribute
(
item
,
PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET
,
"s"
,
"account"
);
g_menu_append_item
(
menu
,
item
);
g_object_unref
(
item
);
item
=
g_menu_item_new
(
_
(
"Load from file..."
),
"prpl-gg.load-buddylist"
);
g_menu_item_set_attribute
(
item
,
PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET
,
"s"
,
"account"
);
g_menu_append_item
(
menu
,
item
);
g_object_unref
(
item
);
g_menu_append_submenu
(
menu
,
_
(
"Buddy list"
),
G_MENU_MODEL
(
submenu
));
g_object_unref
(
submenu
);
return
menu
;
}
static
const
char
*
ggp_list_emblem
(
PurpleProtocolClient
*
client
,
PurpleBuddy
*
buddy
)
{
ggp_buddy_data
*
buddy_data
=
ggp_buddy_get_data
(
buddy
);
if
(
buddy_data
->
blocked
)
return
"not-authorized"
;
if
(
buddy_data
->
not_a_friend
)
return
"unavailable"
;
return
NULL
;
}
static
gboolean
ggp_offline_message
(
PurpleProtocolClient
*
client
,
PurpleBuddy
*
buddy
)
{
return
TRUE
;
}
static
GHashTable
*
ggp_get_account_text_table
(
PurpleProtocolClient
*
client
,
PurpleAccount
*
account
)
{
GHashTable
*
table
;
table
=
g_hash_table_new
(
g_str_hash
,
g_str_equal
);
g_hash_table_insert
(
table
,
"login_label"
,
(
gpointer
)
_
(
"GG number..."
));
return
table
;
}
static
gssize
ggp_get_max_message_size
(
PurpleProtocolClient
*
client
,
PurpleConversation
*
conv
)
{
/* TODO: it may depend on protocol version or other factors */
return
1200
;
/* no more than 1232 */
}
static
void
ggp_protocol_init
(
GGPProtocol
*
self
)
{
}
static
void
ggp_protocol_class_init
(
GGPProtocolClass
*
klass
)
{
PurpleProtocolClass
*
protocol_class
=
PURPLE_PROTOCOL_CLASS
(
klass
);
protocol_class
->
login
=
ggp_login
;
protocol_class
->
close
=
ggp_close
;
protocol_class
->
status_types
=
ggp_status_types
;
protocol_class
->
list_icon
=
ggp_list_icon
;
protocol_class
->
get_account_options
=
ggp_protocol_get_account_options
;
protocol_class
->
get_buddy_icon_spec
=
ggp_protocol_get_buddy_icon_spec
;
}
static
void
ggp_protocol_class_finalize
(
G_GNUC_UNUSED
GGPProtocolClass
*
klass
)
{
}
static
void
ggp_protocol_actions_iface_init
(
PurpleProtocolActionsInterface
*
iface
)
{
iface
->
get_prefix
=
ggp_protocol_actions_get_prefix
;
iface
->
get_action_group
=
ggp_protocol_actions_get_action_group
;
iface
->
get_menu
=
ggp_protocol_actions_get_menu
;
}
static
void
ggp_protocol_client_iface_init
(
PurpleProtocolClientInterface
*
client_iface
)
{
client_iface
->
list_emblem
=
ggp_list_emblem
;
client_iface
->
status_text
=
ggp_status_buddy_text
;
client_iface
->
tooltip_text
=
ggp_tooltip_text
;
client_iface
->
buddy_free
=
ggp_buddy_free
;
client_iface
->
normalize
=
ggp_normalize
;
client_iface
->
offline_message
=
ggp_offline_message
;
client_iface
->
get_account_text_table
=
ggp_get_account_text_table
;
client_iface
->
get_max_message_size
=
ggp_get_max_message_size
;
}
static
void
ggp_protocol_server_iface_init
(
PurpleProtocolServerInterface
*
server_iface
)
{
server_iface
->
get_info
=
ggp_pubdir_get_info_protocol
;
server_iface
->
set_status
=
ggp_status_set_purplestatus
;
server_iface
->
add_buddy
=
ggp_add_buddy
;
server_iface
->
remove_buddy
=
ggp_remove_buddy
;
server_iface
->
keepalive
=
ggp_keepalive
;
server_iface
->
alias_buddy
=
ggp_roster_alias_buddy
;
server_iface
->
group_buddy
=
ggp_roster_group_buddy
;
server_iface
->
rename_group
=
ggp_roster_rename_group
;
server_iface
->
set_buddy_icon
=
ggp_avatar_own_set
;
}
static
void
ggp_protocol_im_iface_init
(
PurpleProtocolIMInterface
*
im_iface
)
{
im_iface
->
send
=
ggp_message_send_im
;
im_iface
->
send_typing
=
ggp_send_typing
;
}
static
void
ggp_protocol_chat_iface_init
(
PurpleProtocolChatInterface
*
chat_iface
)
{
chat_iface
->
info
=
ggp_chat_info
;
chat_iface
->
info_defaults
=
ggp_chat_info_defaults
;
chat_iface
->
join
=
ggp_chat_join
;
chat_iface
->
get_name
=
ggp_chat_get_name
;
chat_iface
->
invite
=
ggp_chat_invite
;
chat_iface
->
leave
=
ggp_chat_leave
;
chat_iface
->
send
=
ggp_chat_send
;
chat_iface
->
reject
=
NULL
;
/* TODO */
}
static
void
ggp_protocol_roomlist_iface_init
(
PurpleProtocolRoomlistInterface
*
roomlist_iface
)
{
roomlist_iface
->
get_list
=
ggp_chat_roomlist_get_list
;
}
static
void
ggp_protocol_privacy_iface_init
(
PurpleProtocolPrivacyInterface
*
privacy_iface
)
{
privacy_iface
->
add_deny
=
ggp_add_deny
;
privacy_iface
->
remove_deny
=
ggp_remove_deny
;
}
static
void
ggp_protocol_xfer_iface_init
(
PurpleProtocolXferInterface
*
xfer_iface
)
{
xfer_iface
->
can_receive
=
ggp_edisc_xfer_can_receive_file
;
xfer_iface
->
send_file
=
ggp_edisc_xfer_send_file
;
xfer_iface
->
new_xfer
=
ggp_edisc_xfer_send_new
;
}
G_DEFINE_DYNAMIC_TYPE_EXTENDED
(
GGPProtocol
,
ggp_protocol
,
PURPLE_TYPE_PROTOCOL
,
0
,
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_ACTIONS
,
ggp_protocol_actions_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_CLIENT
,
ggp_protocol_client_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_SERVER
,
ggp_protocol_server_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_IM
,
ggp_protocol_im_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_CHAT
,
ggp_protocol_chat_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_ROOMLIST
,
ggp_protocol_roomlist_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_PRIVACY
,
ggp_protocol_privacy_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_XFER
,
ggp_protocol_xfer_iface_init
));
static
PurpleProtocol
*
ggp_protocol_new
(
void
)
{
return
PURPLE_PROTOCOL
(
g_object_new
(
GGP_TYPE_PROTOCOL
,
"id"
,
"prpl-gg"
,
"name"
,
"Gadu-Gadu"
,
"description"
,
_
(
"Gadu-Gadu is a Polish instant messaging network."
),
"icon-name"
,
"im-gadu-gadu"
,
"icon-resource-path"
,
"/im/pidgin/libpurple/gg/icons"
,
NULL
));
}
static
GPluginPluginInfo
*
gg_query
(
GError
**
error
)
{
GPluginPluginInfo
*
info
=
NULL
;
gchar
*
description
=
NULL
;
const
gchar
*
const
authors
[]
=
{
"boler@sourceforge.net"
,
NULL
};
description
=
g_strdup_printf
(
N_
(
"Polish popular IM
\n
libgadu version %s"
),
gg_libgadu_version
());
info
=
purple_plugin_info_new
(
"id"
,
"prpl-gg"
,
"name"
,
"Gadu-Gadu Protocol"
,
"version"
,
DISPLAY_VERSION
,
"category"
,
N_
(
"Protocol"
),
"summary"
,
N_
(
"Gadu-Gadu Protocol Plugin"
),
"description"
,
description
,
"authors"
,
authors
,
"website"
,
PURPLE_WEBSITE
,
"abi-version"
,
PURPLE_ABI_VERSION
,
"flags"
,
PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
|
PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD
,
NULL
);
g_free
(
description
);
return
info
;
}
static
gboolean
gg_load
(
GPluginPlugin
*
plugin
,
GError
**
error
)
{
PurpleProtocolManager
*
manager
=
purple_protocol_manager_get_default
();
ggp_protocol_register_type
(
G_TYPE_MODULE
(
plugin
));
ggp_xfer_register
(
G_TYPE_MODULE
(
plugin
));
my_protocol
=
ggp_protocol_new
();
if
(
!
purple_protocol_manager_register
(
manager
,
my_protocol
,
error
))
{
g_clear_object
(
&
my_protocol
);
return
FALSE
;
}
purple_prefs_add_none
(
"/plugins/prpl/gg"
);
purple_debug_info
(
"gg"
,
"Loading Gadu-Gadu protocol plugin with "
"libgadu %s...
\n
"
,
gg_libgadu_version
());
ggp_libgaduw_setup
();
ggp_resolver_purple_setup
();
ggp_servconn_setup
(
NULL
);
ggp_html_setup
();
ggp_message_setup_global
();
purple_signal_connect
(
purple_get_core
(),
"uri-handler"
,
plugin
,
G_CALLBACK
(
gg_uri_handler
),
NULL
);
return
TRUE
;
}
static
gboolean
gg_unload
(
GPluginPlugin
*
plugin
,
gboolean
shutdown
,
GError
**
error
)
{
PurpleProtocolManager
*
manager
=
purple_protocol_manager_get_default
();
if
(
!
purple_protocol_manager_unregister
(
manager
,
my_protocol
,
error
))
{
return
FALSE
;
}
purple_signal_disconnect
(
purple_get_core
(),
"uri-handler"
,
plugin
,
G_CALLBACK
(
gg_uri_handler
));
ggp_servconn_cleanup
();
ggp_html_cleanup
();
ggp_message_cleanup_global
();
ggp_libgaduw_cleanup
();
g_clear_object
(
&
my_protocol
);
return
TRUE
;
}
GPLUGIN_NATIVE_PLUGIN_DECLARE
(
gg
)
/* vim: set ts=8 sts=0 sw=8 noet: */