pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Fix conditional.
release-2.x.y
2014-01-13, Mark Doliner
c03eefbf89a8
Fix conditional.
I think this would have incorrectly found a Connection: header in the
XML body. No idea what the harm is.
/*
* purple
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
#include
"internal.h"
#include
"account.h"
#include
"accountopt.h"
#include
"blist.h"
#include
"cipher.h"
#include
"cmds.h"
#include
"core.h"
#include
"debug.h"
#include
"network.h"
#include
"notify.h"
#include
"privacy.h"
#include
"prpl.h"
#include
"proxy.h"
#include
"request.h"
#include
"server.h"
#include
"util.h"
#include
"version.h"
#include
"xmlnode.h"
#include
"libymsg.h"
#include
"yahoochat.h"
#include
"yahoo_aliases.h"
#include
"yahoo_doodle.h"
#include
"yahoo_filexfer.h"
#include
"yahoo_friend.h"
#include
"yahoo_packet.h"
#include
"yahoo_picture.h"
#include
"ycht.h"
/* #define YAHOO_DEBUG */
/* #define TRY_WEBMESSENGER_LOGIN 0 */
/* One hour */
#define PING_TIMEOUT 3600
/* One minute */
#define KEEPALIVE_TIMEOUT 60
#ifdef TRY_WEBMESSENGER_LOGIN
static
void
yahoo_login_page_cb
(
PurpleUtilFetchUrlData
*
url_data
,
gpointer
user_data
,
const
gchar
*
url_text
,
size_t
len
,
const
gchar
*
error_message
);
#endif
/* TRY_WEBMESSENGER_LOGIN */
static
gboolean
yahoo_is_japan
(
PurpleAccount
*
account
)
{
return
purple_strequal
(
purple_account_get_protocol_id
(
account
),
"prpl-yahoojp"
);
}
static
void
yahoo_update_status
(
PurpleConnection
*
gc
,
const
char
*
name
,
YahooFriend
*
f
)
{
char
*
status
=
NULL
;
if
(
!
gc
||
!
name
||
!
f
||
!
purple_find_buddy
(
purple_connection_get_account
(
gc
),
name
))
return
;
switch
(
f
->
status
)
{
case
YAHOO_STATUS_OFFLINE
:
status
=
YAHOO_STATUS_TYPE_OFFLINE
;
break
;
case
YAHOO_STATUS_AVAILABLE
:
status
=
YAHOO_STATUS_TYPE_AVAILABLE
;
break
;
case
YAHOO_STATUS_BRB
:
status
=
YAHOO_STATUS_TYPE_BRB
;
break
;
case
YAHOO_STATUS_BUSY
:
status
=
YAHOO_STATUS_TYPE_BUSY
;
break
;
case
YAHOO_STATUS_NOTATHOME
:
status
=
YAHOO_STATUS_TYPE_NOTATHOME
;
break
;
case
YAHOO_STATUS_NOTATDESK
:
status
=
YAHOO_STATUS_TYPE_NOTATDESK
;
break
;
case
YAHOO_STATUS_NOTINOFFICE
:
status
=
YAHOO_STATUS_TYPE_NOTINOFFICE
;
break
;
case
YAHOO_STATUS_ONPHONE
:
status
=
YAHOO_STATUS_TYPE_ONPHONE
;
break
;
case
YAHOO_STATUS_ONVACATION
:
status
=
YAHOO_STATUS_TYPE_ONVACATION
;
break
;
case
YAHOO_STATUS_OUTTOLUNCH
:
status
=
YAHOO_STATUS_TYPE_OUTTOLUNCH
;
break
;
case
YAHOO_STATUS_STEPPEDOUT
:
status
=
YAHOO_STATUS_TYPE_STEPPEDOUT
;
break
;
case
YAHOO_STATUS_INVISIBLE
:
/* this should never happen? */
status
=
YAHOO_STATUS_TYPE_INVISIBLE
;
break
;
case
YAHOO_STATUS_CUSTOM
:
case
YAHOO_STATUS_IDLE
:
if
(
!
f
->
away
)
status
=
YAHOO_STATUS_TYPE_AVAILABLE
;
else
status
=
YAHOO_STATUS_TYPE_AWAY
;
break
;
default
:
purple_debug_warning
(
"yahoo"
,
"Warning, unknown status %d
\n
"
,
f
->
status
);
break
;
}
if
(
status
)
{
if
(
f
->
status
==
YAHOO_STATUS_CUSTOM
)
purple_prpl_got_user_status
(
purple_connection_get_account
(
gc
),
name
,
status
,
"message"
,
yahoo_friend_get_status_message
(
f
),
NULL
);
else
purple_prpl_got_user_status
(
purple_connection_get_account
(
gc
),
name
,
status
,
NULL
);
}
if
(
f
->
idle
!=
0
)
purple_prpl_got_user_idle
(
purple_connection_get_account
(
gc
),
name
,
TRUE
,
f
->
idle
);
else
purple_prpl_got_user_idle
(
purple_connection_get_account
(
gc
),
name
,
FALSE
,
0
);
if
(
f
->
sms
)
purple_prpl_got_user_status
(
purple_connection_get_account
(
gc
),
name
,
YAHOO_STATUS_TYPE_MOBILE
,
NULL
);
else
purple_prpl_got_user_status_deactive
(
purple_connection_get_account
(
gc
),
name
,
YAHOO_STATUS_TYPE_MOBILE
);
}
static
void
yahoo_process_status
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
GSList
*
l
=
pkt
->
hash
;
YahooFriend
*
f
=
NULL
;
char
*
name
=
NULL
;
gboolean
unicode
=
FALSE
;
char
*
message
=
NULL
;
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
char
*
fedname
=
NULL
;
if
(
pkt
->
service
==
YAHOO_SERVICE_LOGOFF
&&
pkt
->
status
==
-1
)
{
if
(
!
purple_account_get_remember_password
(
account
))
purple_account_set_password
(
account
,
NULL
);
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NAME_IN_USE
,
_
(
"You have signed on from another location"
));
return
;
}
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
switch
(
pair
->
key
)
{
case
0
:
/* we won't actually do anything with this */
case
1
:
/* we won't actually do anything with this */
break
;
case
8
:
/* how many online buddies we have */
break
;
case
7
:
/* the current buddy */
/* update the previous buddy before changing the variables */
if
(
f
)
{
if
(
message
)
yahoo_friend_set_status_message
(
f
,
yahoo_string_decode
(
gc
,
message
,
unicode
));
if
(
name
)
yahoo_update_status
(
gc
,
name
,
f
);
}
name
=
message
=
NULL
;
f
=
NULL
;
if
(
pair
->
value
&&
g_utf8_validate
(
pair
->
value
,
-1
,
NULL
))
{
GSList
*
tmplist
;
name
=
pair
->
value
;
/* Look ahead to see if we have the federation info about the buddy */
for
(
tmplist
=
l
->
next
;
tmplist
;
tmplist
=
tmplist
->
next
)
{
struct
yahoo_pair
*
p
=
tmplist
->
data
;
if
(
p
->
key
==
7
)
break
;
if
(
p
->
key
==
241
)
{
fed
=
strtol
(
p
->
value
,
NULL
,
10
);
g_free
(
fedname
);
switch
(
fed
)
{
case
YAHOO_FEDERATION_MSN
:
name
=
fedname
=
g_strconcat
(
"msn/"
,
name
,
NULL
);
break
;
case
YAHOO_FEDERATION_OCS
:
name
=
fedname
=
g_strconcat
(
"ocs/"
,
name
,
NULL
);
break
;
case
YAHOO_FEDERATION_IBM
:
name
=
fedname
=
g_strconcat
(
"ibm/"
,
name
,
NULL
);
break
;
case
YAHOO_FEDERATION_NONE
:
default
:
fedname
=
NULL
;
break
;
}
break
;
}
}
f
=
yahoo_friend_find_or_new
(
gc
,
name
);
f
->
fed
=
fed
;
}
break
;
case
10
:
/* state */
if
(
!
f
)
break
;
f
->
status
=
strtol
(
pair
->
value
,
NULL
,
10
);
if
((
f
->
status
>=
YAHOO_STATUS_BRB
)
&&
(
f
->
status
<=
YAHOO_STATUS_STEPPEDOUT
))
f
->
away
=
1
;
else
f
->
away
=
0
;
if
(
f
->
status
==
YAHOO_STATUS_IDLE
)
{
/* Idle may have already been set in a more precise way in case 137 */
if
(
f
->
idle
==
0
)
{
if
(
pkt
->
service
==
YAHOO_SERVICE_STATUS_15
)
f
->
idle
=
-1
;
else
f
->
idle
=
time
(
NULL
);
}
}
else
f
->
idle
=
0
;
if
(
f
->
status
!=
YAHOO_STATUS_CUSTOM
)
yahoo_friend_set_status_message
(
f
,
NULL
);
f
->
sms
=
0
;
break
;
case
19
:
/* custom message */
if
(
f
)
message
=
pair
->
value
;
break
;
case
11
:
/* this is the buddy's session id */
if
(
f
)
f
->
session_id
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
case
17
:
/* in chat? */
break
;
case
47
:
/* is custom status away or not? 2=idle*/
if
(
!
f
)
break
;
/* I have no idea what it means when this is
* set when someone's available, but it doesn't
* mean idle. */
if
(
f
->
status
==
YAHOO_STATUS_AVAILABLE
)
break
;
f
->
away
=
strtol
(
pair
->
value
,
NULL
,
10
);
if
(
f
->
away
==
2
)
{
/* Idle may have already been set in a more precise way in case 137 */
if
(
f
->
idle
==
0
)
{
if
(
pkt
->
service
==
YAHOO_SERVICE_STATUS_15
)
f
->
idle
=
-1
;
else
f
->
idle
=
time
(
NULL
);
}
}
break
;
case
138
:
/* when value is 1, either we're not idle, or we are but won't say how long */
if
(
!
f
)
break
;
if
(
(
strtol
(
pair
->
value
,
NULL
,
10
)
==
1
)
&&
(
f
->
idle
)
)
f
->
idle
=
-1
;
break
;
case
137
:
/* usually idle time in seconds, sometimes login time */
if
(
!
f
)
break
;
if
(
f
->
status
!=
YAHOO_STATUS_AVAILABLE
)
f
->
idle
=
time
(
NULL
)
-
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
case
13
:
/* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
if
(
strtol
(
pair
->
value
,
NULL
,
10
)
==
0
)
{
if
(
f
)
f
->
status
=
YAHOO_STATUS_OFFLINE
;
if
(
name
)
{
purple_prpl_got_user_status
(
account
,
name
,
"offline"
,
NULL
);
purple_prpl_got_user_status_deactive
(
account
,
name
,
YAHOO_STATUS_TYPE_MOBILE
);
}
break
;
}
break
;
case
60
:
/* SMS */
if
(
f
)
{
f
->
sms
=
strtol
(
pair
->
value
,
NULL
,
10
);
yahoo_update_status
(
gc
,
name
,
f
);
}
break
;
case
197
:
/* Avatars */
{
guchar
*
decoded
;
char
*
tmp
;
gsize
len
;
if
(
pair
->
value
)
{
decoded
=
purple_base64_decode
(
pair
->
value
,
&
len
);
if
(
decoded
&&
len
>
0
)
{
tmp
=
purple_str_binary_to_ascii
(
decoded
,
len
);
purple_debug_info
(
"yahoo"
,
"Got key 197, value = %s
\n
"
,
tmp
);
g_free
(
tmp
);
}
g_free
(
decoded
);
}
break
;
}
case
192
:
/* Pictures, aka Buddy Icons, checksum */
{
/* FIXME: Please, if you know this protocol,
* FIXME: fix up the strtol() stuff if possible. */
int
cksum
=
strtol
(
pair
->
value
,
NULL
,
10
);
const
char
*
locksum
=
NULL
;
PurpleBuddy
*
b
;
if
(
!
name
)
break
;
b
=
purple_find_buddy
(
gc
->
account
,
name
);
if
(
!
cksum
||
(
cksum
==
-1
))
{
if
(
f
)
yahoo_friend_set_buddy_icon_need_request
(
f
,
TRUE
);
purple_buddy_icons_set_for_user
(
gc
->
account
,
name
,
NULL
,
0
,
NULL
);
break
;
}
if
(
!
f
)
break
;
yahoo_friend_set_buddy_icon_need_request
(
f
,
FALSE
);
if
(
b
)
{
locksum
=
purple_buddy_icons_get_checksum_for_user
(
b
);
if
(
!
locksum
||
(
cksum
!=
strtol
(
locksum
,
NULL
,
10
)))
yahoo_send_picture_request
(
gc
,
name
);
}
break
;
}
case
16
:
/* Custom error message */
{
char
*
tmp
=
yahoo_string_decode
(
gc
,
pair
->
value
,
TRUE
);
purple_notify_error
(
gc
,
NULL
,
tmp
,
NULL
);
g_free
(
tmp
);
}
break
;
case
97
:
/* Unicode status message */
unicode
=
!
strcmp
(
pair
->
value
,
"1"
);
break
;
case
244
:
/* client version number. Yahoo Client Detection */
if
(
f
&&
strtol
(
pair
->
value
,
NULL
,
10
))
f
->
version_id
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
case
241
:
/* Federated network buddy belongs to */
break
;
/* We process this when get '7' */
default
:
purple_debug_warning
(
"yahoo"
,
"Unknown status key %d
\n
"
,
pair
->
key
);
break
;
}
l
=
l
->
next
;
}
if
(
f
)
{
if
(
pkt
->
service
==
YAHOO_SERVICE_LOGOFF
)
f
->
status
=
YAHOO_STATUS_OFFLINE
;
if
(
message
)
yahoo_friend_set_status_message
(
f
,
yahoo_string_decode
(
gc
,
message
,
unicode
));
if
(
name
)
/* update the last buddy */
yahoo_update_status
(
gc
,
name
,
f
);
}
g_free
(
fedname
);
}
static
void
yahoo_do_group_check
(
PurpleAccount
*
account
,
GHashTable
*
ht
,
const
char
*
name
,
const
char
*
group
)
{
PurpleBuddy
*
b
;
PurpleGroup
*
g
;
GSList
*
list
,
*
i
;
gboolean
onlist
=
FALSE
;
char
*
oname
=
NULL
;
if
(
g_hash_table_lookup_extended
(
ht
,
name
,
(
gpointer
*
)
&
oname
,
(
gpointer
*
)
&
list
))
g_hash_table_steal
(
ht
,
oname
);
else
list
=
purple_find_buddies
(
account
,
name
);
for
(
i
=
list
;
i
;
i
=
i
->
next
)
{
b
=
i
->
data
;
g
=
purple_buddy_get_group
(
b
);
if
(
!
purple_utf8_strcasecmp
(
group
,
purple_group_get_name
(
g
)))
{
purple_debug_misc
(
"yahoo"
,
"Oh good, %s is in the right group (%s).
\n
"
,
name
,
group
);
list
=
g_slist_delete_link
(
list
,
i
);
onlist
=
TRUE
;
break
;
}
}
if
(
!
onlist
)
{
purple_debug_misc
(
"yahoo"
,
"Uhoh, %s isn't on the list (or not in this group), adding him to group %s.
\n
"
,
name
,
group
);
if
(
!
(
g
=
purple_find_group
(
group
)))
{
g
=
purple_group_new
(
group
);
purple_blist_add_group
(
g
,
NULL
);
}
b
=
purple_buddy_new
(
account
,
name
,
NULL
);
purple_blist_add_buddy
(
b
,
NULL
,
g
,
NULL
);
}
if
(
list
)
{
if
(
!
oname
)
oname
=
g_strdup
(
name
);
g_hash_table_insert
(
ht
,
oname
,
list
);
}
else
g_free
(
oname
);
}
static
void
yahoo_do_group_cleanup
(
gpointer
key
,
gpointer
value
,
gpointer
user_data
)
{
char
*
name
=
key
;
GSList
*
list
=
value
,
*
i
;
PurpleBuddy
*
b
;
PurpleGroup
*
g
;
for
(
i
=
list
;
i
;
i
=
i
->
next
)
{
b
=
i
->
data
;
g
=
purple_buddy_get_group
(
b
);
purple_debug_misc
(
"yahoo"
,
"Deleting Buddy %s from group %s.
\n
"
,
name
,
purple_group_get_name
(
g
));
purple_blist_remove_buddy
(
b
);
}
}
static
char
*
_getcookie
(
char
*
rawcookie
)
{
char
*
cookie
=
NULL
;
char
*
tmpcookie
;
char
*
cookieend
;
if
(
strlen
(
rawcookie
)
<
2
)
return
NULL
;
tmpcookie
=
g_strdup
(
rawcookie
+
2
);
cookieend
=
strchr
(
tmpcookie
,
';'
);
if
(
cookieend
)
*
cookieend
=
'\0'
;
cookie
=
g_strdup
(
tmpcookie
);
g_free
(
tmpcookie
);
return
cookie
;
}
static
void
yahoo_process_cookie
(
YahooData
*
yd
,
char
*
c
)
{
if
(
c
[
0
]
==
'Y'
)
{
if
(
yd
->
cookie_y
)
g_free
(
yd
->
cookie_y
);
yd
->
cookie_y
=
_getcookie
(
c
);
}
else
if
(
c
[
0
]
==
'T'
)
{
if
(
yd
->
cookie_t
)
g_free
(
yd
->
cookie_t
);
yd
->
cookie_t
=
_getcookie
(
c
);
}
else
purple_debug_info
(
"yahoo"
,
"Unrecognized cookie '%c'
\n
"
,
c
[
0
]);
yd
->
cookies
=
g_slist_prepend
(
yd
->
cookies
,
g_strdup
(
c
));
}
static
void
yahoo_process_list_15
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
GSList
*
l
=
pkt
->
hash
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
YahooData
*
yd
=
gc
->
proto_data
;
GHashTable
*
ht
;
char
*
norm_bud
=
NULL
;
char
*
temp
=
NULL
;
YahooFriend
*
f
=
NULL
;
/* It's your friends. They're going to want you to share your StarBursts. */
/* But what if you had no friends? */
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
int
stealth
=
0
;
ht
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
(
GDestroyNotify
)
g_slist_free
);
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
l
=
l
->
next
;
switch
(
pair
->
key
)
{
case
302
:
/* This is always 318 before a group, 319 before the first s/n in a group, 320 before any ignored s/n.
* It is not sent for s/n's in a group after the first.
* All ignored s/n's are listed last, so when we see a 320 we clear the group and begin marking the
* s/n's as ignored. It is always followed by an identical 300 key.
*/
if
(
pair
->
value
&&
!
strcmp
(
pair
->
value
,
"320"
))
{
/* No longer in any group; this indicates the start of the ignore list. */
g_free
(
yd
->
current_list15_grp
);
yd
->
current_list15_grp
=
NULL
;
}
break
;
case
301
:
/* This is 319 before all s/n's in a group after the first. It is followed by an identical 300. */
if
(
temp
!=
NULL
)
{
switch
(
fed
)
{
case
YAHOO_FEDERATION_MSN
:
norm_bud
=
g_strconcat
(
"msn/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_OCS
:
norm_bud
=
g_strconcat
(
"ocs/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_IBM
:
norm_bud
=
g_strconcat
(
"ibm/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_PBX
:
norm_bud
=
g_strconcat
(
"pbx/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_NONE
:
norm_bud
=
g_strdup
(
temp
);
break
;
}
if
(
yd
->
current_list15_grp
)
{
/* This buddy is in a group */
f
=
yahoo_friend_find_or_new
(
gc
,
norm_bud
);
if
(
!
purple_find_buddy
(
account
,
norm_bud
))
{
PurpleBuddy
*
b
;
PurpleGroup
*
g
;
if
(
!
(
g
=
purple_find_group
(
yd
->
current_list15_grp
)))
{
g
=
purple_group_new
(
yd
->
current_list15_grp
);
purple_blist_add_group
(
g
,
NULL
);
}
b
=
purple_buddy_new
(
account
,
norm_bud
,
NULL
);
purple_blist_add_buddy
(
b
,
NULL
,
g
,
NULL
);
}
yahoo_do_group_check
(
account
,
ht
,
norm_bud
,
yd
->
current_list15_grp
);
if
(
fed
)
{
f
->
fed
=
fed
;
purple_debug_info
(
"yahoo"
,
"Setting federation to %d
\n
"
,
f
->
fed
);
}
if
(
stealth
==
2
)
f
->
presence
=
YAHOO_PRESENCE_PERM_OFFLINE
;
/* set p2p status not connected and no p2p packet sent */
if
(
fed
==
YAHOO_FEDERATION_NONE
)
{
yahoo_friend_set_p2p_status
(
f
,
YAHOO_P2PSTATUS_NOT_CONNECTED
);
f
->
p2p_packet_sent
=
0
;
}
else
yahoo_friend_set_p2p_status
(
f
,
YAHOO_P2PSTATUS_DO_NOT_CONNECT
);
}
else
{
/* This buddy is on the ignore list (and therefore in no group) */
purple_debug_info
(
"yahoo"
,
"%s adding %s to the deny list because of the ignore list / no group was found
\n
"
,
account
->
username
,
norm_bud
);
purple_privacy_deny_add
(
account
,
norm_bud
,
1
);
}
g_free
(
norm_bud
);
norm_bud
=
NULL
;
fed
=
YAHOO_FEDERATION_NONE
;
stealth
=
0
;
g_free
(
temp
);
temp
=
NULL
;
}
break
;
case
300
:
/* This is 318 before a group, 319 before any s/n in a group, and 320 before any ignored s/n. */
break
;
case
65
:
/* This is the group */
g_free
(
yd
->
current_list15_grp
);
yd
->
current_list15_grp
=
yahoo_string_decode
(
gc
,
pair
->
value
,
FALSE
);
break
;
case
7
:
/* buddy's s/n */
g_free
(
temp
);
temp
=
g_strdup
(
purple_normalize
(
account
,
pair
->
value
));
break
;
case
241
:
/* user on federated network */
fed
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
case
59
:
/* somebody told cookies come here too, but im not sure */
yahoo_process_cookie
(
yd
,
pair
->
value
);
break
;
case
317
:
/* Stealth Setting */
stealth
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
/* case 242: */
/* this seems related to 241 */
/* break; */
}
}
g_hash_table_foreach
(
ht
,
yahoo_do_group_cleanup
,
NULL
);
/* The reporter of ticket #9745 determined that we weren't retrieving the
* aliases during buddy list retrieval, so we never updated aliases that
* changed while we were signed off. */
yahoo_fetch_aliases
(
gc
);
/* Now that we have processed the buddy list, we can say yahoo has connected */
purple_connection_set_display_name
(
gc
,
purple_normalize
(
account
,
purple_account_get_username
(
account
)));
yd
->
logged_in
=
TRUE
;
purple_debug_info
(
"yahoo"
,
"Authentication: Connection established
\n
"
);
purple_connection_set_state
(
gc
,
PURPLE_CONNECTED
);
if
(
yd
->
picture_upload_todo
)
{
yahoo_buddy_icon_upload
(
gc
,
yd
->
picture_upload_todo
);
yd
->
picture_upload_todo
=
NULL
;
}
yahoo_set_status
(
account
,
purple_account_get_active_status
(
account
));
g_hash_table_destroy
(
ht
);
g_free
(
temp
);
}
static
void
yahoo_process_list
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
GSList
*
l
=
pkt
->
hash
;
gboolean
export
=
FALSE
;
gboolean
got_serv_list
=
FALSE
;
YahooFriend
*
f
=
NULL
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
YahooData
*
yd
=
gc
->
proto_data
;
GHashTable
*
ht
;
char
**
lines
;
char
**
split
;
char
**
buddies
;
char
**
tmp
,
**
bud
,
*
norm_bud
;
char
*
grp
=
NULL
;
if
(
pkt
->
id
)
yd
->
session_id
=
pkt
->
id
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
l
=
l
->
next
;
switch
(
pair
->
key
)
{
case
87
:
if
(
!
yd
->
tmp_serv_blist
)
yd
->
tmp_serv_blist
=
g_string_new
(
pair
->
value
);
else
g_string_append
(
yd
->
tmp_serv_blist
,
pair
->
value
);
break
;
case
88
:
if
(
!
yd
->
tmp_serv_ilist
)
yd
->
tmp_serv_ilist
=
g_string_new
(
pair
->
value
);
else
g_string_append
(
yd
->
tmp_serv_ilist
,
pair
->
value
);
break
;
case
89
:
yd
->
profiles
=
g_strsplit
(
pair
->
value
,
","
,
-1
);
break
;
case
59
:
/* cookies, yum */
yahoo_process_cookie
(
yd
,
pair
->
value
);
break
;
case
YAHOO_SERVICE_PRESENCE_PERM
:
if
(
!
yd
->
tmp_serv_plist
)
yd
->
tmp_serv_plist
=
g_string_new
(
pair
->
value
);
else
g_string_append
(
yd
->
tmp_serv_plist
,
pair
->
value
);
break
;
}
}
if
(
pkt
->
status
!=
0
)
return
;
if
(
yd
->
tmp_serv_blist
)
{
ht
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
(
GDestroyNotify
)
g_slist_free
);
lines
=
g_strsplit
(
yd
->
tmp_serv_blist
->
str
,
"
\n
"
,
-1
);
for
(
tmp
=
lines
;
*
tmp
;
tmp
++
)
{
split
=
g_strsplit
(
*
tmp
,
":"
,
2
);
if
(
!
split
)
continue
;
if
(
!
split
[
0
]
||
!
split
[
1
])
{
g_strfreev
(
split
);
continue
;
}
grp
=
yahoo_string_decode
(
gc
,
split
[
0
],
FALSE
);
buddies
=
g_strsplit
(
split
[
1
],
","
,
-1
);
for
(
bud
=
buddies
;
bud
&&
*
bud
;
bud
++
)
{
norm_bud
=
g_strdup
(
purple_normalize
(
account
,
*
bud
));
f
=
yahoo_friend_find_or_new
(
gc
,
norm_bud
);
if
(
!
purple_find_buddy
(
account
,
norm_bud
))
{
PurpleBuddy
*
b
;
PurpleGroup
*
g
;
if
(
!
(
g
=
purple_find_group
(
grp
)))
{
g
=
purple_group_new
(
grp
);
purple_blist_add_group
(
g
,
NULL
);
}
b
=
purple_buddy_new
(
account
,
norm_bud
,
NULL
);
purple_blist_add_buddy
(
b
,
NULL
,
g
,
NULL
);
export
=
TRUE
;
}
yahoo_do_group_check
(
account
,
ht
,
norm_bud
,
grp
);
/* set p2p status not connected and no p2p packet sent */
yahoo_friend_set_p2p_status
(
f
,
YAHOO_P2PSTATUS_NOT_CONNECTED
);
f
->
p2p_packet_sent
=
0
;
g_free
(
norm_bud
);
}
g_strfreev
(
buddies
);
g_strfreev
(
split
);
g_free
(
grp
);
}
g_strfreev
(
lines
);
g_string_free
(
yd
->
tmp_serv_blist
,
TRUE
);
yd
->
tmp_serv_blist
=
NULL
;
g_hash_table_foreach
(
ht
,
yahoo_do_group_cleanup
,
NULL
);
g_hash_table_destroy
(
ht
);
}
if
(
yd
->
tmp_serv_ilist
)
{
buddies
=
g_strsplit
(
yd
->
tmp_serv_ilist
->
str
,
","
,
-1
);
for
(
bud
=
buddies
;
bud
&&
*
bud
;
bud
++
)
{
/* The server is already ignoring the user */
got_serv_list
=
TRUE
;
purple_privacy_deny_add
(
account
,
*
bud
,
1
);
}
g_strfreev
(
buddies
);
g_string_free
(
yd
->
tmp_serv_ilist
,
TRUE
);
yd
->
tmp_serv_ilist
=
NULL
;
}
if
(
got_serv_list
&&
((
account
->
perm_deny
!=
PURPLE_PRIVACY_ALLOW_BUDDYLIST
)
&&
(
account
->
perm_deny
!=
PURPLE_PRIVACY_DENY_ALL
)
&&
(
account
->
perm_deny
!=
PURPLE_PRIVACY_ALLOW_USERS
)))
{
account
->
perm_deny
=
PURPLE_PRIVACY_DENY_USERS
;
purple_debug_info
(
"yahoo"
,
"%s privacy defaulting to PURPLE_PRIVACY_DENY_USERS.
\n
"
,
account
->
username
);
}
if
(
yd
->
tmp_serv_plist
)
{
buddies
=
g_strsplit
(
yd
->
tmp_serv_plist
->
str
,
","
,
-1
);
for
(
bud
=
buddies
;
bud
&&
*
bud
;
bud
++
)
{
f
=
yahoo_friend_find
(
gc
,
*
bud
);
if
(
f
)
{
purple_debug_info
(
"yahoo"
,
"%s setting presence for %s to PERM_OFFLINE
\n
"
,
account
->
username
,
*
bud
);
f
->
presence
=
YAHOO_PRESENCE_PERM_OFFLINE
;
}
}
g_strfreev
(
buddies
);
g_string_free
(
yd
->
tmp_serv_plist
,
TRUE
);
yd
->
tmp_serv_plist
=
NULL
;
}
/* Now that we've got the list, request aliases */
yahoo_fetch_aliases
(
gc
);
}
/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */
static
void
yahoo_process_notify
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
,
yahoo_pkt_type
pkt_type
)
{
PurpleAccount
*
account
;
char
*
msg
=
NULL
;
char
*
from
=
NULL
;
char
*
stat
=
NULL
;
char
*
game
=
NULL
;
YahooFriend
*
f
=
NULL
;
GSList
*
l
=
pkt
->
hash
;
gint
val_11
=
0
;
YahooData
*
yd
=
gc
->
proto_data
;
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
account
=
purple_connection_get_account
(
gc
);
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
if
(
pair
->
key
==
4
||
pair
->
key
==
1
)
from
=
pair
->
value
;
if
(
pair
->
key
==
49
)
msg
=
pair
->
value
;
if
(
pair
->
key
==
13
)
stat
=
pair
->
value
;
if
(
pair
->
key
==
14
)
game
=
pair
->
value
;
if
(
pair
->
key
==
11
)
val_11
=
strtol
(
pair
->
value
,
NULL
,
10
);
if
(
pair
->
key
==
241
)
fed
=
strtol
(
pair
->
value
,
NULL
,
10
);
l
=
l
->
next
;
}
if
(
!
from
||
!
msg
)
return
;
/* disconnect the peer if connected through p2p and sends wrong value for session id */
if
(
(
pkt_type
==
YAHOO_PKT_TYPE_P2P
)
&&
(
val_11
!=
yd
->
session_id
)
)
{
purple_debug_warning
(
"yahoo"
,
"p2p: %s sent us notify with wrong session id. Disconnecting p2p connection to peer
\n
"
,
from
);
/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
g_hash_table_remove
(
yd
->
peers
,
from
);
return
;
}
if
(
!
g_ascii_strncasecmp
(
msg
,
"TYPING"
,
strlen
(
"TYPING"
))
&&
(
purple_privacy_check
(
account
,
from
)))
{
char
*
fed_from
=
from
;
switch
(
fed
)
{
case
YAHOO_FEDERATION_MSN
:
fed_from
=
g_strconcat
(
"msn/"
,
from
,
NULL
);
break
;
case
YAHOO_FEDERATION_OCS
:
fed_from
=
g_strconcat
(
"ocs/"
,
from
,
NULL
);
break
;
case
YAHOO_FEDERATION_IBM
:
fed_from
=
g_strconcat
(
"ibm/"
,
from
,
NULL
);
break
;
case
YAHOO_FEDERATION_PBX
:
fed_from
=
g_strconcat
(
"pbx/"
,
from
,
NULL
);
break
;
case
YAHOO_FEDERATION_NONE
:
default
:
break
;
}
if
(
stat
&&
*
stat
==
'1'
)
serv_got_typing
(
gc
,
fed_from
,
0
,
PURPLE_TYPING
);
else
serv_got_typing_stopped
(
gc
,
fed_from
);
if
(
fed_from
!=
from
)
g_free
(
fed_from
);
}
else
if
(
!
g_ascii_strncasecmp
(
msg
,
"GAME"
,
strlen
(
"GAME"
)))
{
PurpleBuddy
*
bud
=
purple_find_buddy
(
account
,
from
);
if
(
!
bud
)
{
purple_debug_warning
(
"yahoo"
,
"%s is playing a game, and doesn't want you to know.
\n
"
,
from
);
}
f
=
yahoo_friend_find
(
gc
,
from
);
if
(
!
f
)
return
;
/* if they're not on the list, don't bother */
yahoo_friend_set_game
(
f
,
NULL
);
if
(
stat
&&
*
stat
==
'1'
)
{
yahoo_friend_set_game
(
f
,
game
);
if
(
bud
)
yahoo_update_status
(
gc
,
from
,
f
);
}
}
else
if
(
!
g_ascii_strncasecmp
(
msg
,
"WEBCAMINVITE"
,
strlen
(
"WEBCAMINVITE"
)))
{
PurpleConversation
*
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
from
,
account
);
char
*
buf
=
g_strdup_printf
(
_
(
"%s has sent you a webcam invite, which is not yet supported."
),
from
);
purple_conversation_write
(
conv
,
NULL
,
buf
,
PURPLE_MESSAGE_SYSTEM
|
PURPLE_MESSAGE_NOTIFY
,
time
(
NULL
));
g_free
(
buf
);
}
}
struct
_yahoo_im
{
char
*
from
;
char
*
active_id
;
int
time
;
int
utf8
;
int
buddy_icon
;
char
*
id
;
char
*
msg
;
YahooFederation
fed
;
char
*
fed_from
;
};
static
void
yahoo_process_sms_message
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
PurpleAccount
*
account
;
GSList
*
l
=
pkt
->
hash
;
struct
_yahoo_im
*
sms
=
NULL
;
YahooData
*
yd
;
char
*
server_msg
=
NULL
;
char
*
m
;
yd
=
gc
->
proto_data
;
account
=
purple_connection_get_account
(
gc
);
while
(
l
!=
NULL
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
if
(
pair
->
key
==
4
)
{
sms
=
g_new0
(
struct
_yahoo_im
,
1
);
sms
->
from
=
g_strdup_printf
(
"+%s"
,
pair
->
value
);
sms
->
time
=
time
(
NULL
);
sms
->
utf8
=
TRUE
;
}
if
(
pair
->
key
==
14
)
{
if
(
sms
)
sms
->
msg
=
pair
->
value
;
}
if
(
pair
->
key
==
68
)
if
(
sms
)
g_hash_table_insert
(
yd
->
sms_carrier
,
g_strdup
(
sms
->
from
),
g_strdup
(
pair
->
value
));
if
(
pair
->
key
==
16
)
server_msg
=
pair
->
value
;
l
=
l
->
next
;
}
if
(
!
sms
)
{
purple_debug_info
(
"yahoo"
,
"Received a malformed SMS packet!
\n
"
);
return
;
}
if
(
(
pkt
->
status
==
-1
)
||
(
pkt
->
status
==
YAHOO_STATUS_DISCONNECTED
)
)
{
if
(
server_msg
)
{
PurpleConversation
*
c
;
c
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
sms
->
from
,
account
);
if
(
c
==
NULL
)
c
=
purple_conversation_new
(
PURPLE_CONV_TYPE_IM
,
account
,
sms
->
from
);
purple_conversation_write
(
c
,
NULL
,
server_msg
,
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
}
else
purple_notify_error
(
gc
,
NULL
,
_
(
"Your SMS was not delivered"
),
NULL
);
g_free
(
sms
->
from
);
g_free
(
sms
);
return
;
}
if
(
!
sms
->
from
||
!
sms
->
msg
)
{
g_free
(
sms
);
return
;
}
m
=
yahoo_string_decode
(
gc
,
sms
->
msg
,
sms
->
utf8
);
serv_got_im
(
gc
,
sms
->
from
,
m
,
0
,
sms
->
time
);
g_free
(
m
);
g_free
(
sms
->
from
);
g_free
(
sms
);
}
/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */
static
void
yahoo_process_message
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
,
yahoo_pkt_type
pkt_type
)
{
PurpleAccount
*
account
;
YahooData
*
yd
=
gc
->
proto_data
;
GSList
*
l
=
pkt
->
hash
;
GSList
*
list
=
NULL
;
struct
_yahoo_im
*
im
=
NULL
;
account
=
purple_connection_get_account
(
gc
);
if
(
pkt
->
status
<=
1
||
pkt
->
status
==
5
||
pkt
->
status
==
YAHOO_STATUS_OFFLINE
)
{
/* messages are received with status YAHOO_STATUS_OFFLINE in case of p2p */
while
(
l
!=
NULL
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
if
(
pair
->
key
==
4
||
pair
->
key
==
1
)
{
im
=
g_new0
(
struct
_yahoo_im
,
1
);
list
=
g_slist_append
(
list
,
im
);
im
->
from
=
pair
->
value
;
im
->
time
=
time
(
NULL
);
im
->
utf8
=
TRUE
;
im
->
fed
=
YAHOO_FEDERATION_NONE
;
im
->
fed_from
=
g_strdup
(
im
->
from
);
}
if
(
im
&&
pair
->
key
==
5
)
im
->
active_id
=
pair
->
value
;
if
(
pair
->
key
==
97
)
if
(
im
)
im
->
utf8
=
strtol
(
pair
->
value
,
NULL
,
10
);
if
(
pair
->
key
==
15
)
if
(
im
)
im
->
time
=
strtol
(
pair
->
value
,
NULL
,
10
);
if
(
pair
->
key
==
206
)
if
(
im
)
im
->
buddy_icon
=
strtol
(
pair
->
value
,
NULL
,
10
);
if
(
pair
->
key
==
14
)
{
if
(
im
)
im
->
msg
=
pair
->
value
;
}
if
(
im
&&
pair
->
key
==
241
)
{
im
->
fed
=
strtol
(
pair
->
value
,
NULL
,
10
);
g_free
(
im
->
fed_from
);
switch
(
im
->
fed
)
{
case
YAHOO_FEDERATION_MSN
:
im
->
fed_from
=
g_strconcat
(
"msn/"
,
im
->
from
,
NULL
);
break
;
case
YAHOO_FEDERATION_OCS
:
im
->
fed_from
=
g_strconcat
(
"ocs/"
,
im
->
from
,
NULL
);
break
;
case
YAHOO_FEDERATION_IBM
:
im
->
fed_from
=
g_strconcat
(
"ibm/"
,
im
->
from
,
NULL
);
break
;
case
YAHOO_FEDERATION_PBX
:
im
->
fed_from
=
g_strconcat
(
"pbx/"
,
im
->
from
,
NULL
);
break
;
case
YAHOO_FEDERATION_NONE
:
default
:
im
->
fed_from
=
g_strdup
(
im
->
from
);
break
;
}
purple_debug_info
(
"yahoo"
,
"Message from federated (%d) buddy %s.
\n
"
,
im
->
fed
,
im
->
fed_from
);
}
/* peer session id */
if
(
im
&&
(
pair
->
key
==
11
))
{
/* disconnect the peer if connected through p2p and sends wrong value for session id */
if
(
(
im
->
fed
==
YAHOO_FEDERATION_NONE
)
&&
(
pkt_type
==
YAHOO_PKT_TYPE_P2P
)
&&
(
yd
->
session_id
!=
strtol
(
pair
->
value
,
NULL
,
10
))
)
{
purple_debug_warning
(
"yahoo"
,
"p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer
\n
"
,
im
->
fed_from
);
/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
g_hash_table_remove
(
yd
->
peers
,
im
->
fed_from
);
g_free
(
im
->
fed_from
);
g_free
(
im
);
return
;
/* Not sure whether we should process remaining IMs in this packet */
}
}
/* IMV key */
if
(
im
&&
pair
->
key
==
63
)
{
/* Check for the Doodle IMV, no IMvironment for federated buddies */
if
(
im
->
from
!=
NULL
&&
im
->
fed
==
YAHOO_FEDERATION_NONE
)
{
g_hash_table_replace
(
yd
->
imvironments
,
g_strdup
(
im
->
from
),
g_strdup
(
pair
->
value
));
if
(
strstr
(
pair
->
value
,
"doodle;"
)
!=
NULL
)
{
PurpleWhiteboard
*
wb
;
if
(
!
purple_privacy_check
(
account
,
im
->
from
))
{
purple_debug_info
(
"yahoo"
,
"Doodle request from %s dropped.
\n
"
,
im
->
from
);
g_free
(
im
->
fed_from
);
g_free
(
im
);
return
;
}
/* I'm not sure the following ever happens -DAA */
wb
=
purple_whiteboard_get_session
(
account
,
im
->
from
);
/* If a Doodle session doesn't exist between this user */
if
(
wb
==
NULL
)
{
doodle_session
*
ds
;
wb
=
purple_whiteboard_create
(
account
,
im
->
from
,
DOODLE_STATE_REQUESTED
);
ds
=
wb
->
proto_data
;
ds
->
imv_key
=
g_strdup
(
pair
->
value
);
yahoo_doodle_command_send_request
(
gc
,
im
->
from
,
pair
->
value
);
yahoo_doodle_command_send_ready
(
gc
,
im
->
from
,
pair
->
value
);
}
}
}
}
if
(
pair
->
key
==
429
)
if
(
im
)
im
->
id
=
pair
->
value
;
l
=
l
->
next
;
}
}
else
if
(
pkt
->
status
==
2
)
{
purple_notify_error
(
gc
,
NULL
,
_
(
"Your Yahoo! message did not get sent."
),
NULL
);
}
for
(
l
=
list
;
l
;
l
=
l
->
next
)
{
YahooFriend
*
f
;
char
*
m
,
*
m2
;
im
=
l
->
data
;
if
(
!
im
->
fed_from
||
!
im
->
msg
)
{
g_free
(
im
->
fed_from
);
g_free
(
im
);
continue
;
}
if
(
!
purple_privacy_check
(
account
,
im
->
fed_from
))
{
purple_debug_info
(
"yahoo"
,
"Message from %s dropped.
\n
"
,
im
->
fed_from
);
return
;
}
/*
* TODO: Is there anything else we should check when determining whether
* we should send an acknowledgement?
*/
if
(
im
->
id
!=
NULL
)
{
/* Send acknowledgement. If we don't do this then the official
* Yahoo Messenger client for Windows will send us the same
* message 7 seconds later as an offline message. This is true
* for at least version 9.0.0.2162 on Windows XP. */
struct
yahoo_packet
*
pkt2
;
pkt2
=
yahoo_packet_new
(
YAHOO_SERVICE_MESSAGE_ACK
,
YAHOO_STATUS_AVAILABLE
,
pkt
->
id
);
yahoo_packet_hash
(
pkt2
,
"ssisii"
,
1
,
im
->
active_id
,
/* May not always be the connection's display name */
5
,
im
->
from
,
302
,
430
,
430
,
im
->
id
,
303
,
430
,
450
,
0
);
yahoo_packet_send_and_free
(
pkt2
,
yd
);
}
m
=
yahoo_string_decode
(
gc
,
im
->
msg
,
im
->
utf8
);
/* This may actually not be necessary, but it appears
* that at least at one point some clients were sending
* "\r\n" as line delimiters, so we want to avoid double
* lines. */
m2
=
purple_strreplace
(
m
,
"
\r\n
"
,
"
\n
"
);
g_free
(
m
);
m
=
m2
;
purple_util_chrreplace
(
m
,
'\r'
,
'\n'
);
if
(
!
strcmp
(
m
,
"<ding>"
))
{
char
*
username
;
username
=
g_markup_escape_text
(
im
->
fed_from
,
-1
);
purple_prpl_got_attention
(
gc
,
username
,
YAHOO_BUZZ
);
g_free
(
username
);
g_free
(
m
);
g_free
(
im
->
fed_from
);
g_free
(
im
);
continue
;
}
m2
=
yahoo_codes_to_html
(
m
);
g_free
(
m
);
serv_got_im
(
gc
,
im
->
fed_from
,
m2
,
0
,
im
->
time
);
g_free
(
m2
);
/* Official clients don't share buddy images with federated buddies */
if
(
im
->
fed
==
YAHOO_FEDERATION_NONE
)
{
if
((
f
=
yahoo_friend_find
(
gc
,
im
->
from
))
&&
im
->
buddy_icon
==
2
)
{
if
(
yahoo_friend_get_buddy_icon_need_request
(
f
))
{
yahoo_send_picture_request
(
gc
,
im
->
from
);
yahoo_friend_set_buddy_icon_need_request
(
f
,
FALSE
);
}
}
}
g_free
(
im
->
fed_from
);
g_free
(
im
);
}
g_slist_free
(
list
);
}
static
void
yahoo_process_sysmessage
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
GSList
*
l
=
pkt
->
hash
;
char
*
prim
,
*
me
=
NULL
,
*
msg
=
NULL
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
if
(
pair
->
key
==
5
)
me
=
pair
->
value
;
if
(
pair
->
key
==
14
)
msg
=
pair
->
value
;
l
=
l
->
next
;
}
if
(
!
msg
||
!
g_utf8_validate
(
msg
,
-1
,
NULL
))
return
;
prim
=
g_strdup_printf
(
_
(
"Yahoo! system message for %s:"
),
me
?
me
:
purple_connection_get_display_name
(
gc
));
purple_notify_info
(
NULL
,
NULL
,
prim
,
msg
);
g_free
(
prim
);
}
struct
yahoo_add_request
{
PurpleConnection
*
gc
;
char
*
id
;
char
*
who
;
YahooFederation
fed
;
};
static
void
yahoo_buddy_add_authorize_cb
(
gpointer
data
)
{
struct
yahoo_add_request
*
add_req
=
data
;
struct
yahoo_packet
*
pkt
;
YahooData
*
yd
=
add_req
->
gc
->
proto_data
;
const
char
*
who
=
add_req
->
who
;
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_AUTH_REQ_15
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
if
(
add_req
->
fed
)
{
who
+=
4
;
yahoo_packet_hash
(
pkt
,
"ssiii"
,
1
,
add_req
->
id
,
5
,
who
,
241
,
add_req
->
fed
,
13
,
1
,
334
,
0
);
}
else
{
yahoo_packet_hash
(
pkt
,
"ssii"
,
1
,
add_req
->
id
,
5
,
who
,
13
,
1
,
334
,
0
);
}
yahoo_packet_send_and_free
(
pkt
,
yd
);
g_free
(
add_req
->
id
);
g_free
(
add_req
->
who
);
g_free
(
add_req
);
}
static
void
yahoo_buddy_add_deny_cb
(
struct
yahoo_add_request
*
add_req
,
const
char
*
msg
)
{
YahooData
*
yd
=
add_req
->
gc
->
proto_data
;
struct
yahoo_packet
*
pkt
;
char
*
encoded_msg
=
NULL
;
const
char
*
who
=
add_req
->
who
;
if
(
msg
&&
*
msg
)
encoded_msg
=
yahoo_string_encode
(
add_req
->
gc
,
msg
,
NULL
);
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_AUTH_REQ_15
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
if
(
add_req
->
fed
)
{
who
+=
4
;
/* Skip fed identifier (msn|ocs|ibm)/' */
yahoo_packet_hash
(
pkt
,
"ssiiiis"
,
1
,
add_req
->
id
,
5
,
who
,
241
,
add_req
->
fed
,
13
,
2
,
334
,
0
,
97
,
1
,
14
,
encoded_msg
?
encoded_msg
:
""
);
}
else
{
yahoo_packet_hash
(
pkt
,
"ssiiis"
,
1
,
add_req
->
id
,
5
,
who
,
13
,
2
,
334
,
0
,
97
,
1
,
14
,
encoded_msg
?
encoded_msg
:
""
);
}
yahoo_packet_send_and_free
(
pkt
,
yd
);
g_free
(
encoded_msg
);
g_free
(
add_req
->
id
);
g_free
(
add_req
->
who
);
g_free
(
add_req
);
}
static
void
yahoo_buddy_add_deny_noreason_cb
(
struct
yahoo_add_request
*
add_req
,
const
char
*
msg
)
{
yahoo_buddy_add_deny_cb
(
add_req
,
NULL
);
}
static
void
yahoo_buddy_add_deny_reason_cb
(
gpointer
data
)
{
struct
yahoo_add_request
*
add_req
=
data
;
purple_request_input
(
add_req
->
gc
,
NULL
,
_
(
"Authorization denied message:"
),
NULL
,
_
(
"No reason given."
),
TRUE
,
FALSE
,
NULL
,
_
(
"OK"
),
G_CALLBACK
(
yahoo_buddy_add_deny_cb
),
_
(
"Cancel"
),
G_CALLBACK
(
yahoo_buddy_add_deny_noreason_cb
),
purple_connection_get_account
(
add_req
->
gc
),
add_req
->
who
,
NULL
,
add_req
);
}
static
void
yahoo_buddy_denied_our_add
(
PurpleConnection
*
gc
,
const
char
*
who
,
const
char
*
reason
)
{
char
*
notify_msg
;
YahooData
*
yd
=
gc
->
proto_data
;
if
(
who
==
NULL
)
return
;
if
(
reason
!=
NULL
)
{
char
*
msg2
=
yahoo_string_decode
(
gc
,
reason
,
FALSE
);
notify_msg
=
g_strdup_printf
(
_
(
"%s has (retroactively) denied your request to add them to your list for the following reason: %s."
),
who
,
msg2
);
g_free
(
msg2
);
}
else
notify_msg
=
g_strdup_printf
(
_
(
"%s has (retroactively) denied your request to add them to your list."
),
who
);
purple_notify_info
(
gc
,
NULL
,
_
(
"Add buddy rejected"
),
notify_msg
);
g_free
(
notify_msg
);
g_hash_table_remove
(
yd
->
friends
,
who
);
purple_prpl_got_user_status
(
purple_connection_get_account
(
gc
),
who
,
"offline"
,
NULL
);
/* FIXME: make this set not on list status instead */
/* TODO: Shouldn't we remove the buddy from our local list? */
}
static
void
yahoo_buddy_auth_req_15
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
PurpleAccount
*
account
;
GSList
*
l
=
pkt
->
hash
;
const
char
*
msg
=
NULL
;
account
=
purple_connection_get_account
(
gc
);
/* Buddy authorized/declined our addition */
if
(
pkt
->
status
==
1
)
{
char
*
temp
=
NULL
;
char
*
who
=
NULL
;
int
response
=
0
;
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
switch
(
pair
->
key
)
{
case
4
:
temp
=
pair
->
value
;
break
;
case
13
:
response
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
case
14
:
msg
=
pair
->
value
;
break
;
case
241
:
fed
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
}
l
=
l
->
next
;
}
switch
(
fed
)
{
case
YAHOO_FEDERATION_MSN
:
who
=
g_strconcat
(
"msn/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_OCS
:
who
=
g_strconcat
(
"ocs/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_IBM
:
who
=
g_strconcat
(
"ibm/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_NONE
:
default
:
who
=
g_strdup
(
temp
);
break
;
}
if
(
response
==
1
)
/* Authorized */
purple_debug_info
(
"yahoo"
,
"Received authorization from buddy '%s'.
\n
"
,
who
?
who
:
"(Unknown Buddy)"
);
else
if
(
response
==
2
)
{
/* Declined */
purple_debug_info
(
"yahoo"
,
"Received authorization decline from buddy '%s'.
\n
"
,
who
?
who
:
"(Unknown Buddy)"
);
yahoo_buddy_denied_our_add
(
gc
,
who
,
msg
);
}
else
purple_debug_error
(
"yahoo"
,
"Received unknown authorization response of %d from buddy '%s'.
\n
"
,
response
,
who
?
who
:
"(Unknown Buddy)"
);
g_free
(
who
);
}
/* Buddy requested authorization to add us. */
else
if
(
pkt
->
status
==
3
)
{
struct
yahoo_add_request
*
add_req
;
const
char
*
firstname
=
NULL
,
*
lastname
=
NULL
;
char
*
temp
=
NULL
;
add_req
=
g_new0
(
struct
yahoo_add_request
,
1
);
add_req
->
gc
=
gc
;
add_req
->
fed
=
YAHOO_FEDERATION_NONE
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
switch
(
pair
->
key
)
{
case
4
:
temp
=
pair
->
value
;
break
;
case
5
:
add_req
->
id
=
g_strdup
(
pair
->
value
);
break
;
case
14
:
msg
=
pair
->
value
;
break
;
case
216
:
firstname
=
pair
->
value
;
break
;
case
241
:
add_req
->
fed
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
case
254
:
lastname
=
pair
->
value
;
break
;
}
l
=
l
->
next
;
}
switch
(
add_req
->
fed
)
{
case
YAHOO_FEDERATION_MSN
:
add_req
->
who
=
g_strconcat
(
"msn/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_OCS
:
add_req
->
who
=
g_strconcat
(
"ocs/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_IBM
:
add_req
->
who
=
g_strconcat
(
"ibm/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_NONE
:
default
:
add_req
->
who
=
g_strdup
(
temp
);
break
;
}
if
(
add_req
->
id
&&
add_req
->
who
)
{
char
*
alias
=
NULL
,
*
dec_msg
=
NULL
;
if
(
!
purple_privacy_check
(
account
,
add_req
->
who
))
{
purple_debug_misc
(
"yahoo"
,
"Auth. request from %s dropped and automatically denied due to privacy settings!
\n
"
,
add_req
->
who
);
yahoo_buddy_add_deny_cb
(
add_req
,
NULL
);
return
;
}
if
(
msg
)
dec_msg
=
yahoo_string_decode
(
gc
,
msg
,
FALSE
);
if
(
firstname
&&
lastname
)
alias
=
g_strdup_printf
(
"%s %s"
,
firstname
,
lastname
);
else
if
(
firstname
)
alias
=
g_strdup
(
firstname
);
else
if
(
lastname
)
alias
=
g_strdup
(
lastname
);
/* DONE! this is almost exactly the same as what MSN does,
* this should probably be moved to the core.
*/
purple_account_request_authorization
(
account
,
add_req
->
who
,
add_req
->
id
,
alias
,
dec_msg
,
purple_find_buddy
(
account
,
add_req
->
who
)
!=
NULL
,
yahoo_buddy_add_authorize_cb
,
yahoo_buddy_add_deny_reason_cb
,
add_req
);
g_free
(
alias
);
g_free
(
dec_msg
);
}
else
{
g_free
(
add_req
->
id
);
g_free
(
add_req
->
who
);
g_free
(
add_req
);
}
}
else
{
purple_debug_error
(
"yahoo"
,
"Received authorization of unknown status (%d).
\n
"
,
pkt
->
status
);
}
}
/* I don't think this happens anymore in Version 15 */
static
void
yahoo_buddy_added_us
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
PurpleAccount
*
account
;
struct
yahoo_add_request
*
add_req
;
char
*
msg
=
NULL
;
GSList
*
l
=
pkt
->
hash
;
account
=
purple_connection_get_account
(
gc
);
add_req
=
g_new0
(
struct
yahoo_add_request
,
1
);
add_req
->
gc
=
gc
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
switch
(
pair
->
key
)
{
case
1
:
add_req
->
id
=
g_strdup
(
pair
->
value
);
break
;
case
3
:
add_req
->
who
=
g_strdup
(
pair
->
value
);
break
;
case
15
:
/* time, for when they add us and we're offline */
break
;
case
14
:
msg
=
pair
->
value
;
break
;
}
l
=
l
->
next
;
}
if
(
add_req
->
id
&&
add_req
->
who
)
{
char
*
dec_msg
=
NULL
;
if
(
!
purple_privacy_check
(
account
,
add_req
->
who
))
{
purple_debug_misc
(
"yahoo"
,
"Auth. request from %s dropped and automatically denied due to privacy settings!
\n
"
,
add_req
->
who
);
yahoo_buddy_add_deny_cb
(
add_req
,
NULL
);
return
;
}
if
(
msg
)
dec_msg
=
yahoo_string_decode
(
gc
,
msg
,
FALSE
);
/* DONE! this is almost exactly the same as what MSN does,
* this should probably be moved to the core.
*/
purple_account_request_authorization
(
account
,
add_req
->
who
,
add_req
->
id
,
NULL
,
dec_msg
,
purple_find_buddy
(
account
,
add_req
->
who
)
!=
NULL
,
yahoo_buddy_add_authorize_cb
,
yahoo_buddy_add_deny_reason_cb
,
add_req
);
g_free
(
dec_msg
);
}
else
{
g_free
(
add_req
->
id
);
g_free
(
add_req
->
who
);
g_free
(
add_req
);
}
}
/* I have no idea if this every gets called in version 15 */
static
void
yahoo_buddy_denied_our_add_old
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
char
*
who
=
NULL
;
char
*
msg
=
NULL
;
GSList
*
l
=
pkt
->
hash
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
switch
(
pair
->
key
)
{
case
3
:
who
=
pair
->
value
;
break
;
case
14
:
msg
=
pair
->
value
;
break
;
}
l
=
l
->
next
;
}
yahoo_buddy_denied_our_add
(
gc
,
who
,
msg
);
}
static
void
yahoo_process_contact
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
switch
(
pkt
->
status
)
{
case
1
:
yahoo_process_status
(
gc
,
pkt
);
return
;
case
3
:
yahoo_buddy_added_us
(
gc
,
pkt
);
break
;
case
7
:
yahoo_buddy_denied_our_add_old
(
gc
,
pkt
);
break
;
default
:
break
;
}
}
#define OUT_CHARSET "utf-8"
static
char
*
yahoo_decode
(
const
char
*
text
)
{
char
*
converted
=
NULL
;
char
*
n
,
*
new
;
const
char
*
end
,
*
p
;
int
i
,
k
;
n
=
new
=
g_malloc
(
strlen
(
text
)
+
1
);
end
=
text
+
strlen
(
text
);
for
(
p
=
text
;
p
<
end
;
p
++
,
n
++
)
{
if
(
*
p
==
'\\'
)
{
if
(
p
[
1
]
>=
'0'
&&
p
[
1
]
<=
'7'
)
{
p
+=
1
;
for
(
i
=
0
,
k
=
0
;
k
<
3
;
k
+=
1
)
{
char
c
=
p
[
k
];
if
(
c
<
'0'
||
c
>
'7'
)
break
;
i
*=
8
;
i
+=
c
-
'0'
;
}
*
n
=
i
;
p
+=
k
-
1
;
}
else
{
/* bug 959248 */
/* If we see a \ not followed by an octal number,
* it means that it is actually a \\ with one \
* already eaten by some unknown function.
* This is arguably broken.
*
* I think wing is wrong here, there is no function
* called that I see that could have done it. I guess
* it is just really sending single \'s. That's yahoo
* for you.
*/
*
n
=
*
p
;
}
}
else
*
n
=
*
p
;
}
*
n
=
'\0'
;
if
(
strstr
(
text
,
"
\033
$B"
))
converted
=
g_convert
(
new
,
n
-
new
,
OUT_CHARSET
,
"iso-2022-jp"
,
NULL
,
NULL
,
NULL
);
if
(
!
converted
)
converted
=
g_convert
(
new
,
n
-
new
,
OUT_CHARSET
,
"iso-8859-1"
,
NULL
,
NULL
,
NULL
);
g_free
(
new
);
return
converted
;
}
static
void
yahoo_process_mail
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
YahooData
*
yd
=
gc
->
proto_data
;
const
char
*
who
=
NULL
;
const
char
*
email
=
NULL
;
const
char
*
subj
=
NULL
;
const
char
*
yahoo_mail_url
=
(
yd
->
jp
?
YAHOOJP_MAIL_URL
:
YAHOO_MAIL_URL
);
int
count
=
0
;
GSList
*
l
=
pkt
->
hash
;
if
(
!
purple_account_get_check_mail
(
account
))
return
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
if
(
pair
->
key
==
9
)
count
=
strtol
(
pair
->
value
,
NULL
,
10
);
else
if
(
pair
->
key
==
43
)
who
=
pair
->
value
;
else
if
(
pair
->
key
==
42
)
email
=
pair
->
value
;
else
if
(
pair
->
key
==
18
)
subj
=
pair
->
value
;
l
=
l
->
next
;
}
if
(
who
&&
subj
&&
email
&&
*
email
)
{
char
*
dec_who
=
yahoo_decode
(
who
);
char
*
dec_subj
=
yahoo_decode
(
subj
);
char
*
from
=
g_strdup_printf
(
"%s (%s)"
,
dec_who
,
email
);
purple_notify_email
(
gc
,
dec_subj
,
from
,
purple_account_get_username
(
account
),
yahoo_mail_url
,
NULL
,
NULL
);
g_free
(
dec_who
);
g_free
(
dec_subj
);
g_free
(
from
);
}
else
if
(
count
>
0
)
{
const
char
*
tos
[
2
]
=
{
purple_account_get_username
(
account
)
};
const
char
*
urls
[
2
]
=
{
yahoo_mail_url
};
purple_notify_emails
(
gc
,
count
,
FALSE
,
NULL
,
NULL
,
tos
,
urls
,
NULL
,
NULL
);
}
}
/* We use this structure once while we authenticate */
struct
yahoo_auth_data
{
PurpleConnection
*
gc
;
char
*
seed
;
};
/* This is the y64 alphabet... it's like base64, but has a . and a _ */
static
const
char
base64digits
[]
=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._"
;
/* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function
* in util.c, but it is different from the one yahoo uses */
static
void
to_y64
(
char
*
out
,
const
unsigned
char
*
in
,
gsize
inlen
)
/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
{
for
(;
inlen
>=
3
;
inlen
-=
3
)
{
*
out
++
=
base64digits
[
in
[
0
]
>>
2
];
*
out
++
=
base64digits
[((
in
[
0
]
<<
4
)
&
0x30
)
|
(
in
[
1
]
>>
4
)];
*
out
++
=
base64digits
[((
in
[
1
]
<<
2
)
&
0x3c
)
|
(
in
[
2
]
>>
6
)];
*
out
++
=
base64digits
[
in
[
2
]
&
0x3f
];
in
+=
3
;
}
if
(
inlen
>
0
)
{
unsigned
char
fragment
;
*
out
++
=
base64digits
[
in
[
0
]
>>
2
];
fragment
=
(
in
[
0
]
<<
4
)
&
0x30
;
if
(
inlen
>
1
)
fragment
|=
in
[
1
]
>>
4
;
*
out
++
=
base64digits
[
fragment
];
*
out
++
=
(
inlen
<
2
)
?
'-'
:
base64digits
[(
in
[
1
]
<<
2
)
&
0x3c
];
*
out
++
=
'-'
;
}
*
out
=
'\0'
;
}
static
void
yahoo_auth16_stage3
(
PurpleConnection
*
gc
,
const
char
*
crypt
)
{
YahooData
*
yd
=
gc
->
proto_data
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
const
char
*
name
=
purple_normalize
(
account
,
purple_account_get_username
(
account
));
PurpleCipher
*
md5_cipher
;
PurpleCipherContext
*
md5_ctx
;
guchar
md5_digest
[
16
];
gchar
base64_string
[
25
];
struct
yahoo_packet
*
pkt
;
purple_debug_info
(
"yahoo"
,
"Authentication: In yahoo_auth16_stage3
\n
"
);
g_return_if_fail
(
crypt
!=
NULL
);
md5_cipher
=
purple_ciphers_find_cipher
(
"md5"
);
md5_ctx
=
purple_cipher_context_new
(
md5_cipher
,
NULL
);
purple_cipher_context_append
(
md5_ctx
,
(
guchar
*
)
crypt
,
strlen
(
crypt
));
purple_cipher_context_digest
(
md5_ctx
,
sizeof
(
md5_digest
),
md5_digest
,
NULL
);
to_y64
(
base64_string
,
md5_digest
,
16
);
purple_debug_info
(
"yahoo"
,
"yahoo status: %d
\n
"
,
yd
->
current_status
);
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_AUTHRESP
,
yd
->
current_status
,
yd
->
session_id
);
if
(
yd
->
cookie_b
)
{
/* send B cookie if we have it */
yahoo_packet_hash
(
pkt
,
"ssssssssss"
,
1
,
name
,
0
,
name
,
277
,
yd
->
cookie_y
,
278
,
yd
->
cookie_t
,
307
,
base64_string
,
244
,
yd
->
jp
?
YAHOOJP_CLIENT_VERSION_ID
:
YAHOO_CLIENT_VERSION_ID
,
2
,
name
,
2
,
"1"
,
59
,
yd
->
cookie_b
,
98
,
purple_account_get_string
(
account
,
"room_list_locale"
,
yd
->
jp
?
"jp"
:
"us"
),
135
,
yd
->
jp
?
YAHOOJP_CLIENT_VERSION
:
YAHOO_CLIENT_VERSION
);
}
else
{
/* don't try to send an empty B cookie - the server will be mad */
yahoo_packet_hash
(
pkt
,
"sssssssss"
,
1
,
name
,
0
,
name
,
277
,
yd
->
cookie_y
,
278
,
yd
->
cookie_t
,
307
,
base64_string
,
244
,
yd
->
jp
?
YAHOOJP_CLIENT_VERSION_ID
:
YAHOO_CLIENT_VERSION_ID
,
2
,
name
,
2
,
"1"
,
98
,
purple_account_get_string
(
account
,
"room_list_locale"
,
yd
->
jp
?
"jp"
:
"us"
),
135
,
yd
->
jp
?
YAHOOJP_CLIENT_VERSION
:
YAHOO_CLIENT_VERSION
);
}
if
(
yd
->
picture_checksum
)
yahoo_packet_hash_int
(
pkt
,
192
,
yd
->
picture_checksum
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
purple_cipher_context_destroy
(
md5_ctx
);
}
static
gchar
*
yahoo_auth16_get_cookie_b
(
gchar
*
headers
)
{
gchar
**
splits
=
g_strsplit
(
headers
,
"
\r\n
"
,
-1
);
gchar
*
tmp
=
NULL
,
*
tmp2
=
NULL
,
*
sem
=
NULL
;
int
elements
=
g_strv_length
(
splits
),
i
;
if
(
elements
>
1
)
{
for
(
i
=
0
;
i
<
elements
;
i
++
)
{
if
(
g_ascii_strncasecmp
(
splits
[
i
],
"Set-Cookie: B="
,
14
)
==
0
)
{
tmp
=
&
splits
[
i
][
14
];
sem
=
strchr
(
tmp
,
';'
);
if
(
sem
!=
NULL
)
{
tmp2
=
g_strndup
(
tmp
,
sem
-
tmp
);
purple_debug_info
(
"yahoo"
,
"Got needed part of B cookie: %s
\n
"
,
tmp2
?
tmp2
:
"(null)"
);
break
;
}
}
}
}
g_strfreev
(
splits
);
return
tmp2
;
}
static
void
yahoo_auth16_stage2
(
PurpleUtilFetchUrlData
*
url_data
,
gpointer
user_data
,
const
gchar
*
ret_data
,
size_t
len
,
const
gchar
*
error_message
)
{
struct
yahoo_auth_data
*
auth_data
=
user_data
;
PurpleConnection
*
gc
=
auth_data
->
gc
;
YahooData
*
yd
=
purple_connection_get_protocol_data
(
gc
);
gboolean
try_login_on_error
=
FALSE
;
purple_debug_info
(
"yahoo"
,
"Authentication: In yahoo_auth16_stage2
\n
"
);
yd
->
url_datas
=
g_slist_remove
(
yd
->
url_datas
,
url_data
);
if
(
error_message
!=
NULL
)
{
purple_debug_error
(
"yahoo"
,
"Login Failed, unable to retrieve stage 2 url: %s
\n
"
,
error_message
);
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
error_message
);
g_free
(
auth_data
->
seed
);
g_free
(
auth_data
);
return
;
}
else
if
(
len
>
0
&&
ret_data
&&
*
ret_data
)
{
gchar
**
splits
=
g_strsplit
(
ret_data
,
"
\r\n\r\n
"
,
-1
),
**
split_data
=
NULL
;
int
totalelements
=
0
;
int
response_no
=
-1
;
char
*
crumb
=
NULL
;
char
*
crypt
=
NULL
;
if
(
g_strv_length
(
splits
)
>
1
)
{
yd
->
cookie_b
=
yahoo_auth16_get_cookie_b
(
splits
[
0
]);
split_data
=
g_strsplit
(
splits
[
1
],
"
\r\n
"
,
-1
);
totalelements
=
g_strv_length
(
split_data
);
}
if
(
totalelements
>=
4
)
{
int
i
;
for
(
i
=
0
;
i
<
totalelements
;
i
++
)
{
/* I'm not exactly a fan of the magic numbers, but it's obvious,
* so no sense in wasting a bajillion vars or calls to strlen */
if
(
g_ascii_isdigit
(
split_data
[
i
][
0
]))
{
/* if the current line and the next line both start with numbers,
* the current line is the length of the body, so skip. If not,
* then the current line is the response code from the login process. */
if
(
!
g_ascii_isdigit
(
split_data
[
i
+
1
][
0
]))
{
response_no
=
strtol
(
split_data
[
i
],
NULL
,
10
);
purple_debug_info
(
"yahoo"
,
"Got auth16 stage 2 response code: %d
\n
"
,
response_no
);
}
}
else
if
(
strncmp
(
split_data
[
i
],
"crumb="
,
6
)
==
0
)
{
crumb
=
g_strdup
(
&
split_data
[
i
][
6
]);
if
(
purple_debug_is_unsafe
())
purple_debug_info
(
"yahoo"
,
"Got crumb: %s
\n
"
,
crumb
);
}
else
if
(
strncmp
(
split_data
[
i
],
"Y="
,
2
)
==
0
)
{
yd
->
cookie_y
=
g_strdup
(
&
split_data
[
i
][
2
]);
if
(
purple_debug_is_unsafe
())
purple_debug_info
(
"yahoo"
,
"Got Y cookie: %s
\n
"
,
yd
->
cookie_y
);
}
else
if
(
strncmp
(
split_data
[
i
],
"T="
,
2
)
==
0
)
{
yd
->
cookie_t
=
g_strdup
(
&
split_data
[
i
][
2
]);
if
(
purple_debug_is_unsafe
())
purple_debug_info
(
"yahoo"
,
"Got T cookie: %s
\n
"
,
yd
->
cookie_t
);
}
}
}
g_strfreev
(
splits
);
g_strfreev
(
split_data
);
if
(
crumb
==
NULL
)
response_no
=
-1
;
if
(
response_no
!=
0
)
{
/* Some error in the login process */
PurpleConnectionError
error
;
char
*
error_reason
=
NULL
;
switch
(
response_no
)
{
case
-1
:
/* Some error in the received stream */
error_reason
=
g_strdup
(
_
(
"Received invalid data"
));
error
=
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
break
;
case
100
:
/* Unknown error */
error_reason
=
g_strdup
(
_
(
"Unknown error"
));
error
=
PURPLE_CONNECTION_ERROR_OTHER_ERROR
;
break
;
default
:
/* if we have everything we need, why not try to login irrespective of response */
if
((
crumb
!=
NULL
)
&&
(
yd
->
cookie_y
!=
NULL
)
&&
(
yd
->
cookie_t
!=
NULL
))
{
try_login_on_error
=
TRUE
;
break
;
}
error_reason
=
g_strdup
(
_
(
"Unknown error"
));
error
=
PURPLE_CONNECTION_ERROR_OTHER_ERROR
;
break
;
}
if
(
error_reason
)
{
purple_debug_error
(
"yahoo"
,
"Authentication error: %s. "
"Code %d
\n
"
,
error_reason
,
response_no
);
purple_connection_error_reason
(
gc
,
error
,
error_reason
);
g_free
(
error_reason
);
g_free
(
auth_data
->
seed
);
g_free
(
auth_data
);
return
;
}
}
crypt
=
g_strconcat
(
crumb
,
auth_data
->
seed
,
NULL
);
yahoo_auth16_stage3
(
gc
,
crypt
);
g_free
(
crypt
);
g_free
(
crumb
);
}
g_free
(
auth_data
->
seed
);
g_free
(
auth_data
);
}
static
void
yahoo_auth16_stage1_cb
(
PurpleUtilFetchUrlData
*
url_data
,
gpointer
user_data
,
const
gchar
*
ret_data
,
size_t
len
,
const
gchar
*
error_message
)
{
struct
yahoo_auth_data
*
auth_data
=
user_data
;
PurpleConnection
*
gc
=
auth_data
->
gc
;
YahooData
*
yd
=
purple_connection_get_protocol_data
(
gc
);
purple_debug_info
(
"yahoo"
,
"Authentication: In yahoo_auth16_stage1_cb
\n
"
);
yd
->
url_datas
=
g_slist_remove
(
yd
->
url_datas
,
url_data
);
if
(
error_message
!=
NULL
)
{
purple_debug_error
(
"yahoo"
,
"Login Failed, unable to retrieve login url: %s
\n
"
,
error_message
);
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
error_message
);
g_free
(
auth_data
->
seed
);
g_free
(
auth_data
);
return
;
}
else
if
(
len
>
0
&&
ret_data
&&
*
ret_data
)
{
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
gchar
**
split_data
=
g_strsplit
(
ret_data
,
"
\r\n
"
,
-1
);
int
totalelements
=
0
;
int
response_no
=
-1
;
char
*
token
=
NULL
;
totalelements
=
g_strv_length
(
split_data
);
if
(
totalelements
==
1
)
{
/* Received an error code */
response_no
=
strtol
(
split_data
[
0
],
NULL
,
10
);
}
else
if
(
totalelements
==
2
||
totalelements
==
3
)
{
/* received valid data */
response_no
=
strtol
(
split_data
[
0
],
NULL
,
10
);
token
=
g_strdup
(
split_data
[
1
]
+
strlen
(
"ymsgr="
));
}
else
{
/* It looks like a transparent proxy has returned a document we don't want */
response_no
=
-1
;
}
g_strfreev
(
split_data
);
if
(
response_no
!=
0
)
{
/* Some error in the login process */
PurpleConnectionError
error
;
char
*
error_reason
;
switch
(
response_no
)
{
case
-1
:
/* Some error in the received stream */
error_reason
=
g_strdup
(
_
(
"Received invalid data"
));
error
=
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
break
;
case
1212
:
/* Password incorrect */
/* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */
if
(
!
purple_account_get_remember_password
(
account
))
purple_account_set_password
(
account
,
NULL
);
error_reason
=
g_strdup
(
_
(
"Incorrect password"
));
error
=
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
;
break
;
case
1213
:
/* security lock from too many failed login attempts */
error_reason
=
g_strdup
(
_
(
"Account locked: Too many failed login "
"attempts. Logging into the Yahoo! website may fix this."
));
error
=
PURPLE_CONNECTION_ERROR_OTHER_ERROR
;
break
;
case
1235
:
/* the username does not exist */
error_reason
=
g_strdup
(
_
(
"Username does not exist"
));
error
=
PURPLE_CONNECTION_ERROR_INVALID_USERNAME
;
break
;
case
1214
:
/* indicates a lock of some description */
error_reason
=
g_strdup
(
_
(
"Account locked: Unknown reason. Logging "
"into the Yahoo! website may fix this."
));
error
=
PURPLE_CONNECTION_ERROR_OTHER_ERROR
;
break
;
case
1236
:
/* indicates a lock due to logging in too frequently */
error_reason
=
g_strdup
(
_
(
"Account locked: You have been logging in too "
"frequently. Wait a few minutes before trying to connect "
"again. Logging into the Yahoo! website may help."
));
error
=
PURPLE_CONNECTION_ERROR_OTHER_ERROR
;
break
;
case
100
:
/* username or password missing */
error_reason
=
g_strdup
(
_
(
"Username or password missing"
));
error
=
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
;
break
;
default
:
/* Unknown error! */
error_reason
=
g_strdup_printf
(
_
(
"Unknown error (%d)"
),
response_no
);
error
=
PURPLE_CONNECTION_ERROR_OTHER_ERROR
;
break
;
}
purple_debug_error
(
"yahoo"
,
"Authentication error: %s. Code %d
\n
"
,
error_reason
,
response_no
);
purple_connection_error_reason
(
gc
,
error
,
error_reason
);
g_free
(
error_reason
);
g_free
(
auth_data
->
seed
);
g_free
(
auth_data
);
g_free
(
token
);
}
else
{
/* OK to login, correct information provided */
PurpleUtilFetchUrlData
*
url_data
=
NULL
;
char
*
url
=
NULL
;
gboolean
yahoojp
=
yahoo_is_japan
(
account
);
gboolean
proxy_ssl
=
purple_account_get_bool
(
account
,
"proxy_ssl"
,
FALSE
);
url
=
g_strdup_printf
(
yahoojp
?
YAHOOJP_LOGIN_URL
:
YAHOO_LOGIN_URL
,
token
);
url_data
=
purple_util_fetch_url_request_len_with_account
(
proxy_ssl
?
account
:
NULL
,
url
,
TRUE
,
YAHOO_CLIENT_USERAGENT
,
TRUE
,
NULL
,
TRUE
,
-1
,
yahoo_auth16_stage2
,
auth_data
);
if
(
url_data
)
yd
->
url_datas
=
g_slist_prepend
(
yd
->
url_datas
,
url_data
);
g_free
(
url
);
g_free
(
token
);
}
}
}
static
void
yahoo_auth16_stage1
(
PurpleConnection
*
gc
,
const
char
*
seed
)
{
YahooData
*
yd
=
purple_connection_get_protocol_data
(
gc
);
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
PurpleUtilFetchUrlData
*
url_data
=
NULL
;
struct
yahoo_auth_data
*
auth_data
=
NULL
;
char
*
url
=
NULL
;
char
*
encoded_username
;
char
*
encoded_password
;
gboolean
yahoojp
=
yahoo_is_japan
(
account
);
gboolean
proxy_ssl
=
purple_account_get_bool
(
account
,
"proxy_ssl"
,
FALSE
);
purple_debug_info
(
"yahoo"
,
"Authentication: In yahoo_auth16_stage1
\n
"
);
if
(
!
purple_ssl_is_supported
())
{
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
,
_
(
"SSL support unavailable"
));
return
;
}
auth_data
=
g_new0
(
struct
yahoo_auth_data
,
1
);
auth_data
->
gc
=
gc
;
auth_data
->
seed
=
g_strdup
(
seed
);
encoded_username
=
g_strdup
(
purple_url_encode
(
purple_account_get_username
(
purple_connection_get_account
(
gc
))));
encoded_password
=
g_strdup
(
purple_url_encode
(
purple_connection_get_password
(
gc
)));
url
=
g_strdup_printf
(
yahoojp
?
YAHOOJP_TOKEN_URL
:
YAHOO_TOKEN_URL
,
encoded_username
,
encoded_password
,
purple_url_encode
(
seed
));
g_free
(
encoded_password
);
g_free
(
encoded_username
);
url_data
=
purple_util_fetch_url_request_len_with_account
(
proxy_ssl
?
account
:
NULL
,
url
,
TRUE
,
YAHOO_CLIENT_USERAGENT
,
TRUE
,
NULL
,
FALSE
,
-1
,
yahoo_auth16_stage1_cb
,
auth_data
);
if
(
url_data
)
yd
->
url_datas
=
g_slist_prepend
(
yd
->
url_datas
,
url_data
);
g_free
(
url
);
}
static
void
yahoo_process_auth
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
char
*
seed
=
NULL
;
char
*
sn
=
NULL
;
GSList
*
l
=
pkt
->
hash
;
int
m
=
0
;
gchar
*
buf
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
if
(
pair
->
key
==
94
)
seed
=
pair
->
value
;
if
(
pair
->
key
==
1
)
sn
=
pair
->
value
;
if
(
pair
->
key
==
13
)
m
=
atoi
(
pair
->
value
);
l
=
l
->
next
;
}
if
(
seed
)
{
switch
(
m
)
{
case
0
:
/* used to be for really old auth routine, dont support now */
case
1
:
case
2
:
/* Yahoo ver 16 authentication */
yahoo_auth16_stage1
(
gc
,
seed
);
break
;
default
:
{
GHashTable
*
ui_info
=
purple_core_get_ui_info
();
buf
=
g_strdup_printf
(
_
(
"The Yahoo server has requested the use of an unrecognized "
"authentication method. You will probably not be able "
"to successfully sign on to Yahoo. Check %s for updates."
),
((
ui_info
&&
g_hash_table_lookup
(
ui_info
,
"website"
))
?
(
char
*
)
g_hash_table_lookup
(
ui_info
,
"website"
)
:
PURPLE_WEBSITE
));
purple_notify_error
(
gc
,
""
,
_
(
"Failed Yahoo! Authentication"
),
buf
);
g_free
(
buf
);
yahoo_auth16_stage1
(
gc
,
seed
);
/* Can't hurt to try it anyway. */
break
;
}
}
}
}
static
void
ignore_buddy
(
PurpleBuddy
*
buddy
)
{
PurpleGroup
*
group
;
PurpleAccount
*
account
;
gchar
*
name
;
if
(
!
buddy
)
return
;
group
=
purple_buddy_get_group
(
buddy
);
name
=
g_strdup
(
purple_buddy_get_name
(
buddy
));
account
=
purple_buddy_get_account
(
buddy
);
purple_debug_info
(
"yahoo"
,
"blist: Removing '%s' from buddy list.
\n
"
,
name
);
purple_account_remove_buddy
(
account
,
buddy
,
group
);
purple_blist_remove_buddy
(
buddy
);
serv_add_deny
(
purple_account_get_connection
(
account
),
name
);
g_free
(
name
);
}
static
void
keep_buddy
(
PurpleBuddy
*
b
)
{
purple_privacy_deny_remove
(
purple_buddy_get_account
(
b
),
purple_buddy_get_name
(
b
),
1
);
}
static
void
yahoo_process_ignore
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
PurpleBuddy
*
b
;
GSList
*
l
;
gchar
*
who
=
NULL
;
gchar
*
me
=
NULL
;
gchar
buf
[
BUF_LONG
];
gboolean
ignore
=
TRUE
;
gint
status
=
0
;
for
(
l
=
pkt
->
hash
;
l
;
l
=
l
->
next
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
switch
(
pair
->
key
)
{
case
0
:
who
=
pair
->
value
;
break
;
case
1
:
me
=
pair
->
value
;
break
;
case
13
:
/* 1 == ignore, 2 == unignore */
ignore
=
(
strtol
(
pair
->
value
,
NULL
,
10
)
==
1
);
break
;
case
66
:
status
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
default
:
break
;
}
}
/*
* status
* 0 - ok
* 2 - already in ignore list, could not add
* 3 - not in ignore list, could not delete
* 12 - is a buddy, could not add (and possibly also a not-in-ignore list condition?)
*/
switch
(
status
)
{
case
12
:
purple_debug_info
(
"yahoo"
,
"Server reported
\"
is a buddy
\"
for %s while %s"
,
who
,
(
ignore
?
"ignoring"
:
"unignoring"
));
if
(
ignore
)
{
b
=
purple_find_buddy
(
gc
->
account
,
who
);
g_snprintf
(
buf
,
sizeof
(
buf
),
_
(
"You have tried to ignore %s, but the "
"user is on your buddy list. Clicking
\"
Yes
\"
"
"will remove and ignore the buddy."
),
who
);
purple_request_yes_no
(
gc
,
NULL
,
_
(
"Ignore buddy?"
),
buf
,
0
,
gc
->
account
,
who
,
NULL
,
b
,
G_CALLBACK
(
ignore_buddy
),
G_CALLBACK
(
keep_buddy
));
break
;
}
case
2
:
purple_debug_info
(
"yahoo"
,
"Server reported that %s is already in the ignore list.
\n
"
,
who
);
break
;
case
3
:
purple_debug_info
(
"yahoo"
,
"Server reported that %s is not in the ignore list; could not delete
\n
"
,
who
);
case
0
:
default
:
break
;
}
}
static
void
yahoo_process_authresp
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
#ifdef TRY_WEBMESSENGER_LOGIN
YahooData
*
yd
=
gc
->
proto_data
;
#endif
/* TRY_WEBMESSENGER_LOGIN */
GSList
*
l
=
pkt
->
hash
;
int
err
=
0
;
char
*
msg
;
char
*
url
=
NULL
;
char
*
fullmsg
;
PurpleAccount
*
account
=
gc
->
account
;
PurpleConnectionError
reason
=
PURPLE_CONNECTION_ERROR_OTHER_ERROR
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
if
(
pair
->
key
==
66
)
err
=
strtol
(
pair
->
value
,
NULL
,
10
);
else
if
(
pair
->
key
==
20
)
url
=
pair
->
value
;
l
=
l
->
next
;
}
switch
(
err
)
{
case
0
:
msg
=
g_strdup
(
_
(
"Unknown error"
));
reason
=
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
break
;
case
3
:
msg
=
g_strdup
(
_
(
"Username does not exist"
));
reason
=
PURPLE_CONNECTION_ERROR_INVALID_USERNAME
;
break
;
case
13
:
#ifdef TRY_WEBMESSENGER_LOGIN
if
(
!
yd
->
wm
)
{
PurpleUtilFetchUrlData
*
url_data
;
yd
->
wm
=
TRUE
;
if
(
yd
->
fd
>=
0
)
close
(
yd
->
fd
);
if
(
gc
->
inpa
)
purple_input_remove
(
gc
->
inpa
);
url_data
=
purple_util_fetch_url
(
WEBMESSENGER_URL
,
TRUE
,
"Purple/"
VERSION
,
FALSE
,
yahoo_login_page_cb
,
gc
);
if
(
url_data
!=
NULL
)
yd
->
url_datas
=
g_slist_prepend
(
yd
->
url_datas
,
url_data
);
return
;
}
#endif
/* TRY_WEBMESSENGER_LOGIN */
if
(
!
purple_account_get_remember_password
(
account
))
purple_account_set_password
(
account
,
NULL
);
msg
=
g_strdup
(
_
(
"Invalid username or password"
));
reason
=
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
;
break
;
case
14
:
msg
=
g_strdup
(
_
(
"Your account has been locked due to too many failed login attempts."
" Please try logging into the Yahoo! website."
));
reason
=
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
;
break
;
case
52
:
/* See #9660. As much as we know, reconnecting shouldn't hurt */
purple_debug_info
(
"yahoo"
,
"Got error 52, Set to autoreconnect
\n
"
);
msg
=
g_strdup
(
_
(
"Unknown error 52. Reconnecting should fix this."
));
reason
=
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
break
;
case
1013
:
msg
=
g_strdup
(
_
(
"Error 1013: The username you have entered is invalid."
" The most common cause of this error is entering your email"
" address instead of your Yahoo! ID."
));
reason
=
PURPLE_CONNECTION_ERROR_INVALID_USERNAME
;
break
;
default
:
msg
=
g_strdup_printf
(
_
(
"Unknown error number %d. Logging into the Yahoo! website may fix this."
),
err
);
}
if
(
url
)
fullmsg
=
g_strdup_printf
(
"%s
\n
%s"
,
msg
,
url
);
else
fullmsg
=
g_strdup
(
msg
);
purple_connection_error_reason
(
gc
,
reason
,
fullmsg
);
g_free
(
msg
);
g_free
(
fullmsg
);
}
static
void
yahoo_process_addbuddy
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
int
err
=
0
;
char
*
who
=
NULL
;
char
*
temp
=
NULL
;
char
*
group
=
NULL
;
char
*
decoded_group
;
char
*
buf
;
YahooFriend
*
f
;
GSList
*
l
=
pkt
->
hash
;
YahooData
*
yd
=
gc
->
proto_data
;
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
switch
(
pair
->
key
)
{
case
66
:
err
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
case
7
:
temp
=
pair
->
value
;
break
;
case
65
:
group
=
pair
->
value
;
break
;
case
241
:
fed
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
}
l
=
l
->
next
;
}
if
(
!
temp
)
return
;
if
(
!
group
)
group
=
""
;
switch
(
fed
)
{
case
YAHOO_FEDERATION_MSN
:
who
=
g_strconcat
(
"msn/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_OCS
:
who
=
g_strconcat
(
"ocs/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_IBM
:
who
=
g_strconcat
(
"ibm/"
,
temp
,
NULL
);
break
;
case
YAHOO_FEDERATION_NONE
:
default
:
who
=
g_strdup
(
temp
);
break
;
}
if
(
!
err
||
(
err
==
2
))
{
/* 0 = ok, 2 = already on serv list */
f
=
yahoo_friend_find_or_new
(
gc
,
who
);
yahoo_update_status
(
gc
,
who
,
f
);
f
->
fed
=
fed
;
if
(
!
g_hash_table_lookup
(
yd
->
peers
,
who
)
)
{
/* we are not connected as client, so set friend to not connected */
if
(
fed
)
yahoo_friend_set_p2p_status
(
f
,
YAHOO_P2PSTATUS_DO_NOT_CONNECT
);
else
{
yahoo_friend_set_p2p_status
(
f
,
YAHOO_P2PSTATUS_NOT_CONNECTED
);
f
->
p2p_packet_sent
=
0
;
}
}
else
/* we are already connected. set friend to YAHOO_P2PSTATUS_WE_ARE_CLIENT */
yahoo_friend_set_p2p_status
(
f
,
YAHOO_P2PSTATUS_WE_ARE_CLIENT
);
g_free
(
who
);
return
;
}
decoded_group
=
yahoo_string_decode
(
gc
,
group
,
FALSE
);
buf
=
g_strdup_printf
(
_
(
"Unable to add buddy %s to group %s to the server list on account %s."
),
who
,
decoded_group
,
purple_connection_get_display_name
(
gc
));
if
(
!
purple_conv_present_error
(
who
,
purple_connection_get_account
(
gc
),
buf
))
purple_notify_error
(
gc
,
NULL
,
_
(
"Unable to add buddy to server list"
),
buf
);
g_free
(
buf
);
g_free
(
decoded_group
);
g_free
(
who
);
}
/* write pkt to the source */
static
void
yahoo_p2p_write_pkt
(
gint
source
,
struct
yahoo_packet
*
pkt
)
{
size_t
pkt_len
;
guchar
*
raw_packet
;
/*build the raw packet and send it to the host*/
pkt_len
=
yahoo_packet_build
(
pkt
,
0
,
0
,
0
,
&
raw_packet
);
if
(
write
(
source
,
raw_packet
,
pkt_len
)
!=
pkt_len
)
purple_debug_warning
(
"yahoo"
,
"p2p: couldn't write to the source
\n
"
);
g_free
(
raw_packet
);
}
static
void
yahoo_p2p_keepalive_cb
(
gpointer
key
,
gpointer
value
,
gpointer
user_data
)
{
struct
yahoo_p2p_data
*
p2p_data
=
value
;
PurpleConnection
*
gc
=
user_data
;
struct
yahoo_packet
*
pkt_to_send
;
PurpleAccount
*
account
;
YahooData
*
yd
=
gc
->
proto_data
;
account
=
purple_connection_get_account
(
gc
);
pkt_to_send
=
yahoo_packet_new
(
YAHOO_SERVICE_P2PFILEXFER
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash
(
pkt_to_send
,
"ssisi"
,
4
,
purple_normalize
(
account
,
purple_account_get_username
(
account
)),
5
,
p2p_data
->
host_username
,
241
,
0
,
/* Protocol identifier */
49
,
"PEERTOPEER"
,
13
,
7
);
yahoo_p2p_write_pkt
(
p2p_data
->
source
,
pkt_to_send
);
yahoo_packet_free
(
pkt_to_send
);
}
static
gboolean
yahoo_p2p_keepalive
(
gpointer
data
)
{
PurpleConnection
*
gc
=
data
;
YahooData
*
yd
=
gc
->
proto_data
;
g_hash_table_foreach
(
yd
->
peers
,
yahoo_p2p_keepalive_cb
,
gc
);
return
TRUE
;
}
/* destroy p2p_data associated with a peer and close p2p connection.
* g_hash_table_remove() calls this function to destroy p2p_data associated with the peer,
* call g_hash_table_remove() instead of this fucntion if peer has an entry in the table */
static
void
yahoo_p2p_disconnect_destroy_data
(
gpointer
data
)
{
struct
yahoo_p2p_data
*
p2p_data
;
YahooFriend
*
f
;
if
(
!
(
p2p_data
=
data
))
return
;
/* If friend, set him not connected */
f
=
yahoo_friend_find
(
p2p_data
->
gc
,
p2p_data
->
host_username
);
if
(
f
)
yahoo_friend_set_p2p_status
(
f
,
YAHOO_P2PSTATUS_NOT_CONNECTED
);
if
(
p2p_data
->
source
>=
0
)
close
(
p2p_data
->
source
);
if
(
p2p_data
->
input_event
>
0
)
purple_input_remove
(
p2p_data
->
input_event
);
g_free
(
p2p_data
->
host_ip
);
g_free
(
p2p_data
->
host_username
);
g_free
(
p2p_data
);
}
/* exchange of initial p2pfilexfer packets, service type YAHOO_SERVICE_P2PFILEXFER */
static
void
yahoo_p2p_process_p2pfilexfer
(
gpointer
data
,
gint
source
,
struct
yahoo_packet
*
pkt
)
{
struct
yahoo_p2p_data
*
p2p_data
;
char
*
who
=
NULL
;
GSList
*
l
=
pkt
->
hash
;
struct
yahoo_packet
*
pkt_to_send
;
PurpleAccount
*
account
;
int
val_13_to_send
=
0
;
YahooData
*
yd
;
YahooFriend
*
f
;
if
(
!
(
p2p_data
=
data
))
return
;
yd
=
p2p_data
->
gc
->
proto_data
;
/* lets see whats in the packet */
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
switch
(
pair
->
key
)
{
case
4
:
who
=
pair
->
value
;
if
(
strncmp
(
who
,
p2p_data
->
host_username
,
strlen
(
p2p_data
->
host_username
))
!=
0
)
{
/* from whom are we receiving the packets ?? */
purple_debug_warning
(
"yahoo"
,
"p2p: received data from wrong user
\n
"
);
return
;
}
break
;
case
13
:
p2p_data
->
val_13
=
strtol
(
pair
->
value
,
NULL
,
10
);
/* Value should be 5-7 */
break
;
/* case 5, 49 look laters, no use right now */
}
l
=
l
->
next
;
}
account
=
purple_connection_get_account
(
p2p_data
->
gc
);
/* key_13: sort of a counter.
* WHEN WE ARE CLIENT: yahoo server sends val_13 = 0, we send to peer val_13 = 1, receive back val_13 = 5,
* we send val_13=6, receive val_13=7, we send val_13=7, HALT. Keep sending val_13 = 7 as keep alive.
* WHEN WE ARE SERVER: we send val_13 = 0 to yahoo server, peer sends us val_13 = 1, we send val_13 = 5,
* receive val_13 = 6, send val_13 = 7, receive val_13 = 7. HALT. Keep sending val_13 = 7 as keep alive. */
switch
(
p2p_data
->
val_13
)
{
case
1
:
val_13_to_send
=
5
;
break
;
case
5
:
val_13_to_send
=
6
;
break
;
case
6
:
val_13_to_send
=
7
;
break
;
case
7
:
if
(
g_hash_table_lookup
(
yd
->
peers
,
p2p_data
->
host_username
)
)
return
;
val_13_to_send
=
7
;
break
;
default
:
purple_debug_warning
(
"yahoo"
,
"p2p:Unknown value for key 13
\n
"
);
return
;
}
/* Build the yahoo packet */
pkt_to_send
=
yahoo_packet_new
(
YAHOO_SERVICE_P2PFILEXFER
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash
(
pkt_to_send
,
"ssisi"
,
4
,
purple_normalize
(
account
,
purple_account_get_username
(
account
)),
5
,
p2p_data
->
host_username
,
241
,
0
,
/* Protocol identifier */
49
,
"PEERTOPEER"
,
13
,
val_13_to_send
);
/* build the raw packet and send it to the host */
yahoo_p2p_write_pkt
(
source
,
pkt_to_send
);
yahoo_packet_free
(
pkt_to_send
);
if
(
val_13_to_send
==
7
)
if
(
!
g_hash_table_lookup
(
yd
->
peers
,
p2p_data
->
host_username
)
)
{
g_hash_table_insert
(
yd
->
peers
,
g_strdup
(
p2p_data
->
host_username
),
p2p_data
);
/* If the peer is a friend, set him connected */
f
=
yahoo_friend_find
(
p2p_data
->
gc
,
p2p_data
->
host_username
);
if
(
f
)
{
if
(
p2p_data
->
connection_type
==
YAHOO_P2P_WE_ARE_SERVER
)
{
p2p_data
->
session_id
=
f
->
session_id
;
yahoo_friend_set_p2p_status
(
f
,
YAHOO_P2PSTATUS_WE_ARE_SERVER
);
}
else
yahoo_friend_set_p2p_status
(
f
,
YAHOO_P2PSTATUS_WE_ARE_CLIENT
);
}
}
}
/* callback function associated with receiving of data, not considering receipt of multiple YMSG packets in a single TCP packet */
static
void
yahoo_p2p_read_pkt_cb
(
gpointer
data
,
gint
source
,
PurpleInputCondition
cond
)
{
guchar
buf
[
1024
];
/* is it safe to assume a fixed array length of 1024 ?? */
int
len
;
int
pos
=
0
;
int
pktlen
;
struct
yahoo_packet
*
pkt
;
guchar
*
start
;
struct
yahoo_p2p_data
*
p2p_data
;
YahooData
*
yd
;
if
(
!
(
p2p_data
=
data
))
return
;
yd
=
p2p_data
->
gc
->
proto_data
;
len
=
read
(
source
,
buf
,
sizeof
(
buf
));
if
((
len
<
0
)
&&
((
errno
==
EAGAIN
)
||
(
errno
==
EWOULDBLOCK
)))
return
;
/* No Worries*/
else
if
(
len
<=
0
)
{
purple_debug_warning
(
"yahoo"
,
"p2p: Error in connection, or host disconnected
\n
"
);
/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
if
(
g_hash_table_lookup
(
yd
->
peers
,
p2p_data
->
host_username
)
)
g_hash_table_remove
(
yd
->
peers
,
p2p_data
->
host_username
);
else
yahoo_p2p_disconnect_destroy_data
(
data
);
return
;
}
/* TODO: It looks like there's a bug here (and above) where an incorrect
* assumtion is being made that the buffer will be added to when this
* is next called, but that's not really the case! */
if
(
len
<
YAHOO_PACKET_HDRLEN
)
return
;
if
(
strncmp
((
char
*
)
buf
,
"YMSG"
,
4
)
!=
0
)
{
/* Not a YMSG packet */
purple_debug_warning
(
"yahoo"
,
"p2p: Got something other than YMSG packet
\n
"
);
start
=
(
guchar
*
)
g_strstr_len
((
char
*
)
buf
+
1
,
len
-
1
,
"YMSG"
);
if
(
start
==
NULL
)
{
/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
if
(
g_hash_table_lookup
(
yd
->
peers
,
p2p_data
->
host_username
))
g_hash_table_remove
(
yd
->
peers
,
p2p_data
->
host_username
);
else
yahoo_p2p_disconnect_destroy_data
(
data
);
return
;
}
purple_debug_warning
(
"yahoo"
,
"p2p: Got something other than YMSG packet
\n
"
);
len
-=
(
start
-
buf
);
g_memmove
(
buf
,
start
,
len
);
}
pos
+=
4
;
/* YMSG */
pos
+=
2
;
pos
+=
2
;
pktlen
=
yahoo_get16
(
buf
+
pos
);
pos
+=
2
;
if
(
len
<
(
YAHOO_PACKET_HDRLEN
+
pktlen
))
{
purple_debug_error
(
"yahoo"
,
"p2p: packet length(%d) > buffer length(%d)
\n
"
,
pktlen
,
(
len
-
pos
));
/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
if
(
g_hash_table_lookup
(
yd
->
peers
,
p2p_data
->
host_username
))
g_hash_table_remove
(
yd
->
peers
,
p2p_data
->
host_username
);
else
yahoo_p2p_disconnect_destroy_data
(
data
);
return
;
}
else
purple_debug_misc
(
"yahoo"
,
"p2p: %d bytes to read
\n
"
,
pktlen
);
pkt
=
yahoo_packet_new
(
0
,
0
,
0
);
pkt
->
service
=
yahoo_get16
(
buf
+
pos
);
pos
+=
2
;
pkt
->
status
=
yahoo_get32
(
buf
+
pos
);
pos
+=
4
;
pkt
->
id
=
yahoo_get32
(
buf
+
pos
);
pos
+=
4
;
purple_debug_misc
(
"yahoo"
,
"p2p: Yahoo Service: 0x%02x Status: %d
\n
"
,
pkt
->
service
,
pkt
->
status
);
yahoo_packet_read
(
pkt
,
buf
+
pos
,
pktlen
);
/* packet processing */
switch
(
pkt
->
service
)
{
case
YAHOO_SERVICE_P2PFILEXFER
:
yahoo_p2p_process_p2pfilexfer
(
data
,
source
,
pkt
);
break
;
case
YAHOO_SERVICE_MESSAGE
:
yahoo_process_message
(
p2p_data
->
gc
,
pkt
,
YAHOO_PKT_TYPE_P2P
);
break
;
case
YAHOO_SERVICE_NOTIFY
:
yahoo_process_notify
(
p2p_data
->
gc
,
pkt
,
YAHOO_PKT_TYPE_P2P
);
break
;
default
:
purple_debug_warning
(
"yahoo"
,
"p2p: p2p service %d Unhandled
\n
"
,
pkt
->
service
);
}
yahoo_packet_free
(
pkt
);
}
static
void
yahoo_p2p_server_send_connected_cb
(
gpointer
data
,
gint
source
,
PurpleInputCondition
cond
)
{
int
acceptfd
;
struct
yahoo_p2p_data
*
p2p_data
;
YahooData
*
yd
;
if
(
!
(
p2p_data
=
data
))
return
;
yd
=
p2p_data
->
gc
->
proto_data
;
acceptfd
=
accept
(
source
,
NULL
,
0
);
if
(
acceptfd
==
-1
&&
(
errno
==
EAGAIN
||
errno
==
EWOULDBLOCK
))
return
;
else
if
(
acceptfd
==
-1
)
{
purple_debug_warning
(
"yahoo"
,
"yahoo_p2p_server_send_connected_cb: accept: %s
\n
"
,
g_strerror
(
errno
));
yahoo_p2p_disconnect_destroy_data
(
data
);
return
;
}
/* remove timeout */
if
(
yd
->
yahoo_p2p_server_timeout_handle
)
{
purple_timeout_remove
(
yd
->
yahoo_p2p_server_timeout_handle
);
yd
->
yahoo_p2p_server_timeout_handle
=
0
;
}
/* remove watcher and close p2p server */
if
(
yd
->
yahoo_p2p_server_watcher
)
{
purple_input_remove
(
yd
->
yahoo_p2p_server_watcher
);
yd
->
yahoo_p2p_server_watcher
=
0
;
}
if
(
yd
->
yahoo_local_p2p_server_fd
>=
0
)
{
close
(
yd
->
yahoo_local_p2p_server_fd
);
yd
->
yahoo_local_p2p_server_fd
=
-1
;
}
/* Add an Input Read event to the file descriptor */
p2p_data
->
input_event
=
purple_input_add
(
acceptfd
,
PURPLE_INPUT_READ
,
yahoo_p2p_read_pkt_cb
,
data
);
p2p_data
->
source
=
acceptfd
;
}
static
gboolean
yahoo_cancel_p2p_server_listen_cb
(
gpointer
data
)
{
struct
yahoo_p2p_data
*
p2p_data
;
YahooData
*
yd
;
if
(
!
(
p2p_data
=
data
))
return
FALSE
;
yd
=
p2p_data
->
gc
->
proto_data
;
purple_debug_warning
(
"yahoo"
,
"yahoo p2p server timeout, peer failed to connect
\n
"
);
yahoo_p2p_disconnect_destroy_data
(
data
);
purple_input_remove
(
yd
->
yahoo_p2p_server_watcher
);
yd
->
yahoo_p2p_server_watcher
=
0
;
close
(
yd
->
yahoo_local_p2p_server_fd
);
yd
->
yahoo_local_p2p_server_fd
=
-1
;
yd
->
yahoo_p2p_server_timeout_handle
=
0
;
return
FALSE
;
}
static
void
yahoo_p2p_server_listen_cb
(
int
listenfd
,
gpointer
data
)
{
struct
yahoo_p2p_data
*
p2p_data
;
YahooData
*
yd
;
if
(
!
(
p2p_data
=
data
))
return
;
yd
=
p2p_data
->
gc
->
proto_data
;
yd
->
listen_data
=
NULL
;
if
(
listenfd
==
-1
)
{
purple_debug_warning
(
"yahoo"
,
"p2p: error starting p2p server
\n
"
);
yahoo_p2p_disconnect_destroy_data
(
data
);
return
;
}
/* Add an Input Read event to the file descriptor */
yd
->
yahoo_local_p2p_server_fd
=
listenfd
;
yd
->
yahoo_p2p_server_watcher
=
purple_input_add
(
listenfd
,
PURPLE_INPUT_READ
,
yahoo_p2p_server_send_connected_cb
,
data
);
/* add timeout */
yd
->
yahoo_p2p_server_timeout_handle
=
purple_timeout_add_seconds
(
YAHOO_P2P_SERVER_TIMEOUT
,
yahoo_cancel_p2p_server_listen_cb
,
data
);
}
/* send p2p pkt containing our encoded ip, asking peer to connect to us */
void
yahoo_send_p2p_pkt
(
PurpleConnection
*
gc
,
const
char
*
who
,
int
val_13
)
{
const
char
*
public_ip
;
guint32
temp
[
4
];
guint32
ip
;
char
temp_str
[
100
];
gchar
*
base64_ip
=
NULL
;
YahooFriend
*
f
;
struct
yahoo_packet
*
pkt
;
PurpleAccount
*
account
;
YahooData
*
yd
=
gc
->
proto_data
;
struct
yahoo_p2p_data
*
p2p_data
;
const
char
*
norm_username
;
f
=
yahoo_friend_find
(
gc
,
who
);
account
=
purple_connection_get_account
(
gc
);
/* Do not send invitation if already listening for other connection */
if
(
yd
->
yahoo_local_p2p_server_fd
>=
0
)
return
;
/* One shouldn't try to connect to self */
if
(
strcmp
(
purple_normalize
(
account
,
purple_account_get_username
(
account
)),
who
)
==
0
)
return
;
/* send packet to only those friends who arent p2p connected and to whom we havent already sent. Do not send if this condition doesn't hold good */
if
(
!
(
f
&&
(
yahoo_friend_get_p2p_status
(
f
)
==
YAHOO_P2PSTATUS_NOT_CONNECTED
)
&&
(
f
->
p2p_packet_sent
==
0
))
)
return
;
/* Dont send p2p packet to buddies of other protocols */
if
(
f
->
fed
)
return
;
/* Finally, don't try to connect to buddies not online or on sms */
if
(
(
f
->
status
==
YAHOO_STATUS_OFFLINE
)
||
f
->
sms
)
return
;
public_ip
=
purple_network_get_public_ip
();
if
(
(
sscanf
(
public_ip
,
"%u.%u.%u.%u"
,
&
temp
[
0
],
&
temp
[
1
],
&
temp
[
2
],
&
temp
[
3
]))
!=
4
)
return
;
ip
=
(
temp
[
3
]
<<
24
)
|
(
temp
[
2
]
<<
16
)
|
(
temp
[
1
]
<<
8
)
|
temp
[
0
];
sprintf
(
temp_str
,
"%d"
,
ip
);
base64_ip
=
purple_base64_encode
(
(
guchar
*
)
temp_str
,
strlen
(
temp_str
)
);
norm_username
=
purple_normalize
(
account
,
purple_account_get_username
(
account
));
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_PEERTOPEER
,
YAHOO_STATUS_AVAILABLE
,
0
);
yahoo_packet_hash
(
pkt
,
"sssissis"
,
1
,
norm_username
,
4
,
norm_username
,
12
,
base64_ip
,
/* base64 encode ip */
61
,
0
,
/* To-do : figure out what is 61 for?? */
2
,
""
,
5
,
who
,
13
,
val_13
,
49
,
"PEERTOPEER"
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
f
->
p2p_packet_sent
=
1
;
/* set p2p_packet_sent to sent */
p2p_data
=
g_new0
(
struct
yahoo_p2p_data
,
1
);
p2p_data
->
gc
=
gc
;
p2p_data
->
host_ip
=
NULL
;
p2p_data
->
host_username
=
g_strdup
(
who
);
p2p_data
->
val_13
=
val_13
;
p2p_data
->
connection_type
=
YAHOO_P2P_WE_ARE_SERVER
;
p2p_data
->
source
=
-1
;
/* FIXME: If the port is already used, purple_network_listener returns NULL and old listener won't be canceled
* in yahoo_close function. */
if
(
yd
->
listen_data
)
purple_debug_warning
(
"yahoo"
,
"p2p: Failed to create p2p server - server already exists
\n
"
);
else
{
yd
->
listen_data
=
purple_network_listen
(
YAHOO_PAGER_PORT_P2P
,
SOCK_STREAM
,
yahoo_p2p_server_listen_cb
,
p2p_data
);
if
(
yd
->
listen_data
==
NULL
)
purple_debug_warning
(
"yahoo"
,
"p2p: Failed to created p2p server
\n
"
);
}
g_free
(
base64_ip
);
}
/* function called when connection to p2p host is setup */
static
void
yahoo_p2p_init_cb
(
gpointer
data
,
gint
source
,
const
gchar
*
error_message
)
{
struct
yahoo_p2p_data
*
p2p_data
;
struct
yahoo_packet
*
pkt_to_send
;
PurpleAccount
*
account
;
YahooData
*
yd
;
p2p_data
=
data
;
yd
=
p2p_data
->
gc
->
proto_data
;
if
(
error_message
!=
NULL
)
{
purple_debug_warning
(
"yahoo"
,
"p2p: %s
\n
"
,
error_message
);
yahoo_send_p2p_pkt
(
p2p_data
->
gc
,
p2p_data
->
host_username
,
2
);
/* send p2p init packet with val_13=2 */
yahoo_p2p_disconnect_destroy_data
(
p2p_data
);
return
;
}
/* Add an Input Read event to the file descriptor */
p2p_data
->
input_event
=
purple_input_add
(
source
,
PURPLE_INPUT_READ
,
yahoo_p2p_read_pkt_cb
,
data
);
p2p_data
->
source
=
source
;
account
=
purple_connection_get_account
(
p2p_data
->
gc
);
/* Build the yahoo packet */
pkt_to_send
=
yahoo_packet_new
(
YAHOO_SERVICE_P2PFILEXFER
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash
(
pkt_to_send
,
"ssisi"
,
4
,
purple_normalize
(
account
,
purple_account_get_username
(
account
)),
5
,
p2p_data
->
host_username
,
241
,
0
,
/* Protocol identifier */
49
,
"PEERTOPEER"
,
13
,
1
);
/* we receive key13= 0 or 2, we send key13=1 */
yahoo_p2p_write_pkt
(
source
,
pkt_to_send
);
/* build raw packet and send */
yahoo_packet_free
(
pkt_to_send
);
}
static
void
yahoo_process_p2p
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
GSList
*
l
=
pkt
->
hash
;
char
*
who
=
NULL
;
char
*
base64
=
NULL
;
guchar
*
decoded
;
gsize
len
;
gint
val_13
=
0
;
gint
val_11
=
0
;
PurpleAccount
*
account
;
YahooFriend
*
f
;
/* if status is not YAHOO_STATUS_BRB or YAHOO_STATUS_P2P, the packet bounced back,
* so it contains our own ip */
if
(
pkt
->
status
!=
YAHOO_STATUS_BRB
&&
pkt
->
status
!=
YAHOO_STATUS_P2P
)
return
;
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
switch
(
pair
->
key
)
{
case
5
:
/* our identity */
break
;
case
4
:
who
=
pair
->
value
;
break
;
case
1
:
/* who again, the master identity this time? */
break
;
case
12
:
base64
=
pair
->
value
;
/* so, this is an ip address. in base64. decoded it's in ascii.
after strtol, it's in reversed byte order. Who thought this up?*/
break
;
case
13
:
val_13
=
strtol
(
pair
->
value
,
NULL
,
10
);
break
;
case
11
:
val_11
=
strtol
(
pair
->
value
,
NULL
,
10
);
/* session id of peer */
if
(
(
f
=
yahoo_friend_find
(
gc
,
who
))
)
f
->
session_id
=
val_11
;
break
;
/*
TODO: figure these out
yahoo: Key: 61 Value: 0
yahoo: Key: 2 Value:
yahoo: Key: 13 Value: 0 packet count ??
yahoo: Key: 49 Value: PEERTOPEER
yahoo: Key: 140 Value: 1
*/
}
l
=
l
->
next
;
}
if
(
base64
)
{
guint32
ip
;
YahooFriend
*
f
;
char
*
host_ip
,
*
tmp
;
struct
yahoo_p2p_data
*
p2p_data
;
decoded
=
purple_base64_decode
(
base64
,
&
len
);
if
(
decoded
==
NULL
)
{
purple_debug_info
(
"yahoo"
,
"p2p: Unable to decode base64 IP (%s)
\n
"
,
base64
);
return
;
}
tmp
=
purple_str_binary_to_ascii
(
decoded
,
len
);
purple_debug_info
(
"yahoo"
,
"Got P2P service packet (from server): who = %s, ip = %s
\n
"
,
who
,
tmp
);
g_free
(
tmp
);
ip
=
strtol
((
gchar
*
)
decoded
,
NULL
,
10
);
g_free
(
decoded
);
host_ip
=
g_strdup_printf
(
"%u.%u.%u.%u"
,
ip
&
0xff
,
(
ip
>>
8
)
&
0xff
,
(
ip
>>
16
)
&
0xff
,
(
ip
>>
24
)
&
0xff
);
f
=
yahoo_friend_find
(
gc
,
who
);
if
(
f
)
yahoo_friend_set_ip
(
f
,
host_ip
);
purple_debug_info
(
"yahoo"
,
"IP : %s
\n
"
,
host_ip
);
account
=
purple_connection_get_account
(
gc
);
if
(
val_11
==
0
)
{
if
(
!
f
)
return
;
else
val_11
=
f
->
session_id
;
}
p2p_data
=
g_new0
(
struct
yahoo_p2p_data
,
1
);
p2p_data
->
host_username
=
g_strdup
(
who
);
p2p_data
->
val_13
=
val_13
;
p2p_data
->
session_id
=
val_11
;
p2p_data
->
host_ip
=
host_ip
;
p2p_data
->
gc
=
gc
;
p2p_data
->
connection_type
=
YAHOO_P2P_WE_ARE_CLIENT
;
p2p_data
->
source
=
-1
;
/* connect to host */
if
((
purple_proxy_connect
(
gc
,
account
,
host_ip
,
YAHOO_PAGER_PORT_P2P
,
yahoo_p2p_init_cb
,
p2p_data
))
==
NULL
)
{
purple_debug_info
(
"yahoo"
,
"p2p: Connection to %s failed
\n
"
,
host_ip
);
g_free
(
p2p_data
->
host_ip
);
g_free
(
p2p_data
->
host_username
);
g_free
(
p2p_data
);
}
}
}
static
void
yahoo_process_audible
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
PurpleAccount
*
account
;
char
*
who
=
NULL
,
*
msg
=
NULL
,
*
id
=
NULL
;
GSList
*
l
=
pkt
->
hash
;
account
=
purple_connection_get_account
(
gc
);
while
(
l
)
{
struct
yahoo_pair
*
pair
=
l
->
data
;
switch
(
pair
->
key
)
{
case
4
:
who
=
pair
->
value
;
break
;
case
5
:
/* us */
break
;
case
230
:
/* the audible, in foo.locale.bar.baz format
eg: base.tw.smiley.smiley43 */
id
=
pair
->
value
;
break
;
case
231
:
/* the text of the audible */
msg
=
pair
->
value
;
break
;
case
232
:
/* SHA-1 hash of audible SWF file (eg: 4e8691499d9c0fb8374478ff9720f4a9ea4a4915) */
break
;
}
l
=
l
->
next
;
}
if
(
!
msg
)
msg
=
id
;
if
(
!
who
||
!
msg
)
return
;
if
(
!
g_utf8_validate
(
msg
,
-1
,
NULL
))
{
purple_debug_misc
(
"yahoo"
,
"Warning, nonutf8 audible, ignoring!
\n
"
);
return
;
}
if
(
!
purple_privacy_check
(
account
,
who
))
{
purple_debug_misc
(
"yahoo"
,
"Audible message from %s for %s dropped!
\n
"
,
purple_account_get_username
(
account
),
who
);
return
;
}
if
(
id
)
{
/* "http://l.yimg.com/pu/dl/aud/"+locale+"/"+id+".swf" */
char
**
audible_locale
=
g_strsplit
(
id
,
"."
,
0
);
char
*
buf
=
g_strdup_printf
(
_
(
"[ Audible %s/%s/%s.swf ] %s"
),
YAHOO_AUDIBLE_URL
,
audible_locale
[
1
],
id
,
msg
);
g_strfreev
(
audible_locale
);
serv_got_im
(
gc
,
who
,
buf
,
0
,
time
(
NULL
));
g_free
(
buf
);
}
else
serv_got_im
(
gc
,
who
,
msg
,
0
,
time
(
NULL
));
}
static
void
yahoo_packet_process
(
PurpleConnection
*
gc
,
struct
yahoo_packet
*
pkt
)
{
switch
(
pkt
->
service
)
{
case
YAHOO_SERVICE_LOGON
:
case
YAHOO_SERVICE_LOGOFF
:
case
YAHOO_SERVICE_ISAWAY
:
case
YAHOO_SERVICE_ISBACK
:
case
YAHOO_SERVICE_GAMELOGON
:
case
YAHOO_SERVICE_GAMELOGOFF
:
case
YAHOO_SERVICE_CHATLOGON
:
case
YAHOO_SERVICE_CHATLOGOFF
:
case
YAHOO_SERVICE_Y6_STATUS_UPDATE
:
case
YAHOO_SERVICE_STATUS_15
:
yahoo_process_status
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_NOTIFY
:
yahoo_process_notify
(
gc
,
pkt
,
YAHOO_PKT_TYPE_SERVER
);
break
;
case
YAHOO_SERVICE_MESSAGE
:
case
YAHOO_SERVICE_GAMEMSG
:
case
YAHOO_SERVICE_CHATMSG
:
yahoo_process_message
(
gc
,
pkt
,
YAHOO_PKT_TYPE_SERVER
);
break
;
case
YAHOO_SERVICE_SYSMESSAGE
:
yahoo_process_sysmessage
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_NEWMAIL
:
yahoo_process_mail
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_NEWCONTACT
:
yahoo_process_contact
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_AUTHRESP
:
yahoo_process_authresp
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_LIST
:
yahoo_process_list
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_LIST_15
:
yahoo_process_list_15
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_AUTH
:
yahoo_process_auth
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_AUTH_REQ_15
:
yahoo_buddy_auth_req_15
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_ADDBUDDY
:
yahoo_process_addbuddy
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_IGNORECONTACT
:
yahoo_process_ignore
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CONFINVITE
:
case
YAHOO_SERVICE_CONFADDINVITE
:
yahoo_process_conference_invite
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CONFDECLINE
:
yahoo_process_conference_decline
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CONFLOGON
:
yahoo_process_conference_logon
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CONFLOGOFF
:
yahoo_process_conference_logoff
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CONFMSG
:
yahoo_process_conference_message
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CHATONLINE
:
yahoo_process_chat_online
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CHATLOGOUT
:
yahoo_process_chat_logout
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CHATGOTO
:
yahoo_process_chat_goto
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CHATJOIN
:
yahoo_process_chat_join
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CHATLEAVE
:
/* XXX is this right? */
case
YAHOO_SERVICE_CHATEXIT
:
yahoo_process_chat_exit
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CHATINVITE
:
/* XXX never seen this one, might not do it right */
case
YAHOO_SERVICE_CHATADDINVITE
:
yahoo_process_chat_addinvite
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_COMMENT
:
yahoo_process_chat_message
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_PRESENCE_PERM
:
case
YAHOO_SERVICE_PRESENCE_SESSION
:
yahoo_process_presence
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_P2PFILEXFER
:
/* This case had no break and continued; thus keeping it this way.*/
yahoo_process_p2p
(
gc
,
pkt
);
/* P2PFILEXFER handled the same way as process_p2p */
yahoo_process_p2pfilexfer
(
gc
,
pkt
);
/* redundant ??, need to have a break now */
case
YAHOO_SERVICE_FILETRANSFER
:
yahoo_process_filetransfer
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_PEERTOPEER
:
yahoo_process_p2p
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_PICTURE
:
yahoo_process_picture
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_PICTURE_CHECKSUM
:
yahoo_process_picture_checksum
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_PICTURE_UPLOAD
:
yahoo_process_picture_upload
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_PICTURE_UPDATE
:
case
YAHOO_SERVICE_AVATAR_UPDATE
:
yahoo_process_avatar_update
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_AUDIBLE
:
yahoo_process_audible
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_CONTACT_DETAILS
:
yahoo_process_contact_details
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_FILETRANS_15
:
yahoo_process_filetrans_15
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_FILETRANS_INFO_15
:
yahoo_process_filetrans_info_15
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_FILETRANS_ACC_15
:
yahoo_process_filetrans_acc_15
(
gc
,
pkt
);
break
;
case
YAHOO_SERVICE_SMS_MSG
:
yahoo_process_sms_message
(
gc
,
pkt
);
break
;
default
:
purple_debug_error
(
"yahoo"
,
"Unhandled service 0x%02x
\n
"
,
pkt
->
service
);
break
;
}
}
static
void
yahoo_pending
(
gpointer
data
,
gint
source
,
PurpleInputCondition
cond
)
{
PurpleConnection
*
gc
=
data
;
YahooData
*
yd
=
gc
->
proto_data
;
char
buf
[
1024
];
int
len
;
len
=
read
(
yd
->
fd
,
buf
,
sizeof
(
buf
));
if
(
len
<
0
)
{
gchar
*
tmp
;
if
(
errno
==
EAGAIN
)
/* No worries */
return
;
tmp
=
g_strdup_printf
(
_
(
"Lost connection with server: %s"
),
g_strerror
(
errno
));
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
tmp
);
g_free
(
tmp
);
return
;
}
else
if
(
len
==
0
)
{
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Server closed the connection"
));
return
;
}
gc
->
last_received
=
time
(
NULL
);
yd
->
rxqueue
=
g_realloc
(
yd
->
rxqueue
,
len
+
yd
->
rxlen
);
memcpy
(
yd
->
rxqueue
+
yd
->
rxlen
,
buf
,
len
);
yd
->
rxlen
+=
len
;
while
(
1
)
{
struct
yahoo_packet
*
pkt
;
int
pos
=
0
;
int
pktlen
;
if
(
yd
->
rxlen
<
YAHOO_PACKET_HDRLEN
)
return
;
if
(
strncmp
((
char
*
)
yd
->
rxqueue
,
"YMSG"
,
MIN
(
4
,
yd
->
rxlen
))
!=
0
)
{
/* HEY! This isn't even a YMSG packet. What
* are you trying to pull? */
guchar
*
start
;
purple_debug_warning
(
"yahoo"
,
"Error in YMSG stream, got something not a YMSG packet!
\n
"
);
start
=
memchr
(
yd
->
rxqueue
+
1
,
'Y'
,
yd
->
rxlen
-
1
);
if
(
start
)
{
g_memmove
(
yd
->
rxqueue
,
start
,
yd
->
rxlen
-
(
start
-
yd
->
rxqueue
));
yd
->
rxlen
-=
start
-
yd
->
rxqueue
;
continue
;
}
else
{
g_free
(
yd
->
rxqueue
);
yd
->
rxqueue
=
NULL
;
yd
->
rxlen
=
0
;
return
;
}
}
pos
+=
4
;
/* YMSG */
pos
+=
2
;
pos
+=
2
;
pktlen
=
yahoo_get16
(
yd
->
rxqueue
+
pos
);
pos
+=
2
;
purple_debug_misc
(
"yahoo"
,
"%d bytes to read, rxlen is %d
\n
"
,
pktlen
,
yd
->
rxlen
);
if
(
yd
->
rxlen
<
(
YAHOO_PACKET_HDRLEN
+
pktlen
))
return
;
yahoo_packet_dump
(
yd
->
rxqueue
,
YAHOO_PACKET_HDRLEN
+
pktlen
);
pkt
=
yahoo_packet_new
(
0
,
0
,
0
);
pkt
->
service
=
yahoo_get16
(
yd
->
rxqueue
+
pos
);
pos
+=
2
;
pkt
->
status
=
yahoo_get32
(
yd
->
rxqueue
+
pos
);
pos
+=
4
;
purple_debug_misc
(
"yahoo"
,
"Yahoo Service: 0x%02x Status: %d
\n
"
,
pkt
->
service
,
pkt
->
status
);
pkt
->
id
=
yahoo_get32
(
yd
->
rxqueue
+
pos
);
pos
+=
4
;
yahoo_packet_read
(
pkt
,
yd
->
rxqueue
+
pos
,
pktlen
);
yd
->
rxlen
-=
YAHOO_PACKET_HDRLEN
+
pktlen
;
if
(
yd
->
rxlen
)
{
guchar
*
tmp
=
g_memdup
(
yd
->
rxqueue
+
YAHOO_PACKET_HDRLEN
+
pktlen
,
yd
->
rxlen
);
g_free
(
yd
->
rxqueue
);
yd
->
rxqueue
=
tmp
;
}
else
{
g_free
(
yd
->
rxqueue
);
yd
->
rxqueue
=
NULL
;
}
yahoo_packet_process
(
gc
,
pkt
);
yahoo_packet_free
(
pkt
);
}
}
static
void
yahoo_got_connected
(
gpointer
data
,
gint
source
,
const
gchar
*
error_message
)
{
PurpleConnection
*
gc
=
data
;
YahooData
*
yd
;
struct
yahoo_packet
*
pkt
;
if
(
source
<
0
)
{
gchar
*
tmp
;
tmp
=
g_strdup_printf
(
_
(
"Unable to connect: %s"
),
error_message
);
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
tmp
);
g_free
(
tmp
);
return
;
}
yd
=
gc
->
proto_data
;
yd
->
fd
=
source
;
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_AUTH
,
yd
->
current_status
,
yd
->
session_id
);
yahoo_packet_hash_str
(
pkt
,
1
,
purple_normalize
(
gc
->
account
,
purple_account_get_username
(
purple_connection_get_account
(
gc
))));
yahoo_packet_send_and_free
(
pkt
,
yd
);
gc
->
inpa
=
purple_input_add
(
yd
->
fd
,
PURPLE_INPUT_READ
,
yahoo_pending
,
gc
);
}
#ifdef TRY_WEBMESSENGER_LOGIN
static
void
yahoo_got_web_connected
(
gpointer
data
,
gint
source
,
const
gchar
*
error_message
)
{
PurpleConnection
*
gc
=
data
;
YahooData
*
yd
;
struct
yahoo_packet
*
pkt
;
if
(
source
<
0
)
{
gchar
*
tmp
;
tmp
=
g_strdup_printf
(
_
(
"Unable to connect: %s"
),
error_message
);
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
tmp
);
g_free
(
tmp
);
return
;
}
yd
=
gc
->
proto_data
;
yd
->
fd
=
source
;
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_WEBLOGIN
,
YAHOO_STATUS_WEBLOGIN
,
yd
->
session_id
);
yahoo_packet_hash
(
pkt
,
"sss"
,
0
,
purple_normalize
(
gc
->
account
,
purple_account_get_username
(
purple_connection_get_account
(
gc
))),
1
,
purple_normalize
(
gc
->
account
,
purple_account_get_username
(
purple_connection_get_account
(
gc
))),
6
,
yd
->
auth
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
g_free
(
yd
->
auth
);
gc
->
inpa
=
purple_input_add
(
yd
->
fd
,
PURPLE_INPUT_READ
,
yahoo_pending
,
gc
);
}
static
void
yahoo_web_pending
(
gpointer
data
,
gint
source
,
PurpleInputCondition
cond
)
{
PurpleConnection
*
gc
=
data
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
YahooData
*
yd
=
gc
->
proto_data
;
char
bufread
[
2048
],
*
i
=
bufread
,
*
buf
=
bufread
;
int
len
;
GString
*
s
;
len
=
read
(
source
,
bufread
,
sizeof
(
bufread
)
-
1
);
if
(
len
<
0
)
{
gchar
*
tmp
;
if
(
errno
==
EAGAIN
)
/* No worries */
return
;
tmp
=
g_strdup_printf
(
_
(
"Lost connection with server: %s"
),
g_strerror
(
errno
));
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
tmp
);
g_free
(
tmp
);
return
;
}
else
if
(
len
==
0
)
{
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Server closed the connection"
));
return
;
}
if
(
yd
->
rxlen
>
0
||
!
g_strstr_len
(
buf
,
len
,
"
\r\n\r\n
"
))
{
yd
->
rxqueue
=
g_realloc
(
yd
->
rxqueue
,
yd
->
rxlen
+
len
+
1
);
memcpy
(
yd
->
rxqueue
+
yd
->
rxlen
,
buf
,
len
);
yd
->
rxlen
+=
len
;
i
=
buf
=
(
char
*
)
yd
->
rxqueue
;
len
=
yd
->
rxlen
;
}
buf
[
len
]
=
'\0'
;
if
((
strncmp
(
buf
,
"HTTP/1.0 302"
,
strlen
(
"HTTP/1.0 302"
))
&&
strncmp
(
buf
,
"HTTP/1.1 302"
,
strlen
(
"HTTP/1.1 302"
))))
{
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Received unexpected HTTP response from server"
));
purple_debug_misc
(
"yahoo"
,
"Unexpected HTTP response: %s
\n
"
,
buf
);
return
;
}
s
=
g_string_sized_new
(
len
);
while
((
i
=
strstr
(
i
,
"Set-Cookie: "
)))
{
i
+=
strlen
(
"Set-Cookie: "
);
for
(;
*
i
!=
';'
&&
*
i
!=
'\0'
;
i
++
)
g_string_append_c
(
s
,
*
i
);
g_string_append
(
s
,
"; "
);
/* Should these cookies be included too when trying for xfer?
* It seems to work without these
*/
}
yd
->
auth
=
g_string_free
(
s
,
FALSE
);
purple_input_remove
(
gc
->
inpa
);
close
(
source
);
g_free
(
yd
->
rxqueue
);
yd
->
rxqueue
=
NULL
;
yd
->
rxlen
=
0
;
/* Now we have our cookies to login with. I'll go get the milk. */
if
(
purple_proxy_connect
(
gc
,
account
,
"wcs2.msg.dcn.yahoo.com"
,
purple_account_get_int
(
account
,
"port"
,
YAHOO_PAGER_PORT
),
yahoo_got_web_connected
,
gc
)
==
NULL
)
{
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to connect"
));
return
;
}
}
static
void
yahoo_got_cookies_send_cb
(
gpointer
data
,
gint
source
,
PurpleInputCondition
cond
)
{
PurpleConnection
*
gc
;
YahooData
*
yd
;
int
written
,
remaining
;
gc
=
data
;
yd
=
gc
->
proto_data
;
remaining
=
strlen
(
yd
->
auth
)
-
yd
->
auth_written
;
written
=
write
(
source
,
yd
->
auth
+
yd
->
auth_written
,
remaining
);
if
(
written
<
0
&&
errno
==
EAGAIN
)
written
=
0
;
else
if
(
written
<=
0
)
{
gchar
*
tmp
;
g_free
(
yd
->
auth
);
yd
->
auth
=
NULL
;
if
(
gc
->
inpa
)
purple_input_remove
(
gc
->
inpa
);
gc
->
inpa
=
0
;
tmp
=
g_strdup_printf
(
_
(
"Lost connection with %s: %s"
),
"login.yahoo.com:80"
,
g_strerror
(
errno
));
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
tmp
);
g_free
(
tmp
);
return
;
}
if
(
written
<
remaining
)
{
yd
->
auth_written
+=
written
;
return
;
}
g_free
(
yd
->
auth
);
yd
->
auth
=
NULL
;
yd
->
auth_written
=
0
;
purple_input_remove
(
gc
->
inpa
);
gc
->
inpa
=
purple_input_add
(
source
,
PURPLE_INPUT_READ
,
yahoo_web_pending
,
gc
);
}
static
void
yahoo_got_cookies
(
gpointer
data
,
gint
source
,
const
gchar
*
error_message
)
{
PurpleConnection
*
gc
=
data
;
if
(
source
<
0
)
{
gchar
*
tmp
;
tmp
=
g_strdup_printf
(
_
(
"Unable to establish a connection with %s: %s"
),
"login.yahoo.com:80"
,
error_message
);
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
tmp
);
g_free
(
tmp
);
return
;
}
if
(
gc
->
inpa
==
0
)
{
gc
->
inpa
=
purple_input_add
(
source
,
PURPLE_INPUT_WRITE
,
yahoo_got_cookies_send_cb
,
gc
);
yahoo_got_cookies_send_cb
(
gc
,
source
,
PURPLE_INPUT_WRITE
);
}
}
static
void
yahoo_login_page_hash_iter
(
const
char
*
key
,
const
char
*
val
,
GString
*
url
)
{
if
(
!
strcmp
(
key
,
"passwd"
)
||
!
strcmp
(
key
,
"login"
))
return
;
g_string_append_c
(
url
,
'&'
);
g_string_append
(
url
,
key
);
g_string_append_c
(
url
,
'='
);
if
(
!
strcmp
(
key
,
".save"
)
||
!
strcmp
(
key
,
".js"
))
g_string_append_c
(
url
,
'1'
);
else
if
(
!
strcmp
(
key
,
".challenge"
))
g_string_append
(
url
,
val
);
else
g_string_append
(
url
,
purple_url_encode
(
val
));
}
static
GHashTable
*
yahoo_login_page_hash
(
const
char
*
buf
,
size_t
len
)
{
GHashTable
*
hash
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
const
char
*
c
=
buf
;
char
*
d
;
char
name
[
64
],
value
[
64
];
int
count
;
int
input_len
=
strlen
(
"<input "
);
int
name_len
=
strlen
(
"name=
\"
"
);
int
value_len
=
strlen
(
"value=
\"
"
);
while
((
len
>
((
c
-
buf
)
+
input_len
))
&&
(
c
=
strstr
(
c
,
"<input "
)))
{
if
(
!
(
c
=
g_strstr_len
(
c
,
len
-
(
c
-
buf
),
"name=
\"
"
)))
continue
;
c
+=
name_len
;
count
=
sizeof
(
name
)
-1
;
for
(
d
=
name
;
(
len
>
((
c
-
buf
)
+
1
))
&&
*
c
!=
'"'
&&
count
;
c
++
,
d
++
,
count
--
)
*
d
=
*
c
;
*
d
=
'\0'
;
count
=
sizeof
(
value
)
-1
;
if
(
!
(
d
=
g_strstr_len
(
c
,
len
-
(
c
-
buf
),
"value=
\"
"
)))
continue
;
d
+=
value_len
;
if
(
strchr
(
c
,
'>'
)
<
d
)
break
;
for
(
c
=
d
,
d
=
value
;
(
len
>
((
c
-
buf
)
+
1
))
&&
*
c
!=
'"'
&&
count
;
c
++
,
d
++
,
count
--
)
*
d
=
*
c
;
*
d
=
'\0'
;
g_hash_table_insert
(
hash
,
g_strdup
(
name
),
g_strdup
(
value
));
}
return
hash
;
}
static
void
yahoo_login_page_cb
(
PurpleUtilFetchUrlData
*
url_data
,
gpointer
user_data
,
const
gchar
*
url_text
,
size_t
len
,
const
gchar
*
error_message
)
{
PurpleConnection
*
gc
=
(
PurpleConnection
*
)
user_data
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
YahooData
*
yd
=
gc
->
proto_data
;
const
char
*
sn
=
purple_account_get_username
(
account
);
const
char
*
pass
=
purple_connection_get_password
(
gc
);
GHashTable
*
hash
=
yahoo_login_page_hash
(
url_text
,
len
);
GString
*
url
=
g_string_new
(
"GET http://login.yahoo.com/config/login?login="
);
char
md5
[
33
],
*
hashp
=
md5
,
*
chal
;
int
i
;
PurpleCipher
*
cipher
;
PurpleCipherContext
*
context
;
guchar
digest
[
16
];
yd
->
url_datas
=
g_slist_remove
(
yd
->
url_datas
,
url_data
);
if
(
error_message
!=
NULL
)
{
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
error_message
);
return
;
}
url
=
g_string_append
(
url
,
sn
);
url
=
g_string_append
(
url
,
"&passwd="
);
cipher
=
purple_ciphers_find_cipher
(
"md5"
);
context
=
purple_cipher_context_new
(
cipher
,
NULL
);
purple_cipher_context_append
(
context
,
(
const
guchar
*
)
pass
,
strlen
(
pass
));
purple_cipher_context_digest
(
context
,
sizeof
(
digest
),
digest
,
NULL
);
for
(
i
=
0
;
i
<
16
;
++
i
)
{
g_snprintf
(
hashp
,
3
,
"%02x"
,
digest
[
i
]);
hashp
+=
2
;
}
chal
=
g_strconcat
(
md5
,
g_hash_table_lookup
(
hash
,
".challenge"
),
NULL
);
purple_cipher_context_reset
(
context
,
NULL
);
purple_cipher_context_append
(
context
,
(
const
guchar
*
)
chal
,
strlen
(
chal
));
purple_cipher_context_digest
(
context
,
sizeof
(
digest
),
digest
,
NULL
);
hashp
=
md5
;
for
(
i
=
0
;
i
<
16
;
++
i
)
{
g_snprintf
(
hashp
,
3
,
"%02x"
,
digest
[
i
]);
hashp
+=
2
;
}
/*
* I dunno why this is here and commented out.. but in case it's needed
* I updated it..
purple_cipher_context_reset(context, NULL);
purple_cipher_context_append(context, md5, strlen(md5));
purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
hashp = md5;
for (i = 0; i < 16; ++i) {
g_snprintf(hashp, 3, "%02x", digest[i]);
hashp += 2;
}
*/
g_free
(
chal
);
url
=
g_string_append
(
url
,
md5
);
g_hash_table_foreach
(
hash
,
(
GHFunc
)
yahoo_login_page_hash_iter
,
url
);
url
=
g_string_append
(
url
,
"&.hash=1&.md5=1 HTTP/1.1
\r\n
"
"Host: login.yahoo.com
\r\n\r\n
"
);
g_hash_table_destroy
(
hash
);
yd
->
auth
=
g_string_free
(
url
,
FALSE
);
if
(
purple_proxy_connect
(
gc
,
account
,
"login.yahoo.com"
,
80
,
yahoo_got_cookies
,
gc
)
==
NULL
)
{
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to connect"
));
return
;
}
purple_cipher_context_destroy
(
context
);
}
#endif
/* TRY_WEBMESSENGER_LOGIN */
static
void
yahoo_picture_check
(
PurpleAccount
*
account
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
account
);
PurpleStoredImage
*
img
=
purple_buddy_icons_find_account_icon
(
account
);
yahoo_set_buddy_icon
(
gc
,
img
);
purple_imgstore_unref
(
img
);
}
static
int
get_yahoo_status_from_purple_status
(
PurpleStatus
*
status
)
{
PurplePresence
*
presence
;
const
char
*
status_id
;
const
char
*
msg
;
presence
=
purple_status_get_presence
(
status
);
status_id
=
purple_status_get_id
(
status
);
msg
=
purple_status_get_attr_string
(
status
,
"message"
);
if
((
msg
!=
NULL
)
&&
(
*
msg
!=
'\0'
))
{
return
YAHOO_STATUS_CUSTOM
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_AVAILABLE
))
{
return
YAHOO_STATUS_AVAILABLE
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_BRB
))
{
return
YAHOO_STATUS_BRB
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_BUSY
))
{
return
YAHOO_STATUS_BUSY
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_NOTATHOME
))
{
return
YAHOO_STATUS_NOTATHOME
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_NOTATDESK
))
{
return
YAHOO_STATUS_NOTATDESK
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_NOTINOFFICE
))
{
return
YAHOO_STATUS_NOTINOFFICE
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_ONPHONE
))
{
return
YAHOO_STATUS_ONPHONE
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_ONVACATION
))
{
return
YAHOO_STATUS_ONVACATION
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_OUTTOLUNCH
))
{
return
YAHOO_STATUS_OUTTOLUNCH
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_STEPPEDOUT
))
{
return
YAHOO_STATUS_STEPPEDOUT
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_INVISIBLE
))
{
return
YAHOO_STATUS_INVISIBLE
;
}
else
if
(
!
strcmp
(
status_id
,
YAHOO_STATUS_TYPE_AWAY
))
{
return
YAHOO_STATUS_CUSTOM
;
}
else
if
(
purple_presence_is_idle
(
presence
))
{
return
YAHOO_STATUS_IDLE
;
}
else
{
purple_debug_error
(
"yahoo"
,
"Unexpected PurpleStatus!
\n
"
);
return
YAHOO_STATUS_AVAILABLE
;
}
}
static
void
yahoo_got_pager_server
(
PurpleUtilFetchUrlData
*
url_data
,
gpointer
user_data
,
const
gchar
*
url_text
,
gsize
len
,
const
gchar
*
error_message
)
{
YahooData
*
yd
=
user_data
;
PurpleConnection
*
gc
=
yd
->
gc
;
PurpleAccount
*
a
=
purple_connection_get_account
(
gc
);
gchar
**
strings
=
NULL
,
*
cs_server
=
NULL
;
int
port
=
purple_account_get_int
(
a
,
"port"
,
YAHOO_PAGER_PORT
);
int
stringslen
=
0
;
yd
->
url_datas
=
g_slist_remove
(
yd
->
url_datas
,
url_data
);
if
(
error_message
!=
NULL
||
len
==
0
)
{
purple_debug_error
(
"yahoo"
,
"Unable to retrieve server info. %"
G_GSIZE_FORMAT
" bytes retrieved with error message: %s
\n
"
,
len
,
error_message
?
error_message
:
"(null)"
);
if
(
yahoo_is_japan
(
a
))
{
/* We don't know fallback hosts for Yahoo Japan :( */
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to connect: The server returned an empty response."
));
}
else
{
if
(
purple_proxy_connect
(
gc
,
a
,
YAHOO_PAGER_HOST_FALLBACK
,
port
,
yahoo_got_connected
,
gc
)
==
NULL
)
{
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to connect"
));
}
}
}
else
{
strings
=
g_strsplit
(
url_text
,
"
\r\n
"
,
-1
);
if
((
stringslen
=
g_strv_length
(
strings
))
>
1
)
{
int
i
;
for
(
i
=
0
;
i
<
stringslen
;
i
++
)
{
if
(
g_ascii_strncasecmp
(
strings
[
i
],
"COLO_CAPACITY="
,
14
)
==
0
)
{
purple_debug_info
(
"yahoo"
,
"Got COLO Capacity: %s
\n
"
,
&
(
strings
[
i
][
14
]));
}
else
if
(
g_ascii_strncasecmp
(
strings
[
i
],
"CS_IP_ADDRESS="
,
14
)
==
0
)
{
cs_server
=
g_strdup
(
&
strings
[
i
][
14
]);
purple_debug_info
(
"yahoo"
,
"Got CS IP address: %s
\n
"
,
cs_server
);
}
}
}
if
(
cs_server
)
{
/* got an address; get on with connecting */
if
(
purple_proxy_connect
(
gc
,
a
,
cs_server
,
port
,
yahoo_got_connected
,
gc
)
==
NULL
)
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to connect"
));
}
else
{
purple_debug_error
(
"yahoo"
,
"No CS address retrieved! Server "
"response:
\n
%s
\n
"
,
url_text
?
url_text
:
"(null)"
);
if
(
yahoo_is_japan
(
a
))
{
/* We don't know fallback hosts for Yahoo Japan :( */
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to connect: The server's response did not contain "
"the necessary information"
));
}
else
if
(
purple_proxy_connect
(
gc
,
a
,
YAHOO_PAGER_HOST_FALLBACK
,
port
,
yahoo_got_connected
,
gc
)
==
NULL
)
{
purple_connection_error_reason
(
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"Unable to connect"
));
}
}
}
g_strfreev
(
strings
);
g_free
(
cs_server
);
}
void
yahoo_login
(
PurpleAccount
*
account
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
account
);
YahooData
*
yd
=
gc
->
proto_data
=
g_new0
(
YahooData
,
1
);
PurpleStatus
*
status
=
purple_account_get_active_status
(
account
);
gboolean
use_whole_url
=
yahoo_account_use_http_proxy
(
gc
);
gboolean
proxy_ssl
=
purple_account_get_bool
(
account
,
"proxy_ssl"
,
FALSE
);
PurpleUtilFetchUrlData
*
url_data
;
gc
->
flags
|=
PURPLE_CONNECTION_HTML
|
PURPLE_CONNECTION_NO_BGCOLOR
|
PURPLE_CONNECTION_NO_URLDESC
;
purple_connection_update_progress
(
gc
,
_
(
"Connecting"
),
1
,
2
);
purple_connection_set_display_name
(
gc
,
purple_account_get_username
(
account
));
yd
->
gc
=
gc
;
yd
->
jp
=
yahoo_is_japan
(
account
);
yd
->
yahoo_local_p2p_server_fd
=
-1
;
yd
->
fd
=
-1
;
yd
->
txhandler
=
0
;
/* TODO: Is there a good grow size for the buffer? */
yd
->
txbuf
=
purple_circ_buffer_new
(
0
);
yd
->
friends
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
yahoo_friend_free
);
yd
->
imvironments
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
yd
->
xfer_peer_idstring_map
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
NULL
,
NULL
);
yd
->
peers
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
yahoo_p2p_disconnect_destroy_data
);
yd
->
sms_carrier
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
yd
->
yahoo_p2p_timer
=
purple_timeout_add_seconds
(
YAHOO_P2P_KEEPALIVE_SECS
,
yahoo_p2p_keepalive
,
gc
);
yd
->
confs
=
NULL
;
yd
->
conf_id
=
2
;
yd
->
last_keepalive
=
yd
->
last_ping
=
time
(
NULL
);
yd
->
current_status
=
get_yahoo_status_from_purple_status
(
status
);
yahoo_picture_check
(
account
);
/* Get the pager server. Actually start connecting in the callback since we
* must have the contents of the HTTP response to proceed. */
url_data
=
purple_util_fetch_url_request_len_with_account
(
proxy_ssl
?
purple_connection_get_account
(
gc
)
:
NULL
,
yd
->
jp
?
YAHOOJP_PAGER_HOST_REQ_URL
:
YAHOO_PAGER_HOST_REQ_URL
,
use_whole_url
?
TRUE
:
FALSE
,
YAHOO_CLIENT_USERAGENT
,
FALSE
,
NULL
,
FALSE
,
-1
,
yahoo_got_pager_server
,
yd
);
if
(
url_data
)
yd
->
url_datas
=
g_slist_prepend
(
yd
->
url_datas
,
url_data
);
return
;
}
void
yahoo_close
(
PurpleConnection
*
gc
)
{
YahooData
*
yd
=
(
YahooData
*
)
gc
->
proto_data
;
GSList
*
l
;
if
(
gc
->
inpa
)
purple_input_remove
(
gc
->
inpa
);
while
(
yd
->
url_datas
)
{
purple_util_fetch_url_cancel
(
yd
->
url_datas
->
data
);
yd
->
url_datas
=
g_slist_delete_link
(
yd
->
url_datas
,
yd
->
url_datas
);
}
for
(
l
=
yd
->
confs
;
l
;
l
=
l
->
next
)
{
PurpleConversation
*
conv
=
l
->
data
;
yahoo_conf_leave
(
yd
,
purple_conversation_get_name
(
conv
),
purple_connection_get_display_name
(
gc
),
purple_conv_chat_get_users
(
PURPLE_CONV_CHAT
(
conv
)));
}
g_slist_free
(
yd
->
confs
);
for
(
l
=
yd
->
cookies
;
l
;
l
=
l
->
next
)
{
g_free
(
l
->
data
);
l
->
data
=
NULL
;
}
g_slist_free
(
yd
->
cookies
);
yd
->
chat_online
=
FALSE
;
if
(
yd
->
in_chat
)
yahoo_c_leave
(
gc
,
1
);
/* 1 = YAHOO_CHAT_ID */
purple_timeout_remove
(
yd
->
yahoo_p2p_timer
);
if
(
yd
->
yahoo_p2p_server_timeout_handle
!=
0
)
{
purple_timeout_remove
(
yd
->
yahoo_p2p_server_timeout_handle
);
yd
->
yahoo_p2p_server_timeout_handle
=
0
;
}
/* close p2p server if it is waiting for a peer to connect */
if
(
yd
->
yahoo_p2p_server_watcher
)
{
purple_input_remove
(
yd
->
yahoo_p2p_server_watcher
);
yd
->
yahoo_p2p_server_watcher
=
0
;
}
if
(
yd
->
yahoo_local_p2p_server_fd
>=
0
)
{
close
(
yd
->
yahoo_local_p2p_server_fd
);
yd
->
yahoo_local_p2p_server_fd
=
-1
;
}
g_hash_table_destroy
(
yd
->
sms_carrier
);
g_hash_table_destroy
(
yd
->
peers
);
g_hash_table_destroy
(
yd
->
friends
);
g_hash_table_destroy
(
yd
->
imvironments
);
g_hash_table_destroy
(
yd
->
xfer_peer_idstring_map
);
g_free
(
yd
->
chat_name
);
g_free
(
yd
->
cookie_y
);
g_free
(
yd
->
cookie_t
);
g_free
(
yd
->
cookie_b
);
if
(
yd
->
txhandler
)
purple_input_remove
(
yd
->
txhandler
);
purple_circ_buffer_destroy
(
yd
->
txbuf
);
if
(
yd
->
fd
>=
0
)
close
(
yd
->
fd
);
g_free
(
yd
->
rxqueue
);
yd
->
rxlen
=
0
;
g_free
(
yd
->
picture_url
);
if
(
yd
->
buddy_icon_connect_data
)
purple_proxy_connect_cancel
(
yd
->
buddy_icon_connect_data
);
if
(
yd
->
picture_upload_todo
)
yahoo_buddy_icon_upload_data_free
(
yd
->
picture_upload_todo
);
if
(
yd
->
ycht
)
ycht_connection_close
(
yd
->
ycht
);
if
(
yd
->
listen_data
!=
NULL
)
purple_network_listen_cancel
(
yd
->
listen_data
);
g_free
(
yd
->
pending_chat_room
);
g_free
(
yd
->
pending_chat_id
);
g_free
(
yd
->
pending_chat_topic
);
g_free
(
yd
->
pending_chat_goto
);
g_strfreev
(
yd
->
profiles
);
yahoo_personal_details_reset
(
&
yd
->
ypd
,
TRUE
);
g_free
(
yd
->
current_list15_grp
);
g_free
(
yd
);
gc
->
proto_data
=
NULL
;
}
const
char
*
yahoo_list_icon
(
PurpleAccount
*
a
,
PurpleBuddy
*
b
)
{
return
"yahoo"
;
}
const
char
*
yahoo_list_emblem
(
PurpleBuddy
*
b
)
{
PurpleAccount
*
account
;
PurpleConnection
*
gc
;
YahooFriend
*
f
;
PurplePresence
*
presence
;
if
(
!
b
||
!
(
account
=
purple_buddy_get_account
(
b
))
||
!
(
gc
=
purple_account_get_connection
(
account
))
||
!
gc
->
proto_data
)
return
NULL
;
f
=
yahoo_friend_find
(
gc
,
purple_buddy_get_name
(
b
));
if
(
!
f
)
{
return
"not-authorized"
;
}
presence
=
purple_buddy_get_presence
(
b
);
if
(
purple_presence_is_online
(
presence
))
{
if
(
yahoo_friend_get_game
(
f
))
return
"game"
;
if
(
f
->
fed
)
return
"external"
;
}
return
NULL
;
}
static
const
char
*
yahoo_get_status_string
(
enum
yahoo_status
a
)
{
switch
(
a
)
{
case
YAHOO_STATUS_BRB
:
return
_
(
"Be Right Back"
);
case
YAHOO_STATUS_BUSY
:
return
_
(
"Busy"
);
case
YAHOO_STATUS_NOTATHOME
:
return
_
(
"Not at Home"
);
case
YAHOO_STATUS_NOTATDESK
:
return
_
(
"Not at Desk"
);
case
YAHOO_STATUS_NOTINOFFICE
:
return
_
(
"Not in Office"
);
case
YAHOO_STATUS_ONPHONE
:
return
_
(
"On the Phone"
);
case
YAHOO_STATUS_ONVACATION
:
return
_
(
"On Vacation"
);
case
YAHOO_STATUS_OUTTOLUNCH
:
return
_
(
"Out to Lunch"
);
case
YAHOO_STATUS_STEPPEDOUT
:
return
_
(
"Stepped Out"
);
case
YAHOO_STATUS_INVISIBLE
:
return
_
(
"Invisible"
);
case
YAHOO_STATUS_IDLE
:
return
_
(
"Idle"
);
case
YAHOO_STATUS_OFFLINE
:
return
_
(
"Offline"
);
default
:
return
_
(
"Available"
);
}
}
static
void
yahoo_initiate_conference
(
PurpleBlistNode
*
node
,
gpointer
data
)
{
PurpleBuddy
*
buddy
;
PurpleConnection
*
gc
;
GHashTable
*
components
;
YahooData
*
yd
;
int
id
;
g_return_if_fail
(
PURPLE_BLIST_NODE_IS_BUDDY
(
node
));
buddy
=
(
PurpleBuddy
*
)
node
;
gc
=
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
));
yd
=
gc
->
proto_data
;
id
=
yd
->
conf_id
;
components
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
g_hash_table_replace
(
components
,
g_strdup
(
"room"
),
g_strdup_printf
(
"%s-%d"
,
purple_connection_get_display_name
(
gc
),
id
));
g_hash_table_replace
(
components
,
g_strdup
(
"topic"
),
g_strdup
(
"Join my conference..."
));
g_hash_table_replace
(
components
,
g_strdup
(
"type"
),
g_strdup
(
"Conference"
));
yahoo_c_join
(
gc
,
components
);
g_hash_table_destroy
(
components
);
yahoo_c_invite
(
gc
,
id
,
"Join my conference..."
,
purple_buddy_get_name
(
buddy
));
}
static
void
yahoo_presence_settings
(
PurpleBlistNode
*
node
,
gpointer
data
)
{
PurpleBuddy
*
buddy
;
PurpleConnection
*
gc
;
int
presence_val
=
GPOINTER_TO_INT
(
data
);
buddy
=
(
PurpleBuddy
*
)
node
;
gc
=
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
));
yahoo_friend_update_presence
(
gc
,
purple_buddy_get_name
(
buddy
),
presence_val
);
}
static
void
yahoo_game
(
PurpleBlistNode
*
node
,
gpointer
data
)
{
PurpleBuddy
*
buddy
;
PurpleConnection
*
gc
;
const
char
*
game
;
char
*
game2
;
char
*
t
;
char
url
[
256
];
YahooFriend
*
f
;
g_return_if_fail
(
PURPLE_BLIST_NODE_IS_BUDDY
(
node
));
buddy
=
(
PurpleBuddy
*
)
node
;
gc
=
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
));
f
=
yahoo_friend_find
(
gc
,
purple_buddy_get_name
(
buddy
));
if
(
!
f
)
return
;
game
=
yahoo_friend_get_game
(
f
);
if
(
!
game
)
return
;
t
=
game2
=
g_strdup
(
strstr
(
game
,
"ante?room="
));
while
(
*
t
&&
*
t
!=
'\t'
)
t
++
;
*
t
=
0
;
g_snprintf
(
url
,
sizeof
url
,
"http://games.yahoo.com/games/%s"
,
game2
);
purple_notify_uri
(
gc
,
url
);
g_free
(
game2
);
}
char
*
yahoo_status_text
(
PurpleBuddy
*
b
)
{
YahooFriend
*
f
=
NULL
;
const
char
*
msg
;
char
*
msg2
;
PurpleAccount
*
account
;
PurpleConnection
*
gc
;
account
=
purple_buddy_get_account
(
b
);
gc
=
purple_account_get_connection
(
account
);
if
(
!
gc
||
!
purple_connection_get_protocol_data
(
gc
))
return
NULL
;
f
=
yahoo_friend_find
(
gc
,
purple_buddy_get_name
(
b
));
if
(
!
f
)
return
g_strdup
(
_
(
"Not on server list"
));
switch
(
f
->
status
)
{
case
YAHOO_STATUS_AVAILABLE
:
return
NULL
;
case
YAHOO_STATUS_IDLE
:
if
(
f
->
idle
==
-1
)
return
g_strdup
(
yahoo_get_status_string
(
f
->
status
));
return
NULL
;
case
YAHOO_STATUS_CUSTOM
:
if
(
!
(
msg
=
yahoo_friend_get_status_message
(
f
)))
return
NULL
;
msg2
=
g_markup_escape_text
(
msg
,
strlen
(
msg
));
purple_util_chrreplace
(
msg2
,
'\n'
,
' '
);
return
msg2
;
default
:
return
g_strdup
(
yahoo_get_status_string
(
f
->
status
));
}
}
void
yahoo_tooltip_text
(
PurpleBuddy
*
b
,
PurpleNotifyUserInfo
*
user_info
,
gboolean
full
)
{
YahooFriend
*
f
;
char
*
status
=
NULL
;
const
char
*
presence
=
NULL
;
PurpleAccount
*
account
;
account
=
purple_buddy_get_account
(
b
);
f
=
yahoo_friend_find
(
purple_account_get_connection
(
account
),
purple_buddy_get_name
(
b
));
if
(
!
f
)
status
=
g_strdup_printf
(
"
\n
%s"
,
_
(
"Not on server list"
));
else
{
switch
(
f
->
status
)
{
case
YAHOO_STATUS_CUSTOM
:
if
(
!
yahoo_friend_get_status_message
(
f
))
return
;
status
=
g_strdup
(
yahoo_friend_get_status_message
(
f
));
break
;
case
YAHOO_STATUS_OFFLINE
:
break
;
default
:
status
=
g_strdup
(
yahoo_get_status_string
(
f
->
status
));
break
;
}
switch
(
f
->
presence
)
{
case
YAHOO_PRESENCE_ONLINE
:
presence
=
_
(
"Appear Online"
);
break
;
case
YAHOO_PRESENCE_PERM_OFFLINE
:
presence
=
_
(
"Appear Permanently Offline"
);
break
;
case
YAHOO_PRESENCE_DEFAULT
:
break
;
default
:
purple_debug_error
(
"yahoo"
,
"Unknown presence in yahoo_tooltip_text
\n
"
);
break
;
}
}
if
(
status
!=
NULL
)
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Status"
),
status
);
g_free
(
status
);
}
if
(
presence
!=
NULL
)
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Presence"
),
presence
);
if
(
f
&&
full
)
{
YahooPersonalDetails
*
ypd
=
&
f
->
ypd
;
int
i
;
struct
{
char
*
id
;
char
*
text
;
char
*
value
;
}
yfields
[]
=
{
{
"hp"
,
N_
(
"Home Phone Number"
),
ypd
->
phone
.
home
},
{
"wp"
,
N_
(
"Work Phone Number"
),
ypd
->
phone
.
work
},
{
"mo"
,
N_
(
"Mobile Phone Number"
),
ypd
->
phone
.
mobile
},
{
NULL
,
NULL
,
NULL
}
};
for
(
i
=
0
;
yfields
[
i
].
id
;
i
++
)
{
if
(
!
yfields
[
i
].
value
||
!*
yfields
[
i
].
value
)
continue
;
purple_notify_user_info_add_pair
(
user_info
,
_
(
yfields
[
i
].
text
),
yfields
[
i
].
value
);
}
}
}
static
void
yahoo_addbuddyfrommenu_cb
(
PurpleBlistNode
*
node
,
gpointer
data
)
{
PurpleBuddy
*
buddy
;
PurpleConnection
*
gc
;
g_return_if_fail
(
PURPLE_BLIST_NODE_IS_BUDDY
(
node
));
buddy
=
(
PurpleBuddy
*
)
node
;
gc
=
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
));
yahoo_add_buddy
(
gc
,
buddy
,
NULL
);
}
static
void
yahoo_chat_goto_menu
(
PurpleBlistNode
*
node
,
gpointer
data
)
{
PurpleBuddy
*
buddy
;
PurpleConnection
*
gc
;
g_return_if_fail
(
PURPLE_BLIST_NODE_IS_BUDDY
(
node
));
buddy
=
(
PurpleBuddy
*
)
node
;
gc
=
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
));
yahoo_chat_goto
(
gc
,
purple_buddy_get_name
(
buddy
));
}
static
GList
*
build_presence_submenu
(
YahooFriend
*
f
,
PurpleConnection
*
gc
)
{
GList
*
m
=
NULL
;
PurpleMenuAction
*
act
;
YahooData
*
yd
=
(
YahooData
*
)
gc
->
proto_data
;
if
(
yd
->
current_status
==
YAHOO_STATUS_INVISIBLE
)
{
if
(
f
->
presence
!=
YAHOO_PRESENCE_ONLINE
)
{
act
=
purple_menu_action_new
(
_
(
"Appear Online"
),
PURPLE_CALLBACK
(
yahoo_presence_settings
),
GINT_TO_POINTER
(
YAHOO_PRESENCE_ONLINE
),
NULL
);
m
=
g_list_append
(
m
,
act
);
}
else
if
(
f
->
presence
!=
YAHOO_PRESENCE_DEFAULT
)
{
act
=
purple_menu_action_new
(
_
(
"Appear Offline"
),
PURPLE_CALLBACK
(
yahoo_presence_settings
),
GINT_TO_POINTER
(
YAHOO_PRESENCE_DEFAULT
),
NULL
);
m
=
g_list_append
(
m
,
act
);
}
}
if
(
f
->
presence
==
YAHOO_PRESENCE_PERM_OFFLINE
)
{
act
=
purple_menu_action_new
(
_
(
"Don't Appear Permanently Offline"
),
PURPLE_CALLBACK
(
yahoo_presence_settings
),
GINT_TO_POINTER
(
YAHOO_PRESENCE_DEFAULT
),
NULL
);
m
=
g_list_append
(
m
,
act
);
}
else
{
act
=
purple_menu_action_new
(
_
(
"Appear Permanently Offline"
),
PURPLE_CALLBACK
(
yahoo_presence_settings
),
GINT_TO_POINTER
(
YAHOO_PRESENCE_PERM_OFFLINE
),
NULL
);
m
=
g_list_append
(
m
,
act
);
}
return
m
;
}
static
void
yahoo_doodle_blist_node
(
PurpleBlistNode
*
node
,
gpointer
data
)
{
PurpleBuddy
*
b
=
(
PurpleBuddy
*
)
node
;
PurpleAccount
*
account
=
purple_buddy_get_account
(
b
);
PurpleConnection
*
gc
=
purple_account_get_connection
(
account
);
yahoo_doodle_initiate
(
gc
,
purple_buddy_get_name
(
b
));
}
static
void
yahoo_userinfo_blist_node
(
PurpleBlistNode
*
node
,
gpointer
data
)
{
PurpleBuddy
*
b
=
(
PurpleBuddy
*
)
node
;
PurpleAccount
*
account
=
purple_buddy_get_account
(
b
);
PurpleConnection
*
gc
=
purple_account_get_connection
(
account
);
yahoo_set_userinfo_for_buddy
(
gc
,
b
);
}
static
GList
*
yahoo_buddy_menu
(
PurpleBuddy
*
buddy
)
{
GList
*
m
=
NULL
;
PurpleMenuAction
*
act
;
PurpleConnection
*
gc
=
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
));
YahooData
*
yd
=
gc
->
proto_data
;
static
char
buf2
[
1024
];
YahooFriend
*
f
;
f
=
yahoo_friend_find
(
gc
,
purple_buddy_get_name
(
buddy
));
if
(
!
f
&&
!
yd
->
wm
)
{
act
=
purple_menu_action_new
(
_
(
"Add Buddy"
),
PURPLE_CALLBACK
(
yahoo_addbuddyfrommenu_cb
),
NULL
,
NULL
);
m
=
g_list_append
(
m
,
act
);
return
m
;
}
if
(
f
&&
f
->
status
!=
YAHOO_STATUS_OFFLINE
&&
f
->
fed
==
YAHOO_FEDERATION_NONE
)
{
if
(
!
yd
->
wm
)
{
act
=
purple_menu_action_new
(
_
(
"Join in Chat"
),
PURPLE_CALLBACK
(
yahoo_chat_goto_menu
),
NULL
,
NULL
);
m
=
g_list_append
(
m
,
act
);
}
act
=
purple_menu_action_new
(
_
(
"Initiate Conference"
),
PURPLE_CALLBACK
(
yahoo_initiate_conference
),
NULL
,
NULL
);
m
=
g_list_append
(
m
,
act
);
if
(
yahoo_friend_get_game
(
f
))
{
const
char
*
game
=
yahoo_friend_get_game
(
f
);
char
*
room
;
char
*
t
;
if
((
room
=
strstr
(
game
,
"&follow="
)))
{
/* skip ahead to the url */
while
(
*
room
&&
*
room
!=
'\t'
)
/* skip to the tab */
room
++
;
t
=
room
++
;
/* room as now at the name */
while
(
*
t
!=
'\n'
)
t
++
;
/* replace the \n with a space */
*
t
=
' '
;
g_snprintf
(
buf2
,
sizeof
buf2
,
"%s"
,
room
);
act
=
purple_menu_action_new
(
buf2
,
PURPLE_CALLBACK
(
yahoo_game
),
NULL
,
NULL
);
m
=
g_list_append
(
m
,
act
);
}
}
}
if
(
f
)
{
act
=
purple_menu_action_new
(
_
(
"Presence Settings"
),
NULL
,
NULL
,
build_presence_submenu
(
f
,
gc
));
m
=
g_list_append
(
m
,
act
);
if
(
f
->
fed
==
YAHOO_FEDERATION_NONE
)
{
act
=
purple_menu_action_new
(
_
(
"Start Doodling"
),
PURPLE_CALLBACK
(
yahoo_doodle_blist_node
),
NULL
,
NULL
);
m
=
g_list_append
(
m
,
act
);
}
act
=
purple_menu_action_new
(
_
(
"Set User Info..."
),
PURPLE_CALLBACK
(
yahoo_userinfo_blist_node
),
NULL
,
NULL
);
m
=
g_list_append
(
m
,
act
);
}
return
m
;
}
GList
*
yahoo_blist_node_menu
(
PurpleBlistNode
*
node
)
{
if
(
PURPLE_BLIST_NODE_IS_BUDDY
(
node
))
{
return
yahoo_buddy_menu
((
PurpleBuddy
*
)
node
);
}
else
{
return
NULL
;
}
}
static
void
yahoo_act_id
(
PurpleConnection
*
gc
,
PurpleRequestFields
*
fields
)
{
YahooData
*
yd
=
gc
->
proto_data
;
const
char
*
name
=
yd
->
profiles
[
purple_request_fields_get_choice
(
fields
,
"id"
)];
struct
yahoo_packet
*
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_IDACT
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash_str
(
pkt
,
3
,
name
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
purple_connection_set_display_name
(
gc
,
name
);
}
static
void
yahoo_get_inbox_token_cb
(
PurpleUtilFetchUrlData
*
url_data
,
gpointer
user_data
,
const
gchar
*
token
,
size_t
len
,
const
gchar
*
error_message
)
{
PurpleConnection
*
gc
=
user_data
;
gboolean
set_cookie
=
FALSE
;
gchar
*
url
;
YahooData
*
yd
=
gc
->
proto_data
;
g_return_if_fail
(
PURPLE_CONNECTION_IS_VALID
(
gc
));
yd
->
url_datas
=
g_slist_remove
(
yd
->
url_datas
,
url_data
);
if
(
error_message
!=
NULL
)
purple_debug_error
(
"yahoo"
,
"Requesting mail login token failed: %s
\n
"
,
error_message
);
else
if
(
len
>
0
&&
token
&&
*
token
)
{
/* Should we not be hardcoding the rd url? */
url
=
g_strdup_printf
(
"http://login.yahoo.com/config/reset_cookies_token?"
".token=%s"
"&.done=http://us.rd.yahoo.com/messenger/client/%%3fhttp://mail.yahoo.com/"
,
token
);
set_cookie
=
TRUE
;
}
if
(
!
set_cookie
)
{
purple_debug_error
(
"yahoo"
,
"No mail login token; forwarding to login screen.
\n
"
);
url
=
g_strdup
(
yd
->
jp
?
YAHOOJP_MAIL_URL
:
YAHOO_MAIL_URL
);
}
/* Open the mailbox with the parsed url data */
purple_notify_uri
(
gc
,
url
);
g_free
(
url
);
}
static
void
yahoo_show_inbox
(
PurplePluginAction
*
action
)
{
/* Setup a cookie that can be used by the browser */
/* XXX I have no idea how this will work with Yahoo! Japan. */
PurpleConnection
*
gc
=
action
->
context
;
YahooData
*
yd
=
gc
->
proto_data
;
PurpleUtilFetchUrlData
*
url_data
;
const
char
*
base_url
=
"http://login.yahoo.com"
;
/* use whole URL if using HTTP Proxy */
gboolean
use_whole_url
=
yahoo_account_use_http_proxy
(
gc
);
gchar
*
request
=
g_strdup_printf
(
"POST %s/config/cookie_token HTTP/1.0
\r\n
"
"Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s;
\r\n
"
"User-Agent: "
YAHOO_CLIENT_USERAGENT
"
\r\n
"
"Host: login.yahoo.com
\r\n
"
"Content-Length: 0
\r\n\r\n
"
,
use_whole_url
?
base_url
:
""
,
yd
->
cookie_t
,
yd
->
cookie_y
);
url_data
=
purple_util_fetch_url_request_len_with_account
(
purple_connection_get_account
(
gc
),
base_url
,
use_whole_url
,
YAHOO_CLIENT_USERAGENT
,
TRUE
,
request
,
FALSE
,
-1
,
yahoo_get_inbox_token_cb
,
gc
);
g_free
(
request
);
if
(
url_data
!=
NULL
)
yd
->
url_datas
=
g_slist_prepend
(
yd
->
url_datas
,
url_data
);
else
{
const
char
*
yahoo_mail_url
=
(
yd
->
jp
?
YAHOOJP_MAIL_URL
:
YAHOO_MAIL_URL
);
purple_debug_error
(
"yahoo"
,
"Unable to request mail login token; forwarding to login screen."
);
purple_notify_uri
(
gc
,
yahoo_mail_url
);
}
}
static
void
yahoo_set_userinfo_fn
(
PurplePluginAction
*
action
)
{
yahoo_set_userinfo
(
action
->
context
);
}
static
void
yahoo_show_act_id
(
PurplePluginAction
*
action
)
{
PurpleRequestFields
*
fields
;
PurpleRequestFieldGroup
*
group
;
PurpleRequestField
*
field
;
PurpleConnection
*
gc
=
(
PurpleConnection
*
)
action
->
context
;
YahooData
*
yd
=
purple_connection_get_protocol_data
(
gc
);
const
char
*
name
=
purple_connection_get_display_name
(
gc
);
int
iter
;
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
(
"id"
,
"Activate which ID?"
,
0
);
purple_request_field_group_add_field
(
group
,
field
);
for
(
iter
=
0
;
yd
->
profiles
[
iter
];
iter
++
)
{
purple_request_field_choice_add
(
field
,
yd
->
profiles
[
iter
]);
if
(
purple_strequal
(
yd
->
profiles
[
iter
],
name
))
purple_request_field_choice_set_default_value
(
field
,
iter
);
}
purple_request_fields
(
gc
,
NULL
,
_
(
"Select the ID you want to activate"
),
NULL
,
fields
,
_
(
"OK"
),
G_CALLBACK
(
yahoo_act_id
),
_
(
"Cancel"
),
NULL
,
purple_connection_get_account
(
gc
),
NULL
,
NULL
,
gc
);
}
static
void
yahoo_show_chat_goto
(
PurplePluginAction
*
action
)
{
PurpleConnection
*
gc
=
(
PurpleConnection
*
)
action
->
context
;
purple_request_input
(
gc
,
NULL
,
_
(
"Join whom in chat?"
),
NULL
,
""
,
FALSE
,
FALSE
,
NULL
,
_
(
"OK"
),
G_CALLBACK
(
yahoo_chat_goto
),
_
(
"Cancel"
),
NULL
,
purple_connection_get_account
(
gc
),
NULL
,
NULL
,
gc
);
}
GList
*
yahoo_actions
(
PurplePlugin
*
plugin
,
gpointer
context
)
{
GList
*
m
=
NULL
;
PurplePluginAction
*
act
;
act
=
purple_plugin_action_new
(
_
(
"Set User Info..."
),
yahoo_set_userinfo_fn
);
m
=
g_list_append
(
m
,
act
);
act
=
purple_plugin_action_new
(
_
(
"Activate ID..."
),
yahoo_show_act_id
);
m
=
g_list_append
(
m
,
act
);
act
=
purple_plugin_action_new
(
_
(
"Join User in Chat..."
),
yahoo_show_chat_goto
);
m
=
g_list_append
(
m
,
act
);
m
=
g_list_append
(
m
,
NULL
);
act
=
purple_plugin_action_new
(
_
(
"Open Inbox"
),
yahoo_show_inbox
);
m
=
g_list_append
(
m
,
act
);
return
m
;
}
struct
yahoo_sms_carrier_cb_data
{
PurpleConnection
*
gc
;
char
*
who
;
char
*
what
;
};
static
void
yahoo_get_sms_carrier_cb
(
PurpleUtilFetchUrlData
*
url_data
,
gpointer
user_data
,
const
gchar
*
webdata
,
size_t
len
,
const
gchar
*
error_message
)
{
struct
yahoo_sms_carrier_cb_data
*
sms_cb_data
=
user_data
;
PurpleConnection
*
gc
=
sms_cb_data
->
gc
;
YahooData
*
yd
=
gc
->
proto_data
;
char
*
status
=
NULL
;
char
*
carrier
=
NULL
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
PurpleConversation
*
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
sms_cb_data
->
who
,
account
);
yd
->
url_datas
=
g_slist_remove
(
yd
->
url_datas
,
url_data
);
if
(
error_message
!=
NULL
)
{
purple_conversation_write
(
conv
,
NULL
,
_
(
"Can't send SMS. Unable to obtain mobile carrier."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
g_free
(
sms_cb_data
->
who
);
g_free
(
sms_cb_data
->
what
);
g_free
(
sms_cb_data
);
return
;
}
else
if
(
len
>
0
&&
webdata
&&
*
webdata
)
{
xmlnode
*
validate_data_root
=
xmlnode_from_str
(
webdata
,
-1
);
xmlnode
*
validate_data_child
=
xmlnode_get_child
(
validate_data_root
,
"mobile_no"
);
const
char
*
mobile_no
=
xmlnode_get_attrib
(
validate_data_child
,
"msisdn"
);
validate_data_root
=
xmlnode_copy
(
validate_data_child
);
validate_data_child
=
xmlnode_get_child
(
validate_data_root
,
"status"
);
status
=
xmlnode_get_data
(
validate_data_child
);
validate_data_child
=
xmlnode_get_child
(
validate_data_root
,
"carrier"
);
carrier
=
xmlnode_get_data
(
validate_data_child
);
purple_debug_info
(
"yahoo"
,
"SMS validate data: %s
\n
"
,
webdata
);
if
(
status
&&
g_str_equal
(
status
,
"Valid"
))
{
g_hash_table_insert
(
yd
->
sms_carrier
,
g_strdup_printf
(
"+%s"
,
mobile_no
),
g_strdup
(
carrier
));
yahoo_send_im
(
sms_cb_data
->
gc
,
sms_cb_data
->
who
,
sms_cb_data
->
what
,
PURPLE_MESSAGE_SEND
);
}
else
{
g_hash_table_insert
(
yd
->
sms_carrier
,
g_strdup_printf
(
"+%s"
,
mobile_no
),
g_strdup
(
"Unknown"
));
purple_conversation_write
(
conv
,
NULL
,
_
(
"Can't send SMS. Unknown mobile carrier."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
}
xmlnode_free
(
validate_data_child
);
xmlnode_free
(
validate_data_root
);
g_free
(
sms_cb_data
->
who
);
g_free
(
sms_cb_data
->
what
);
g_free
(
sms_cb_data
);
g_free
(
status
);
g_free
(
carrier
);
}
}
static
void
yahoo_get_sms_carrier
(
PurpleConnection
*
gc
,
gpointer
data
)
{
YahooData
*
yd
=
gc
->
proto_data
;
PurpleUtilFetchUrlData
*
url_data
;
struct
yahoo_sms_carrier_cb_data
*
sms_cb_data
;
char
*
validate_request_str
=
NULL
;
char
*
request
=
NULL
;
gboolean
use_whole_url
=
FALSE
;
xmlnode
*
validate_request_root
=
NULL
;
xmlnode
*
validate_request_child
=
NULL
;
if
(
!
(
sms_cb_data
=
data
))
return
;
validate_request_root
=
xmlnode_new
(
"validate"
);
xmlnode_set_attrib
(
validate_request_root
,
"intl"
,
"us"
);
xmlnode_set_attrib
(
validate_request_root
,
"version"
,
YAHOO_CLIENT_VERSION
);
xmlnode_set_attrib
(
validate_request_root
,
"qos"
,
"0"
);
validate_request_child
=
xmlnode_new_child
(
validate_request_root
,
"mobile_no"
);
xmlnode_set_attrib
(
validate_request_child
,
"msisdn"
,
sms_cb_data
->
who
+
1
);
validate_request_str
=
xmlnode_to_str
(
validate_request_root
,
NULL
);
xmlnode_free
(
validate_request_child
);
xmlnode_free
(
validate_request_root
);
request
=
g_strdup_printf
(
"POST /mobileno?intl=us&version=%s HTTP/1.1
\r\n
"
"Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s; path=/; domain=.yahoo.com;
\r\n
"
"User-Agent: "
YAHOO_CLIENT_USERAGENT
"
\r\n
"
"Host: validate.msg.yahoo.com
\r\n
"
"Content-Length: %"
G_GSIZE_FORMAT
"
\r\n
"
"Cache-Control: no-cache
\r\n\r\n
%s"
,
YAHOO_CLIENT_VERSION
,
yd
->
cookie_t
,
yd
->
cookie_y
,
strlen
(
validate_request_str
),
validate_request_str
);
/* use whole URL if using HTTP Proxy */
if
((
gc
->
account
->
proxy_info
)
&&
(
gc
->
account
->
proxy_info
->
type
==
PURPLE_PROXY_HTTP
))
use_whole_url
=
TRUE
;
url_data
=
purple_util_fetch_url_request_len_with_account
(
purple_connection_get_account
(
gc
),
YAHOO_SMS_CARRIER_URL
,
use_whole_url
,
YAHOO_CLIENT_USERAGENT
,
TRUE
,
request
,
FALSE
,
-1
,
yahoo_get_sms_carrier_cb
,
data
);
g_free
(
request
);
g_free
(
validate_request_str
);
if
(
url_data
)
yd
->
url_datas
=
g_slist_prepend
(
yd
->
url_datas
,
url_data
);
else
{
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
PurpleConversation
*
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
sms_cb_data
->
who
,
account
);
purple_conversation_write
(
conv
,
NULL
,
_
(
"Can't send SMS. Unable to obtain mobile carrier."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
g_free
(
sms_cb_data
->
who
);
g_free
(
sms_cb_data
->
what
);
g_free
(
sms_cb_data
);
}
}
int
yahoo_send_im
(
PurpleConnection
*
gc
,
const
char
*
who
,
const
char
*
what
,
PurpleMessageFlags
flags
)
{
YahooData
*
yd
=
gc
->
proto_data
;
struct
yahoo_packet
*
pkt
=
NULL
;
char
*
msg
=
yahoo_html_to_codes
(
what
);
char
*
msg2
;
gboolean
utf8
=
TRUE
;
PurpleWhiteboard
*
wb
;
int
ret
=
1
;
const
char
*
fed_who
;
gsize
lenb
=
0
;
glong
lenc
=
0
;
struct
yahoo_p2p_data
*
p2p_data
;
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
msg2
=
yahoo_string_encode
(
gc
,
msg
,
&
utf8
);
if
(
msg2
)
{
lenb
=
strlen
(
msg2
);
lenc
=
g_utf8_strlen
(
msg2
,
-1
);
if
(
lenb
>
YAHOO_MAX_MESSAGE_LENGTH_BYTES
||
lenc
>
YAHOO_MAX_MESSAGE_LENGTH_CHARS
)
{
purple_debug_info
(
"yahoo"
,
"Message too big. Length is %"
G_GSIZE_FORMAT
" bytes, %ld characters. Max is %d bytes, %d chars."
" Message is '%s'.
\n
"
,
lenb
,
lenc
,
YAHOO_MAX_MESSAGE_LENGTH_BYTES
,
YAHOO_MAX_MESSAGE_LENGTH_CHARS
,
msg2
);
g_free
(
msg
);
g_free
(
msg2
);
return
-
E2BIG
;
}
}
fed
=
yahoo_get_federation_from_name
(
who
);
if
(
who
[
0
]
==
'+'
)
{
/* we have an sms to be sent */
gchar
*
carrier
=
NULL
;
const
char
*
alias
=
NULL
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
PurpleConversation
*
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
who
,
account
);
carrier
=
g_hash_table_lookup
(
yd
->
sms_carrier
,
who
);
if
(
!
carrier
)
{
struct
yahoo_sms_carrier_cb_data
*
sms_cb_data
;
sms_cb_data
=
g_malloc
(
sizeof
(
struct
yahoo_sms_carrier_cb_data
));
sms_cb_data
->
gc
=
gc
;
sms_cb_data
->
who
=
g_strdup
(
who
);
sms_cb_data
->
what
=
g_strdup
(
what
);
purple_conversation_write
(
conv
,
NULL
,
_
(
"Getting mobile carrier to send the SMS."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
yahoo_get_sms_carrier
(
gc
,
sms_cb_data
);
g_free
(
msg
);
g_free
(
msg2
);
return
ret
;
}
else
if
(
strcmp
(
carrier
,
"Unknown"
)
==
0
)
{
purple_conversation_write
(
conv
,
NULL
,
_
(
"Can't send SMS. Unknown mobile carrier."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
g_free
(
msg
);
g_free
(
msg2
);
return
-1
;
}
alias
=
purple_account_get_alias
(
account
);
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_SMS_MSG
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash
(
pkt
,
"sssss"
,
1
,
purple_connection_get_display_name
(
gc
),
69
,
alias
,
5
,
who
+
1
,
68
,
carrier
,
14
,
msg2
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
g_free
(
msg
);
g_free
(
msg2
);
return
ret
;
}
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_MESSAGE
,
YAHOO_STATUS_OFFLINE
,
yd
->
session_id
);
fed_who
=
who
;
switch
(
fed
)
{
case
YAHOO_FEDERATION_MSN
:
case
YAHOO_FEDERATION_OCS
:
case
YAHOO_FEDERATION_IBM
:
case
YAHOO_FEDERATION_PBX
:
fed_who
+=
4
;
break
;
case
YAHOO_FEDERATION_NONE
:
default
:
break
;
}
yahoo_packet_hash
(
pkt
,
"ss"
,
1
,
purple_connection_get_display_name
(
gc
),
5
,
fed_who
);
if
(
fed
)
yahoo_packet_hash_int
(
pkt
,
241
,
fed
);
if
(
utf8
)
yahoo_packet_hash_str
(
pkt
,
97
,
"1"
);
yahoo_packet_hash_str
(
pkt
,
14
,
msg2
);
/*
* IMVironment.
*
* If this message is to a user who is also Doodling with the local user,
* format the chat packet with the correct IMV information (thanks Yahoo!)
*
* Otherwise attempt to use the same IMVironment as the remote user,
* just so that we don't inadvertantly reset their IMVironment back
* to nothing.
*
* If they have not set an IMVironment, then use the default.
*/
wb
=
purple_whiteboard_get_session
(
gc
->
account
,
who
);
if
(
wb
)
yahoo_packet_hash_str
(
pkt
,
63
,
DOODLE_IMV_KEY
);
else
{
const
char
*
imv
;
imv
=
g_hash_table_lookup
(
yd
->
imvironments
,
who
);
if
(
imv
!=
NULL
)
yahoo_packet_hash_str
(
pkt
,
63
,
imv
);
else
yahoo_packet_hash_str
(
pkt
,
63
,
";0"
);
}
yahoo_packet_hash_str
(
pkt
,
64
,
"0"
);
/* no idea */
yahoo_packet_hash_str
(
pkt
,
1002
,
"1"
);
/* no idea, Yahoo 6 or later only it seems */
if
(
!
yd
->
picture_url
)
yahoo_packet_hash_str
(
pkt
,
206
,
"0"
);
/* 0 = no picture, 2 = picture, maybe 1 = avatar? */
else
yahoo_packet_hash_str
(
pkt
,
206
,
"2"
);
/* We may need to not send any packets over 2000 bytes, but I'm not sure yet. */
if
((
YAHOO_PACKET_HDRLEN
+
yahoo_packet_length
(
pkt
))
<=
2000
)
{
/* if p2p link exists, send through it. To-do: key 15, time value to be sent in case of p2p */
if
(
(
p2p_data
=
g_hash_table_lookup
(
yd
->
peers
,
who
))
&&
!
fed
)
{
yahoo_packet_hash_int
(
pkt
,
11
,
p2p_data
->
session_id
);
yahoo_p2p_write_pkt
(
p2p_data
->
source
,
pkt
);
}
else
{
yahoo_packet_send
(
pkt
,
yd
);
if
(
!
fed
)
yahoo_send_p2p_pkt
(
gc
,
who
,
0
);
/* send p2p packet, with val_13=0 */
}
}
else
ret
=
-
E2BIG
;
yahoo_packet_free
(
pkt
);
g_free
(
msg
);
g_free
(
msg2
);
return
ret
;
}
unsigned
int
yahoo_send_typing
(
PurpleConnection
*
gc
,
const
char
*
who
,
PurpleTypingState
state
)
{
YahooData
*
yd
=
gc
->
proto_data
;
struct
yahoo_p2p_data
*
p2p_data
;
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
struct
yahoo_packet
*
pkt
=
NULL
;
fed
=
yahoo_get_federation_from_name
(
who
);
/* Don't do anything if sms is being typed */
if
(
strncmp
(
who
,
"+"
,
1
)
==
0
)
return
0
;
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_NOTIFY
,
YAHOO_STATUS_TYPING
,
yd
->
session_id
);
/* check to see if p2p link exists, send through it */
if
(
(
p2p_data
=
g_hash_table_lookup
(
yd
->
peers
,
who
))
&&
!
fed
)
{
yahoo_packet_hash
(
pkt
,
"sssssis"
,
49
,
"TYPING"
,
1
,
purple_connection_get_display_name
(
gc
),
14
,
" "
,
13
,
state
==
PURPLE_TYPING
?
"1"
:
"0"
,
5
,
who
,
11
,
p2p_data
->
session_id
,
1002
,
"1"
);
/* To-do: key 15 to be sent in case of p2p */
yahoo_p2p_write_pkt
(
p2p_data
->
source
,
pkt
);
yahoo_packet_free
(
pkt
);
}
else
{
/* send through yahoo server */
const
char
*
fed_who
=
who
;
switch
(
fed
)
{
case
YAHOO_FEDERATION_MSN
:
case
YAHOO_FEDERATION_OCS
:
case
YAHOO_FEDERATION_IBM
:
case
YAHOO_FEDERATION_PBX
:
fed_who
+=
4
;
break
;
case
YAHOO_FEDERATION_NONE
:
default
:
break
;
}
yahoo_packet_hash
(
pkt
,
"ssssss"
,
49
,
"TYPING"
,
1
,
purple_connection_get_display_name
(
gc
),
14
,
" "
,
13
,
state
==
PURPLE_TYPING
?
"1"
:
"0"
,
5
,
fed_who
,
1002
,
"1"
);
if
(
fed
)
yahoo_packet_hash_int
(
pkt
,
241
,
fed
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
}
return
0
;
}
static
void
yahoo_session_presence_remove
(
gpointer
key
,
gpointer
value
,
gpointer
data
)
{
YahooFriend
*
f
=
value
;
if
(
f
&&
f
->
presence
==
YAHOO_PRESENCE_ONLINE
)
f
->
presence
=
YAHOO_PRESENCE_DEFAULT
;
}
void
yahoo_set_status
(
PurpleAccount
*
account
,
PurpleStatus
*
status
)
{
PurpleConnection
*
gc
;
PurplePresence
*
presence
;
YahooData
*
yd
;
struct
yahoo_packet
*
pkt
;
int
old_status
;
const
char
*
msg
=
NULL
;
char
*
tmp
=
NULL
;
char
*
conv_msg
=
NULL
;
gboolean
utf8
=
TRUE
;
if
(
!
purple_status_is_active
(
status
))
return
;
gc
=
purple_account_get_connection
(
account
);
presence
=
purple_status_get_presence
(
status
);
yd
=
(
YahooData
*
)
gc
->
proto_data
;
old_status
=
yd
->
current_status
;
yd
->
current_status
=
get_yahoo_status_from_purple_status
(
status
);
if
(
yd
->
current_status
==
YAHOO_STATUS_CUSTOM
)
{
msg
=
purple_status_get_attr_string
(
status
,
"message"
);
if
(
purple_status_is_available
(
status
))
{
tmp
=
yahoo_string_encode
(
gc
,
msg
,
&
utf8
);
conv_msg
=
purple_markup_strip_html
(
tmp
);
g_free
(
tmp
);
}
else
{
if
((
msg
==
NULL
)
||
(
*
msg
==
'\0'
))
msg
=
_
(
"Away"
);
tmp
=
yahoo_string_encode
(
gc
,
msg
,
&
utf8
);
conv_msg
=
purple_markup_strip_html
(
tmp
);
g_free
(
tmp
);
}
}
if
(
yd
->
current_status
==
YAHOO_STATUS_INVISIBLE
)
{
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_Y6_VISIBLE_TOGGLE
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash_str
(
pkt
,
13
,
"2"
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
return
;
}
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_Y6_STATUS_UPDATE
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash_int
(
pkt
,
10
,
yd
->
current_status
);
if
(
yd
->
current_status
==
YAHOO_STATUS_CUSTOM
)
{
yahoo_packet_hash_str
(
pkt
,
97
,
utf8
?
"1"
:
0
);
yahoo_packet_hash_str
(
pkt
,
19
,
conv_msg
);
}
else
{
yahoo_packet_hash_str
(
pkt
,
19
,
""
);
}
g_free
(
conv_msg
);
if
(
purple_presence_is_idle
(
presence
))
yahoo_packet_hash_str
(
pkt
,
47
,
"2"
);
else
{
if
(
!
purple_status_is_available
(
status
))
yahoo_packet_hash_str
(
pkt
,
47
,
"1"
);
else
yahoo_packet_hash_str
(
pkt
,
47
,
"0"
);
}
yahoo_packet_send_and_free
(
pkt
,
yd
);
if
(
old_status
==
YAHOO_STATUS_INVISIBLE
)
{
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_Y6_VISIBLE_TOGGLE
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash_str
(
pkt
,
13
,
"1"
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
/* Any per-session presence settings are removed */
g_hash_table_foreach
(
yd
->
friends
,
yahoo_session_presence_remove
,
NULL
);
}
}
void
yahoo_set_idle
(
PurpleConnection
*
gc
,
int
idle
)
{
YahooData
*
yd
=
gc
->
proto_data
;
struct
yahoo_packet
*
pkt
=
NULL
;
char
*
msg
=
NULL
,
*
msg2
=
NULL
;
PurpleStatus
*
status
=
NULL
;
gboolean
invisible
=
FALSE
;
if
(
idle
&&
yd
->
current_status
!=
YAHOO_STATUS_CUSTOM
)
yd
->
current_status
=
YAHOO_STATUS_IDLE
;
else
if
(
!
idle
&&
yd
->
current_status
==
YAHOO_STATUS_IDLE
)
{
status
=
purple_presence_get_active_status
(
purple_account_get_presence
(
purple_connection_get_account
(
gc
)));
yd
->
current_status
=
get_yahoo_status_from_purple_status
(
status
);
}
invisible
=
(
yd
->
current_status
==
YAHOO_STATUS_INVISIBLE
);
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_Y6_STATUS_UPDATE
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
if
(
!
idle
&&
invisible
)
yahoo_packet_hash_int
(
pkt
,
10
,
YAHOO_STATUS_AVAILABLE
);
else
yahoo_packet_hash_int
(
pkt
,
10
,
yd
->
current_status
);
if
(
yd
->
current_status
==
YAHOO_STATUS_CUSTOM
)
{
const
char
*
tmp
;
if
(
status
==
NULL
)
status
=
purple_presence_get_active_status
(
purple_account_get_presence
(
purple_connection_get_account
(
gc
)));
tmp
=
purple_status_get_attr_string
(
status
,
"message"
);
if
(
tmp
!=
NULL
)
{
gboolean
utf8
=
TRUE
;
msg
=
yahoo_string_encode
(
gc
,
tmp
,
&
utf8
);
msg2
=
purple_markup_strip_html
(
msg
);
yahoo_packet_hash_str
(
pkt
,
97
,
utf8
?
"1"
:
0
);
yahoo_packet_hash_str
(
pkt
,
19
,
msg2
);
}
else
{
/* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
* the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message */
yahoo_packet_hash_str
(
pkt
,
19
,
_
(
"Away"
));
}
}
else
{
yahoo_packet_hash_str
(
pkt
,
19
,
""
);
}
if
(
idle
)
yahoo_packet_hash_str
(
pkt
,
47
,
"2"
);
else
if
(
yd
->
current_status
==
YAHOO_STATUS_CUSTOM
&&
!
purple_status_is_available
(
status
))
/* We are still unavailable in this case.
* Make sure Yahoo knows that */
yahoo_packet_hash_str
(
pkt
,
47
,
"1"
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
g_free
(
msg
);
g_free
(
msg2
);
}
GList
*
yahoo_status_types
(
PurpleAccount
*
account
)
{
PurpleStatusType
*
type
;
GList
*
types
=
NULL
;
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_AVAILABLE
,
YAHOO_STATUS_TYPE_AVAILABLE
,
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
,
YAHOO_STATUS_TYPE_AWAY
,
NULL
,
TRUE
,
TRUE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
PURPLE_TYPE_STRING
),
NULL
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new
(
PURPLE_STATUS_AWAY
,
YAHOO_STATUS_TYPE_BRB
,
_
(
"Be Right Back"
),
TRUE
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_UNAVAILABLE
,
YAHOO_STATUS_TYPE_BUSY
,
_
(
"Busy"
),
TRUE
,
TRUE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
PURPLE_TYPE_STRING
),
NULL
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new
(
PURPLE_STATUS_AWAY
,
YAHOO_STATUS_TYPE_NOTATHOME
,
_
(
"Not at Home"
),
TRUE
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new
(
PURPLE_STATUS_AWAY
,
YAHOO_STATUS_TYPE_NOTATDESK
,
_
(
"Not at Desk"
),
TRUE
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new
(
PURPLE_STATUS_AWAY
,
YAHOO_STATUS_TYPE_NOTINOFFICE
,
_
(
"Not in Office"
),
TRUE
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new
(
PURPLE_STATUS_UNAVAILABLE
,
YAHOO_STATUS_TYPE_ONPHONE
,
_
(
"On the Phone"
),
TRUE
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new
(
PURPLE_STATUS_EXTENDED_AWAY
,
YAHOO_STATUS_TYPE_ONVACATION
,
_
(
"On Vacation"
),
TRUE
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new
(
PURPLE_STATUS_AWAY
,
YAHOO_STATUS_TYPE_OUTTOLUNCH
,
_
(
"Out to Lunch"
),
TRUE
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new
(
PURPLE_STATUS_AWAY
,
YAHOO_STATUS_TYPE_STEPPEDOUT
,
_
(
"Stepped Out"
),
TRUE
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new
(
PURPLE_STATUS_INVISIBLE
,
YAHOO_STATUS_TYPE_INVISIBLE
,
NULL
,
TRUE
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new
(
PURPLE_STATUS_OFFLINE
,
YAHOO_STATUS_TYPE_OFFLINE
,
NULL
,
TRUE
);
types
=
g_list_append
(
types
,
type
);
type
=
purple_status_type_new_full
(
PURPLE_STATUS_MOBILE
,
YAHOO_STATUS_TYPE_MOBILE
,
NULL
,
FALSE
,
FALSE
,
TRUE
);
types
=
g_list_append
(
types
,
type
);
return
types
;
}
void
yahoo_keepalive
(
PurpleConnection
*
gc
)
{
struct
yahoo_packet
*
pkt
;
YahooData
*
yd
=
gc
->
proto_data
;
time_t
now
=
time
(
NULL
);
/* We're only allowed to send a ping once an hour or the servers will boot us */
if
((
now
-
yd
->
last_ping
)
>=
PING_TIMEOUT
)
{
yd
->
last_ping
=
now
;
/* The native client will only send PING or CHATPING */
if
(
yd
->
chat_online
)
{
if
(
yd
->
wm
)
{
ycht_chat_send_keepalive
(
yd
->
ycht
);
}
else
{
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_CHATPING
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash_str
(
pkt
,
109
,
purple_connection_get_display_name
(
gc
));
yahoo_packet_send_and_free
(
pkt
,
yd
);
}
}
else
{
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_PING
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
}
}
if
((
now
-
yd
->
last_keepalive
)
>=
KEEPALIVE_TIMEOUT
)
{
yd
->
last_keepalive
=
now
;
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_KEEPALIVE
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash_str
(
pkt
,
0
,
purple_connection_get_display_name
(
gc
));
yahoo_packet_send_and_free
(
pkt
,
yd
);
}
}
void
yahoo_add_buddy
(
PurpleConnection
*
gc
,
PurpleBuddy
*
buddy
,
PurpleGroup
*
g
)
{
YahooData
*
yd
=
(
YahooData
*
)
gc
->
proto_data
;
struct
yahoo_packet
*
pkt
;
const
char
*
group
=
NULL
;
char
*
group2
;
const
char
*
bname
;
const
char
*
fed_bname
;
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
if
(
!
yd
->
logged_in
)
return
;
fed_bname
=
bname
=
purple_buddy_get_name
(
buddy
);
if
(
!
purple_privacy_check
(
purple_connection_get_account
(
gc
),
bname
))
return
;
fed
=
yahoo_get_federation_from_name
(
bname
);
if
(
fed
!=
YAHOO_FEDERATION_NONE
)
fed_bname
+=
4
;
g
=
purple_buddy_get_group
(
buddy
);
if
(
g
)
group
=
purple_group_get_name
(
g
);
else
group
=
"Buddies"
;
group2
=
yahoo_string_encode
(
gc
,
group
,
NULL
);
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_ADDBUDDY
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
if
(
fed
)
{
yahoo_packet_hash
(
pkt
,
"sssssssisss"
,
14
,
""
,
65
,
group2
,
97
,
"1"
,
1
,
purple_connection_get_display_name
(
gc
),
302
,
"319"
,
300
,
"319"
,
7
,
fed_bname
,
241
,
fed
,
334
,
"0"
,
301
,
"319"
,
303
,
"319"
);
}
else
{
yahoo_packet_hash
(
pkt
,
"ssssssssss"
,
14
,
""
,
65
,
group2
,
97
,
"1"
,
1
,
purple_connection_get_display_name
(
gc
),
302
,
"319"
,
300
,
"319"
,
7
,
fed_bname
,
334
,
"0"
,
301
,
"319"
,
303
,
"319"
);
}
yahoo_packet_send_and_free
(
pkt
,
yd
);
g_free
(
group2
);
}
void
yahoo_remove_buddy
(
PurpleConnection
*
gc
,
PurpleBuddy
*
buddy
,
PurpleGroup
*
group
)
{
YahooData
*
yd
=
(
YahooData
*
)
gc
->
proto_data
;
struct
yahoo_packet
*
pkt
;
GSList
*
buddies
,
*
l
;
PurpleGroup
*
g
;
gboolean
remove
=
TRUE
;
char
*
cg
;
const
char
*
bname
,
*
gname
;
YahooFriend
*
f
=
NULL
;
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
bname
=
purple_buddy_get_name
(
buddy
);
f
=
yahoo_friend_find
(
gc
,
bname
);
if
(
!
f
)
return
;
fed
=
f
->
fed
;
gname
=
purple_group_get_name
(
group
);
buddies
=
purple_find_buddies
(
purple_connection_get_account
(
gc
),
bname
);
for
(
l
=
buddies
;
l
;
l
=
l
->
next
)
{
g
=
purple_buddy_get_group
(
l
->
data
);
if
(
purple_utf8_strcasecmp
(
gname
,
purple_group_get_name
(
g
)))
{
remove
=
FALSE
;
break
;
}
}
g_slist_free
(
buddies
);
if
(
remove
)
{
g_hash_table_remove
(
yd
->
friends
,
bname
);
f
=
NULL
;
/* f no longer valid - Just making it clear */
}
cg
=
yahoo_string_encode
(
gc
,
gname
,
NULL
);
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_REMBUDDY
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
switch
(
fed
)
{
case
YAHOO_FEDERATION_MSN
:
case
YAHOO_FEDERATION_OCS
:
case
YAHOO_FEDERATION_IBM
:
bname
+=
4
;
break
;
case
YAHOO_FEDERATION_NONE
:
default
:
break
;
}
yahoo_packet_hash
(
pkt
,
"sss"
,
1
,
purple_connection_get_display_name
(
gc
),
7
,
bname
,
65
,
cg
);
if
(
fed
)
yahoo_packet_hash_int
(
pkt
,
241
,
fed
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
g_free
(
cg
);
}
void
yahoo_add_deny
(
PurpleConnection
*
gc
,
const
char
*
who
)
{
YahooData
*
yd
=
(
YahooData
*
)
gc
->
proto_data
;
struct
yahoo_packet
*
pkt
;
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
if
(
!
yd
->
logged_in
)
return
;
if
(
!
who
||
who
[
0
]
==
'\0'
)
return
;
fed
=
yahoo_get_federation_from_name
(
who
);
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_IGNORECONTACT
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
if
(
fed
)
yahoo_packet_hash
(
pkt
,
"ssis"
,
1
,
purple_connection_get_display_name
(
gc
),
7
,
who
+
4
,
241
,
fed
,
13
,
"1"
);
else
yahoo_packet_hash
(
pkt
,
"sss"
,
1
,
purple_connection_get_display_name
(
gc
),
7
,
who
,
13
,
"1"
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
}
void
yahoo_rem_deny
(
PurpleConnection
*
gc
,
const
char
*
who
)
{
YahooData
*
yd
=
(
YahooData
*
)
gc
->
proto_data
;
struct
yahoo_packet
*
pkt
;
YahooFederation
fed
=
YAHOO_FEDERATION_NONE
;
if
(
!
yd
->
logged_in
)
return
;
if
(
!
who
||
who
[
0
]
==
'\0'
)
return
;
fed
=
yahoo_get_federation_from_name
(
who
);
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_IGNORECONTACT
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
if
(
fed
)
yahoo_packet_hash
(
pkt
,
"ssis"
,
1
,
purple_connection_get_display_name
(
gc
),
7
,
who
+
4
,
241
,
fed
,
13
,
"2"
);
else
yahoo_packet_hash
(
pkt
,
"sss"
,
1
,
purple_connection_get_display_name
(
gc
),
7
,
who
,
13
,
"2"
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
}
void
yahoo_set_permit_deny
(
PurpleConnection
*
gc
)
{
PurpleAccount
*
account
;
GSList
*
deny
;
account
=
purple_connection_get_account
(
gc
);
switch
(
account
->
perm_deny
)
{
case
PURPLE_PRIVACY_ALLOW_ALL
:
for
(
deny
=
account
->
deny
;
deny
;
deny
=
deny
->
next
)
yahoo_rem_deny
(
gc
,
deny
->
data
);
break
;
case
PURPLE_PRIVACY_ALLOW_BUDDYLIST
:
case
PURPLE_PRIVACY_ALLOW_USERS
:
case
PURPLE_PRIVACY_DENY_USERS
:
case
PURPLE_PRIVACY_DENY_ALL
:
for
(
deny
=
account
->
deny
;
deny
;
deny
=
deny
->
next
)
yahoo_add_deny
(
gc
,
deny
->
data
);
break
;
}
}
void
yahoo_change_buddys_group
(
PurpleConnection
*
gc
,
const
char
*
who
,
const
char
*
old_group
,
const
char
*
new_group
)
{
YahooData
*
yd
=
gc
->
proto_data
;
struct
yahoo_packet
*
pkt
;
char
*
gpn
,
*
gpo
;
YahooFriend
*
f
=
yahoo_friend_find
(
gc
,
who
);
const
char
*
temp
=
NULL
;
/* Step 0: If they aren't on the server list anyway,
* don't bother letting the server know.
*/
if
(
!
f
)
return
;
if
(
f
->
fed
)
{
temp
=
who
+
4
;
}
else
temp
=
who
;
/* If old and new are the same, we would probably
* end up deleting the buddy, which would be bad.
* This might happen because of the charset conversation.
*/
gpn
=
yahoo_string_encode
(
gc
,
new_group
,
NULL
);
gpo
=
yahoo_string_encode
(
gc
,
old_group
,
NULL
);
if
(
!
strcmp
(
gpn
,
gpo
))
{
g_free
(
gpn
);
g_free
(
gpo
);
return
;
}
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_CHGRP_15
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
if
(
f
->
fed
)
yahoo_packet_hash
(
pkt
,
"ssssissss"
,
1
,
purple_connection_get_display_name
(
gc
),
302
,
"240"
,
300
,
"240"
,
7
,
temp
,
241
,
f
->
fed
,
224
,
gpo
,
264
,
gpn
,
301
,
"240"
,
303
,
"240"
);
else
yahoo_packet_hash
(
pkt
,
"ssssssss"
,
1
,
purple_connection_get_display_name
(
gc
),
302
,
"240"
,
300
,
"240"
,
7
,
temp
,
224
,
gpo
,
264
,
gpn
,
301
,
"240"
,
303
,
"240"
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
g_free
(
gpn
);
g_free
(
gpo
);
}
void
yahoo_rename_group
(
PurpleConnection
*
gc
,
const
char
*
old_name
,
PurpleGroup
*
group
,
GList
*
moved_buddies
)
{
YahooData
*
yd
=
gc
->
proto_data
;
struct
yahoo_packet
*
pkt
;
char
*
gpn
,
*
gpo
;
gpn
=
yahoo_string_encode
(
gc
,
purple_group_get_name
(
group
),
NULL
);
gpo
=
yahoo_string_encode
(
gc
,
old_name
,
NULL
);
if
(
!
strcmp
(
gpn
,
gpo
))
{
g_free
(
gpn
);
g_free
(
gpo
);
return
;
}
pkt
=
yahoo_packet_new
(
YAHOO_SERVICE_GROUPRENAME
,
YAHOO_STATUS_AVAILABLE
,
yd
->
session_id
);
yahoo_packet_hash
(
pkt
,
"sss"
,
1
,
purple_connection_get_display_name
(
gc
),
65
,
gpo
,
67
,
gpn
);
yahoo_packet_send_and_free
(
pkt
,
yd
);
g_free
(
gpn
);
g_free
(
gpo
);
}
/********************************* Commands **********************************/
PurpleCmdRet
yahoopurple_cmd_buzz
(
PurpleConversation
*
c
,
const
gchar
*
cmd
,
gchar
**
args
,
gchar
**
error
,
void
*
data
)
{
PurpleAccount
*
account
=
purple_conversation_get_account
(
c
);
if
(
*
args
&&
args
[
0
])
return
PURPLE_CMD_RET_FAILED
;
purple_prpl_send_attention
(
account
->
gc
,
c
->
name
,
YAHOO_BUZZ
);
return
PURPLE_CMD_RET_OK
;
}
PurpleCmdRet
yahoopurple_cmd_chat_join
(
PurpleConversation
*
conv
,
const
char
*
cmd
,
char
**
args
,
char
**
error
,
void
*
data
)
{
GHashTable
*
comp
;
PurpleConnection
*
gc
;
if
(
!
args
||
!
args
[
0
])
return
PURPLE_CMD_RET_FAILED
;
gc
=
purple_conversation_get_gc
(
conv
);
purple_debug_info
(
"yahoo"
,
"Trying to join %s
\n
"
,
args
[
0
]);
comp
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
g_hash_table_replace
(
comp
,
g_strdup
(
"room"
),
g_ascii_strdown
(
args
[
0
],
-1
));
g_hash_table_replace
(
comp
,
g_strdup
(
"type"
),
g_strdup
(
"Chat"
));
yahoo_c_join
(
gc
,
comp
);
g_hash_table_destroy
(
comp
);
return
PURPLE_CMD_RET_OK
;
}
PurpleCmdRet
yahoopurple_cmd_chat_list
(
PurpleConversation
*
conv
,
const
char
*
cmd
,
char
**
args
,
char
**
error
,
void
*
data
)
{
PurpleAccount
*
account
=
purple_conversation_get_account
(
conv
);
if
(
*
args
&&
args
[
0
])
return
PURPLE_CMD_RET_FAILED
;
purple_roomlist_show_with_account
(
account
);
return
PURPLE_CMD_RET_OK
;
}
gboolean
yahoo_offline_message
(
const
PurpleBuddy
*
buddy
)
{
return
TRUE
;
}
gboolean
yahoo_send_attention
(
PurpleConnection
*
gc
,
const
char
*
username
,
guint
type
)
{
PurpleConversation
*
c
;
c
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
username
,
gc
->
account
);
g_return_val_if_fail
(
c
!=
NULL
,
FALSE
);
purple_debug_info
(
"yahoo"
,
"Sending <ding> on account %s to buddy %s.
\n
"
,
username
,
c
->
name
);
purple_conv_im_send_with_flags
(
PURPLE_CONV_IM
(
c
),
"<ding>"
,
PURPLE_MESSAGE_INVISIBLE
);
return
TRUE
;
}
GList
*
yahoo_attention_types
(
PurpleAccount
*
account
)
{
static
GList
*
list
=
NULL
;
if
(
!
list
)
{
/* Yahoo only supports one attention command: the 'buzz'. */
/* This is index number YAHOO_BUZZ. */
list
=
g_list_append
(
list
,
purple_attention_type_new
(
"Buzz"
,
_
(
"Buzz"
),
_
(
"%s has buzzed you!"
),
_
(
"Buzzing %s..."
)));
}
return
list
;
}