qulogic/pidgin
Clone
Summary
Browse
Changes
Graph
Add disconnection reasons to oscar.
cpw.resiak.disconnectreason
2007-10-01, Will Thompson
a9fc6198b5c6
Add disconnection reasons to oscar.
/**
* @file buddy_list.c
*
* 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
<string.h>
#include
"qq.h"
#include
"debug.h"
#include
"notify.h"
#include
"utils.h"
#include
"packet_parse.h"
#include
"buddy_info.h"
#include
"buddy_list.h"
#include
"buddy_status.h"
#include
"buddy_opt.h"
#include
"char_conv.h"
#include
"crypt.h"
#include
"header_info.h"
#include
"keep_alive.h"
#include
"send_core.h"
#include
"group.h"
#include
"group_find.h"
#include
"group_internal.h"
#include
"group_info.h"
#include
"qq_proxy.h"
#define QQ_GET_ONLINE_BUDDY_02 0x02
#define QQ_GET_ONLINE_BUDDY_03 0x03
/* unknown function */
#define QQ_ONLINE_BUDDY_ENTRY_LEN 38
typedef
struct
_qq_friends_online_entry
{
qq_buddy_status
*
s
;
guint16
unknown1
;
guint8
flag1
;
guint8
comm_flag
;
guint16
unknown2
;
guint8
ending
;
/* 0x00 */
}
qq_friends_online_entry
;
/* get a list of online_buddies */
void
qq_send_packet_get_buddies_online
(
PurpleConnection
*
gc
,
guint8
position
)
{
qq_data
*
qd
;
guint8
*
raw_data
,
*
cursor
;
qd
=
(
qq_data
*
)
gc
->
proto_data
;
raw_data
=
g_newa
(
guint8
,
5
);
cursor
=
raw_data
;
/* 000-000 get online friends cmd
* only 0x02 and 0x03 returns info from server, other valuse all return 0xff
* I can also only send the first byte (0x02, or 0x03)
* and the result is the same */
create_packet_b
(
raw_data
,
&
cursor
,
QQ_GET_ONLINE_BUDDY_02
);
/* 001-001 seems it supports 255 online buddies at most */
create_packet_b
(
raw_data
,
&
cursor
,
position
);
/* 002-002 */
create_packet_b
(
raw_data
,
&
cursor
,
0x00
);
/* 003-004 */
create_packet_w
(
raw_data
,
&
cursor
,
0x0000
);
qq_send_cmd
(
gc
,
QQ_CMD_GET_FRIENDS_ONLINE
,
TRUE
,
0
,
TRUE
,
raw_data
,
5
);
qd
->
last_get_online
=
time
(
NULL
);
}
/* position starts with 0x0000,
* server may return a position tag if list is too long for one packet */
void
qq_send_packet_get_buddies_list
(
PurpleConnection
*
gc
,
guint16
position
)
{
guint8
*
raw_data
,
*
cursor
;
gint
data_len
;
data_len
=
3
;
raw_data
=
g_newa
(
guint8
,
data_len
);
cursor
=
raw_data
;
/* 000-001 starting position, can manually specify */
create_packet_w
(
raw_data
,
&
cursor
,
position
);
/* before Mar 18, 2004, any value can work, and we sent 00
* I do not know what data QQ server is expecting, as QQ2003iii 0304 itself
* even can sending packets 00 and get no response.
* Now I tested that 00,00,00,00,00,01 work perfectly
* March 22, found the 00,00,00 starts to work as well */
create_packet_b
(
raw_data
,
&
cursor
,
0x00
);
qq_send_cmd
(
gc
,
QQ_CMD_GET_FRIENDS_LIST
,
TRUE
,
0
,
TRUE
,
raw_data
,
data_len
);
}
/* get all list, buddies & Quns with groupsid support */
void
qq_send_packet_get_all_list_with_group
(
PurpleConnection
*
gc
,
guint32
position
)
{
guint8
*
raw_data
,
*
cursor
;
gint
data_len
;
data_len
=
10
;
raw_data
=
g_newa
(
guint8
,
data_len
);
cursor
=
raw_data
;
/* 0x01 download, 0x02, upload */
create_packet_b
(
raw_data
,
&
cursor
,
0x01
);
/* unknown 0x02 */
create_packet_b
(
raw_data
,
&
cursor
,
0x02
);
/* unknown 00 00 00 00 */
create_packet_dw
(
raw_data
,
&
cursor
,
0x00000000
);
create_packet_dw
(
raw_data
,
&
cursor
,
position
);
qq_send_cmd
(
gc
,
QQ_CMD_GET_ALL_LIST_WITH_GROUP
,
TRUE
,
0
,
TRUE
,
raw_data
,
data_len
);
}
static
void
_qq_buddies_online_reply_dump_unclear
(
qq_friends_online_entry
*
fe
)
{
GString
*
dump
;
g_return_if_fail
(
fe
!=
NULL
);
qq_buddy_status_dump_unclear
(
fe
->
s
);
dump
=
g_string_new
(
""
);
g_string_append_printf
(
dump
,
"unclear fields for [%d]:
\n
"
,
fe
->
s
->
uid
);
g_string_append_printf
(
dump
,
"031-032: %04x (unknown)
\n
"
,
fe
->
unknown1
);
g_string_append_printf
(
dump
,
"033: %02x (flag1)
\n
"
,
fe
->
flag1
);
g_string_append_printf
(
dump
,
"034: %02x (comm_flag)
\n
"
,
fe
->
comm_flag
);
g_string_append_printf
(
dump
,
"035-036: %04x (unknown)
\n
"
,
fe
->
unknown2
);
purple_debug
(
PURPLE_DEBUG_INFO
,
"QQ"
,
"Online buddy entry, %s"
,
dump
->
str
);
g_string_free
(
dump
,
TRUE
);
}
/* process the reply packet for get_buddies_online packet */
void
qq_process_get_buddies_online_reply
(
guint8
*
buf
,
gint
buf_len
,
PurpleConnection
*
gc
)
{
qq_data
*
qd
;
gint
len
,
bytes
;
guint8
*
data
,
*
cursor
,
position
;
PurpleBuddy
*
b
;
qq_buddy
*
q_bud
;
qq_friends_online_entry
*
fe
;
g_return_if_fail
(
buf
!=
NULL
&&
buf_len
!=
0
);
qd
=
(
qq_data
*
)
gc
->
proto_data
;
len
=
buf_len
;
data
=
g_newa
(
guint8
,
len
);
cursor
=
data
;
purple_debug
(
PURPLE_DEBUG_INFO
,
"QQ"
,
"processing get_buddies_online_reply
\n
"
);
if
(
qq_crypt
(
DECRYPT
,
buf
,
buf_len
,
qd
->
session_key
,
data
,
&
len
))
{
_qq_show_packet
(
"Get buddies online reply packet"
,
data
,
len
);
read_packet_b
(
data
,
&
cursor
,
len
,
&
position
);
fe
=
g_newa
(
qq_friends_online_entry
,
1
);
fe
->
s
=
g_newa
(
qq_buddy_status
,
1
);
while
(
cursor
<
(
data
+
len
))
{
/* based on one online buddy entry */
bytes
=
0
;
/* 000-030 qq_buddy_status */
bytes
+=
qq_buddy_status_read
(
data
,
&
cursor
,
len
,
fe
->
s
);
/* 031-032: unknown4 */
bytes
+=
read_packet_w
(
data
,
&
cursor
,
len
,
&
fe
->
unknown1
);
/* 033-033: flag1 */
bytes
+=
read_packet_b
(
data
,
&
cursor
,
len
,
&
fe
->
flag1
);
/* 034-034: comm_flag */
bytes
+=
read_packet_b
(
data
,
&
cursor
,
len
,
&
fe
->
comm_flag
);
/* 035-036: */
bytes
+=
read_packet_w
(
data
,
&
cursor
,
len
,
&
fe
->
unknown2
);
/* 037-037: */
bytes
+=
read_packet_b
(
data
,
&
cursor
,
len
,
&
fe
->
ending
);
/* 0x00 */
if
(
fe
->
s
->
uid
==
0
||
bytes
!=
QQ_ONLINE_BUDDY_ENTRY_LEN
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"QQ"
,
"uid=0 or entry complete len(%d) != %d"
,
bytes
,
QQ_ONLINE_BUDDY_ENTRY_LEN
);
g_free
(
fe
->
s
->
ip
);
g_free
(
fe
->
s
->
unknown_key
);
continue
;
}
/* check if it is a valid entry */
if
(
QQ_DEBUG
)
_qq_buddies_online_reply_dump_unclear
(
fe
);
/* update buddy information */
b
=
purple_find_buddy
(
purple_connection_get_account
(
gc
),
uid_to_purple_name
(
fe
->
s
->
uid
));
q_bud
=
(
b
==
NULL
)
?
NULL
:
(
qq_buddy
*
)
b
->
proto_data
;
if
(
q_bud
!=
NULL
)
{
/* we find one and update qq_buddy */
if
(
0
!=
fe
->
s
->
client_version
)
q_bud
->
client_version
=
fe
->
s
->
client_version
;
g_memmove
(
q_bud
->
ip
,
fe
->
s
->
ip
,
4
);
q_bud
->
port
=
fe
->
s
->
port
;
q_bud
->
status
=
fe
->
s
->
status
;
q_bud
->
flag1
=
fe
->
flag1
;
q_bud
->
comm_flag
=
fe
->
comm_flag
;
qq_update_buddy_contact
(
gc
,
q_bud
);
}
else
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"QQ"
,
"Got an online buddy %d, but not in my buddy list
\n
"
,
fe
->
s
->
uid
);
}
g_free
(
fe
->
s
->
ip
);
g_free
(
fe
->
s
->
unknown_key
);
}
if
(
cursor
>
(
data
+
len
))
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"QQ"
,
"qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!
\n
"
);
}
if
(
position
!=
QQ_FRIENDS_ONLINE_POSITION_END
)
{
purple_debug
(
PURPLE_DEBUG_INFO
,
"QQ"
,
"Has more online buddies, position from %d
\n
"
,
position
);
qq_send_packet_get_buddies_online
(
gc
,
position
);
}
else
{
qq_send_packet_get_buddies_levels
(
gc
);
qq_refresh_all_buddy_status
(
gc
);
}
}
else
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"QQ"
,
"Error decrypt buddies online"
);
}
}
/* process reply for get_buddies_list */
void
qq_process_get_buddies_list_reply
(
guint8
*
buf
,
gint
buf_len
,
PurpleConnection
*
gc
)
{
qq_data
*
qd
;
qq_buddy
*
q_bud
;
gint
len
,
bytes
,
bytes_expected
,
i
;
guint16
position
,
unknown
;
guint8
*
data
,
*
cursor
,
pascal_len
;
gchar
*
name
;
PurpleBuddy
*
b
;
g_return_if_fail
(
buf
!=
NULL
&&
buf_len
!=
0
);
qd
=
(
qq_data
*
)
gc
->
proto_data
;
len
=
buf_len
;
data
=
g_newa
(
guint8
,
len
);
cursor
=
data
;
if
(
qq_crypt
(
DECRYPT
,
buf
,
buf_len
,
qd
->
session_key
,
data
,
&
len
))
{
read_packet_w
(
data
,
&
cursor
,
len
,
&
position
);
/* the following data is buddy list in this packet */
i
=
0
;
while
(
cursor
<
(
data
+
len
))
{
q_bud
=
g_new0
(
qq_buddy
,
1
);
bytes
=
0
;
/* 000-003: uid */
bytes
+=
read_packet_dw
(
data
,
&
cursor
,
len
,
&
q_bud
->
uid
);
/* 004-005: icon index (1-255) */
bytes
+=
read_packet_w
(
data
,
&
cursor
,
len
,
&
q_bud
->
face
);
/* 006-006: age */
bytes
+=
read_packet_b
(
data
,
&
cursor
,
len
,
&
q_bud
->
age
);
/* 007-007: gender */
bytes
+=
read_packet_b
(
data
,
&
cursor
,
len
,
&
q_bud
->
gender
);
pascal_len
=
convert_as_pascal_string
(
cursor
,
&
q_bud
->
nickname
,
QQ_CHARSET_DEFAULT
);
cursor
+=
pascal_len
;
bytes
+=
pascal_len
;
bytes
+=
read_packet_w
(
data
,
&
cursor
,
len
,
&
unknown
);
/* flag1: (0-7)
* bit1 => qq show
* comm_flag: (0-7)
* bit1 => member
* bit4 => TCP mode
* bit5 => open mobile QQ
* bit6 => bind to mobile
* bit7 => whether having a video
*/
bytes
+=
read_packet_b
(
data
,
&
cursor
,
len
,
&
q_bud
->
flag1
);
bytes
+=
read_packet_b
(
data
,
&
cursor
,
len
,
&
q_bud
->
comm_flag
);
bytes_expected
=
12
+
pascal_len
;
if
(
q_bud
->
uid
==
0
||
bytes
!=
bytes_expected
)
{
purple_debug
(
PURPLE_DEBUG_INFO
,
"QQ"
,
"Buddy entry, expect %d bytes, read %d bytes
\n
"
,
bytes_expected
,
bytes
);
g_free
(
q_bud
->
nickname
);
g_free
(
q_bud
);
continue
;
}
else
{
i
++
;
}
if
(
QQ_DEBUG
)
{
purple_debug
(
PURPLE_DEBUG_INFO
,
"QQ"
,
"buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x
\n
"
,
q_bud
->
uid
,
q_bud
->
flag1
,
q_bud
->
comm_flag
);
}
name
=
uid_to_purple_name
(
q_bud
->
uid
);
b
=
purple_find_buddy
(
gc
->
account
,
name
);
g_free
(
name
);
if
(
b
==
NULL
)
b
=
qq_add_buddy_by_recv_packet
(
gc
,
q_bud
->
uid
,
TRUE
,
FALSE
);
b
->
proto_data
=
q_bud
;
qd
->
buddies
=
g_list_append
(
qd
->
buddies
,
q_bud
);
qq_update_buddy_contact
(
gc
,
q_bud
);
}
if
(
cursor
>
(
data
+
len
))
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"QQ"
,
"qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!"
);
}
if
(
position
==
QQ_FRIENDS_LIST_POSITION_END
)
{
purple_debug
(
PURPLE_DEBUG_INFO
,
"QQ"
,
"Get friends list done, %d buddies
\n
"
,
i
);
qq_send_packet_get_buddies_online
(
gc
,
QQ_FRIENDS_ONLINE_POSITION_START
);
}
else
{
qq_send_packet_get_buddies_list
(
gc
,
position
);
}
}
else
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"QQ"
,
"Error decrypt buddies list"
);
}
}
void
qq_process_get_all_list_with_group_reply
(
guint8
*
buf
,
gint
buf_len
,
PurpleConnection
*
gc
)
{
qq_data
*
qd
;
gint
len
,
i
,
j
;
guint8
*
data
,
*
cursor
;
guint8
sub_cmd
,
reply_code
;
guint32
unknown
,
position
;
guint32
uid
;
guint8
type
,
groupid
;
qq_group
*
group
;
g_return_if_fail
(
buf
!=
NULL
&&
buf_len
!=
0
);
qd
=
(
qq_data
*
)
gc
->
proto_data
;
len
=
buf_len
;
data
=
g_newa
(
guint8
,
len
);
cursor
=
data
;
if
(
qq_crypt
(
DECRYPT
,
buf
,
buf_len
,
qd
->
session_key
,
data
,
&
len
))
{
read_packet_b
(
data
,
&
cursor
,
len
,
&
sub_cmd
);
g_return_if_fail
(
sub_cmd
==
0x01
);
read_packet_b
(
data
,
&
cursor
,
len
,
&
reply_code
);
if
(
0
!=
reply_code
)
{
purple_debug
(
PURPLE_DEBUG_WARNING
,
"QQ"
,
"Get all list with group reply, reply_code(%d) is not zero"
,
reply_code
);
}
read_packet_dw
(
data
,
&
cursor
,
len
,
&
unknown
);
read_packet_dw
(
data
,
&
cursor
,
len
,
&
position
);
/* the following data is all list in this packet */
i
=
0
;
j
=
0
;
while
(
cursor
<
(
data
+
len
))
{
/* 00-03: uid */
read_packet_dw
(
data
,
&
cursor
,
len
,
&
uid
);
/* 04: type 0x1:buddy 0x4:Qun */
read_packet_b
(
data
,
&
cursor
,
len
,
&
type
);
/* 05: groupid*4 */
/* seems to always be 0 */
read_packet_b
(
data
,
&
cursor
,
len
,
&
groupid
);
/*
purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid);
groupid >>= 2;
*/
if
(
uid
==
0
||
(
type
!=
0x1
&&
type
!=
0x4
))
{
purple_debug
(
PURPLE_DEBUG_INFO
,
"QQ"
,
"Buddy entry, uid=%d, type=%d"
,
uid
,
type
);
continue
;
}
if
(
0x1
==
type
)
{
/* a buddy */
/* don't do anything but count - buddies are handled by
* qq_send_packet_get_buddies_list */
++
i
;
}
else
{
/* a group */
group
=
qq_group_find_by_id
(
gc
,
uid
,
QQ_INTERNAL_ID
);
if
(
group
==
NULL
)
{
qq_set_pending_id
(
&
qd
->
adding_groups_from_server
,
uid
,
TRUE
);
group
=
g_newa
(
qq_group
,
1
);
group
->
internal_group_id
=
uid
;
qq_send_cmd_group_get_group_info
(
gc
,
group
);
}
else
{
group
->
my_status
=
QQ_GROUP_MEMBER_STATUS_IS_MEMBER
;
qq_group_refresh
(
gc
,
group
);
qq_send_cmd_group_get_group_info
(
gc
,
group
);
}
++
j
;
}
}
if
(
cursor
>
(
data
+
len
))
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"QQ"
,
"qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!"
);
}
purple_debug
(
PURPLE_DEBUG_INFO
,
"QQ"
,
"Get all list done, %d buddies and %d Quns
\n
"
,
i
,
j
);
}
else
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"QQ"
,
"Error decrypt all list with group"
);
}
}