pidgin/purple-plugin-pack
Clone
Summary
Browse
Changes
Graph
updated for 3.0
2013-04-07, Gary Kramlich
dca537a081fe
updated for 3.0
/*
* libning
*
* libning is the property of its developers. See the COPYRIGHT file
* for more details.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include
"ning.h"
#include
"ning_connection.h"
#include
"ning_chat.h"
JsonObject
*
ning_json_parse
(
const
gchar
*
data
,
gssize
data_len
)
{
JsonParser
*
parser
;
JsonNode
*
root_node
;
JsonObject
*
root_obj
;
parser
=
json_parser_new
();
json_parser_load_from_data
(
parser
,
data
,
(
gssize
)
data_len
,
NULL
);
root_node
=
json_parser_get_root
(
parser
);
root_obj
=
json_node_dup_object
(
root_node
);
g_object_unref
(
parser
);
return
root_obj
;
}
gchar
*
build_user_json
(
NingAccount
*
na
)
{
gchar
*
user_json
;
gchar
*
escaped_name
;
gchar
*
escaped_icon
;
gchar
*
escaped_id
;
if
(
na
&&
na
->
name
)
{
escaped_name
=
g_strescape
(
na
->
name
,
""
);
}
else
{
escaped_name
=
g_strdup
(
""
);
}
if
(
na
&&
na
->
icon_url
)
{
escaped_icon
=
g_strescape
(
na
->
icon_url
,
""
);
}
else
{
escaped_icon
=
g_strdup
(
""
);
}
if
(
na
&&
na
->
ning_id
)
{
escaped_id
=
g_strescape
(
na
->
ning_id
,
""
);
}
else
{
escaped_id
=
g_strdup
(
""
);
}
user_json
=
g_strdup_printf
(
"{
\"
name
\"
:
\"
%s
\"
,
\"
iconUrl
\"
:
\"
%s
\"
,
\"
isAdmin
\"
:
\"
0
\"
,
\"
ningId
\"
:
\"
%s
\"
,
\"
isNC
\"
:
\"
0
\"
}"
,
escaped_name
,
escaped_icon
,
escaped_id
);
g_free
(
escaped_name
);
g_free
(
escaped_icon
);
g_free
(
escaped_id
);
return
user_json
;
}
/******************************************************************************/
/* PRPL functions */
/******************************************************************************/
static
const
char
*
ning_list_icon
(
PurpleAccount
*
account
,
PurpleBuddy
*
buddy
)
{
return
"ning"
;
}
static
GList
*
ning_statuses
(
PurpleAccount
*
account
)
{
GList
*
types
=
NULL
;
PurpleStatusType
*
status
;
purple_debug_info
(
"ning"
,
"statuses
\n
"
);
/* Ning people are either online or offline */
status
=
purple_status_type_new_full
(
PURPLE_STATUS_AVAILABLE
,
NULL
,
NULL
,
TRUE
,
TRUE
,
FALSE
);
types
=
g_list_append
(
types
,
status
);
status
=
purple_status_type_new_full
(
PURPLE_STATUS_OFFLINE
,
NULL
,
NULL
,
TRUE
,
TRUE
,
FALSE
);
types
=
g_list_append
(
types
,
status
);
purple_debug_info
(
"ning"
,
"statuses return
\n
"
);
return
types
;
}
void
ning_chat_login_cb
(
NingAccount
*
na
,
gchar
*
data
,
gsize
data_len
,
gpointer
userdata
)
{
JsonObject
*
obj
;
const
gchar
*
result
;
const
gchar
*
roomId
;
obj
=
ning_json_parse
(
data
,
data_len
);
purple_debug_info
(
"ning"
,
"chat_login_cb: %s
\n
"
,
data
?
data
:
"(null)"
);
if
(
data
==
NULL
||
data_len
==
0
)
return
;
//{"command": "login","result": "ok","roomId": "thoughtleaders.thoughtleaders",
// "count": 2,"token": "37lfxean70eqh_122d86d5cf7_6f95cb8e_122d49f8e48"}
result
=
json_node_get_string
(
json_object_get_member
(
obj
,
"result"
));
if
(
!
result
||
!
g_str_equal
(
result
,
"ok"
))
{
purple_connection_error
(
na
->
pc
,
_
(
"Could not log on"
));
return
;
}
purple_connection_update_progress
(
na
->
pc
,
_
(
"Joining public chat"
),
5
,
5
);
purple_connection_set_state
(
na
->
pc
,
PURPLE_CONNECTED
);
g_free
(
na
->
chat_token
);
na
->
chat_token
=
g_strdup
(
json_node_get_string
(
json_object_get_member
(
obj
,
"token"
)));
roomId
=
json_node_get_string
(
json_object_get_member
(
obj
,
"roomId"
));
ning_join_chat_by_name
(
na
,
roomId
);
json_object_unref
(
obj
);
}
void
ning_chat_redir_cb
(
NingAccount
*
na
,
gchar
*
data
,
gsize
data_len
,
gpointer
userdata
)
{
JsonObject
*
obj
;
gchar
*
postdata
,
*
encoded_app
,
*
encoded_id
;
gchar
*
user_json
,
*
user_encoded
;
purple_debug_info
(
"ning"
,
"ning_chat_redir_cb: %s
\n
"
,
data
?
data
:
"(null)"
);
//We get a randomly generated chat domain to use
// eg {"domain": "3841.chat07.ningim.com"}
obj
=
ning_json_parse
(
data
,
data_len
);
g_free
(
na
->
chat_domain
);
na
->
chat_domain
=
g_strdup
(
json_node_get_string
(
json_object_get_member
(
obj
,
"domain"
)));
json_object_unref
(
obj
);
//Use our new domain to log into the chat servers
purple_connection_update_progress
(
na
->
pc
,
_
(
"Logging into chat"
),
4
,
5
);
encoded_app
=
g_strdup
(
purple_url_encode
(
na
->
ning_app
));
encoded_id
=
g_strdup
(
purple_url_encode
(
na
->
ning_id
));
user_json
=
build_user_json
(
na
);
user_encoded
=
g_strdup
(
purple_url_encode
(
user_json
));
postdata
=
g_strdup_printf
(
"a=%s&t=%s%s&i=%s&user=%s"
,
encoded_app
,
encoded_app
,
encoded_id
,
encoded_id
,
user_encoded
);
ning_post_or_get
(
na
,
NING_METHOD_POST
,
na
->
chat_domain
,
"/xn/presence/login"
,
postdata
,
ning_chat_login_cb
,
NULL
,
FALSE
);
g_free
(
postdata
);
g_free
(
encoded_app
);
g_free
(
encoded_id
);
g_free
(
user_encoded
);
g_free
(
user_json
);
}
void
ning_login_home_cb
(
NingAccount
*
na
,
gchar
*
data
,
gsize
data_len
,
gpointer
userdata
)
{
//We need to look for
//<script>window.bzplcm.add({"app":"thoughtleaders","user":"37lfxean70eqh"
//and
//xg.token = 'b1a7f3ce1719481334cdcc5fe8eabcaa';
const
gchar
*
start_string
=
"
\n
ning = "
;
const
gchar
*
mid_string
=
"}};
\n
"
;
const
gchar
*
xgtoken_start
=
"xg.token = '"
;
gchar
*
tmp
,
*
ning_json_string
,
*
xg_token
;
gchar
*
url
;
JsonObject
*
obj
,
*
profile
;
tmp
=
g_strstr_len
(
data
,
data_len
,
start_string
);
if
(
tmp
==
NULL
)
{
purple_connection_error
(
na
->
pc
,
_
(
"NingID not found"
));
return
;
}
tmp
+=
strlen
(
start_string
);
ning_json_string
=
g_strndup
(
tmp
,
strstr
(
tmp
,
mid_string
)
-
tmp
+
2
);
purple_debug_info
(
"ning"
,
"ning_json_string: %s
\n
"
,
ning_json_string
);
obj
=
ning_json_parse
(
ning_json_string
,
strlen
(
ning_json_string
));
profile
=
json_node_get_object
(
json_object_get_member
(
obj
,
"CurrentProfile"
));
g_free
(
na
->
ning_id
);
na
->
ning_id
=
g_strdup
(
json_node_get_string
(
json_object_get_member
(
profile
,
"id"
)));
g_free
(
na
->
name
);
na
->
name
=
g_strdup
(
json_node_get_string
(
json_object_get_member
(
profile
,
"fullName"
)));
g_free
(
na
->
icon_url
);
na
->
icon_url
=
g_strdup_printf
(
"%s&width=16&height=16"
,
json_node_get_string
(
json_object_get_member
(
profile
,
"photoUrl"
)));
tmp
=
g_strstr_len
(
data
,
data_len
,
xgtoken_start
);
if
(
tmp
==
NULL
)
{
purple_connection_error
(
na
->
pc
,
_
(
"xgToken not found"
));
return
;
}
tmp
+=
strlen
(
xgtoken_start
);
xg_token
=
g_strndup
(
tmp
,
strchr
(
tmp
,
'\''
)
-
tmp
);
g_free
(
na
->
xg_token
);
na
->
xg_token
=
xg_token
;
//Now we should have everything we need to sign into chat
purple_connection_update_progress
(
na
->
pc
,
_
(
"Fetching chat server"
),
3
,
5
);
url
=
g_strdup_printf
(
"/xn/redirector/redirect?a=%s"
,
purple_url_encode
(
na
->
ning_app
));
ning_post_or_get
(
na
,
NING_METHOD_GET
,
"chat01.ningim.com"
,
url
,
NULL
,
ning_chat_redir_cb
,
NULL
,
FALSE
);
g_free
(
url
);
}
void
ning_scan_cookies_for_id
(
gchar
*
key
,
gchar
*
value
,
NingAccount
*
na
)
{
if
(
g_str_has_prefix
(
key
,
"xn_id_"
))
{
g_free
(
na
->
ning_app
);
na
->
ning_app
=
g_strdup
(
&
key
[
6
]);
}
}
static
void
ning_login_cb
(
NingAccount
*
na
,
gchar
*
response
,
gsize
len
,
gpointer
userdata
)
{
purple_connection_update_progress
(
na
->
pc
,
_
(
"Fetching token"
),
2
,
4
);
// ok, we're logged into the host website now
// Pull the host's Ning account id from the cookie
g_hash_table_foreach
(
na
->
cookie_table
,
(
GHFunc
)
ning_scan_cookies_for_id
,
na
);
//Load the homepage to grab the interesting info
ning_post_or_get
(
na
,
NING_METHOD_GET
,
purple_account_get_string
(
na
->
account
,
"host"
,
NULL
),
"/"
,
NULL
,
ning_login_home_cb
,
NULL
,
FALSE
);
}
static
void
ning_login
(
PurpleAccount
*
account
)
{
NingAccount
*
na
;
gchar
*
postdata
,
*
encoded_username
,
*
encoded_password
;
gchar
*
encoded_host
,
*
url
;
const
gchar
*
host
;
purple_debug_info
(
"ning"
,
"login
\n
"
);
/* Create account and initialize state */
na
=
g_new0
(
NingAccount
,
1
);
na
->
account
=
account
;
na
->
pc
=
purple_account_get_connection
(
account
);
na
->
last_messages_download_time
=
time
(
NULL
)
-
60
;
/* 60 secs is a safe buffer */
na
->
cookie_table
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
na
->
hostname_ip_cache
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
g_hash_table_replace
(
na
->
cookie_table
,
g_strdup
(
"xg_cookie_check"
),
g_strdup
(
"1"
));
account
->
gc
->
proto_data
=
na
;
purple_connection_set_state
(
na
->
pc
,
PURPLE_CONNECTING
);
purple_connection_update_progress
(
na
->
pc
,
_
(
"Logging in"
),
1
,
4
);
encoded_username
=
g_strdup
(
purple_url_encode
(
purple_account_get_username
(
account
)));
encoded_password
=
g_strdup
(
purple_url_encode
(
purple_account_get_password
(
account
)));
postdata
=
g_strdup_printf
(
"xg_token=&emailAddress=%s&password=%s"
,
encoded_username
,
encoded_password
);
g_free
(
encoded_username
);
g_free
(
encoded_password
);
host
=
purple_account_get_string
(
account
,
"host"
,
NULL
);
if
(
host
==
NULL
||
host
[
0
]
==
'\0'
)
{
purple_connection_error
(
na
->
pc
,
_
(
"Host not set"
));
return
;
}
encoded_host
=
g_strdup
(
purple_url_encode
(
host
));
url
=
g_strdup_printf
(
"/main/authorization/doSignIn?target=http%%3A%%2F%%2F%s"
,
host
);
ning_post_or_get
(
na
,
NING_METHOD_POST
|
NING_METHOD_SSL
,
host
,
url
,
postdata
,
ning_login_cb
,
NULL
,
FALSE
);
g_free
(
postdata
);
}
static
void
ning_close
(
PurpleConnection
*
pc
)
{
NingAccount
*
na
;
gchar
*
postdata
;
gchar
*
host_encoded
;
gchar
*
xg_token_encoded
;
PurpleDnsQueryData
*
dns_query
;
purple_debug_info
(
"ning"
,
"disconnecting account
\n
"
);
na
=
pc
->
proto_data
;
host_encoded
=
g_strdup
(
purple_url_encode
(
purple_account_get_string
(
na
->
account
,
"host"
,
""
)));
xg_token_encoded
=
g_strdup
(
purple_url_encode
(
na
->
xg_token
));
postdata
=
g_strdup_printf
(
"target=%s&xg_token=%s"
,
host_encoded
,
xg_token_encoded
);
ning_post_or_get
(
na
,
NING_METHOD_POST
,
purple_account_get_string
(
na
->
account
,
"host"
,
NULL
),
"/main/authorization/signOut"
,
postdata
,
NULL
,
NULL
,
FALSE
);
g_free
(
host_encoded
);
g_free
(
xg_token_encoded
);
g_free
(
postdata
);
purple_debug_info
(
"ning"
,
"destroying %d incomplete connections
\n
"
,
g_slist_length
(
na
->
conns
));
while
(
na
->
conns
!=
NULL
)
ning_connection_destroy
(
na
->
conns
->
data
);
while
(
na
->
dns_queries
!=
NULL
)
{
dns_query
=
na
->
dns_queries
->
data
;
purple_debug_info
(
"ning"
,
"canceling dns query for %s
\n
"
,
purple_dnsquery_get_host
(
dns_query
));
na
->
dns_queries
=
g_slist_remove
(
na
->
dns_queries
,
dns_query
);
purple_dnsquery_destroy
(
dns_query
);
}
g_hash_table_destroy
(
na
->
cookie_table
);
g_hash_table_destroy
(
na
->
hostname_ip_cache
);
while
(
na
->
chats
!=
NULL
)
{
NingChat
*
chat
=
na
->
chats
->
data
;
na
->
chats
=
g_list_remove
(
na
->
chats
,
chat
);
purple_timeout_remove
(
chat
->
userlist_timer
);
purple_timeout_remove
(
chat
->
message_poll_timer
);
purple_conv_chat_left
(
PURPLE_CONV_CHAT
(
purple_find_chat
(
pc
,
chat
->
purple_id
)));
g_free
(
chat
->
roomId
);
g_free
(
chat
->
ning_hash
);
g_free
(
chat
);
}
g_free
(
na
->
ning_id
);
g_free
(
na
->
name
);
g_free
(
na
->
icon_url
);
g_free
(
na
->
xg_token
);
g_free
(
na
->
ning_app
);
g_free
(
na
);
}
#if PURPLE_MAJOR_VERSION >= 2 && PURPLE_MINOR_VERSION >= 5
static
GHashTable
*
ning_get_account_text_table
(
PurpleAccount
*
account
)
{
GHashTable
*
table
;
table
=
g_hash_table_new
(
g_str_hash
,
g_str_equal
);
g_hash_table_insert
(
table
,
"login_label"
,
(
gpointer
)
_
(
"Email Address..."
));
return
table
;
}
#endif
void
ning_change_passwd
(
PurpleConnection
*
pc
,
const
char
*
old_pass
,
const
char
*
new_pass
)
{
NingAccount
*
na
;
PurpleAccount
*
account
;
gchar
*
encoded_username
;
gchar
*
encoded_password
;
gchar
*
encoded_token
;
gchar
*
postdata
;
if
(
pc
==
NULL
)
return
;
na
=
pc
->
proto_data
;
if
(
na
==
NULL
||
na
->
xg_token
==
NULL
)
return
;
account
=
pc
->
account
;
if
(
account
==
NULL
)
return
;
encoded_username
=
g_strdup
(
purple_url_encode
(
purple_account_get_username
(
account
)));
encoded_password
=
g_strdup
(
purple_url_encode
(
new_pass
));
encoded_token
=
g_strdup
(
purple_url_encode
(
na
->
xg_token
));
postdata
=
g_strdup_printf
(
"emailAddress=%s&password=%s&xg_token=%s"
,
encoded_username
,
encoded_password
,
encoded_token
);
ning_post_or_get
(
na
,
NING_METHOD_POST
,
purple_account_get_string
(
na
->
account
,
"host"
,
NULL
),
"/profiles/settings/updateEmailAddress"
,
postdata
,
NULL
,
NULL
,
FALSE
);
g_free
(
postdata
);
g_free
(
encoded_token
);
g_free
(
encoded_password
);
g_free
(
encoded_username
);
}
/******************************************************************************/
/* Plugin functions */
/******************************************************************************/
static
gboolean
plugin_load
(
PurplePlugin
*
plugin
)
{
return
TRUE
;
}
static
gboolean
plugin_unload
(
PurplePlugin
*
plugin
)
{
return
TRUE
;
}
static
void
plugin_init
(
PurplePlugin
*
plugin
)
{
#ifdef ENABLE_NLS
bindtextdomain
(
GETTEXT_PACKAGE
,
PP_LOCALEDIR
);
bind_textdomain_codeset
(
GETTEXT_PACKAGE
,
"UTF-8"
);
#endif
PurpleAccountOption
*
option
;
PurplePluginInfo
*
info
=
plugin
->
info
;
PurplePluginProtocolInfo
*
prpl_info
=
info
->
extra_info
;
option
=
purple_account_option_string_new
(
"Host"
,
"host"
,
""
);
prpl_info
->
protocol_options
=
g_list_append
(
prpl_info
->
protocol_options
,
option
);
}
static
PurplePluginProtocolInfo
prpl_info
=
{
/* options */
0
,
NULL
,
/* user_splits */
NULL
,
/* protocol_options */
NO_BUDDY_ICONS
/* icon_spec */
/*{"jpg", 0, 0, 50, 50, -1, PURPLE_ICON_SCALE_SEND}*/
,
/* icon_spec */
ning_list_icon
,
/* list_icon */
NULL
,
/* list_emblems */
NULL
,
/* status_text */
NULL
,
/* tooltip_text */
ning_statuses
,
/* status_types */
NULL
,
/* blist_node_menu */
NULL
,
/* chat_info */
NULL
,
/* chat_info_defaults */
ning_login
,
/* login */
ning_close
,
/* close */
ning_send_im
,
/* send_im */
NULL
,
/* set_info */
NULL
,
/* send_typing */
NULL
,
/* get_info */
NULL
,
/* set_status */
NULL
,
/* set_idle */
ning_change_passwd
,
/* change_passwd */
NULL
,
/* add_buddy */
NULL
,
/* add_buddies */
NULL
,
/* remove_buddy */
NULL
,
/* remove_buddies */
NULL
,
/* add_permit */
NULL
,
/* add_deny */
NULL
,
/* rem_permit */
NULL
,
/* rem_deny */
NULL
,
/* set_permit_deny */
ning_join_chat
,
/* join_chat */
NULL
,
/* reject chat invite */
NULL
,
/* get_chat_name */
NULL
,
/* chat_invite */
NULL
,
/* chat_leave */
ning_chat_whisper
,
/* chat_whisper */
ning_chat_send
,
/* chat_send */
NULL
,
/* 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 */
purple_normalize_nocase
,
/* 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 */
NULL
,
/* offline_message */
NULL
,
/* whiteboard_prpl_ops */
NULL
,
/* send_raw */
NULL
,
/* roomlist_room_serialize */
NULL
,
/* unregister_user */
NULL
,
/* send_attention */
NULL
,
/* attention_types */
#if PURPLE_MAJOR_VERSION >= 2 && PURPLE_MINOR_VERSION >= 5
sizeof
(
PurplePluginProtocolInfo
),
/* struct_size */
ning_get_account_text_table
,
/* get_account_text_table */
#else
(
gpointer
)
sizeof
(
PurplePluginProtocolInfo
)
#endif
};
static
PurplePluginInfo
info
=
{
PURPLE_PLUGIN_MAGIC
,
2
,
/* major_version */
3
,
/* minor version */
PURPLE_PLUGIN_PROTOCOL
,
/* type */
NULL
,
/* ui_requirement */
0
,
/* flags */
NULL
,
/* dependencies */
PURPLE_PRIORITY_DEFAULT
,
/* priority */
"prpl-bigbrownchunx-ning"
,
/* id */
"Ning"
,
/* name */
PP_VERSION
,
/* version */
N_
(
"Ning Protocol Plugin"
),
/* summary */
N_
(
"Ning Protocol Plugin"
),
/* description */
"Eion Robb <eionrobb@gmail.com>"
,
/* author */
PP_WEBSITE
,
/* homepage */
plugin_load
,
/* load */
plugin_unload
,
/* unload */
NULL
,
/* destroy */
NULL
,
/* ui_info */
&
prpl_info
,
/* extra_info */
NULL
,
/* prefs_info */
NULL
,
/* actions */
/* padding */
NULL
,
NULL
,
NULL
,
NULL
};
PURPLE_INIT_PLUGIN
(
ning
,
plugin_init
,
info
);