# HG changeset patch # User Marcus Lundblad # Date 1228613909 0 # Node ID e1331f6126ba7a958c498b4a1a9bbf4eb6d758c8 # Parent 918738a0bba48f5219bb55c73e62500ae2584a2d# Parent 21b21bffd946cfb6a5ffb46539bcdd1e71952a70 propagate from branch 'im.pidgin.pidgin' (head 1203a733696f95eef53b04b5078e0a779371b4d3) to branch 'im.pidgin.cpw.malu.xmpp.attention' (head 29f8ee822fcf7af288aefa0f39587c9918f46876) diff -r 21b21bffd946 -r e1331f6126ba ChangeLog --- a/ChangeLog Tue Dec 02 22:11:39 2008 +0000 +++ b/ChangeLog Sun Dec 07 01:38:29 2008 +0000 @@ -16,6 +16,9 @@ * Fix a crash in SIMPLE when a malformed message is received. * purple-remote now has a "getstatusmessage" command to retrieve the text of the current status message. + * Various fixes to the nullprpl (Paul Aurich) + * Fix a crash when accessing the roomlist for an account that's not + connected (Paul Aurich) Gadu-Gadu: * Fix some problems with Gadu-Gadu buddy icons (Adam Strzelecki) @@ -47,6 +50,9 @@ gtk-enable-tooltips = 0 * Moved the release notification dialog to a mini-dialog in the buddylist. (Thanks to Casey Ho) + * Fix a crash when closing an authorization minidialog with the X then + immediately going offline (Paul Aurich) + * Fix compatibility with old GTK+ yet again Finch: * Allow binding meta+arrow keys for actions. diff -r 21b21bffd946 -r e1331f6126ba finch/gntaccount.c --- a/finch/gntaccount.c Tue Dec 02 22:11:39 2008 +0000 +++ b/finch/gntaccount.c Sun Dec 07 01:38:29 2008 +0000 @@ -1069,6 +1069,8 @@ _("Authorize"), auth_cb, _("Deny"), deny_cb); } + g_signal_connect(G_OBJECT(uihandle), "destroy", + G_CALLBACK(purple_account_request_close), NULL); g_free(buffer); return uihandle; } diff -r 21b21bffd946 -r e1331f6126ba finch/gntroomlist.c --- a/finch/gntroomlist.c Tue Dec 02 22:11:39 2008 +0000 +++ b/finch/gntroomlist.c Sun Dec 07 01:38:29 2008 +0000 @@ -239,7 +239,8 @@ PurpleConnection *gc = list->data; prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - if (prpl_info->roomlist_get_list != NULL) { + if (PURPLE_CONNECTION_IS_CONNECTED(gc) && + prpl_info->roomlist_get_list != NULL) { PurpleAccount *account = purple_connection_get_account(gc); char *text = g_strdup_printf("%s (%s)", purple_account_get_username(account), diff -r 21b21bffd946 -r e1331f6126ba finch/libgnt/gntentry.c --- a/finch/libgnt/gntentry.c Tue Dec 02 22:11:39 2008 +0000 +++ b/finch/libgnt/gntentry.c Sun Dec 07 01:38:29 2008 +0000 @@ -856,7 +856,7 @@ gnt_entry_class_init(GntEntryClass *klass) { GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass); - char s[2] = {erasechar(), 0}; + char s[3] = {'\033', erasechar(), 0}; parent_class = GNT_WIDGET_CLASS(klass); parent_class->clicked = gnt_entry_clicked; @@ -892,7 +892,7 @@ gnt_bindable_register_binding(bindable, "cursor-end", GNT_KEY_END, NULL); gnt_bindable_class_register_action(bindable, "delete-prev", backspace, GNT_KEY_BACKSPACE, NULL); - gnt_bindable_register_binding(bindable, "delete-prev", s, NULL); + gnt_bindable_register_binding(bindable, "delete-prev", s + 1, NULL); gnt_bindable_register_binding(bindable, "delete-prev", GNT_KEY_CTRL_H, NULL); gnt_bindable_class_register_action(bindable, "delete-next", delkey, GNT_KEY_DEL, NULL); @@ -903,7 +903,7 @@ GNT_KEY_CTRL_K, NULL); gnt_bindable_class_register_action(bindable, "delete-prev-word", del_prev_word, GNT_KEY_CTRL_W, NULL); - gnt_bindable_register_binding(bindable, "delete-prev-word", "\033", s, NULL); + gnt_bindable_register_binding(bindable, "delete-prev-word", s, NULL); gnt_bindable_class_register_action(bindable, "cursor-prev-word", move_back_word, "\033" "b", NULL); gnt_bindable_class_register_action(bindable, "cursor-prev", move_back, diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sun Dec 07 01:38:29 2008 +0000 @@ -154,7 +154,7 @@ return NULL; if (strstr(input, "__HOSTNAME__") == NULL) - return input; + return g_strdup(input); /* Replace __HOSTNAME__ with hostname */ if (gethostname(hostname, sizeof(hostname) - 1)) { diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/null/nullprpl.c --- a/libpurple/protocols/null/nullprpl.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/null/nullprpl.c Sun Dec 07 01:38:29 2008 +0000 @@ -160,8 +160,8 @@ static void discover_status(PurpleConnection *from, PurpleConnection *to, gpointer userdata) { - char *from_username = from->account->username; - char *to_username = to->account->username; + const char *from_username = from->account->username; + const char *to_username = to->account->username; if (purple_find_buddy(from->account, to_username)) { PurpleStatus *status = purple_account_get_active_status(to->account); @@ -262,7 +262,7 @@ } else { purple_debug_info("nullprpl", "...but %s is not logged in\n", buddy->name); - return "Not logged in"; + return g_strdup("Not logged in"); } } @@ -275,9 +275,10 @@ /* they're logged in */ PurplePresence *presence = purple_buddy_get_presence(buddy); PurpleStatus *status = purple_presence_get_active_status(presence); - const char *msg = nullprpl_status_text(buddy); + char *msg = nullprpl_status_text(buddy); purple_notify_user_info_add_pair(info, purple_status_get_name(status), msg); + g_free(msg); if (full) { const char *user_info = purple_account_get_user_info(gc->account); @@ -289,7 +290,7 @@ /* they're not logged in */ purple_notify_user_info_add_pair(info, _("User info"), _("not logged in")); } - + purple_debug_info("nullprpl", "showing %s tooltip for %s\n", (full) ? "full" : "short", buddy->name); } @@ -307,21 +308,21 @@ NULL_STATUS_ONLINE, TRUE); purple_status_type_add_attr(type, "message", _("Online"), purple_value_new(PURPLE_TYPE_STRING)); - types = g_list_append(types, type); + types = g_list_prepend(types, type); type = purple_status_type_new(PURPLE_STATUS_AWAY, NULL_STATUS_AWAY, NULL_STATUS_AWAY, TRUE); purple_status_type_add_attr(type, "message", _("Away"), purple_value_new(PURPLE_TYPE_STRING)); - types = g_list_append(types, type); + types = g_list_prepend(types, type); type = purple_status_type_new(PURPLE_STATUS_OFFLINE, NULL_STATUS_OFFLINE, NULL_STATUS_OFFLINE, TRUE); purple_status_type_add_attr(type, "message", _("Offline"), purple_value_new(PURPLE_TYPE_STRING)); - types = g_list_append(types, type); + types = g_list_prepend(types, type); - return types; + return g_list_reverse(types); } static void blist_example_menu_item(PurpleBlistNode *node, gpointer userdata) { @@ -355,7 +356,7 @@ purple_debug_info("nullprpl", "returning chat setting 'room'\n"); pce = g_new0(struct proto_chat_entry, 1); - pce->label = _(_("Chat _room")); + pce->label = _("Chat _room"); pce->identifier = "room"; pce->required = TRUE; @@ -477,7 +478,7 @@ gc->account->username, info); } -static char *typing_state_to_string(PurpleTypingState typing) { +static const char *typing_state_to_string(PurpleTypingState typing) { switch (typing) { case PURPLE_NOT_TYPING: return "is not typing"; case PURPLE_TYPING: return "is typing"; @@ -488,8 +489,8 @@ static void notify_typing(PurpleConnection *from, PurpleConnection *to, gpointer typing) { - char *from_username = from->account->username; - char *action = typing_state_to_string((PurpleTypingState)typing); + const char *from_username = from->account->username; + const char *action = typing_state_to_string((PurpleTypingState)typing); purple_debug_info("nullprpl", "notifying %s that %s %s\n", to->account->username, from_username, action); @@ -561,7 +562,7 @@ static void nullprpl_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { - char *username = gc->account->username; + const char *username = gc->account->username; PurpleConnection *buddy_gc = get_nullprpl_gc(buddy->name); purple_debug_info("nullprpl", "adding %s to %s's buddy list\n", buddy->name, @@ -679,8 +680,8 @@ } static void nullprpl_join_chat(PurpleConnection *gc, GHashTable *components) { - char *username = gc->account->username; - char *room = g_hash_table_lookup(components, "room"); + const char *username = gc->account->username; + const char *room = g_hash_table_lookup(components, "room"); int chat_id = g_str_hash(room); purple_debug_info("nullprpl", "%s is joining chat room %s\n", username, room); @@ -690,20 +691,20 @@ /* tell everyone that we joined, and add them if they're already there */ foreach_gc_in_chat(joined_chat, gc, chat_id, NULL); } else { + char *tmp = g_strdup_printf(_("%s is already in chat room %s."), + username, + room); purple_debug_info("nullprpl", "%s is already in chat room %s\n", username, room); - purple_notify_info(gc, - _("Join chat"), - _("Join chat"), - g_strdup_printf("%s is already in chat room %s.", - username, room)); + purple_notify_info(gc, _("Join chat"), _("Join chat"), tmp); + g_free(tmp); } } static void nullprpl_reject_chat(PurpleConnection *gc, GHashTable *components) { - char *invited_by = g_hash_table_lookup(components, "invited_by"); - char *room = g_hash_table_lookup(components, "room"); - char *username = gc->account->username; + const char *invited_by = g_hash_table_lookup(components, "invited_by"); + const char *room = g_hash_table_lookup(components, "room"); + const char *username = gc->account->username; PurpleConnection *invited_by_gc = get_nullprpl_gc(invited_by); char *message = g_strdup_printf( "%s %s %s.", @@ -719,19 +720,20 @@ _("Chat invitation rejected"), _("Chat invitation rejected"), message); + g_free(message); } static char *nullprpl_get_chat_name(GHashTable *components) { - char *room = g_hash_table_lookup(components, "room"); + const char *room = g_hash_table_lookup(components, "room"); purple_debug_info("nullprpl", "reporting chat room name '%s'\n", room); - return room; + return g_strdup(room); } static void nullprpl_chat_invite(PurpleConnection *gc, int id, const char *message, const char *who) { - char *username = gc->account->username; + const char *username = gc->account->username; PurpleConversation *conv = purple_find_chat(gc, id); - char *room = conv->name; + const char *room = conv->name; PurpleAccount *to_acct = purple_accounts_find(who, NULLPRPL_ID); purple_debug_info("nullprpl", "%s is inviting %s to join chat room %s\n", @@ -740,18 +742,16 @@ if (to_acct) { PurpleConversation *to_conv = purple_find_chat(to_acct->gc, id); if (to_conv) { + char *tmp = g_strdup_printf("%s is already in chat room %s.", who, room); purple_debug_info("nullprpl", "%s is already in chat room %s; " "ignoring invitation from %s\n", who, room, username); - purple_notify_info(gc, - _("Chat invitation"), - _("Chat invitation"), - g_strdup_printf("%s is already in chat room %s.", - who, room)); + purple_notify_info(gc, _("Chat invitation"), _("Chat invitation"), tmp); + g_free(tmp); } else { GHashTable *components; - components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free); + components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); g_hash_table_replace(components, "room", g_strdup(room)); g_hash_table_replace(components, "invited_by", g_strdup(username)); serv_got_chat_invite(to_acct->gc, room, username, message, components); @@ -833,7 +833,7 @@ static void nullprpl_chat_whisper(PurpleConnection *gc, int id, const char *who, const char *message) { - char *username = gc->account->username; + const char *username = gc->account->username; PurpleConversation *conv = purple_find_chat(gc, id); purple_debug_info("nullprpl", "%s receives whisper from %s in chat room %s: %s\n", @@ -858,7 +858,7 @@ static int nullprpl_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) { - char *username = gc->account->username; + const char *username = gc->account->username; PurpleConversation *conv = purple_find_chat(gc, id); if (conv) { @@ -981,7 +981,7 @@ } static PurpleRoomlist *nullprpl_roomlist_get_list(PurpleConnection *gc) { - char *username = gc->account->username; + const char *username = gc->account->username; PurpleRoomlist *roomlist = purple_roomlist_new(gc->account); GList *fields = NULL; PurpleRoomlistField *field; @@ -1005,14 +1005,17 @@ for (chats = purple_get_chats(); chats; chats = g_list_next(chats)) { PurpleConversation *conv = (PurpleConversation *)chats->data; PurpleRoomlistRoom *room; - char *name = conv->name; + const char *name = conv->name; int id = purple_conversation_get_chat_data(conv)->id; /* have we already added this room? */ if (g_list_find_custom(seen_ids, name, (GCompareFunc)strcmp)) continue; /* yes! try the next one. */ - seen_ids = g_list_append(seen_ids, name); /* no, it's new. */ + /* This cast is OK because this list is only staying around for the life + * of this function and none of the conversations are being deleted + * in that timespan. */ + seen_ids = g_list_prepend(seen_ids, (char *)name); /* no, it's new. */ purple_debug_info("nullprpl", "%s (%d), ", name, id); room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL); @@ -1021,6 +1024,7 @@ purple_roomlist_room_add(roomlist, room); } + g_list_free(seen_ids); purple_timeout_add(1 /* ms */, nullprpl_finish_get_roomlist, roomlist); return roomlist; } diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/ChangeLog --- a/libpurple/protocols/qq/ChangeLog Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/ChangeLog Sun Dec 07 01:38:29 2008 +0000 @@ -1,3 +1,35 @@ +2008.12.06 - flos + * Removed version checking script in Makefiles since our developers all migrated to monotone + * Use our development revision as OPENQ_VERSION in qq.c + +2008.12.05 - flos + * Fixed a bug after propagating + +2008.11.18 - ccpaging + * Fixed: IM format suuport in IM and QUN IM + * Divide long IM message into segment and sending + * Divide long QUN IM message in to segment and sending + * Add some new function in im.c to put format when sending + * Add some new function in im.c to get format when receiving + * Need improvement: + Merge long IM message when receiving. Need a buffer to store segments of long IM message. + Send segment of long IM message one by one. Need a buffer to store segments of long IM message. + +2008.11.11 - ccpaging + * Change QQ number to unsigned long + * Change Qun ID and Qun extend ID to unsigned long + * Rewrite smiley convert function, use qsort and bsearch + * Update smiley map according EVA and pidgin theme file + * Support long IM message in private and Qun + +2008.10.27 - ccpaging + * Fixed a bug in group_join.c + +2008.10.30 - flos + * Fixed a bug which made xgettext failed in buddy_info.c + * Fixed a bug in Makefile.am and Makefile.mingw + * Updated acknowledgement in qq.c + 2008.10.28 - flos * Updated AUTHORS @@ -22,15 +54,15 @@ 2008.10.10 - ccpaging * Keep group_search.c/h for later use - * Update 'group' + * Update 'group' 2008.10.09 - ccpaging * 20081009-1 2008.10.09 - ccpaging * Update 'group' protocol - * Functions of group_find, group_free, group_search merged into group_join and group_internal - * Removed group_find.c/h, group_free.c/h, group_search.c/h + * Functions of group_find, group_free, group_search merged into group_join and group_internal + * Removed group_find.c/h, group_free.c/h, group_search.c/h 2008.10.08 - ccpaging * Update 'group' protocol @@ -138,7 +170,7 @@ 1. send next package till the previous package received 2. fix duplicated get_room_info and get_room_buddies commands -2008.08.16 - ccpaging +2008.08.16 - ccpaging * Rename group to room. If you used pidginqq before, this may create a new room with same title, you may delete old one * Replace purple_debug with purple_debug_info, purple_debug_warning, purple_debug_error * Add server notice and server new, and two options to turn on/off @@ -151,17 +183,17 @@ 2008.08.10 - csyfek * Commit to Pidgin -2008.08.07 - ccpaging +2008.08.07 - ccpaging * Support managing multi-connections according to simple.c -2008.08.06 - ccpaging +2008.08.06 - ccpaging * Rename names of variables, Group, to Room * Functions of group_network merged into qq_network and qq_process * Canceled managing glist of group packet, add sub_cmdd and room_id to transaction * Fixed error of demo group: If 'room list' and 'room infor' are not setup, response received from server will emits 'room_id = 0' packet. -2008.08.04 - ccpaging +2008.08.04 - ccpaging * Use new crypt/decrypt functions * Rename crypt.c/h to qq_crypt.c/h * Clean code of decrypt functions @@ -180,17 +212,17 @@ Fixes #1902 References #5112 -2008.08.02 - ccpaging +2008.08.02 - ccpaging * Store all keys and md5 values of qq_data in char[QQ_KEY_LENGTH] * Use random value in inikey * TEA header padding in crypt.c * Rewrite login part of qq_process -2008.07.31 - ccpaging +2008.07.31 - ccpaging * Fixed: send reply when get duplicate server command. The server may not get our reply before. * Tag custom picture as text "(Broken)" -2008.07.30 - ccpaging , csyfek +2008.07.30 - ccpaging , csyfek * Change some debug message * Modify buddy status flag according to eva for QQ2006 * Modify buddy status parse and correspond to eva2 @@ -224,10 +256,10 @@ * Rewrite qq_proc_cmd_reply and qq_proc_cmd_server: In QQ protocol, one packet reply may need a new packet send later. We may call it packet trigger. The triggers always is hided in every qq_process_reply. - Now we try to extract those triggers and put into a single function, + Now we try to extract those triggers and put into a single function, and then every trigger should be obviously and easy to manage. - -2008.07.12 - ccpaging + +2008.07.12 - ccpaging * Fixed: Always lost connection. Now send keep alive packet in every 30 seconds * Minor fix for debug information * Filter \r\n and replace with SPCAE in group notive @@ -240,37 +272,37 @@ * Add some doxygen syntax for preparing development documentation * References #6199 -2008.06.28 - ccpaging , moo +2008.06.28 - ccpaging , moo * Patches from moo and ccpaging. * Tickets: * Fixes #4956. * Fixes #2998. -2008.06.07 - ccpaging , csyfek +2008.06.07 - ccpaging , csyfek * Clean code and apply patches from QuLogic -2008.05.19 - ccpaging , csyfek +2008.05.19 - ccpaging , csyfek * Reconnect server 5 time in 5000 ms, when connect failed * Rename sendqueue.c/sendqueue.h to qq_trans.c/qq_trans.h * Rewrite packet_process * Rewrite qq_send_cmd * Create server list, try to connect every server when failed -2008.05.14 - ccpaging +2008.05.14 - ccpaging * Move function for before login packets storing to sendqueue * Use transaction data structure to store before login packets * Rewrite tcp_pending and packet_process in qq_network.c -2008.05.09 - ccpaging +2008.05.09 - ccpaging * Remove function _create_packet_head_seq in qq_network.c * Create new function encap in qq_netowork.c * Clean code of qq_send_packet_request_login_token and qq_send_packet_login in login_out.c -2008.05.09 - ccpaging +2008.05.09 - ccpaging * Clean code of packet_parse.c, enable PARSER_DEBUG * Rewrite send_queue -2008.05.08 - ccpaging +2008.05.08 - ccpaging * Rewrite qq_network * Add srv resolve function when qq_login * Merge function _qq_common_clean in qq_proxy.c to qq_disconnect @@ -278,20 +310,20 @@ * qq_data alloc in qq_open and release in qq_close * Network connect of QQ is created in qq_connect, and release in qq_disconnect -2008.05.05 - ccpaging +2008.05.05 - ccpaging * Merge function _qq_common_clean in qq_proxy.c to qq_disconnect * Move orignal qq_disconnect to qq_close * qq_data alloc in qq_open and release in qq_close * Network connect of QQ is created in qq_connect, and release in qq_disconnect -2008.05.05 - ccpaging +2008.05.05 - ccpaging * Add qq_hex_dump function -2008.04.25 - ccpaging , csyfek +2008.04.25 - ccpaging , csyfek * Rewrite read_packet and create_packet functions, use qq_put and qq_get functions instead * New logic in accord with protocol models to handle packets, some related functions rewritten -2008.03.24 - ccpaging +2008.03.24 - ccpaging * Remove qq_crypt function in crypt.c, use qq_crypt and qq_decrypt directly ** since pidgin-2.4.0 *** diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/Makefile.mingw --- a/libpurple/protocols/qq/Makefile.mingw Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.mingw Sun Dec 07 01:38:29 2008 +0000 @@ -6,6 +6,7 @@ PIDGIN_TREE_TOP := ../../.. include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak + TARGET = libqq TYPE = PLUGIN diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/buddy_info.c --- a/libpurple/protocols/qq/buddy_info.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/buddy_info.c Sun Dec 07 01:38:29 2008 +0000 @@ -228,7 +228,7 @@ g_return_if_fail(uid != 0); qd = (qq_data *) gc->proto_data; - g_snprintf(raw_data, sizeof(raw_data), "%d", uid); + g_snprintf(raw_data, sizeof(raw_data), "%u", uid); qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDY_INFO, (guint8 *) raw_data, strlen(raw_data), update_class, action); } @@ -457,7 +457,7 @@ data[data_len] = '\0'; if (qd->uid != atoi((gchar *) data)) { /* return should be my uid */ purple_debug_info("QQ", "Failed Updating info\n"); - qq_got_attention(gc, _("Could not change buddy information.")); + qq_got_message(gc, _("Could not change buddy information.")); } } @@ -504,6 +504,8 @@ { PurpleAccount *account = purple_connection_get_account(gc); const gchar *icon_path = purple_account_get_buddy_icon_path(account); + gchar **segments; + gint index; g_return_if_fail(icon_path != NULL); @@ -512,6 +514,12 @@ * purple_imgstore_get_filename is always new file * QQ buddy may set custom icon if level is over 16 */ purple_debug_info("QQ", "Change my icon to %s\n", icon_path); + segments = g_strsplit_set(icon_path, G_DIR_SEPARATOR_S, 0); + for (index = 0; segments[index] != NULL; index++) { + purple_debug_info("QQ", "Split to %s\n", segments[index]); + } + + g_strfreev(segments); } gchar *qq_get_icon_name(gint face) @@ -553,7 +561,7 @@ return icon_path; } -static void update_buddy_icon(PurpleAccount *account, const gchar *who, gint face) +void qq_update_buddy_icon(PurpleAccount *account, const gchar *who, gint face) { PurpleBuddy *buddy; const gchar *icon_name_prev = NULL; @@ -564,19 +572,21 @@ g_return_if_fail(account != NULL && who != NULL); - purple_debug_info("QQ", "Update %s icon to %d\n", who, face); + /* purple_debug_info("QQ", "Update %s icon to %d\n", who, face); */ icon_name = qq_get_icon_name(face); - purple_debug_info("QQ", "icon file name is %s\n", icon_name); + g_return_if_fail(icon_name != NULL); + /* purple_debug_info("QQ", "icon file name is %s\n", icon_name); */ if ((buddy = purple_find_buddy(account, who))) { icon_name_prev = purple_buddy_icons_get_checksum_for_user(buddy); - if (icon_name_prev != NULL) { - purple_debug_info("QQ", "Previous icon is %s\n", icon_name_prev); - } + /* + purple_debug_info("QQ", "Previous icon is %s\n", + icon_name_prev != NULL ? icon_name_prev : "(NULL)"); + */ } if (icon_name_prev != NULL && !strcmp(icon_name, icon_name_prev)) { - purple_debug_info("QQ", "Icon is not changed\n"); + /* purple_debug_info("QQ", "Icon is not changed\n"); */ g_free(icon_name); return; } @@ -590,6 +600,8 @@ if (!g_file_get_contents(icon_path, &icon_file_content, &icon_file_size, NULL)) { purple_debug_error("QQ", "Failed reading icon file %s\n", icon_path); } else { + purple_debug_info("QQ", "Update %s icon to %d (%s)\n", + who, face, icon_path); purple_buddy_icons_set_for_user(account, who, icon_file_content, icon_file_size, icon_name); } @@ -610,7 +622,7 @@ qd = (qq_data *) gc->proto_data; - uid = strtol(segments[QQ_INFO_UID], NULL, 10); + uid = strtoul(segments[QQ_INFO_UID], NULL, 10); who = uid_to_purple_name(uid); qq_filter_str(segments[QQ_INFO_NICK]); @@ -648,7 +660,7 @@ purple_blist_server_alias_buddy(buddy, bd->nickname); /* convert face num from packet (0-299) to local face (1-100) */ - update_buddy_icon(gc->account, who, bd->face); + qq_update_buddy_icon(gc->account, who, bd->face); g_free(who); g_free(alias_utf8); @@ -786,12 +798,12 @@ bytes += qq_get32(&onlineTime, data + bytes); bytes += qq_get16(&level, data + bytes); bytes += qq_get16(&timeRemainder, data + bytes); - purple_debug_info("QQ_LEVEL", "%d, tmOnline: %d, level: %d, tmRemainder: %d\n", - uid, onlineTime, level, timeRemainder); + purple_debug_info("QQ", "level: %d, uid %d, tmOnline: %d, tmRemainder: %d\n", + level, uid, onlineTime, timeRemainder); bd = qq_buddy_data_find(gc, uid); if (bd == NULL) { - purple_debug_error("QQ", "Got levels of %d not in my buddy list\n", uid); + purple_debug_error("QQ", "Got levels of %u not in my buddy list\n", uid); continue; } @@ -821,8 +833,8 @@ bytes += qq_get32(&onlineTime, data + bytes); bytes += qq_get16(&level, data + bytes); bytes += qq_get16(&timeRemainder, data + bytes); - purple_debug_info("QQ_LEVEL", "%d, tmOnline: %d, level: %d, tmRemainder: %d\n", - uid, onlineTime, level, timeRemainder); + purple_debug_info("QQ", "level: %d, uid %d, tmOnline: %d, tmRemainder: %d\n", + level, uid, onlineTime, timeRemainder); bd = qq_buddy_data_find(gc, uid); if (bd == NULL) { diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/buddy_info.h --- a/libpurple/protocols/qq/buddy_info.h Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/buddy_info.h Sun Dec 07 01:38:29 2008 +0000 @@ -88,4 +88,6 @@ void qq_request_get_level_2007(PurpleConnection *gc, guint32 uid); void qq_request_get_buddies_level(PurpleConnection *gc, gint update_class); void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); + +void qq_update_buddy_icon(PurpleAccount *account, const gchar *who, gint face); #endif diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/buddy_list.c --- a/libpurple/protocols/qq/buddy_list.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/buddy_list.c Sun Dec 07 01:38:29 2008 +0000 @@ -148,10 +148,9 @@ /* 015-030: unknown key */ bytes += qq_getdata(&(bs->unknown_key[0]), QQ_KEY_LENGTH, data + bytes); - purple_debug_info("QQ_STATUS", - "uid: %d, U1: %d, ip: %s:%d, U2:%d, status:%d, U3:%04X\n", - bs->uid, bs->unknown1, inet_ntoa(bs->ip), bs->port, - bs->unknown2, bs->status, bs->unknown3); + purple_debug_info("QQ", "Status:%d, uid: %u, ip: %s:%d, U: %d - %d - %04X\n", + bs->status, bs->uid, inet_ntoa(bs->ip), bs->port, + bs->unknown1, bs->unknown2, bs->unknown3); return bytes; } @@ -163,6 +162,8 @@ gint bytes, bytes_start; gint count; guint8 position; + gchar *who; + PurpleBuddy *buddy; qq_buddy_data *bd; int entry_len = 38; @@ -220,17 +221,23 @@ } /* check if it is a valid entry */ if (bs.uid == qd->uid) { - purple_debug_warning("QQ", "I am in online list %d\n", bs.uid); + purple_debug_warning("QQ", "I am in online list %u\n", bs.uid); } /* update buddy information */ - bd = qq_buddy_data_find(gc, bs.uid); + who = uid_to_purple_name(bs.uid); + buddy = purple_find_buddy(gc->account, who); + g_free(who); + if (buddy == NULL) { + /* create no-auth buddy */ + buddy = qq_buddy_new(gc, bs.uid); + } + bd = (buddy == NULL) ? NULL : (qq_buddy_data *)buddy->proto_data; if (bd == NULL) { purple_debug_error("QQ", - "Got an online buddy %d, but not in my buddy list\n", bs.uid); + "Got an online buddy %u, but not in my buddy list\n", bs.uid); continue; } - /* we find one and update qq_buddy_data */ /* if(0 != fe->s->client_tag) q_bud->client_tag = fe->s->client_tag; @@ -313,7 +320,8 @@ if (bd.uid == 0 || (bytes - buddy_bytes) != bytes_expected) { purple_debug_info("QQ", - "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes); + "Buddy entry, expect %d bytes, read %d bytes\n", + bytes_expected, bytes - buddy_bytes); g_free(bd.nickname); continue; } else { @@ -387,7 +395,7 @@ /* 05: skip unknow 0x00 */ bytes += 1; if (uid == 0 || (type != 0x1 && type != 0x4)) { - purple_debug_info("QQ", "Buddy entry, uid=%d, type=%d", uid, type); + purple_debug_info("QQ", "Buddy entry, uid=%u, type=%d", uid, type); continue; } if(0x1 == type) { /* a buddy */ @@ -397,7 +405,7 @@ } else { /* a group */ rmd = qq_room_data_find(gc, uid); if(rmd == NULL) { - purple_debug_info("QQ", "Unknow room id %d", uid); + purple_debug_info("QQ", "Unknow room id %u", uid); qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, uid); } else { rmd->my_role = QQ_ROOM_ROLE_YES; @@ -528,6 +536,8 @@ qq_data *qd; gint bytes; guint32 my_uid; + gchar *who; + PurpleBuddy *buddy; qq_buddy_data *bd; qq_buddy_status bs; @@ -549,9 +559,17 @@ * QQ_BUDDY_ONLINE_INVISIBLE */ bytes += qq_get32(&my_uid, data + bytes); - bd = qq_buddy_data_find(gc, bs.uid); + /* update buddy information */ + who = uid_to_purple_name(bs.uid); + buddy = purple_find_buddy(gc->account, who); + g_free(who); + if (buddy == NULL) { + /* create no-auth buddy */ + buddy = qq_buddy_new(gc, bs.uid); + } + bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data; if (bd == NULL) { - purple_debug_warning("QQ", "Get status of unknown buddy %d\n", bs.uid); + purple_debug_warning("QQ", "Got status of no-auth buddy %u\n", bs.uid); return; } @@ -582,8 +600,6 @@ g_return_if_fail(uid != 0); - who = uid_to_purple_name(uid); - /* purple supports signon and idle time * but it is not much use for QQ, I do not use them */ /* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */ @@ -612,7 +628,9 @@ purple_debug_error("QQ", "unknown status: 0x%X\n", status); break; } - purple_debug_info("QQ", "Update buddy %s status as %s\n", who, status_id); + + purple_debug_info("QQ", "buddy %u status = %s\n", uid, status_id); + who = uid_to_purple_name(uid); purple_prpl_got_user_status(gc->account, who, status_id, NULL); if (flag & QQ_COMM_FLAG_MOBILE && status != QQ_BUDDY_OFFLINE) diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/buddy_opt.c --- a/libpurple/protocols/qq/buddy_opt.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/buddy_opt.c Sun Dec 07 01:38:29 2008 +0000 @@ -110,11 +110,11 @@ g_free(who); if (buddy == NULL) { - purple_debug_error("QQ", "Can not find purple buddy of %d\n", uid); + purple_debug_error("QQ", "Can not find purple buddy of %u\n", uid); return NULL; } if (buddy->proto_data == NULL) { - purple_debug_error("QQ", "Can not find buddy data of %d\n", uid); + purple_debug_error("QQ", "Can not find buddy data of %u\n", uid); return NULL; } return (qq_buddy_data *)buddy->proto_data; @@ -146,9 +146,8 @@ return NULL; } + purple_debug_info("QQ", "Add new purple buddy: [%u]\n", uid); who = uid_to_purple_name(uid); - - purple_debug_info("QQ", "Add new purple buddy: [%s]\n", who); buddy = purple_buddy_new(gc->account, who, NULL); /* alias is NULL */ buddy->proto_data = NULL; @@ -214,7 +213,7 @@ g_return_if_fail(uid > 0); - g_snprintf(uid_str, sizeof(uid_str), "%d", uid); + g_snprintf(uid_str, sizeof(uid_str), "%u", uid); bytes = strlen(uid_str); qq_send_cmd_mess(gc, QQ_CMD_REMOVE_BUDDY, (guint8 *) uid_str, bytes, 0, uid); } @@ -234,7 +233,7 @@ bytes += qq_put8(raw_data + bytes, auth_len); bytes += qq_putdata(raw_data + bytes, auth, auth_len); - g_snprintf(uid_str, sizeof(uid_str), "%d", uid); + g_snprintf(uid_str, sizeof(uid_str), "%u", uid); bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str)); qq_send_cmd_mess(gc, QQ_CMD_REMOVE_BUDDY, raw_data, bytes, 0, uid); @@ -318,7 +317,7 @@ add_req->auth_len = 0; who = uid_to_purple_name(uid); - msg = g_strdup_printf(_("%d needs Q&A"), uid); + msg = g_strdup_printf(_("%u needs Q&A"), uid); purple_request_input(gc, _("Add buddy Q&A"), msg, _("Input answer here"), NULL, @@ -481,7 +480,7 @@ g_return_if_fail(uid > 0); /* we need to send the ascii code of this uid to qq server */ - g_snprintf(uid_str, sizeof(uid_str), "%d", uid); + g_snprintf(uid_str, sizeof(uid_str), "%u", uid); qq_send_cmd_mess(gc, QQ_CMD_ADD_BUDDY_NO_AUTH, (guint8 *) uid_str, strlen(uid_str), 0, uid); } @@ -501,25 +500,26 @@ /* this buddy needs authentication, text conversion is done at lowest level */ static void request_add_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text) { - gchar *text_qq, uid_str[11]; - guint8 bar, *raw_data; - gint bytes = 0; + guint8 raw_data[MAX_PACKET_SIZE - 16]; + gint bytes; + gchar *msg, uid_str[11]; + guint8 bar; g_return_if_fail(uid != 0); - g_snprintf(uid_str, sizeof(uid_str), "%d", uid); + g_snprintf(uid_str, sizeof(uid_str), "%u", uid); bar = 0x1f; - raw_data = g_newa(guint8, QQ_MSG_IM_MAX); + bytes = 0; bytes += qq_putdata(raw_data + bytes, (guint8 *) uid_str, strlen(uid_str)); bytes += qq_put8(raw_data + bytes, bar); bytes += qq_put8(raw_data + bytes, response); if (text != NULL) { - text_qq = utf8_to_qq(text, QQ_CHARSET_DEFAULT); + msg = utf8_to_qq(text, QQ_CHARSET_DEFAULT); bytes += qq_put8(raw_data + bytes, bar); - bytes += qq_putdata(raw_data + bytes, (guint8 *) text_qq, strlen(text_qq)); - g_free(text_qq); + bytes += qq_putdata(raw_data + bytes, (guint8 *) msg, strlen(msg)); + g_free(msg); } qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH, raw_data, bytes); @@ -661,7 +661,7 @@ } who = uid_to_purple_name(uid); - msg = g_strdup_printf(_("%d needs authentication"), uid); + msg = g_strdup_printf(_("%u needs authentication"), uid); purple_request_input(gc, _("Add buddy authorize"), msg, _("Input request here"), _("Would you be my friend?"), @@ -706,7 +706,7 @@ return; } - purple_debug_info("QQ", "Remove buddy with invalid QQ number %d\n", uid); + purple_debug_info("QQ", "Remove buddy with invalid QQ number %u\n", uid); qq_buddy_free(buddy); } @@ -745,7 +745,7 @@ buddy = qq_buddy_find(gc, uid); if (data[0] != 0) { - msg = g_strdup_printf(_("Failed removing buddy %d"), uid); + msg = g_strdup_printf(_("Failed removing buddy %u"), uid); purple_notify_info(gc, _("QQ Buddy"), msg, NULL); g_free(msg); } @@ -767,7 +767,7 @@ qd = (qq_data *) gc->proto_data; if (data[0] == 0) { - purple_debug_info("QQ", "Reply OK for removing me from %d's buddy list\n", uid); + purple_debug_info("QQ", "Reply OK for removing me from %u's buddy list\n", uid); return; } msg = g_strdup_printf(_("Failed removing me from %d's buddy list"), uid); @@ -788,7 +788,7 @@ qd = (qq_data *) gc->proto_data; - purple_debug_info("QQ", "Process buddy add for id [%d]\n", uid); + purple_debug_info("QQ", "Process buddy add for id [%u]\n", uid); qq_show_packet("buddy_add_no_auth", data, data_len); if (NULL == (segments = split_data(data, data_len, "\x1f", 2))) @@ -796,7 +796,7 @@ dest_uid = segments[0]; reply = segments[1]; - if (strtol(dest_uid, NULL, 10) != qd->uid) { /* should not happen */ + if (strtoul(dest_uid, NULL, 10) != qd->uid) { /* should not happen */ purple_debug_error("QQ", "Add buddy reply is to [%s], not me!", dest_uid); g_strfreev(segments); return; @@ -814,7 +814,7 @@ } qq_request_get_buddies_online(gc, 0, 0); - purple_debug_info("QQ", "Successed adding into %d's buddy list", uid); + purple_debug_info("QQ", "Successed adding into %u's buddy list", uid); g_strfreev(segments); return; } @@ -850,7 +850,7 @@ qd = (qq_data *) gc->proto_data; - purple_debug_info("QQ", "Process buddy add no auth for id [%d]\n", uid); + purple_debug_info("QQ", "Process buddy add no auth for id [%u]\n", uid); qq_show_packet("buddy_add_no_auth_ex", data, data_len); bytes = 0; @@ -860,7 +860,7 @@ g_return_if_fail(dest_uid == uid); if (reply == 0x99) { - purple_debug_info("QQ", "Successed adding buddy %d\n", uid); + purple_debug_info("QQ", "Successed adding buddy %u\n", uid); qq_buddy_find_or_new(gc, uid); qq_request_buddy_info(gc, uid, 0, 0); @@ -874,7 +874,7 @@ } if (reply != 0) { - purple_debug_info("QQ", "Failed adding buddy %d, Unknow reply 0x%02X\n", + purple_debug_info("QQ", "Failed adding buddy %u, Unknow reply 0x%02X\n", uid, reply); } @@ -943,7 +943,7 @@ g_return_if_fail(uid != 0 && reason != NULL); - purple_debug_info("QQ", "Buddy %d request adding, msg: %s\n", uid, reason); + purple_debug_info("QQ", "Buddy %u request adding, msg: %s\n", uid, reason); add_req = g_new0(qq_buddy_req, 1); add_req->gc = gc; @@ -973,7 +973,7 @@ gchar *msg, *reason; g_return_if_fail(from != NULL && to != NULL); - uid = strtol(from, NULL, 10); + uid = strtoul(from, NULL, 10); g_return_if_fail(uid != 0); if (purple_prefs_get_bool("/plugins/prpl/qq/auto_get_authorize_info")) { @@ -1022,7 +1022,7 @@ g_return_if_fail(uid != 0); bytes += qq_get16(&flag1, data + bytes); bytes += qq_get16(&flag2, data + bytes); - purple_debug_info("QQ", "Check code reply Ok, uid %d, flag 0x%04X-0x%04X\n", + purple_debug_info("QQ", "Check code reply Ok, uid %u, flag 0x%04X-0x%04X\n", uid, flag1, flag2); return; } @@ -1036,7 +1036,7 @@ g_return_if_fail(code != NULL && code_len > 0 && from != NULL); - uid = strtol(from, NULL, 10); + uid = strtoul(from, NULL, 10); raw_data = g_newa(guint8, code_len + 16); bytes = 0; bytes += qq_put8(raw_data + bytes, 0x03); @@ -1085,7 +1085,7 @@ g_return_if_fail(from != NULL && to != NULL); g_return_if_fail(data != NULL && data_len >= 3); - uid = strtol(from, NULL, 10); + uid = strtoul(from, NULL, 10); g_return_if_fail(uid != 0); /* qq_show_packet("server_buddy_add_request_ex", data, data_len); */ @@ -1116,7 +1116,7 @@ g_return_if_fail(from != NULL && to != NULL); - uid = strtol(from, NULL, 10); + uid = strtoul(from, NULL, 10); who = uid_to_purple_name(uid); buddy = purple_find_buddy(account, who); @@ -1189,7 +1189,7 @@ qd = (qq_data *) gc->proto_data; - uid = strtol(from, NULL, 10); + uid = strtoul(from, NULL, 10); g_return_if_fail(uid > 0); server_buddy_check_code(gc, from, data, data_len); @@ -1251,7 +1251,7 @@ g_free(primary); g_free(secondary); - uid = strtol(from, NULL, 10); + uid = strtoul(from, NULL, 10); g_return_if_fail(uid != 0); buddy = qq_buddy_find(gc, uid); diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/char_conv.c --- a/libpurple/protocols/qq/char_conv.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/char_conv.c Sun Dec 07 01:38:29 2008 +0000 @@ -27,77 +27,16 @@ #include "char_conv.h" #include "packet_parse.h" -#include "qq.h" #include "utils.h" -#define QQ_SMILEY_AMOUNT 96 - #define UTF8 "UTF-8" #define QQ_CHARSET_ZH_CN "GB18030" #define QQ_CHARSET_ENG "ISO-8859-1" #define QQ_NULL_MSG "(NULL)" /* return this if conversion fails */ -#define QQ_NULL_SMILEY "" /* return this if smiley conversion fails */ - -const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = { - 0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x73, - 0x74, 0x75, 0x76, 0x77, 0x8a, 0x8b, 0x8c, 0x8d, - 0x8e, 0x8f, 0x78, 0x79, 0x7a, 0x7b, 0x90, 0x91, - 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x59, 0x5a, 0x5c, 0x58, 0x57, 0x55, 0x7c, 0x7d, - 0x7e, 0x7f, 0x9a, 0x9b, 0x60, 0x67, 0x9c, 0x9d, - 0x9e, 0x5e, 0x9f, 0x89, 0x80, 0x81, 0x82, 0x62, - 0x63, 0x64, 0x65, 0x66, 0x83, 0x68, 0x84, 0x85, - 0x86, 0x87, 0x6b, 0x6e, 0x6f, 0x70, 0x88, 0xa0, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x56, 0x5b, 0x5d, - 0x5f, 0x61, 0x69, 0x6a, 0x6c, 0x6d, 0x71, 0x72 -}; - - -const gchar *purple_smiley_map[QQ_SMILEY_AMOUNT] = { - "/jy", "/pz", "/se", "/fd", "/dy", "/ll", "/hx", "/bz", - "/shui", "/dk ", "/gg", "/fn", "/tp", "/cy", "/wx", "/ng", - "/kuk", "/feid", "/zk", "/tu", "/tx", "/ka", "/by", "/am", - "/jie", "/kun", "/jk", "/lh", "/hanx", "/db", "/fendou", - "/zhm", - "/yiw", "/xu", "/yun", "/zhem", "/shuai", "/kl", "/qiao", - "/zj", - "/shan", "/fad", "/aiq", "/tiao", "/zhao", "/mm", "/zt", - "/maom", - "/xg", "/yb", "/qianc", "/dp", "/bei", "/dg", "/shd", - "/zhd", - "/dao", "/zq", "/yy", "/bb", "/gf", "/fan", "/yw", "/mg", - "/dx", "/wen", "/xin", "/xs", "/hy", "/lw", "/dh", "/sj", - "/yj", "/ds", "/ty", "/yl", "/qiang", "/ruo", "/ws", - "/shl", - "/dd", "/mn", "/hl", "/mamao", "/qz", "/fw", "/oh", "/bj", - "/qsh", "/xig", "/xy", "/duoy", "/xr", "/xixing", "/nv", - "/nan" -}; - -/* these functions parse font-attr */ -static gchar _get_size(gchar font_attr) -{ - return font_attr & 0x1f; -} - -static gboolean _check_bold(gchar font_attr) -{ - return (font_attr & 0x20) ? TRUE : FALSE; -} - -static gboolean _check_italic(gchar font_attr) -{ - return (font_attr & 0x40) ? TRUE : FALSE; -} - -static gboolean _check_underline(gchar font_attr) -{ - return (font_attr & 0x80) ? TRUE : FALSE; -} /* convert a string from from_charset to to_charset, using g_convert */ +/* Warning: do not return NULL */ static gchar *do_convert(const gchar *str, gssize len, const gchar *to_charset, const gchar *from_charset) { GError *error = NULL; @@ -109,15 +48,12 @@ ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error); if (error == NULL) { - return ret; /* conversion is OK */ + return ret; /* convert is OK */ } - /* conversion error */ + /* convert error */ purple_debug_error("QQ_CONVERT", "%s\n", error->message); - - qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ_CONVERT", - (guint8 *) str, (len == -1) ? strlen(str) : len, - "Dump failed text"); + qq_show_packet("Dump failed text", (guint8 *) str, (len == -1) ? strlen(str) : len); g_error_free(error); return g_strdup(QQ_NULL_MSG); @@ -127,6 +63,7 @@ * take the input as a pascal string and return a converted c-string in UTF-8 * returns the number of bytes read, return -1 if fatal error * the converted UTF-8 will be saved in ret + * Return: *ret != NULL */ gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data) { @@ -162,156 +99,15 @@ return 1 + len; } -/* convert QQ formatted msg to Purple formatted msg (and UTF-8) */ -gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg, const gint client_version) -{ - GString *encoded; - guint8 font_attr, font_size, color[3], bar; - gboolean is_bold, is_italic, is_underline; - guint16 charset_code; - gchar *font_name, *color_code, *msg_utf8, *tmp, *ret; - gint bytes = 0; - - /* checked qq_show_packet OK */ - /* qq_show_packet("QQ_MESG recv for font style", data, len); */ - - bytes += qq_get8(&font_attr, data + bytes); - bytes += qq_getdata(color, 3, data + bytes); /* red,green,blue */ - color_code = g_strdup_printf("#%02x%02x%02x", color[0], color[1], color[2]); - - bytes += qq_get8(&bar, data + bytes); /* skip, not sure of its use */ - bytes += qq_get16(&charset_code, data + bytes); - - tmp = g_strndup((gchar *)(data + bytes), len - bytes); - font_name = qq_to_utf8(tmp, QQ_CHARSET_DEFAULT); - g_free(tmp); - - font_size = _get_size(font_attr); - is_bold = _check_bold(font_attr); - is_italic = _check_italic(font_attr); - is_underline = _check_underline(font_attr); - - /* Although there is charset returned from QQ msg, it can't be used. - * For example, if a user send a Chinese message from English Windows - * the charset_code in QQ msg is 0x0000, not 0x8602. - * Therefore, it is better to use uniform conversion. - * By default, we use GBK, which includes all character of SC, TC, and EN. */ - msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); - encoded = g_string_new(""); - - /* Henry: The range QQ sends rounds from 8 to 22, where a font size - * of 10 is equal to 3 in html font tag */ - g_string_append_printf(encoded, - "", - color_code, font_name, font_size / 3); - purple_debug_info("QQ_MESG", - "recv \n", - color_code, font_name, font_size / 3); - g_string_append(encoded, msg_utf8); - - if (is_bold) { - g_string_prepend(encoded, ""); - g_string_append(encoded, ""); - } - if (is_italic) { - g_string_prepend(encoded, ""); - g_string_append(encoded, ""); - } - if (is_underline) { - g_string_prepend(encoded, ""); - g_string_append(encoded, ""); - } - - g_string_append(encoded, ""); - ret = encoded->str; - - g_free(msg_utf8); - g_free(font_name); - g_free(color_code); - g_string_free(encoded, FALSE); - - return ret; -} - -/* two convenience methods, using do_convert */ +/* Warning: do not return NULL */ gchar *utf8_to_qq(const gchar *str, const gchar *to_charset) { return do_convert(str, -1, to_charset, UTF8); } +/* Warning: do not return NULL */ gchar *qq_to_utf8(const gchar *str, const gchar *from_charset) { return do_convert(str, -1, UTF8, from_charset); } -/* QQ uses binary code for smiley, while purple uses strings. - * There is a mapping relation between these two. */ -gchar *qq_smiley_to_purple(gchar *text) -{ - gint index; - gchar qq_smiley, *cur_seg, **segments, *ret; - GString *converted; - - converted = g_string_new(""); - segments = split_data((guint8 *) text, strlen(text), "\x14\x15", 0); - if(segments == NULL) - return NULL; - - g_string_append(converted, segments[0]); - while ((*(++segments)) != NULL) { - cur_seg = *segments; - qq_smiley = cur_seg[0]; - for (index = 0; index < QQ_SMILEY_AMOUNT; index++) { - if (qq_smiley_map[index] == qq_smiley) - break; - } - if (index >= QQ_SMILEY_AMOUNT) { - g_string_append(converted, QQ_NULL_SMILEY); - } else { - g_string_append(converted, purple_smiley_map[index]); - g_string_append(converted, (cur_seg + 1)); - } - } - - ret = converted->str; - g_string_free(converted, FALSE); - return ret; -} - -/* convert smiley from purple style to qq binary code */ -gchar *purple_smiley_to_qq(gchar *text) -{ - gchar *begin, *cursor, *ret; - gint index; - GString *converted; - - converted = g_string_new(text); - - for (index = 0; index < QQ_SMILEY_AMOUNT; index++) { - begin = cursor = converted->str; - while ((cursor = g_strstr_len(cursor, -1, purple_smiley_map[index]))) { - g_string_erase(converted, (cursor - begin), strlen(purple_smiley_map[index])); - g_string_insert_c(converted, (cursor - begin), 0x14); - g_string_insert_c(converted, (cursor - begin + 1), qq_smiley_map[index]); - cursor++; - } - } - g_string_append_c(converted, 0x20); /* important for last smiiley */ - - ret = converted->str; - g_string_free(converted, FALSE); - return ret; -} - -void qq_filter_str(gchar *str) { - gchar *temp; - if (str == NULL) { - return; - } - - for (temp = str; *temp != 0; temp++) { - /*if (*temp == '\r' || *temp == '\n') *temp = ' ';*/ - if (*temp > 0 && *temp < 0x20) *temp = ' '; - } - g_strstrip(str); -} diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/char_conv.h --- a/libpurple/protocols/qq/char_conv.h Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/char_conv.h Sun Dec 07 01:38:29 2008 +0000 @@ -32,13 +32,7 @@ gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data); gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset); -gchar *qq_smiley_to_purple(gchar *text); -gchar *purple_smiley_to_qq(gchar *text); - gchar *utf8_to_qq(const gchar *str, const gchar *to_charset); gchar *qq_to_utf8(const gchar *str, const gchar *from_charset); -gchar *qq_encode_to_purple(guint8 *font_attr_data, gint len, const gchar *msg, const gint client_version); -gchar *qq_im_filter_html(const gchar *text); -void qq_filter_str(gchar *str); #endif diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/group.c --- a/libpurple/protocols/qq/group.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/group.c Sun Dec 07 01:38:29 2008 +0000 @@ -40,7 +40,7 @@ guint32 ext_id; g_return_if_fail(input != NULL); - ext_id = strtol(input, NULL, 10); + ext_id = strtoul(input, NULL, 10); /* 0x00000000 means search for demo group */ qq_request_room_search(gc, ext_id, QQ_ROOM_SEARCH_ONLY); } diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/group_im.c --- a/libpurple/protocols/qq/group_im.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/group_im.c Sun Dec 07 01:38:29 2008 +0000 @@ -64,8 +64,12 @@ serv_got_joined_chat(gc, rmd->id, rmd->title_utf8); conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, rmd->title_utf8, purple_connection_get_account(gc)); if (conv != NULL) { - topic_utf8 = g_strdup_printf("%d %s", rmd->ext_id, rmd->notice_utf8); - purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8); + if (rmd->notice_utf8 != NULL) { + topic_utf8 = g_strdup_printf("%u %s", rmd->ext_id, rmd->notice_utf8); + } else { + topic_utf8 = g_strdup_printf("%u", rmd->ext_id); + } + purple_debug_info("QQ", "Chat topic = %s\n", topic_utf8); purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8); g_free(topic_utf8); @@ -157,48 +161,6 @@ g_list_free(flags); } -/* send IM to a group */ -void qq_request_room_send_im(PurpleConnection *gc, guint32 room_id, const gchar *msg) -{ - gint data_len, bytes; - guint8 *raw_data, *send_im_tail; - guint16 msg_len; - gchar *msg_filtered; - - g_return_if_fail(room_id != 0 && msg != NULL); - - msg_filtered = purple_markup_strip_html(msg); - /* purple_debug_info("QQ", "Send qun mesg filterd: %s\n", msg_filtered); */ - msg_len = strlen(msg_filtered); - - data_len = 2 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN; - raw_data = g_newa(guint8, data_len); - - bytes = 0; - bytes += qq_put16(raw_data + bytes, msg_len + QQ_SEND_IM_AFTER_MSG_LEN); - bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len); - send_im_tail = qq_get_send_im_tail(NULL, NULL, NULL, - FALSE, FALSE, FALSE, - QQ_SEND_IM_AFTER_MSG_LEN); - bytes += qq_putdata(raw_data + bytes, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN); - g_free(send_im_tail); - g_free(msg_filtered); - - if (bytes == data_len) /* create OK */ - qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_MSG, room_id, raw_data, data_len); - else - purple_debug_error("QQ", - "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes); -} - -/* this is the ACK */ -void qq_process_room_send_im(PurpleConnection *gc, guint8 *data, gint len) -{ - /* return should be the internal group id - * but we have nothing to do with it */ - return; -} - void qq_room_got_chat_in(PurpleConnection *gc, guint32 room_id, guint32 uid_from, const gchar *msg, time_t in_time) { @@ -208,6 +170,7 @@ gchar *from; g_return_if_fail(gc != NULL && room_id != 0); + g_return_if_fail(msg != NULL); conv = purple_find_chat(gc, room_id); rmd = qq_room_data_find(gc, room_id); @@ -218,6 +181,8 @@ } if (conv == NULL) { + purple_debug_info("QQ", "Conversion of %u is not open, missing from %d:/n%s/v", + room_id, uid_from, msg); return; } @@ -225,7 +190,7 @@ bd = qq_room_buddy_find(rmd, uid_from); if (bd == NULL || bd->nickname == NULL) - from = g_strdup_printf("%d", uid_from); + from = g_strdup_printf("%u", uid_from); else from = g_strdup(bd->nickname); } else { @@ -238,10 +203,9 @@ /* recv an IM from a group chat */ void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type) { - gchar *msg_with_purple_smiley, *msg_utf8_encoded; qq_data *qd; - gint skip_len; - gint bytes ; + gchar *msg_smiley, *msg_fmt, *msg_utf8; + gint bytes, tail_len; struct { guint32 ext_id; guint8 type8; @@ -249,87 +213,224 @@ guint16 unknown; guint16 msg_seq; time_t send_time; - guint32 unknown4; + guint32 version; guint16 msg_len; gchar *msg; - guint8 *font_attr; - gint font_attr_len; - } packet; + } im_text; + guint32 temp_id; + guint16 content_type; + guint8 frag_count, frag_index; + guint16 msg_id; + qq_im_format *fmt = NULL; - g_return_if_fail(data != NULL && data_len > 0); - - /* FIXME: check length here */ - + /* at least include im_text.msg_len */ + g_return_if_fail(data != NULL && data_len > 23); qd = (qq_data *) gc->proto_data; /* qq_show_packet("ROOM_IM", data, data_len); */ - - memset(&packet, 0, sizeof(packet)); + memset(&im_text, 0, sizeof(im_text)); bytes = 0; - bytes += qq_get32(&(packet.ext_id), data + bytes); - bytes += qq_get8(&(packet.type8), data + bytes); + bytes += qq_get32(&(im_text.ext_id), data + bytes); + bytes += qq_get8(&(im_text.type8), data + bytes); if(QQ_MSG_TEMP_QUN_IM == msg_type) { - bytes += qq_get32(&(id), data + bytes); + bytes += qq_get32(&temp_id, data + bytes); } - bytes += qq_get32(&(packet.member_uid), bytes + data); - bytes += qq_get16(&packet.unknown, data + bytes); /* 0x0001? */ - bytes += qq_get16(&(packet.msg_seq), data + bytes); - bytes += qq_getime(&packet.send_time, data + bytes); - bytes += qq_get32(&packet.unknown4, data + bytes); /* versionID */ - /* - * length includes font_attr - * this msg_len includes msg and font_attr - **** the format is **** - * length of all - * 1. unknown 10 bytes - * 2. 0-ended string - * 3. font_attr - */ + bytes += qq_get32(&(im_text.member_uid), bytes + data); + bytes += qq_get16(&im_text.unknown, data + bytes); /* 0x0001? */ + bytes += qq_get16(&(im_text.msg_seq), data + bytes); + bytes += qq_getime(&im_text.send_time, data + bytes); + bytes += qq_get32(&im_text.version, data + bytes); + bytes += qq_get16(&(im_text.msg_len), data + bytes); + purple_debug_info("QQ", "Room IM, ext id %u, seq %u, version 0x%04X, len %u\n", + im_text.ext_id, im_text.msg_seq, im_text.version, im_text.msg_len); + + if (im_text.msg_len != data_len - bytes) { + purple_debug_warning("QQ", "Room IM length %d should be %d\n", + im_text.msg_len, data_len - bytes); + im_text.msg_len = data_len - bytes; + } - bytes += qq_get16(&(packet.msg_len), data + bytes); - g_return_if_fail(packet.msg_len > 0); - /* - * 10 bytes from lumaqq - * contentType = buf.getChar(); - * totalFragments = buf.get() & 255; - * fragmentSequence = buf.get() & 255; - * messageId = buf.getChar(); - * buf.getInt(); - */ + g_return_if_fail(im_text.msg_len > 0 && bytes + im_text.msg_len <= data_len); + if(msg_type != QQ_MSG_QUN_IM_UNKNOWN) { + g_return_if_fail(im_text.msg_len >= 10); - if(msg_type != QQ_MSG_UNKNOWN_QUN_IM) - skip_len = 10; - else - skip_len = 0; - bytes += skip_len; + bytes += qq_get16(&content_type, data + bytes); + bytes += qq_get8(&frag_count, data + bytes); + bytes += qq_get8(&frag_index, data + bytes); + bytes += qq_get16(&msg_id, data + bytes); + bytes += 4; /* skip 0x(00 00 00 00) */ + purple_debug_info("QQ", "Room IM, content %d, frag %d-%d, msg id %u\n", + content_type, frag_count, frag_index, msg_id); + im_text.msg_len -= 10; + } + g_return_if_fail(im_text.msg_len > 0); /* qq_show_packet("Message", data + bytes, data_len - bytes); */ - - packet.msg = g_strdup((gchar *) data + bytes); - bytes += strlen(packet.msg) + 1; - /* there might not be any font_attr, check it */ - packet.font_attr_len = data_len - bytes; - if (packet.font_attr_len > 0) { - packet.font_attr = g_memdup(data + bytes, packet.font_attr_len); - /* qq_show_packet("font_attr", packet.font_attr, packet.font_attr_len); */ + if (frag_count <= 1 || frag_count == frag_index + 1) { + fmt = qq_im_fmt_new(); + tail_len = qq_get_im_tail(fmt, data + bytes, data_len - bytes); + im_text.msg = g_strndup((gchar *)(data + bytes), data_len - tail_len); } else { - packet.font_attr = NULL; + im_text.msg = g_strndup((gchar *)(data + bytes), data_len - bytes); } /* group im_group has no flag to indicate whether it has font_attr or not */ - msg_with_purple_smiley = qq_smiley_to_purple(packet.msg); - if (packet.font_attr_len > 0) { - msg_utf8_encoded = qq_encode_to_purple(packet.font_attr, - packet.font_attr_len, msg_with_purple_smiley, qd->client_version); + msg_smiley = qq_emoticon_to_purple(im_text.msg); + if (fmt != NULL) { + msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley); + msg_utf8 = qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT); + g_free(msg_fmt); + qq_im_fmt_free(fmt); } else { - msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); + msg_utf8 = qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT); + } + g_free(msg_smiley); + + purple_debug_info("QQ", "Room (%u) IM from %u: %s\n", + im_text.ext_id, im_text.member_uid, msg_utf8); + qq_room_got_chat_in(gc, id, im_text.member_uid, msg_utf8, im_text.send_time); + + g_free(msg_utf8); + g_free(im_text.msg); +} + +/* send IM to a group */ +static void request_room_send_im(PurpleConnection *gc, guint32 room_id, qq_im_format *fmt, const gchar *msg) +{ + guint8 raw_data[MAX_PACKET_SIZE - 16]; + gint bytes; + + g_return_if_fail(room_id != 0 && msg != NULL); + + bytes = 0; + bytes += qq_put16(raw_data + bytes, 0); + bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg)); + bytes += qq_put_im_tail(raw_data + bytes, fmt); + /* reset first two bytes */ + qq_put16(raw_data, bytes - 2); + + qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_IM, room_id, raw_data, bytes); +} + +/* this is the ACK */ +void qq_process_room_send_im(PurpleConnection *gc, guint8 *data, gint len) +{ + /* return should be the internal group id + * but we have nothing to do with it */ + return; +} + +void qq_process_room_send_im_ex(PurpleConnection *gc, guint8 *data, gint len) +{ + /* return should be the internal group id + * but we have nothing to do with it */ + return; +} + +static void request_room_send_im_ex(PurpleConnection *gc, guint32 room_id, + qq_im_format *fmt, gchar *msg, guint16 msg_id, guint8 frag_count, guint8 frag_index) +{ + guint8 raw_data[MAX_PACKET_SIZE - 16]; + gint bytes; + + + g_return_if_fail(room_id != 0 && msg != NULL); + + bytes = 0; + bytes += qq_put16(raw_data + bytes, 0); /* packet len */ + /* type 0x0001, text only; 0x0002, with custom emoticon */ + bytes += qq_put16(raw_data + bytes, 0x0001); + bytes += qq_put8(raw_data + bytes, frag_count); + bytes += qq_put8(raw_data + bytes, frag_index); + bytes += qq_put16(raw_data + bytes, msg_id); + bytes += qq_put32(raw_data + bytes, 0); /* unknow 4 bytes */ + bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg)); + if (frag_count == frag_index + 1) { + bytes += qq_put8(raw_data + bytes, 0x20); /* add extra SPACE */ + bytes += qq_put_im_tail(raw_data + bytes, fmt); } - qq_room_got_chat_in(gc, id, packet.member_uid, msg_utf8_encoded, packet.send_time); + + /* reset first two bytes as length */ + qq_put16(raw_data, bytes - 2); + + /*qq_show_packet("QQ_ROOM_CMD_SEND_IM_EX", raw_data, bytes); */ + qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_IM_EX, room_id, raw_data, bytes); +} + +/* send a chat msg to a QQ Qun + * called by purple */ +int qq_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFlags flags) +{ + qq_data *qd; + qq_im_format *fmt; + gchar *msg_stripped, *tmp; + GSList *segments, *it; + gint msg_len; + const gchar *start_invalid; + gboolean is_smiley_none; + guint8 frag_count, frag_index; + + g_return_val_if_fail(NULL != gc && NULL != gc->proto_data, -1); + g_return_val_if_fail(id != 0 && what != NULL, -1); + + qd = (qq_data *) gc->proto_data; + purple_debug_info("QQ", "Send chat IM to %u, len %d:\n%s\n", id, strlen(what), what); + + /* qq_show_packet("chat IM UTF8", (guint8 *)what, strlen(what)); */ + + fmt = qq_im_fmt_new_by_purple(what); + is_smiley_none = qq_im_smiley_none(what); + + msg_stripped = purple_markup_strip_html(what); + g_return_val_if_fail(msg_stripped != NULL, -1); + /* qq_show_packet("IM Stripped", (guint8 *)what, strlen(what)); */ - g_free(msg_with_purple_smiley); - g_free(msg_utf8_encoded); - g_free(packet.msg); - g_free(packet.font_attr); + /* Check and valid utf8 string */ + msg_len = strlen(msg_stripped); + if (!g_utf8_validate(msg_stripped, msg_len, &start_invalid)) { + if (start_invalid > msg_stripped) { + tmp = g_strndup(msg_stripped, start_invalid - msg_stripped); + g_free(msg_stripped); + msg_stripped = g_strconcat(tmp, _("(Invalid UTF-8 string)"), NULL); + g_free(tmp); + } else { + g_free(msg_stripped); + msg_stripped = g_strdup(_("(Invalid UTF-8 string)")); + } + } + + is_smiley_none = qq_im_smiley_none(what); + segments = qq_im_get_segments(msg_stripped, is_smiley_none); + g_free(msg_stripped); + + if (segments == NULL) { + return -1; + } + + qd->send_im_id++; + fmt = qq_im_fmt_new_by_purple(what); + frag_count = g_slist_length(segments); + frag_index = 0; +/* + if (frag_count <= 1) { +*/ + for (it = segments; it; it = it->next) { + request_room_send_im(gc, id, fmt, (gchar *)it->data); + g_free(it->data); + } +/* + } else { + for (it = segments; it; it = it->next) { + request_room_send_im_ex(gc, id, fmt, (gchar *)it->data, + qd->send_im_id, frag_count, frag_index); + g_free(it->data); + frag_index++; + } + } +*/ + qq_im_fmt_free(fmt); + g_slist_free(segments); + return 1; } diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/group_im.h --- a/libpurple/protocols/qq/group_im.h Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/group_im.h Sun Dec 07 01:38:29 2008 +0000 @@ -36,8 +36,9 @@ void qq_room_got_chat_in(PurpleConnection *gc, guint32 room_id, guint32 uid_from, const gchar *msg, time_t in_time); -void qq_request_room_send_im(PurpleConnection *gc, guint32 room_id, const gchar *msg); +int qq_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags); void qq_process_room_send_im(PurpleConnection *gc, guint8 *data, gint len); +void qq_process_room_send_im_ex(PurpleConnection *gc, guint8 *data, gint len); void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type); diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/group_info.c --- a/libpurple/protocols/qq/group_info.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/group_info.c Sun Dec 07 01:38:29 2008 +0000 @@ -144,7 +144,7 @@ purple_notify_user_info_add_section_break(room_info); - utf8_value = g_strdup_printf(("%d"), rmd->creator_uid); + utf8_value = g_strdup_printf(("%u"), rmd->creator_uid); purple_notify_user_info_add_pair(room_info, _("Creator"), utf8_value); g_free(utf8_value); @@ -160,7 +160,7 @@ purple_notify_user_info_add_pair(room_info, _("Authorize"), utf8_value); g_free(utf8_value); - utf8_value = g_strdup_printf(("%d"), rmd->ext_id); + utf8_value = g_strdup_printf(("%u"), rmd->ext_id); purple_notify_userinfo(gc, utf8_value, room_info, NULL, NULL); g_free(utf8_value); @@ -214,7 +214,7 @@ * 2(qunNoticeLen), qunNoticeLen(qunNoticeContent, 1(qunDescLen), * qunDestLen(qunDestcontent)) */ bytes += qq_get8(&unknown1, data + bytes); - purple_debug_info("QQ", "type=%u creatorid=%u category=%u maxmembers=%u\n", + purple_debug_info("QQ", "type: %u creator: %u category: %u maxmembers: %u\n", rmd->type8, rmd->creator_uid, rmd->category, max_members); if (qd->client_version >= 2007) { @@ -241,7 +241,7 @@ #if 0 if(organization != 0 || role != 0) { - purple_debug_info("QQ_GRP", "%d, organization=%d, role=%d\n", member_uid, organization, role); + purple_debug_info("QQ", "%u, organization=%d, role=%d\n", member_uid, organization, role); } #endif @@ -277,7 +277,7 @@ return; } - topic_utf8 = g_strdup_printf("%d %s", rmd->ext_id, rmd->notice_utf8); + topic_utf8 = g_strdup_printf("%u %s", rmd->ext_id, rmd->notice_utf8); purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8); purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8); g_free(topic_utf8); @@ -285,7 +285,7 @@ void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc) { - guint32 id, member_uid; + guint32 room_id, member_uid; guint8 unknown; gint bytes, num; qq_room_data *rmd; @@ -299,13 +299,13 @@ } bytes = 0; - bytes += qq_get32(&id, data + bytes); + bytes += qq_get32(&room_id, data + bytes); bytes += qq_get8(&unknown, data + bytes); /* 0x3c ?? */ - g_return_if_fail(id > 0); + g_return_if_fail(room_id > 0); - rmd = qq_room_data_find(gc, id); + rmd = qq_room_data_find(gc, room_id); if (rmd == NULL) { - purple_debug_error("QQ", "We have no group info for internal id [%d]\n", id); + purple_debug_error("QQ", "Can not info of room id [%u]\n", room_id); return; } @@ -384,7 +384,7 @@ purple_debug_error("QQ", "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!"); } - purple_debug_info("QQ", "Group \"%s\" obtained %d member info\n", rmd->title_utf8, num); + purple_debug_info("QQ", "Group \"%s\" got %d member info\n", rmd->title_utf8, num); rmd->is_got_buddies = TRUE; qq_room_conv_set_onlines(gc, rmd); diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/group_internal.c --- a/libpurple/protocols/qq/group_internal.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/group_internal.c Sun Dec 07 01:38:29 2008 +0000 @@ -34,8 +34,9 @@ { qq_room_data *rmd; - purple_debug_info("QQ", "Created room data: %s, ext id %d, id %d\n", - title, ext_id, id); + purple_debug_info("QQ", "Created room data: %s, ext id %u, id %u\n", + title == NULL ? "(NULL)" : title, + ext_id, id); rmd = g_new0(qq_room_data, 1); rmd->my_role = QQ_ROOM_ROLE_NO; rmd->id = id; @@ -60,9 +61,9 @@ gchar *value; value = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID); - id = value ? strtol(value, NULL, 10) : 0; + id = value ? strtoul(value, NULL, 10) : 0; value= g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID); - ext_id = value ? strtol(value, NULL, 10) : 0; + ext_id = value ? strtoul(value, NULL, 10) : 0; value = g_strdup(g_hash_table_lookup(data, QQ_ROOM_KEY_TITLE_UTF8)); rmd = room_data_new(id, ext_id, value); @@ -107,10 +108,10 @@ } g_hash_table_replace(chat->components, g_strdup(QQ_ROOM_KEY_INTERNAL_ID), - g_strdup_printf("%d", rmd->id)); + g_strdup_printf("%u", rmd->id)); g_hash_table_replace(chat->components, g_strdup(QQ_ROOM_KEY_EXTERNAL_ID), - g_strdup_printf("%d", rmd->ext_id)); + g_strdup_printf("%u", rmd->ext_id)); g_hash_table_replace(chat->components, g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8)); } @@ -121,14 +122,15 @@ PurpleGroup *g; PurpleChat *chat; - purple_debug_info("QQ", "Add new chat: id %d, ext id %d, title %s\n", - rmd->id, rmd->ext_id, rmd->title_utf8); + purple_debug_info("QQ", "Add new chat: id %u, ext id %u, title %s\n", + rmd->id, rmd->ext_id, + rmd->title_utf8 == NULL ? "(NULL)" : rmd->title_utf8); components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); g_hash_table_insert(components, - g_strdup(QQ_ROOM_KEY_INTERNAL_ID), g_strdup_printf("%d", rmd->id)); + g_strdup(QQ_ROOM_KEY_INTERNAL_ID), g_strdup_printf("%u", rmd->id)); g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_EXTERNAL_ID), - g_strdup_printf("%d", rmd->ext_id)); + g_strdup_printf("%u", rmd->ext_id)); g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8)); chat = purple_chat_new(purple_connection_get_account(gc), rmd->title_utf8, components); @@ -150,7 +152,7 @@ g_return_val_if_fail(id != 0 && ext_id != 0, NULL); - purple_debug_info("QQ", "Find or add new room: id %d, ext id %d\n", id, ext_id); + purple_debug_info("QQ", "Find or add new room: id %u, ext id %u\n", id, ext_id); rmd = qq_room_data_find(gc, id); if (rmd == NULL) { @@ -160,7 +162,7 @@ qd->groups = g_list_append(qd->groups, rmd); } - num_str = g_strdup_printf("%d", ext_id); + num_str = g_strdup_printf("%u", ext_id); chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str); g_free(num_str); if (chat) { @@ -181,7 +183,7 @@ g_return_if_fail (gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; - purple_debug_info("QQ", "Find and remove room data, id %d", id); + purple_debug_info("QQ", "Find and remove room data, id %u", id); rmd = qq_room_data_find(gc, id); g_return_if_fail (rmd != NULL); @@ -189,8 +191,8 @@ qd->groups = g_list_remove(qd->groups, rmd); room_data_free(rmd); - purple_debug_info("QQ", "Find and remove chat, ext_id %d", ext_id); - num_str = g_strdup_printf("%d", ext_id); + purple_debug_info("QQ", "Find and remove chat, ext_id %u", ext_id); + num_str = g_strdup_printf("%u", ext_id); chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str); g_free(num_str); diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/group_join.c --- a/libpurple/protocols/qq/group_join.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/group_join.c Sun Dec 07 01:38:29 2008 +0000 @@ -116,7 +116,7 @@ rmd = qq_room_data_find(add_req->gc, add_req->id); if (rmd == NULL) { - purple_debug_error("QQ", "Can not find room data of %d\n", add_req->id); + purple_debug_error("QQ", "Can not find room data of %u\n", add_req->id); g_free(add_req); return; } @@ -137,9 +137,9 @@ qq_room_req *add_req; g_return_if_fail(rmd != NULL); - purple_debug_info("QQ", "Room (internal id: %d) needs authentication\n", rmd->id); + purple_debug_info("QQ", "Room id %u needs authentication\n", rmd->id); - msg = g_strdup_printf("QQ Qun %d needs authentication\n", rmd->ext_id); + msg = g_strdup_printf("QQ Qun %u needs authentication\n", rmd->ext_id); add_req = g_new0(qq_room_req, 1); add_req->gc = gc; add_req->id = rmd->id; @@ -154,7 +154,7 @@ g_free(msg); } -void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd, +void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd, guint8 opt, guint32 uid, const gchar *reason_utf8) { guint8 raw_data[MAX_PACKET_SIZE - 16]; @@ -219,11 +219,11 @@ rmd = qq_room_data_find(gc, id); if (rmd != NULL) { - msg = g_strdup_printf(_("Successfully joined Qun %s (%d)"), rmd->title_utf8, rmd->ext_id); - qq_got_attention(gc, msg); + msg = g_strdup_printf(_("Successed join to Qun %s (%u)"), rmd->title_utf8, rmd->ext_id); + qq_got_message(gc, msg); g_free(msg); } else { - qq_got_attention(gc, _("Successfully joined Qun")); + qq_got_message(gc, _("Successed join to Qun")); } } @@ -261,19 +261,19 @@ break; case QQ_ROOM_JOIN_NEED_AUTH: purple_debug_info("QQ", - "Fail joining group [%d] %s, needs authentication\n", + "Failed to join room ext id %u %s, needs authentication\n", rmd->ext_id, rmd->title_utf8); rmd->my_role = QQ_ROOM_ROLE_NO; do_room_join_request(gc, rmd); break; case QQ_ROOM_JOIN_DENIED: - msg = g_strdup_printf(_("Qun %d denied to join"), rmd->ext_id); + msg = g_strdup_printf(_("Qun %u denied to join"), rmd->ext_id); purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), msg); g_free(msg); break; default: purple_debug_info("QQ", - "Failed joining group [%d] %s, unknown reply: 0x%02x\n", + "Failed to join room ext id %u %s, unknown reply: 0x%02x\n", rmd->ext_id, rmd->title_utf8, reply); purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), _("Join Qun, Unknow Reply")); @@ -298,7 +298,7 @@ purple_debug_info("QQ", "Join room %s, extend id %s\n", id_str, ext_id_str); if (id_str != NULL) { - id = strtol(id_str, NULL, 10); + id = strtoul(id_str, NULL, 10); if (id != 0) { rmd = qq_room_data_find(gc, id); if (rmd) { @@ -312,7 +312,7 @@ if (ext_id_str == NULL) { return; } - ext_id = strtol(ext_id_str, NULL, 10); + ext_id = strtoul(ext_id_str, NULL, 10); if (ext_id == 0) { return; } @@ -345,7 +345,7 @@ gint bytes = 0; guint8 type; - purple_debug_info("QQ", "Search QQ Qun %d\n", ext_id); + purple_debug_info("QQ", "Search QQ Qun %u\n", ext_id); type = (ext_id == 0x00000000) ? QQ_ROOM_SEARCH_TYPE_DEMO : QQ_ROOM_SEARCH_TYPE_BY_ID; bytes = 0; @@ -361,12 +361,12 @@ gchar field[11]; room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, rmd->title_utf8, NULL); - g_snprintf(field, sizeof(field), "%d", rmd->ext_id); + g_snprintf(field, sizeof(field), "%u", rmd->ext_id); purple_roomlist_room_add_field(qd->roomlist, room, field); - g_snprintf(field, sizeof(field), "%d", rmd->creator_uid); + g_snprintf(field, sizeof(field), "%u", rmd->creator_uid); purple_roomlist_room_add_field(qd->roomlist, room, field); purple_roomlist_room_add_field(qd->roomlist, room, rmd->desc_utf8); - g_snprintf(field, sizeof(field), "%d", rmd->id); + g_snprintf(field, sizeof(field), "%u", rmd->id); purple_roomlist_room_add_field(qd->roomlist, room, field); g_snprintf(field, sizeof(field), "%d", rmd->type8); purple_roomlist_room_add_field(qd->roomlist, room, field); diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/group_opt.c --- a/libpurple/protocols/qq/group_opt.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/group_opt.c Sun Dec 07 01:38:29 2008 +0000 @@ -202,7 +202,7 @@ rmd = qq_room_data_find(gc, id); g_return_if_fail(rmd != NULL); - purple_debug_info("QQ", "Succeed in modify members for room %d\n", rmd->ext_id); + purple_debug_info("QQ", "Succeed in modify members for room %u\n", rmd->ext_id); qq_room_got_chat_in(gc, id, 0, _("Successfully changed Qun member"), now); } @@ -246,7 +246,7 @@ bytes += qq_get32(&id, data + bytes); g_return_if_fail(id > 0); - purple_debug_info("QQ", "Succeed modify room info of %d\n", id); + purple_debug_info("QQ", "Successfully modified room info of %u\n", id); qq_room_got_chat_in(gc, id, 0, _("Successfully changed Qun information"), now); } @@ -339,7 +339,7 @@ qq_send_room_cmd_only(gc, QQ_ROOM_CMD_ACTIVATE, id); qq_update_room(gc, 0, rmd->id); - purple_debug_info("QQ", "Succeed in create Qun, external ID %d\n", rmd->ext_id); + purple_debug_info("QQ", "Succeed in create Qun, ext id %u\n", rmd->ext_id); add_req = g_new0(qq_room_req, 1); add_req->gc = gc; @@ -347,7 +347,7 @@ purple_request_action(gc, _("QQ Qun Operation"), _("You have successfully created a Qun"), - _("Would you like to set detailed information now?"), + _("Would you like to set up the detail information now?"), 1, purple_connection_get_account(gc), NULL, NULL, add_req, 2, @@ -370,7 +370,7 @@ rmd = qq_room_data_find(gc, id); g_return_if_fail(rmd != NULL); - purple_debug_info("QQ", "Succeed in activate Qun %d\n", rmd->ext_id); + purple_debug_info("QQ", "Succeed in activate Qun %u\n", rmd->ext_id); } void qq_group_manage_group(PurpleConnection *gc, GHashTable *data) @@ -382,7 +382,7 @@ g_return_if_fail(data != NULL); id_ptr = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID); - id = strtol(id_ptr, NULL, 10); + id = strtoul(id_ptr, NULL, 10); g_return_if_fail(id > 0); rmd = qq_room_data_find(gc, id); @@ -421,13 +421,13 @@ add_req->id = id; add_req->member = member_id; - purple_debug_info("QQ", "%d requested to join room, ext id %d\n", member_id, ext_id); + purple_debug_info("QQ", "%u requested to join room, ext id %u\n", member_id, ext_id); rmd = qq_room_data_find(gc, id); g_return_if_fail(rmd != NULL); if (qq_room_buddy_find(rmd, member_id)) { purple_debug_info("QQ", "Approve join, buddy joined before\n"); - msg = g_strdup_printf(_("%d requested to join Qun %d for %s"), + msg = g_strdup_printf(_("%u requested to join Qun %u for %s"), member_id, ext_id, reason); qq_room_got_chat_in(gc, id, 0, msg, now); qq_send_cmd_group_auth(gc, rmd, QQ_ROOM_AUTH_REQUEST_APPROVE, member_id, ""); @@ -440,7 +440,7 @@ qq_request_buddy_info(gc, member_id, 0, QQ_BUDDY_INFO_DISPLAY); } who = uid_to_purple_name(member_id); - msg = g_strdup_printf(_("%d request to join Qun %d"), member_id, ext_id); + msg = g_strdup_printf(_("%u request to join Qun %u"), member_id, ext_id); purple_request_action(gc, _("QQ Qun Operation"), msg, reason, @@ -453,7 +453,6 @@ g_free(who); g_free(msg); g_free(reason); - g_free(reason); } /* the request to join a group is rejected */ @@ -478,7 +477,7 @@ bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes); msg = g_strdup_printf - (_("Failed to join Qun %d, operated by admin %d"), ext_id, admin_uid); + (_("Failed to join Qun %u, operated by admin %u"), ext_id, admin_uid); purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason); @@ -520,7 +519,7 @@ rmd->my_role = QQ_ROOM_ROLE_YES; } - msg = g_strdup_printf(_("Joining Qun %d is approved by admin %d for %s"), + msg = g_strdup_printf(_("Joinning Qun %u is approved by admin %u for %s"), ext_id, admin_uid, reason); now = time(NULL); qq_room_got_chat_in(gc, id, 0, msg, now); @@ -555,7 +554,7 @@ rmd->my_role = QQ_ROOM_ROLE_NO; } - msg = g_strdup_printf(_("Removed buddy %d."), uid); + msg = g_strdup_printf(_("Removed buddy %u."), uid); qq_room_got_chat_in(gc, id, 0, msg, now); g_free(msg); } @@ -588,7 +587,7 @@ qq_update_room(gc, 0, rmd->id); - msg = g_strdup_printf(_("New buddy %d joined."), uid); + msg = g_strdup_printf(_("New buddy %u joined."), uid); qq_room_got_chat_in(gc, id, 0, msg, now); g_free(msg); } diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/im.c --- a/libpurple/protocols/qq/im.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/im.c Sun Dec 07 01:38:29 2008 +0000 @@ -42,7 +42,12 @@ #include "send_file.h" #include "utils.h" -#define DEFAULT_FONT_NAME_LEN 4 +#define QQ_MSG_IM_MAX 700 /* max length of IM */ + +enum { + QQ_IM_TEXT = 0x01, + QQ_IM_AUTO_REPLY = 0x02 +}; enum { @@ -63,8 +68,6 @@ }; typedef struct _qq_im_header qq_im_header; -typedef struct _qq_recv_extended_im_text qq_recv_extended_im_text; - struct _qq_im_header { /* this is the common part of normal_text */ guint16 version_from; @@ -74,75 +77,6 @@ guint16 im_type; }; -#define QQ_SEND_IM_AFTER_MSG_HEADER_LEN 8 -#define DEFAULT_FONT_NAME "\0xcb\0xce\0xcc\0xe5" - -guint8 *qq_get_send_im_tail(const gchar *font_color, - const gchar *font_size, - const gchar *font_name, - gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len) -{ - gchar *s1; - unsigned char *rgb; - gint font_name_len; - guint8 *send_im_tail; - const guint8 simsun[] = { 0xcb, 0xce, 0xcc, 0xe5 }; - - if (font_name) { - font_name_len = strlen(font_name); - } else { - font_name_len = DEFAULT_FONT_NAME_LEN; - font_name = (const gchar *) simsun; - } - - send_im_tail = g_new0(guint8, tail_len); - - g_strlcpy((gchar *) (send_im_tail + QQ_SEND_IM_AFTER_MSG_HEADER_LEN), - font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN); - send_im_tail[tail_len - 1] = (guint8) tail_len; - - send_im_tail[0] = 0x00; - if (font_size) { - send_im_tail[1] = (guint8) (atoi(font_size) * 3 + 1); - } else { - send_im_tail[1] = 10; - } - if (is_bold) - send_im_tail[1] |= 0x20; - if (is_italic) - send_im_tail[1] |= 0x40; - if (is_underline) - send_im_tail[1] |= 0x80; - - if (font_color) { - s1 = g_strndup(font_color + 1, 6); - /* Henry: maybe this is a bug of purple, the string should have - * the length of odd number @_@ - * George Ang: This BUG maybe fixed by Purple. adding new byte - * would cause a crash. - */ - /* s2 = g_strdup_printf("%sH", s1); */ - rgb = purple_base16_decode(s1, NULL); - g_free(s1); - /* g_free(s2); */ - if (rgb) - { - memcpy(send_im_tail + 2, rgb, 3); - g_free(rgb); - } else { - send_im_tail[2] = send_im_tail[3] = send_im_tail[4] = 0; - } - } else { - send_im_tail[2] = send_im_tail[3] = send_im_tail[4] = 0; - } - - send_im_tail[5] = 0x00; - send_im_tail[6] = 0x86; - send_im_tail[7] = 0x22; /* encoding, 0x8622=GB, 0x0000=EN, define BIG5 support here */ - /* qq_show_packet("QQ_MESG", send_im_tail, tail_len); */ - return (guint8 *) send_im_tail; -} - /* read the common parts of the normal_im, * returns the bytes read if succeed, or -1 if there is any error */ static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len) @@ -159,7 +93,615 @@ return bytes; } -void qq_got_attention(PurpleConnection *gc, const gchar *msg) +typedef struct _qq_emoticon qq_emoticon; +struct _qq_emoticon { + guint8 symbol; + gchar *name; +}; + +static gboolean emoticons_is_sorted = FALSE; +/* Map for purple smiley convert to qq, need qsort */ +static qq_emoticon emoticons_std[] = { + {0x4f, "/:)"}, {0x4f, "/wx"}, {0x4f, "/small_smile"}, + {0x42, "/:~"}, {0x42, "/pz"}, {0x42, "/curl_lip"}, + {0x43, "/:*"}, {0x43, "/se"}, {0x43, "/desire"}, + {0x44, "/:|"}, {0x44, "/fd"}, {0x44, "/dazed"}, + {0x45, "/8-)"}, {0x45, "/dy"}, {0x45, "/revel"}, + {0x46, "/:<"}, {0x46, "/ll"}, {0x46, "/cry"}, + {0x47, "/:$"}, {0x47, "/hx"}, {0x47, "/bashful"}, + {0x48, "/:x"}, {0x48, "/bz"}, {0x48, "/shut_mouth"}, + {0x8f, "/|-)"}, {0x8f, "/kun"}, {0x8f, "/sleepy"}, + {0x49, "/:z"}, {0x49, "/shui"}, {0x49, "/sleep"}, /* after sleepy */ + {0x4a, "/:'"}, {0x4a, "/dk"}, {0x4a, "/weep"}, + {0x4b, "/:-|"}, {0x4b, "/gg"}, {0x4b, "/embarassed"}, + {0x4c, "/:@"}, {0x4c, "/fn"}, {0x4c, "/pissed_off"}, + {0x4d, "/:P"}, {0x4d, "/tp"}, {0x4d, "/act_up"}, + {0x4e, "/:D"}, {0x4e, "/cy"}, {0x4e, "/toothy_smile"}, + {0x41, "/:O"}, {0x41, "/jy"}, {0x41, "/surprised"}, + {0x73, "/:("}, {0x73, "/ng"}, {0x73, "/sad"}, + {0x74, "/:+"}, {0x74, "/kuk"}, {0x74, "/cool"}, + {0xa1, "/--b"}, {0xa1, "/lengh"}, + {0x76, "/:Q"}, {0x76, "/zk"}, {0x76, "/crazy"}, + {0x8a, "/;P"}, {0x8a, "/tx"}, {0x8a, "/titter"}, + {0x8b, "/;-D"}, {0x8b, "/ka"}, {0x8b, "/cute"}, + {0x8c, "/;d"}, {0x8c, "/by"}, {0x8c, "/disdain"}, + {0x8d, "/;o"}, {0x8d, "/am"}, {0x8d, "/arrogant"}, + {0x8e, "/:g"}, {0x8e, "/jie"}, {0x8e, "/starving"}, + {0x78, "/:!"}, {0x78, "/jk"}, {0x78, "/terror"}, + {0x79, "/:L"}, {0x79, "/lh"}, {0x79, "/sweat"}, + {0x7a, "/:>"}, {0x7a, "/hanx"}, {0x7a, "/smirk"}, + {0x7b, "/:;"}, {0x7b, "/db"}, {0x7b, "/soldier"}, + {0x90, "/;f"}, {0x90, "/fendou"}, {0x90, "/struggle"}, + {0x91, "/:-S"}, {0x91, "/zhm"}, {0x91, "/curse"}, + {0x92, "/?"}, {0x92, "/yiw"}, {0x92, "/question"}, + {0x93, "/;x"}, {0x93, "/xu"}, {0x93, "/shh"}, + {0x94, "/;@"}, {0x94, "/yun"}, {0x94, "/dizzy"}, + {0x95, "/:8"}, {0x95, "/zhem"}, {0x95, "/excrutiating"}, + {0x96, "/;!"}, {0x96, "/shuai"}, {0x96, "/freaked_out"}, + {0x97, "/!!!"}, {0x97, "/kl"}, {0x97, "/skeleton"}, + {0x98, "/xx"}, {0x98, "/qiao"}, {0x98, "/hammer"}, + {0x99, "/bye"}, {0x99, "/zj"}, {0x99, "/bye"}, + {0xa2, "/wipe"}, {0xa2, "/ch"}, + {0xa3, "/dig"}, {0xa3, "/kb"}, + {0xa4, "/handclap"},{0xa4, "/gz"}, + {0xa5, "/&-("}, {0xa5, "/qd"}, + {0xa6, "/B-)"}, {0xa6, "/huaix"}, + {0xa7, "/<@"}, {0xa7, "/zhh"}, + {0xa8, "/@>"}, {0xa8, "/yhh"}, + {0xa9, "/:-O"}, {0xa9, "/hq"}, + {0xaa, "/>-|"}, {0xaa, "/bs"}, + {0xab, "/P-("}, {0xab, "/wq"}, + {0xac, "/:'|"}, {0xac, "/kk"}, + {0xad, "/X-)"}, {0xad, "/yx"}, + {0xae, "/:*"}, {0xae, "/qq"}, + {0xaf, "/@x"}, {0xaf, "/xia"}, + {0xb0, "/8*"}, {0xb0, "/kel"}, + {0xb1, "/pd"}, {0xb1, "/cd"}, + {0x61, "/"}, {0x61, "/xig"}, {0x61, "/watermelon"}, + {0xb2, "/beer"}, {0xb2, "/pj"}, + {0xb3, "/basketb"}, {0xb3, "/lq"}, + {0xb4, "/oo"}, {0xb4, "/pp"}, + {0x80, "/coffee"}, {0x80, "/kf"}, + {0x81, "/eat"}, {0x81, "/fan"}, + {0x62, "/rose"}, {0x62, "/mg"}, + {0x63, "/fade"}, {0x63, "/dx"}, {0x63, "/wilt"}, + {0xb5, "/showlove"},{0xb5, "/sa"}, /* after sad */ + {0x65, "/heart"}, {0x65, "/xin"}, + {0x66, "/break"}, {0x66, "/xs"}, {0x66, "/broken_heart"}, + {0x67, "/cake"}, {0x67, "/dg"}, + {0x9c, "/li"}, {0x9c, "/shd"}, {0x9c, "/lightning"}, + {0x9d, "/bome"}, {0x9d, "/zhd"}, {0x9d, "/bomb"}, + {0x9e, "/kn"}, {0x9e, "/dao"}, {0x9e, "/knife"}, + {0x5e, "/footb"}, {0x5e, "/zq"}, {0x5e, "/soccer"}, + {0xb6, "/ladybug"}, {0xb6, "/pc"}, + {0x89, "/shit"}, {0x89, "/bb"}, + {0x6e, "/moon"}, {0x6e, "/yl"}, + {0x6b, "/sun"}, {0x6b, "/ty"}, + {0x68, "/gift"}, {0x68, "/lw"}, + {0x7f, "/hug"}, {0x7f, "/yb"}, + {0x6f, "/strong"}, {0x6f, "/qiang"}, {0x6f, "/thumbs_up"}, + {0x70, "/weak"}, {0x70, "/ruo"}, {0x70, "/thumbs_down"}, + {0x88, "/share"}, {0x88, "/ws"}, {0x88, "/handshake"}, + {0xb7, "/@)"}, {0xb7, "/bq"}, + {0xb8, "/jj"}, {0xb8, "/gy"}, + {0xb9, "/@@"}, {0xb9, "/qt"}, + {0xba, "/bad"}, {0xba, "/cj"}, + {0xbb, "/loveu"}, {0xbb, "/aini"}, + {0xbc, "/no"}, {0xbc, "/bu"}, + {0xbd, "/ok"}, {0xbd, "/hd"}, + {0x5c, "/love"}, {0x5c, "/aiq"}, /* after loveu */ + {0x56, "/"}, {0x56, "/fw"}, {0x56, "/blow_kiss"}, + {0x58, "/jump"}, {0x58, "/tiao"}, + {0x5a, "/shake"}, {0x5a, "/fad"}, /* after fade */ + {0x5b, "/"}, {0x5b, "/oh"}, {0x5b, "/angry"}, + {0xbe, "/circle"}, {0xbe, "/zhq"}, + {0xbf, "/kotow"}, {0xbf, "/kt"}, + {0xc0, "/turn"}, {0xc0, "/ht"}, + {0x77, "/:t"}, {0x77, "/tu"}, {0x77, "/vomit"}, /* after turn */ + {0xa0, "/victory"}, {0xa0, "/shl"}, {0xa0, "/v"}, /* end of v */ + {0xc1, "/skip"}, {0xc1, "/tsh"}, + {0xc2, "/oY"}, {0xc2, "/hsh"}, + {0xc3, "/#-O"}, {0xc3, "/jd"}, + {0xc4, "/hiphop"}, {0xc4, "/jw"}, + {0xc5, "/kiss"}, {0xc5, "/xw"}, + {0xc6, "/<&"}, {0xc6, "/ztj"}, + {0x7c, "/pig"}, {0x7c, "/zt"}, /* after ztj */ + {0xc7, "/&>"}, {0xc7, "/ytj"}, /* must be end of "&" */ + {0x75, "/:#"}, {0x75, "/feid"}, {0x75, "/SARS"}, + {0x59, "/go"}, {0x59, "/shan"}, + {0x57, "/find"}, {0x57, "/zhao"}, {0x57, "/search"}, + {0x55, "/&"}, {0x55, "/mm"}, {0x55, "/beautiful_eyebrows"}, + {0x7d, "/cat"}, {0x7d, "/maom"}, + {0x7e, "/dog"}, {0x7e, "/xg"}, + {0x9a, "/$"}, {0x9a, "/qianc"}, {0x9a, "/money"}, + {0x9b, "/(!)"}, {0x9b, "/dp"}, {0x9b, "/lightbulb"}, + {0x60, "/cup"}, {0x60, "/bei"}, + {0x9f, "/music"}, {0x9f, "/yy"}, + {0x82, "/pill"}, {0x82, "/yw"}, + {0x64, "/kiss"}, {0x64, "/wen"}, + {0x83, "/meeting"}, {0x83, "/hy"}, + {0x84, "/phone"}, {0x84, "/dh"}, + {0x85, "/time"}, {0x85, "/sj"}, + {0x86, "/email"}, {0x86, "/yj"}, + {0x87, "/tv"}, {0x87, "/ds"}, + {0x50, "/"}, {0x50, "/dd"}, + {0x51, "/"}, {0x51, "/mn"}, {0x51, "/beauty"}, + {0x52, "/"}, {0x52, "/hl"}, + {0x53, "/"}, {0x53, "/mamao"}, + {0x54, "/"}, {0x54, "/qz"}, {0x54, "/qq"}, + {0x5d, "/"}, {0x5d, "/bj"}, {0x5d, "/baijiu"}, + {0x5f, "/"}, {0x5f, "/qsh"}, {0x5f, "/soda"}, + {0x69, "/"}, {0x69, "/xy"}, {0x69, "/rain"}, + {0x6a, "/<~>"}, {0x6a, "/duoy"}, {0x6a, "/cloudy"}, + {0x6c, "/"}, {0x6c, "/xr"}, {0x6c, "/snowman"}, + {0x6d, "/<*>"}, {0x6d, "/xixing"}, {0x6d, "/star"}, /* after starving */ + {0x71, "/<00>"}, {0x71, "/nv"}, {0x71, "/woman"}, + {0x72, "/<11>"}, {0x72, "/nan"}, {0x72, "/man"}, + {0, NULL} +}; +gint emoticons_std_num = sizeof(emoticons_std) / sizeof(qq_emoticon) - 1; + +/* Map for purple smiley convert to qq, need qsort */ +static qq_emoticon emoticons_ext[] = { + {0xc7, "/&>"}, {0xa5, "/&-("}, + {0xbb, "/loveu"}, + {0x63, "/fade"}, + {0x8f, "/sleepy"}, {0x73, "/sad"}, {0x8e, "/starving"}, + {0xc0, "/turn"}, + {0xa0, "/victory"}, {0x77, "/vomit"}, + {0xc6, "/ztj"}, + {0, NULL} +}; +gint emoticons_ext_num = sizeof(emoticons_ext) / sizeof(qq_emoticon) - 1; + +/* Map for qq smiley convert to purple */ +static qq_emoticon emoticons_sym[] = { + {0x41, "/jy"}, + {0x42, "/pz"}, + {0x43, "/se"}, + {0x44, "/fd"}, + {0x45, "/dy"}, + {0x46, "/ll"}, + {0x47, "/hx"}, + {0x48, "/bz"}, + {0x49, "/shui"}, + {0x4a, "/dk"}, + {0x4b, "/gg"}, + {0x4c, "/fn"}, + {0x4d, "/tp"}, + {0x4e, "/cy"}, + {0x4f, "/wx"}, + {0x50, "/dd"}, + {0x51, "/mn"}, + {0x52, "/hl"}, + {0x53, "/mamao"}, + {0x54, "/qz"}, + {0x55, "/mm"}, + {0x56, "/fw"}, + {0x57, "/zhao"}, + {0x58, "/tiao"}, + {0x59, "/shan"}, + {0x5a, "/fad"}, + {0x5b, "/oh"}, + {0x5c, "/aiq"}, + {0x5d, "/bj"}, + {0x5e, "/zq"}, + {0x5f, "/qsh"}, + {0x60, "/bei"}, + {0x61, "/xig"}, + {0x62, "/mg"}, + {0x63, "/dx"}, + {0x64, "/wen"}, + {0x65, "/xin"}, + {0x66, "/xs"}, + {0x67, "/dg"}, + {0x68, "/lw"}, + {0x69, "/xy"}, + {0x6a, "/duoy"}, + {0x6b, "/ty"}, + {0x6c, "/xr"}, + {0x6d, "/xixing"}, + {0x6e, "/yl"}, + {0x6f, "/qiang"}, + {0x70, "/ruo"}, + {0x71, "/nv"}, + {0x72, "/nan"}, + {0x73, "/ng"}, + {0x74, "/kuk"}, + {0x75, "/feid"}, + {0x76, "/zk"}, + {0x77, "/tu"}, + {0x78, "/jk"}, + {0x79, "/sweat"}, + {0x7a, "/hanx"}, + {0x7b, "/db"}, + {0x7c, "/zt"}, + {0x7d, "/maom"}, + {0x7e, "/xg"}, + {0x7f, "/yb"}, + {0x80, "/coffee"}, + {0x81, "/fan"}, + {0x82, "/yw"}, + {0x83, "/hy"}, + {0x84, "/dh"}, + {0x85, "/sj"}, + {0x86, "/yj"}, + {0x87, "/ds"}, + {0x88, "/ws"}, + {0x89, "/bb"}, + {0x8a, "/tx"}, + {0x8b, "/ka"}, + {0x8c, "/by"}, + {0x8d, "/am"}, + {0x8e, "/jie"}, + {0x8f, "/kun"}, + {0x90, "/fendou"}, + {0x91, "/zhm"}, + {0x92, "/yiw"}, + {0x93, "/xu"}, + {0x94, "/yun"}, + {0x95, "/zhem"}, + {0x96, "/shuai"}, + {0x97, "/kl"}, + {0x98, "/qiao"}, + {0x99, "/zj"}, + {0x9a, "/qianc"}, + {0x9b, "/dp"}, + {0x9c, "/shd"}, + {0x9d, "/zhd"}, + {0x9e, "/dao"}, + {0x9f, "/yy"}, + {0xa0, "/shl"}, + {0xa1, "/lengh"}, + {0xa2, "/wipe"}, + {0xa3, "/kb"}, + {0xa4, "/gz"}, + {0xa5, "/qd"}, + {0xa6, "/huaix"}, + {0xa7, "/zhh"}, + {0xa8, "/yhh"}, + {0xa9, "/hq"}, + {0xaa, "/bs"}, + {0xab, "/wq"}, + {0xac, "/kk"}, + {0xad, "/yx"}, + {0xae, "/qq"}, + {0xaf, "/xia"}, + {0xb0, "/kel"}, + {0xb1, "/cd"}, + {0xb2, "/pj"}, + {0xb3, "/lq"}, + {0xb4, "/pp"}, + {0xb5, "/sa"}, + {0xb6, "/pc"}, + {0xb7, "/bq"}, + {0xb8, "/gy"}, + {0xb9, "/qt"}, + {0xba, "/cj"}, + {0xbb, "/aini"}, + {0xbc, "/bu"}, + {0xbd, "/hd"}, + {0xbe, "/zhq"}, + {0xbf, "/kt"}, + {0xc0, "/ht"}, + {0xc1, "/tsh"}, + {0xc2, "/hsh"}, + {0xc3, "/jd"}, + {0xc4, "/jw"}, + {0xc5, "/xw"}, + {0xc6, "/ztj"}, + {0xc7, "/ytj"}, + {0, NULL} +}; +gint emoticons_sym_num = sizeof(emoticons_sym) / sizeof(qq_emoticon) - 1;; + +static int emoticon_cmp(const void *k1, const void *k2) +{ + const qq_emoticon *e1 = (const qq_emoticon *) k1; + const qq_emoticon *e2 = (const qq_emoticon *) k2; + if (e1->symbol == 0) { + /* purple_debug_info("QQ", "emoticon_cmp len %d\n", strlen(e2->name)); */ + return strncmp(e1->name, e2->name, strlen(e2->name)); + } + if (e2->symbol == 0) { + /* purple_debug_info("QQ", "emoticon_cmp len %d\n", strlen(e1->name)); */ + return strncmp(e1->name, e2->name, strlen(e1->name)); + } + return strcmp(e1->name, e2->name); +} + +static void emoticon_try_sort() +{ + if (emoticons_is_sorted) + return; + + purple_debug_info("QQ", "qsort stand emoticons\n"); + qsort(emoticons_std, emoticons_std_num, sizeof(qq_emoticon), emoticon_cmp); + purple_debug_info("QQ", "qsort extend emoticons\n"); + qsort(emoticons_ext, emoticons_ext_num, sizeof(qq_emoticon), emoticon_cmp); + emoticons_is_sorted = TRUE; +} + +static qq_emoticon *emoticon_find(gchar *name) +{ + qq_emoticon *ret = NULL; + qq_emoticon key; + + g_return_val_if_fail(name != NULL, NULL); + emoticon_try_sort(); + + key.name = name; + key.symbol = 0; + + /* purple_debug_info("QQ", "bsearch emoticon %.20s\n", name); */ + ret = (qq_emoticon *)bsearch(&key, emoticons_ext, emoticons_ext_num, + sizeof(qq_emoticon), emoticon_cmp); + if (ret != NULL) { + return ret; + } + ret = (qq_emoticon *)bsearch(&key, emoticons_std, emoticons_std_num, + sizeof(qq_emoticon), emoticon_cmp); + return ret; +} + +static gchar *emoticon_get(guint8 symbol) +{ + g_return_val_if_fail(symbol >= emoticons_sym[0].symbol, NULL); + g_return_val_if_fail(symbol <= emoticons_sym[emoticons_sym_num - 2].symbol, NULL); + + return emoticons_sym[symbol - emoticons_sym[0].symbol].name; +} + +/* convert qq emote icon to purple sytle + Notice: text is in qq charset, GB18030 or utf8 */ +gchar *qq_emoticon_to_purple(gchar *text) +{ + gchar *ret; + GString *converted; + gchar **segments; + gboolean have_smiley; + gchar *purple_smiley; + gchar *cur; + guint8 symbol; + + /* qq_show_packet("text", (guint8 *)text, strlen(text)); */ + g_return_val_if_fail(text != NULL && strlen(text) != 0, g_strdup("")); + + segments = g_strsplit_set(text, "\x14\x15", 0); + if(segments == NULL) { + return g_strdup(""); + } + + converted = g_string_new(""); + have_smiley = FALSE; + if (segments[0] != NULL) { + g_string_append(converted, segments[0]); + } else { + purple_debug_info("QQ", "segments[0] is NULL\n"); + } + while ((*(++segments)) != NULL) { + have_smiley = TRUE; + + cur = *segments; + if (cur == NULL) { + purple_debug_info("QQ", "current segment is NULL\n"); + break; + } + if (strlen(cur) == 0) { + purple_debug_info("QQ", "current segment length is 0\n"); + break; + } + symbol = (guint8)cur[0]; + + purple_smiley = emoticon_get(symbol); + if (purple_smiley == NULL) { + purple_debug_info("QQ", "Not found smiley of 0x%02X\n", symbol); + g_string_append(converted, ""); + } else { + purple_debug_info("QQ", "Found 0x%02X smiley is %s\n", symbol, purple_smiley); + g_string_append(converted, purple_smiley); + g_string_append(converted, cur + 1); + } + /* purple_debug_info("QQ", "next segment\n"); */ + } + + /* purple_debug_info("QQ", "end of convert\n"); */ + if (!have_smiley) { + g_string_prepend(converted, ""); + g_string_append(converted, ""); + } + ret = converted->str; + g_string_free(converted, FALSE); + return ret; +} + +void qq_im_fmt_free(qq_im_format *fmt) +{ + g_return_if_fail(fmt != NULL); + if (fmt->font) g_free(fmt->font); + g_free(fmt); +} + +qq_im_format *qq_im_fmt_new(void) +{ + qq_im_format *fmt; + const gchar simsun[] = { 0xcb, 0xce, 0xcc, 0xe5, 0}; /* simsun in Chinese */ + + fmt = g_new0(qq_im_format, 1); + memset(fmt, 0, sizeof(qq_im_format)); + fmt->font_len = strlen(simsun); + fmt->font = g_strdup(simsun); + fmt->attr = 10; + /* encoding, 0x8602=GB, 0x0000=EN, define BIG5 support here */ + fmt->charset = 0x8602; + + return fmt; +} + +qq_im_format *qq_im_fmt_new_by_purple(const gchar *msg) +{ + qq_im_format *fmt; + const gchar *start, *end, *last; + GData *attribs; + gchar *tmp; + unsigned char *rgb; + + g_return_val_if_fail(msg != NULL, NULL); + + fmt = qq_im_fmt_new(); + + last = msg; + while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { + tmp = g_datalist_get_data(&attribs, "face"); + if (tmp && strlen(tmp) > 0) { + if (fmt->font) g_free(fmt->font); + fmt->font_len = strlen(tmp); + fmt->font = g_strdup(tmp); + } + + tmp = g_datalist_get_data(&attribs, "size"); + if (tmp) { + fmt->attr = atoi(tmp) * 3 + 1; + fmt->attr &= 0x0f; + } + + tmp = g_datalist_get_data(&attribs, "color"); + if (tmp && strlen(tmp) > 1) { + rgb = purple_base16_decode(tmp + 1, NULL); + g_memmove(fmt->rgb, rgb, 3); + g_free(rgb); + } + + g_datalist_clear(&attribs); + last = end + 1; + } + + if (purple_markup_find_tag("b", msg, &start, &end, &attribs)) { + fmt->attr |= 0x20; + g_datalist_clear(&attribs); + } + + if (purple_markup_find_tag("i", msg, &start, &end, &attribs)) { + fmt->attr |= 0x40; + g_datalist_clear(&attribs); + } + + if (purple_markup_find_tag("u", msg, &start, &end, &attribs)) { + fmt->attr |= 0x80; + g_datalist_clear(&attribs); + } + + return fmt; +} + +/* convert qq format to purple + Notice: text is in qq charset, GB18030 or utf8 */ +gchar *qq_im_fmt_to_purple(qq_im_format *fmt, gchar *text) +{ + GString *converted, *tmp; + gchar *ret; + gint size; + + converted = g_string_new(text); + tmp = g_string_new(""); + g_string_append_printf(tmp, "", + fmt->rgb[0], fmt->rgb[1], fmt->rgb[2]); + g_string_prepend(converted, tmp->str); + g_string_set_size(tmp, 0); + g_string_append(converted, ""); + + /* Fixme: + * check font face can be convert to utf8 or not? + * If failed, prepending font face cause msg display as "(NULL)" */ + if (fmt->font != NULL) { + g_string_append_printf(tmp, "", fmt->font); + g_string_prepend(converted, tmp->str); + g_string_set_size(tmp, 0); + g_string_append(converted, ""); + } + size = (fmt->attr & 0x1f) / 3; + if (size >= 0) { + g_string_append_printf(tmp, "", size); + g_string_prepend(converted, tmp->str); + g_string_set_size(tmp, 0); + g_string_append(converted, ""); + } + if (fmt->attr & 0x20) { + /* bold */ + g_string_prepend(converted, ""); + g_string_append(converted, ""); + } + if (fmt->attr & 0x40) { + /* italic */ + g_string_prepend(converted, ""); + g_string_append(converted, ""); + } + if (fmt->attr & 0x80) { + /* underline */ + g_string_prepend(converted, ""); + g_string_append(converted, ""); + } + + g_string_free(tmp, TRUE); + ret = converted->str; + g_string_free(converted, FALSE); + return ret; +} + +gint qq_put_im_tail(guint8 *buf, qq_im_format *fmt) +{ + gint bytes; + + g_return_val_if_fail(buf != NULL && fmt != NULL, 0); + + bytes = 0; + bytes += qq_put8(buf + bytes, 0); + bytes += qq_put8(buf + bytes, fmt->attr); + bytes += qq_putdata(buf + bytes, fmt->rgb, sizeof(fmt->rgb)); + bytes += qq_put8(buf + bytes, 0); + bytes += qq_put16(buf + bytes, fmt->charset); + if (fmt->font != NULL && fmt->font_len > 0) { + bytes += qq_putdata(buf + bytes, (guint8 *)fmt->font, fmt->font_len); + } else { + purple_debug_warning("QQ", "Font name is empty\n"); + } + bytes += qq_put8(buf + bytes, bytes + 1); + /* qq_show_packet("IM tail", buf, bytes); */ + return bytes; +} + +/* data includes text msg and font attr*/ +gint qq_get_im_tail(qq_im_format *fmt, guint8 *data, gint data_len) +{ + gint bytes, text_len; + guint8 tail_len; + guint8 font_len; + + g_return_val_if_fail(fmt != NULL && data != NULL, 0); + g_return_val_if_fail(data_len > 1, 0); + tail_len = data[data_len - 1]; + g_return_val_if_fail(tail_len > 2, 0); + text_len = data_len - tail_len; + g_return_val_if_fail(text_len >= 0, 0); + + bytes = text_len; + /* qq_show_packet("IM tail", data + bytes, tail_len); */ + bytes += 1; /* skip 0x00 */ + bytes += qq_get8(&fmt->attr, data + bytes); + bytes += qq_getdata(fmt->rgb, sizeof(fmt->rgb), data + bytes); /* red,green,blue */ + bytes += 1; /* skip 0x00 */ + bytes += qq_get16(&fmt->charset, data + bytes); + + font_len = data_len - bytes - 1; + g_return_val_if_fail(font_len > 0, bytes + 1); + + fmt->font_len = font_len; + if (fmt->font != NULL) g_free(fmt->font); + fmt->font = g_strndup((gchar *)data + bytes, fmt->font_len); + return tail_len; +} + +void qq_got_message(PurpleConnection *gc, const gchar *msg) { qq_data *qd; gchar *from; @@ -180,27 +722,28 @@ /* process received normal text IM */ static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) { + qq_data *qd; guint16 purple_msg_type; gchar *who; - gchar *msg_with_purple_smiley; - gchar *msg_utf8_encoded; - qq_data *qd; - gint bytes = 0; - PurpleBuddy *b; + gchar *msg_smiley, *msg_fmt, *msg_utf8; + PurpleBuddy *buddy; qq_buddy_data *bd; + gint bytes, tail_len; + qq_im_format *fmt = NULL; struct { /* now comes the part for text only */ guint16 msg_seq; guint32 send_time; guint16 sender_icon; - guint8 unknown2[3]; - guint8 is_there_font_attr; - guint8 unknown3[4]; + guint8 unknown1[3]; + guint8 has_font_attr; + guint8 fragment_count; + guint8 fragment_index; + guint8 msg_id; + guint8 unknown2; guint8 msg_type; gchar *msg; /* no fixed length, ends with 0x00 */ - guint8 *font_attr; - gint font_attr_len; } im_text; g_return_if_fail (data != NULL && len > 0); @@ -209,99 +752,97 @@ qd = (qq_data *) gc->proto_data; memset(&im_text, 0, sizeof(im_text)); - /* push data into im_text */ + /* qq_show_packet("IM text", data, len); */ + bytes = 0; bytes += qq_get16(&(im_text.msg_seq), data + bytes); bytes += qq_get32(&(im_text.send_time), data + bytes); bytes += qq_get16(&(im_text.sender_icon), data + bytes); - bytes += qq_getdata((guint8 *) & (im_text.unknown2), 3, data + bytes); - bytes += qq_get8(&(im_text.is_there_font_attr), data + bytes); - /** - * from lumaqq for unknown3 - * totalFragments = buf.get() & 255; - * fragmentSequence = buf.get() & 255; - * messageId = buf.getChar(); - */ - bytes += qq_getdata((guint8 *) & (im_text.unknown3), 4, data + bytes); + bytes += qq_getdata(im_text.unknown1, sizeof(im_text.unknown1), data + bytes); /* 0x(00 00 00)*/ + bytes += qq_get8(&(im_text.has_font_attr), data + bytes); + bytes += qq_get8(&(im_text.fragment_count), data + bytes); + bytes += qq_get8(&(im_text.fragment_index), data + bytes); + bytes += qq_get8(&(im_text.msg_id), data + bytes); + bytes += 1; /* skip 0x00 */ bytes += qq_get8(&(im_text.msg_type), data + bytes); + purple_debug_info("QQ", "IM Seq %u, id %04X, fragment %d-%d, type %d, %s\n", + im_text.msg_seq, im_text.msg_id, + im_text.fragment_count, im_text.fragment_index, + im_text.msg_type, + im_text.has_font_attr ? "exist font atrr" : ""); - /* we need to check if this is auto-reply - * QQ2003iii build 0304, returns the msg without font_attr - * even the is_there_font_attr shows 0x01, and msg does not ends with 0x00 */ - if (im_text.msg_type == QQ_IM_AUTO_REPLY) { - im_text.is_there_font_attr = 0x00; /* indeed there is no this flag */ + if (im_text.has_font_attr) { + fmt = qq_im_fmt_new(); + tail_len = qq_get_im_tail(fmt, data + bytes, len - bytes); + im_text.msg = g_strndup((gchar *)(data + bytes), len - tail_len); + } else { im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); - } else { /* it is normal mesasge */ - if (im_text.is_there_font_attr) { - im_text.msg = g_strdup((gchar *)(data + bytes)); - bytes += strlen(im_text.msg) + 1; /* length decided by strlen! will it cause a crash? */ - im_text.font_attr_len = len - bytes; - im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len); - } else /* not im_text.is_there_font_attr */ - im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); - } /* if im_text.msg_type */ + } + /* qq_show_packet("IM text", (guint8 *)im_text.msg , strlen(im_text.msg) ); */ who = uid_to_purple_name(im_header->uid_from); - b = purple_find_buddy(gc->account, who); - if (b == NULL) { + buddy = purple_find_buddy(gc->account, who); + if (buddy == NULL) { /* create no-auth buddy */ - b = qq_buddy_new(gc, im_header->uid_from); + buddy = qq_buddy_new(gc, im_header->uid_from); } - bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data; + bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data; if (bd != NULL) { bd->client_tag = im_header->version_from; + bd->face = im_text.sender_icon; + qq_update_buddy_icon(gc->account, who, bd->face); } - purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0; + purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY) + ? PURPLE_MESSAGE_AUTO_RESP : 0; - msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg); - msg_utf8_encoded = im_text.is_there_font_attr ? - qq_encode_to_purple(im_text.font_attr, - im_text.font_attr_len, - msg_with_purple_smiley, qd->client_version) - : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); + msg_smiley = qq_emoticon_to_purple(im_text.msg); + if (fmt != NULL) { + msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley); + msg_utf8 = qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT); + g_free(msg_fmt); + qq_im_fmt_free(fmt); + } else { + msg_utf8 = qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT); + } + g_free(msg_smiley); /* send encoded to purple, note that we use im_text.send_time, * not the time we receive the message * as it may have been delayed when I am not online. */ - serv_got_im(gc, who, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time); + purple_debug_info("QQ", "IM from %u: %s\n", im_header->uid_from,msg_utf8); + serv_got_im(gc, who, msg_utf8, purple_msg_type, (time_t) im_text.send_time); - g_free(msg_utf8_encoded); - g_free(msg_with_purple_smiley); + g_free(msg_utf8); g_free(who); g_free(im_text.msg); - if (im_text.font_attr) g_free(im_text.font_attr); } /* process received extended (2007) text IM */ -static void process_extend_im_text( - PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) +static void process_extend_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) { + qq_data *qd; guint16 purple_msg_type; gchar *who; - gchar *msg_with_purple_smiley; - gchar *msg_utf8_encoded; - qq_data *qd; - PurpleBuddy *b; + gchar *msg_smiley, *msg_fmt, *msg_utf8; + PurpleBuddy *buddy; qq_buddy_data *bd; - gint bytes, text_len; + gint bytes, tail_len; + qq_im_format *fmt = NULL; struct { /* now comes the part for text only */ - guint16 sessionId; + guint16 msg_seq; guint32 send_time; - guint16 senderHead; - guint32 flag; - guint8 unknown2[8]; - guint8 fragmentCount; - guint8 fragmentIndex; - guint16 messageId; - guint8 replyType; + guint16 sender_icon; + guint32 has_font_attr; + guint8 unknown1[8]; + guint8 fragment_count; + guint8 fragment_index; + guint8 msg_id; + guint8 unknown2; + guint8 msg_type; gchar *msg; /* no fixed length, ends with 0x00 */ guint8 fromMobileQQ; - - guint8 is_there_font_attr; - guint8 *font_attr; - gint8 font_attr_len; } im_text; g_return_if_fail (data != NULL && len > 0); @@ -310,79 +851,69 @@ qd = (qq_data *) gc->proto_data; memset(&im_text, 0, sizeof(im_text)); - /* push data into im_text */ + /* qq_show_packet("Extend IM text", data, len); */ bytes = 0; - bytes += qq_get16(&(im_text.sessionId), data + bytes); + bytes += qq_get16(&(im_text.msg_seq), data + bytes); bytes += qq_get32(&(im_text.send_time), data + bytes); - bytes += qq_get16(&(im_text.senderHead), data + bytes); - bytes += qq_get32(&(im_text.flag), data + bytes); - - bytes += qq_getdata(im_text.unknown2, 8, data + bytes); - bytes += qq_get8(&(im_text.fragmentCount), data + bytes); - bytes += qq_get8(&(im_text.fragmentIndex), data + bytes); - - bytes += qq_get16(&(im_text.messageId), data + bytes); - bytes += qq_get8(&(im_text.replyType), data + bytes); - - im_text.font_attr_len = data[len-1] & 0xff; + bytes += qq_get16(&(im_text.sender_icon), data + bytes); + bytes += qq_get32(&(im_text.has_font_attr), data + bytes); + bytes += qq_getdata(im_text.unknown1, sizeof(im_text.unknown1), data + bytes); + bytes += qq_get8(&(im_text.fragment_count), data + bytes); + bytes += qq_get8(&(im_text.fragment_index), data + bytes); + bytes += qq_get8(&(im_text.msg_id), data + bytes); + bytes += 1; /* skip 0x00 */ + bytes += qq_get8(&(im_text.msg_type), data + bytes); + purple_debug_info("QQ", "IM Seq %u, id %04X, fragment %d-%d, type %d, %s\n", + im_text.msg_seq, im_text.msg_id, + im_text.fragment_count, im_text.fragment_index, + im_text.msg_type, + im_text.has_font_attr ? "exist font atrr" : ""); - text_len = len - bytes - im_text.font_attr_len; - im_text.msg = g_strndup((gchar *)(data + bytes), text_len); - bytes += text_len; - if(im_text.font_attr_len >= 0) - im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len); - else - { - purple_debug_error("QQ", "Failed to get IM's font attribute len %d\n", - im_text.font_attr_len); - return; + if (im_text.has_font_attr) { + fmt = qq_im_fmt_new(); + tail_len = qq_get_im_tail(fmt, data + bytes, len - bytes); + im_text.msg = g_strndup((gchar *)(data + bytes), len - tail_len); + } else { + im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); } + /* qq_show_packet("IM text", (guint8 *)im_text.msg , strlen(im_text.msg)); */ - if(im_text.fragmentCount == 0) - im_text.fragmentCount = 1; - - /* Filter tail space */ - if(im_text.fragmentIndex == im_text.fragmentCount -1) - { - gint real_len = text_len; - while(real_len > 0 && im_text.msg[real_len - 1] == 0x20) - real_len --; - - text_len = real_len; - /* Null string instead of space */ - im_text.msg[text_len] = 0; - } + if(im_text.fragment_count == 0) im_text.fragment_count = 1; who = uid_to_purple_name(im_header->uid_from); - b = purple_find_buddy(gc->account, who); - if (b == NULL) { + buddy = purple_find_buddy(gc->account, who); + if (buddy == NULL) { /* create no-auth buddy */ - b = qq_buddy_new(gc, im_header->uid_from); + buddy = qq_buddy_new(gc, im_header->uid_from); } - bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data; + bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data; if (bd != NULL) { bd->client_tag = im_header->version_from; + bd->face = im_text.sender_icon; + qq_update_buddy_icon(gc->account, who, bd->face); } purple_msg_type = 0; - msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg); - msg_utf8_encoded = im_text.font_attr ? - qq_encode_to_purple(im_text.font_attr, - im_text.font_attr_len, - msg_with_purple_smiley, qd->client_version) - : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); + msg_smiley = qq_emoticon_to_purple(im_text.msg); + if (fmt != NULL) { + msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley); + msg_utf8 = qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT); + g_free(msg_fmt); + qq_im_fmt_free(fmt); + } else { + msg_utf8 = qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT); + } + g_free(msg_smiley); /* send encoded to purple, note that we use im_text.send_time, * not the time we receive the message * as it may have been delayed when I am not online. */ - serv_got_im(gc, who, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time); + serv_got_im(gc, who, msg_utf8, purple_msg_type, (time_t) im_text.send_time); - g_free(msg_utf8_encoded); - g_free(msg_with_purple_smiley); + g_free(msg_utf8); g_free(who); g_free(im_text.msg); - if (im_text.font_attr) g_free(im_text.font_attr); } /* it is a normal IM, maybe text or video request */ @@ -400,7 +931,7 @@ return; } purple_debug_info("QQ", - "Got IM to %d, type: %02X from: %d ver: %s (%04X)\n", + "Got IM to %u, type: %02X from: %u ver: %s (%04X)\n", im_header.uid_to, im_header.im_type, im_header.uid_from, qq_get_ver_desc(im_header.version_from), im_header.version_from); @@ -461,105 +992,63 @@ return; } purple_debug_info("QQ", - "Got Extend IM to %d, type: %02X from: %d ver: %s (%04X)\n", + "Got Extend IM to %u, type: %02X from: %u ver: %s (%04X)\n", im_header.uid_to, im_header.im_type, im_header.uid_from, qq_get_ver_desc(im_header.version_from), im_header.version_from); switch (im_header.im_type) { - case QQ_NORMAL_IM_TEXT: - process_extend_im_text(gc, data + bytes, len - bytes, &im_header); - break; - case QQ_NORMAL_IM_FILE_REJECT_UDP: - qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_APPROVE_UDP: - qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_REQUEST_UDP: - qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_CANCEL: - qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_NOTIFY: - qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc); - break; - default: - /* a simple process here, maybe more later */ - qq_show_packet ("Unknow", data + bytes, len - bytes); - break; + case QQ_NORMAL_IM_TEXT: + process_extend_im_text(gc, data + bytes, len - bytes, &im_header); + break; + case QQ_NORMAL_IM_FILE_REJECT_UDP: + qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc); + break; + case QQ_NORMAL_IM_FILE_APPROVE_UDP: + qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc); + break; + case QQ_NORMAL_IM_FILE_REQUEST_UDP: + qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc); + break; + case QQ_NORMAL_IM_FILE_CANCEL: + qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc); + break; + case QQ_NORMAL_IM_FILE_NOTIFY: + qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc); + break; + case QQ_NORMAL_IM_FILE_REQUEST_TCP: + /* Check ReceivedFileIM::parseContents in eva*/ + /* some client use this function for detect invisable buddy*/ + case QQ_NORMAL_IM_FILE_APPROVE_TCP: + case QQ_NORMAL_IM_FILE_REJECT_TCP: + case QQ_NORMAL_IM_FILE_PASV: + case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP: + case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT: + case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL: + case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP: + qq_show_packet ("Not support", data, len); + break; + default: + /* a simple process here, maybe more later */ + qq_show_packet ("Unknow", data + bytes, len - bytes); + break; } } /* send an IM to uid_to */ -void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type) +static void request_send_im(PurpleConnection *gc, guint32 uid_to, gint type, + qq_im_format *fmt, gchar *msg, guint8 id, guint8 frag_count, guint8 frag_index) { qq_data *qd; - guint8 *raw_data, *send_im_tail; + guint8 raw_data[MAX_PACKET_SIZE - 16]; guint16 im_type; - gint msg_len, raw_len, font_name_len, tail_len, bytes; + gint bytes; time_t now; - gchar *msg_filtered; - GData *attribs; - gchar *font_size = NULL, *font_color = NULL, *font_name = NULL, *tmp; - gboolean is_bold = FALSE, is_italic = FALSE, is_underline = FALSE; - const gchar *start, *end, *last; qd = (qq_data *) gc->proto_data; im_type = QQ_NORMAL_IM_TEXT; - last = msg; - while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { - tmp = g_datalist_get_data(&attribs, "size"); - if (tmp) { - if (font_size) - g_free(font_size); - font_size = g_strdup(tmp); - } - tmp = g_datalist_get_data(&attribs, "color"); - if (tmp) { - if (font_color) - g_free(font_color); - font_color = g_strdup(tmp); - } - tmp = g_datalist_get_data(&attribs, "face"); - if (tmp) { - if (font_name) - g_free(font_name); - font_name = g_strdup(tmp); - } - - g_datalist_clear(&attribs); - last = end + 1; - } - - if (purple_markup_find_tag("b", msg, &start, &end, &attribs)) { - is_bold = TRUE; - g_datalist_clear(&attribs); - } - - if (purple_markup_find_tag("i", msg, &start, &end, &attribs)) { - is_italic = TRUE; - g_datalist_clear(&attribs); - } - - if (purple_markup_find_tag("u", msg, &start, &end, &attribs)) { - is_underline = TRUE; - g_datalist_clear(&attribs); - } - - purple_debug_info("QQ_MESG", "send mesg: %s\n", msg); - msg_filtered = purple_markup_strip_html(msg); - msg_len = strlen(msg_filtered); - now = time(NULL); - - font_name_len = (font_name) ? strlen(font_name) : DEFAULT_FONT_NAME_LEN; - tail_len = font_name_len + QQ_SEND_IM_AFTER_MSG_HEADER_LEN + 1; - - raw_len = QQ_SEND_IM_BEFORE_MSG_LEN + msg_len + tail_len; - raw_data = g_newa(guint8, raw_len); + /* purple_debug_info("QQ", "Send IM %d-%d\n", frag_count, frag_index); */ bytes = 0; - /* 000-003: receiver uid */ bytes += qq_put32(raw_data + bytes, qd->uid); /* 004-007: sender uid */ @@ -573,44 +1062,256 @@ /* 018-033: md5 of (uid+session_key) */ bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); /* 034-035: message type */ - bytes += qq_put16(raw_data + bytes, im_type); + bytes += qq_put16(raw_data + bytes, QQ_NORMAL_IM_TEXT); /* 036-037: sequence number */ bytes += qq_put16(raw_data + bytes, qd->send_seq); /* 038-041: send time */ + now = time(NULL); bytes += qq_put32(raw_data + bytes, (guint32) now); /* 042-043: sender icon */ bytes += qq_put16(raw_data + bytes, qd->my_icon); /* 044-046: always 0x00 */ bytes += qq_put16(raw_data + bytes, 0x0000); bytes += qq_put8(raw_data + bytes, 0x00); - /* 047-047: we use font attr */ + /* 047-047: always use font attr */ bytes += qq_put8(raw_data + bytes, 0x01); /* 048-051: always 0x00 */ - bytes += qq_put32(raw_data + bytes, 0x00000000); + /* Fixme: frag_count, frag_index not working now */ + bytes += qq_put8(raw_data + bytes, frag_count); + bytes += qq_put8(raw_data + bytes, frag_index); + bytes += qq_put8(raw_data + bytes, id); + bytes += qq_put8(raw_data + bytes, 0); /* 052-052: text message type (normal/auto-reply) */ bytes += qq_put8(raw_data + bytes, type); /* 053- : msg ends with 0x00 */ - bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len); - send_im_tail = qq_get_send_im_tail(font_color, font_size, font_name, is_bold, - is_italic, is_underline, tail_len); - /* qq_show_packet("qq_get_send_im_tail", send_im_tail, tail_len); */ - bytes += qq_putdata(raw_data + bytes, send_im_tail, tail_len); + bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg)); + if (frag_count == frag_index + 1) { + bytes += qq_put8(raw_data + bytes, 0x20); /* add extra SPACE */ + } + bytes += qq_put_im_tail(raw_data + bytes, fmt); + + /* qq_show_packet("QQ_CMD_SEND_IM", raw_data, bytes); */ + qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes); +} + +static void im_convert_and_merge(GString *dest, GString *append) +{ + gchar *converted; + g_return_if_fail(dest != NULL && append != NULL); - /* qq_show_packet("QQ_CMD_SEND_IM, raw_data, bytes); */ + if (append->str == NULL || append->len <= 0) { + return; + } + /* purple_debug_info("QQ", "Append:\n%s\n", append->str); */ + converted = utf8_to_qq(append->str, QQ_CHARSET_DEFAULT); + g_string_append(dest, converted); + g_string_set_size(append, 0); + g_free(converted); +} + +GSList *qq_im_get_segments(gchar *msg_stripped, gboolean is_smiley_none) +{ + GSList *string_list = NULL; + GString *new_string; + GString *append_utf8; + gchar *start, *p; + gint count, len; + qq_emoticon *emoticon; + + g_return_val_if_fail(msg_stripped != NULL, NULL); + + start = msg_stripped; + count = 0; + new_string = g_string_new(""); + append_utf8 = g_string_new(""); + while (*start) { + p = start; - if (bytes == raw_len) /* create packet OK */ - qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes); - else - purple_debug_error("QQ", - "Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes); + /* Convert emoticon */ + if (!is_smiley_none && *p == '/') { + if (new_string->len + append_utf8->len + 2 > QQ_MSG_IM_MAX) { + /* enough chars to send */ + im_convert_and_merge(new_string, append_utf8); + string_list = g_slist_append(string_list, strdup(new_string->str)); + g_string_set_size(new_string, 0); + continue; + } + emoticon = emoticon_find(p); + if (emoticon != NULL) { + purple_debug_info("QQ", "found emoticon %s as 0x%02X\n", + emoticon->name, emoticon->symbol); + /* QQ emoticon code prevent converting from utf8 to QQ charset + * convert append_utf8 to QQ charset + * merge the result to dest + * append qq QQ emoticon code to dest */ + im_convert_and_merge(new_string, append_utf8); + g_string_append_c(new_string, 0x14); + g_string_append_c(new_string, emoticon->symbol); + start += strlen(emoticon->name); + continue; + } else { + purple_debug_info("QQ", "Not found emoticon %.20s\n", p); + } + } - if (font_color) - g_free(font_color); - if (font_size) - g_free(font_size); - g_free(send_im_tail); - g_free(msg_filtered); + /* Get next char */ + start = g_utf8_next_char(p); + len = start - p; + if (new_string->len + append_utf8->len + len > QQ_MSG_IM_MAX) { + /* enough chars to send */ + im_convert_and_merge(new_string, append_utf8); + string_list = g_slist_append(string_list, strdup(new_string->str)); + g_string_set_size(new_string, 0); + } + g_string_append_len(append_utf8, p, len); + } + + if (new_string->len + append_utf8->len > 0) { + im_convert_and_merge(new_string, append_utf8); + string_list = g_slist_append(string_list, strdup(new_string->str)); + } + g_string_free(new_string, TRUE); + g_string_free(append_utf8, TRUE); + return string_list; +} + +gboolean qq_im_smiley_none(const gchar *msg) +{ + const gchar *start, *end, *last; + GData *attribs; + gchar *tmp; + gboolean ret = FALSE; + + g_return_val_if_fail(msg != NULL, TRUE); + + last = msg; + while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { + tmp = g_datalist_get_data(&attribs, "sml"); + if (tmp && strlen(tmp) > 0) { + if (strcmp(tmp, "none") == 0) { + ret = TRUE; + break; + } + } + g_datalist_clear(&attribs); + last = end + 1; + } + return ret; } +/* Grab custom emote icons +static GSList* qq_grab_emoticons(const char *msg, const char*username) +{ + GSList *list; + GList *smileys; + PurpleSmiley *smiley; + const char *smiley_shortcut; + char *ptr; + int length; + PurpleStoredImage *img; + smileys = purple_smileys_get_all(); + length = strlen(msg); + for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { + smiley = smileys->data; + smiley_shortcut = purple_smiley_get_shortcut(smiley); + purple_debug_info("QQ", "Smiley shortcut [%s]\n", smiley_shortcut); + + ptr = g_strstr_len(msg, length, smiley_shortcut); + + if (!ptr) + continue; + + purple_debug_info("QQ", "Found Smiley shortcut [%s]\n", smiley_shortcut); + + img = purple_smiley_get_stored_image(smiley); + + emoticon = g_new0(MsnEmoticon, 1); + emoticon->smile = g_strdup(purple_smiley_get_shortcut(smiley)); + emoticon->obj = msn_object_new_from_image(img, + purple_imgstore_get_filename(img), + username, MSN_OBJECT_EMOTICON); + + purple_imgstore_unref(img); + list = g_slist_prepend(list, emoticon); + } + return list; +} +*/ + +gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *what, PurpleMessageFlags flags) +{ + qq_data *qd; + guint32 uid_to; + gint type; + qq_im_format *fmt; + gchar *msg_stripped, *tmp; + GSList *segments, *it; + gint msg_len; + const gchar *start_invalid; + gboolean is_smiley_none; + guint8 frag_count, frag_index; + guint8 msg_id; + + g_return_val_if_fail(NULL != gc && NULL != gc->proto_data, -1); + g_return_val_if_fail(who != NULL && what != NULL, -1); + + qd = (qq_data *) gc->proto_data; + purple_debug_info("QQ", "Send IM to %s, len %d:\n%s\n", who, strlen(what), what); + + uid_to = purple_name_to_uid(who); + if (uid_to == qd->uid) { + /* if msg is to myself, bypass the network */ + serv_got_im(gc, who, what, flags, time(NULL)); + return 1; + } + + type = (flags == PURPLE_MESSAGE_AUTO_RESP ? QQ_IM_AUTO_REPLY : QQ_IM_TEXT); + /* qq_show_packet("IM UTF8", (guint8 *)what, strlen(what)); */ + + msg_stripped = purple_markup_strip_html(what); + g_return_val_if_fail(msg_stripped != NULL, -1); + /* qq_show_packet("IM Stripped", (guint8 *)what, strlen(what)); */ + + /* Check and valid utf8 string */ + msg_len = strlen(msg_stripped); + g_return_val_if_fail(msg_len > 0, -1); + if (!g_utf8_validate(msg_stripped, msg_len, &start_invalid)) { + if (start_invalid > msg_stripped) { + tmp = g_strndup(msg_stripped, start_invalid - msg_stripped); + g_free(msg_stripped); + msg_stripped = g_strconcat(tmp, _("(Invalid UTF-8 string)"), NULL); + g_free(tmp); + } else { + g_free(msg_stripped); + msg_stripped = g_strdup(_("(Invalid UTF-8 string)")); + } + } + + is_smiley_none = qq_im_smiley_none(what); + segments = qq_im_get_segments(msg_stripped, is_smiley_none); + g_free(msg_stripped); + + if (segments == NULL) { + return -1; + } + + qd->send_im_id++; + msg_id = (guint8)(qd->send_im_id && 0xFF); + fmt = qq_im_fmt_new_by_purple(what); + frag_count = g_slist_length(segments); + frag_index = 0; + for (it = segments; it; it = it->next) { + /* + request_send_im(gc, uid_to, type, fmt, (gchar *)it->data, + msg_id, frag_count, frag_index); + */ + request_send_im(gc, uid_to, type, fmt, (gchar *)it->data, 0, 0, 0); + g_free(it->data); + frag_index++; + } + g_slist_free(segments); + qq_im_fmt_free(fmt); + return 1; +} diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/im.h --- a/libpurple/protocols/qq/im.h Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/im.h Sun Dec 07 01:38:29 2008 +0000 @@ -27,22 +27,13 @@ #include #include "connection.h" -#include "group.h" - -#define QQ_MSG_IM_MAX 500 /* max length of IM */ -#define QQ_SEND_IM_BEFORE_MSG_LEN 53 -#define QQ_SEND_IM_AFTER_MSG_LEN 13 /* there is one 0x00 at the end */ - -enum { - QQ_IM_TEXT = 0x01, - QQ_IM_AUTO_REPLY = 0x02 -}; enum { QQ_MSG_TO_BUDDY = 0x0009, QQ_MSG_TO_UNKNOWN = 0x000a, + QQ_MSG_SMS = 0x0014, /* not sure */ QQ_MSG_NEWS = 0x0018, - QQ_MSG_UNKNOWN_QUN_IM = 0x0020, + QQ_MSG_QUN_IM_UNKNOWN = 0x0020, QQ_MSG_ADD_TO_QUN = 0x0021, QQ_MSG_DEL_FROM_QUN = 0x0022, QQ_MSG_APPLY_ADD_TO_QUN = 0x0023, @@ -57,15 +48,29 @@ QQ_MSG_EXTEND_85 = 0x0085, }; -void qq_got_attention(PurpleConnection *gc, const gchar *msg); +typedef struct { + guint8 attr; + guint8 rgb[3]; + guint16 charset; + gchar *font; /* Attension: font may NULL. font name is in QQ charset */ + guint8 font_len; +} qq_im_format; -guint8 *qq_get_send_im_tail(const gchar *font_color, - const gchar *font_size, - const gchar *font_name, - gboolean is_bold, gboolean is_italic, gboolean is_underline, gint len); +gint qq_put_im_tail(guint8 *buf, qq_im_format *fmt); +gint qq_get_im_tail(qq_im_format *fmt, guint8 *data, gint data_len); -void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type); +qq_im_format *qq_im_fmt_new(void); +void qq_im_fmt_free(qq_im_format *fmt); +qq_im_format *qq_im_fmt_new_by_purple(const gchar *msg); +gchar *qq_im_fmt_to_purple(qq_im_format *fmt, gchar *text); +gboolean qq_im_smiley_none(const gchar *msg); +GSList *qq_im_get_segments(gchar *msg_stripped, gboolean is_smiley_none); + +void qq_got_message(PurpleConnection *gc, const gchar *msg); +gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags); void qq_process_im(PurpleConnection *gc, guint8 *data, gint len); void qq_process_extend_im(PurpleConnection *gc, guint8 *data, gint len); + +gchar *qq_emoticon_to_purple(gchar *text); #endif diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/qq.c --- a/libpurple/protocols/qq/qq.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/qq.c Sun Dec 07 01:38:29 2008 +0000 @@ -56,9 +56,7 @@ #include "utils.h" #include "version.h" -#ifndef OPENQ_VERSION -#define OPENQ_VERSION DISPLAY_VERSION -#endif +#define OPENQ_VERSION "0.3.2-p19" static GList *server_list_build(gchar select) { @@ -91,7 +89,7 @@ PurpleConnection *gc; qq_data *qd; PurpleProxyInfo *gpi; - const gchar *user_server; + const gchar *custom_server; gc = purple_account_get_connection(account); g_return_if_fail(gc != NULL && gc->proto_data != NULL); @@ -101,10 +99,10 @@ qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE); - user_server = purple_account_get_string(account, "server", NULL); - purple_debug_info("QQ", "Select server '%s'\n", user_server); - if ( (user_server != NULL && strlen(user_server) > 0) && strcasecmp(user_server, "auto") != 0) { - qd->servers = g_list_append(qd->servers, g_strdup(user_server)); + custom_server = purple_account_get_string(account, "server", NULL); + purple_debug_info("QQ", "Select server '%s'\n", custom_server); + if ( (custom_server != NULL && strlen(custom_server) > 0) && strcasecmp(custom_server, "auto") != 0) { + qd->servers = g_list_append(qd->servers, g_strdup(custom_server)); return; } @@ -268,9 +266,9 @@ case QQ_BUDDY_ONLINE_INVISIBLE: g_string_append(status, _("Invisible")); break; - case QQ_BUDDY_ONLINE_BUSY: - g_string_append(status, _("Busy")); - break; + case QQ_BUDDY_ONLINE_BUSY: + g_string_append(status, _("Busy")); + break; default: g_string_printf(status, _("Unknown-%d"), bd->status); } @@ -416,9 +414,9 @@ "invisible", _("Invisible"), FALSE, TRUE, FALSE); types = g_list_append(types, status); - status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, - "busy", _("Busy"), TRUE, TRUE, FALSE); - types = g_list_append(types, status); + status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, + "busy", _("Busy"), TRUE, TRUE, FALSE); + types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, "offline", _("Offline"), FALSE, TRUE, FALSE); @@ -439,110 +437,6 @@ qq_request_change_status(gc, 0); } -static void qq_add_deny(PurpleConnection *gc, const char *who) -{ - qq_data *qd; - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - - qd = (qq_data *) gc->proto_data; - if (!qd->is_login) - return; - - if (!who || who[0] == '\0') - return; - - purple_debug_info("QQ", "Add deny for %s\n", who); -} - -static void qq_rem_deny(PurpleConnection *gc, const char *who) -{ - qq_data *qd; - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - - qd = (qq_data *) gc->proto_data; - if (!qd->is_login) - return; - - if (!who || who[0] == '\0') - return; - - purple_debug_info("QQ", "Rem deny for %s\n", who); -} - -static void qq_set_permit_deny(PurpleConnection *gc) -{ - PurpleAccount *account; - GSList *deny; - - purple_debug_info("QQ", "Set permit deny\n"); - account = purple_connection_get_account(gc); - switch (account->perm_deny) - { - case PURPLE_PRIVACY_ALLOW_ALL: - for (deny = account->deny; deny; deny = deny->next) - qq_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) - qq_add_deny(gc, deny->data); - break; - } -} - -/* IMPORTANT: PurpleConvImFlags -> PurpleMessageFlags */ -/* send an instant msg to a buddy */ -static gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags) -{ - gint type, uid_to; - gchar *msg, *msg_with_qq_smiley; - qq_data *qd; - - g_return_val_if_fail(who != NULL, -1); - - qd = (qq_data *) gc->proto_data; - - g_return_val_if_fail(strlen(message) <= QQ_MSG_IM_MAX, -E2BIG); - - type = (flags == PURPLE_MESSAGE_AUTO_RESP ? QQ_IM_AUTO_REPLY : QQ_IM_TEXT); - uid_to = purple_name_to_uid(who); - - /* if msg is to myself, bypass the network */ - if (uid_to == qd->uid) { - serv_got_im(gc, who, message, flags, time(NULL)); - } else { - msg = utf8_to_qq(message, QQ_CHARSET_DEFAULT); - msg_with_qq_smiley = purple_smiley_to_qq(msg); - qq_request_send_im(gc, uid_to, msg_with_qq_smiley, type); - g_free(msg); - g_free(msg_with_qq_smiley); - } - - return 1; -} - -/* send a chat msg to a QQ Qun */ -static int qq_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) -{ - gchar *msg, *msg_with_qq_smiley; - guint32 room_id = id; - - g_return_val_if_fail(message != NULL, -1); - g_return_val_if_fail(strlen(message) <= QQ_MSG_IM_MAX, -E2BIG); - - purple_debug_info("QQ_MESG", "Send qun mesg in utf8: %s\n", message); - msg = utf8_to_qq(message, QQ_CHARSET_DEFAULT); - msg_with_qq_smiley = purple_smiley_to_qq(msg); - qq_request_room_send_im(gc, room_id, msg_with_qq_smiley); - g_free(msg); - g_free(msg_with_qq_smiley); - - return 1; -} - /* send packet to get who's detailed information */ static void qq_show_buddy_info(PurpleConnection *gc, const gchar *who) { @@ -760,13 +654,14 @@ g_string_append(info, "khc(at)pidgin.im
\n"); g_string_append(info, "qulogic(at)pidgin.im
\n"); g_string_append(info, "rlaager(at)pidgin.im
\n"); + g_string_append(info, "Huang Guan : http://home.xxsyzx.com
\n"); g_string_append(info, "OpenQ Google Group : http://groups.google.com/group/openq
\n"); g_string_append(info, "
\n"); g_string_append(info, _("

And, all the boys in the backroom...
\n")); g_string_append(info, _("Feel free to join us! :)")); g_string_append(info, ""); - title = g_strdup_printf(_("About OpenQ r%s"), OPENQ_VERSION); + title = g_strdup_printf(_("About OpenQ %s"), OPENQ_VERSION); purple_notify_formatted(gc, title, title, NULL, info->str, NULL, NULL); g_free(title); @@ -805,7 +700,7 @@ g_return_if_fail(components != NULL); num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID); - room_id = strtol(num_str, NULL, 10); + room_id = strtoul(num_str, NULL, 10); g_return_if_fail(room_id != 0); qq_room_quit(gc, room_id); @@ -824,7 +719,7 @@ g_return_if_fail(components != NULL); num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID); - room_id = strtol(num_str, NULL, 10); + room_id = strtoul(num_str, NULL, 10); g_return_if_fail(room_id != 0); qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0, @@ -1037,26 +932,26 @@ qq_status_types, /* away_states */ qq_blist_node_menu, /* blist_node_menu */ qq_chat_info, /* chat_info */ - qq_chat_info_defaults, /* chat_info_defaults */ - qq_login, /* open */ - qq_close, /* close */ - qq_send_im, /* send_im */ + qq_chat_info_defaults, /* chat_info_defaults */ + qq_login, /* open */ + qq_close, /* close */ + qq_send_im, /* send_im */ NULL, /* set_info */ NULL, /* send_typing */ - qq_show_buddy_info, /* get_info */ - qq_change_status, /* change status */ + qq_show_buddy_info, /* get_info */ + qq_change_status, /* change status */ NULL, /* set_idle */ NULL, /* change_passwd */ - qq_add_buddy, /* add_buddy */ + qq_add_buddy, /* add_buddy */ NULL, /* add_buddies */ - qq_remove_buddy, /* remove_buddy */ + qq_remove_buddy, /* remove_buddy */ NULL, /* remove_buddies */ NULL, /* add_permit */ - qq_add_deny, /* add_deny */ + NULL, /* add_deny */ NULL, /* rem_permit */ NULL, /* rem_deny */ - qq_set_permit_deny, /* set_permit_deny */ - qq_group_join, /* join_chat */ + NULL, /* set_permit_deny */ + qq_group_join, /* join_chat */ NULL, /* reject chat invite */ NULL, /* get_chat_name */ NULL, /* chat_invite */ @@ -1075,11 +970,11 @@ NULL, /* normalize */ qq_set_custom_icon, NULL, /* remove_group */ - qq_get_chat_buddy_real_name, /* get_cb_real_name */ + qq_get_chat_buddy_real_name, /* get_cb_real_name */ NULL, /* set_chat_topic */ NULL, /* find_blist_chat */ - qq_roomlist_get_list, /* roomlist_get_list */ - qq_roomlist_cancel, /* roomlist_cancel */ + qq_roomlist_get_list, /* roomlist_get_list */ + qq_roomlist_cancel, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ NULL, /* can_receive_file */ NULL, /* qq_send_file send_file */ @@ -1170,7 +1065,6 @@ option = purple_account_option_list_new(_("Select Server"), "server", server_kv_list); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); -//#ifdef DEBUG kvp = g_new0(PurpleKeyValuePair, 1); kvp->key = g_strdup(_("QQ2005")); kvp->value = g_strdup("qq2005"); @@ -1188,7 +1082,6 @@ option = purple_account_option_list_new(_("Client Version"), "client_version", version_kv_list); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); -//#endif option = purple_account_option_bool_new(_("Connect by TCP"), "use_tcp", TRUE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); @@ -1208,7 +1101,7 @@ purple_prefs_add_none("/plugins/prpl/qq"); purple_prefs_add_bool("/plugins/prpl/qq/show_status_by_icon", TRUE); purple_prefs_add_bool("/plugins/prpl/qq/show_fake_video", FALSE); - purple_prefs_add_bool("/plugins/prpl/qq/auto_popup_conversation", FALSE); + purple_prefs_add_bool("/plugins/prpl/qq/auto_popup_conversation", TRUE); purple_prefs_add_bool("/plugins/prpl/qq/auto_get_authorize_info", TRUE); purple_prefs_add_int("/plugins/prpl/qq/resend_interval", 3); purple_prefs_add_int("/plugins/prpl/qq/resend_times", 10); diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/qq.h --- a/libpurple/protocols/qq/qq.h Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/qq.h Sun Dec 07 01:38:29 2008 +0000 @@ -182,6 +182,8 @@ gboolean is_show_notice; gboolean is_show_news; + + guint16 send_im_id; /* send IM sequence number */ }; #endif diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/qq_base.c --- a/libpurple/protocols/qq/qq_base.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/qq_base.c Sun Dec 07 01:38:29 2008 +0000 @@ -68,7 +68,8 @@ qd = (qq_data *) gc->proto_data; /* qq_show_packet("Login reply", data, len); */ - if (len < 139) { + if (len < 148) { + qq_show_packet("Login reply OK, but length < 139", data, len); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, _("Can not decrypt server reply")); @@ -82,7 +83,7 @@ purple_debug_info("QQ", "Got session_key\n"); bytes += qq_get32(&uid, data + bytes); if (uid != qd->uid) { - purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid); + purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid); } bytes += qq_getIP(&qd->my_ip, data + bytes); bytes += qq_get16(&qd->my_port, data + bytes); @@ -137,8 +138,8 @@ tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec); /* unknow 9 bytes, 0x(00 0a 00 0a 01 00 00 0e 10) */ - if (len > 139) { - purple_debug_warning("QQ", "Login reply more than expected %d bytes, read %d bytes\n", 139, bytes); + if (len > 148) { + qq_show_packet("Login reply OK, but length > 139", data, len); } return QQ_LOGIN_REPLY_OK; } @@ -330,7 +331,7 @@ if (bytes + token_len > buf_len) { purple_debug_info("QQ", "Extra token data, %d %d\n", token_len, buf_len - bytes); } - qq_show_packet("Got token", buf + bytes, buf_len - bytes); + /* qq_show_packet("Got token", buf + bytes, buf_len - bytes); */ if (qd->ld.token != NULL) { g_free(qd->ld.token); @@ -502,7 +503,7 @@ /* In fact, we can send whatever we like to server * with this command, server return the same result including * the amount of online QQ users, my ip and port */ - uid_str = g_strdup_printf("%u", qd->uid); + uid_str = g_strdup_printf("%u", qd->uid); bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str)); qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes); @@ -521,17 +522,17 @@ /* qq_show_packet("Keep alive reply packet", data, len); */ - bytes = 0; - bytes += qq_get8(&ret, data + bytes); - bytes += qq_get32(&qd->online_total, data + bytes); + bytes = 0; + bytes += qq_get8(&ret, data + bytes); + bytes += qq_get32(&qd->online_total, data + bytes); if(0 == qd->online_total) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Keep alive error")); } - - bytes += qq_getIP(&qd->my_ip, data + bytes); - bytes += qq_get16(&qd->my_port, data + bytes); + + bytes += qq_getIP(&qd->my_ip, data + bytes); + bytes += qq_get16(&qd->my_port, data + bytes); return TRUE; } @@ -565,16 +566,16 @@ /* qq_show_packet("Keep alive reply packet", data, len); */ - bytes = 0; - bytes += qq_get8(&ret, data + bytes); - bytes += qq_get32(&qd->online_total, data + bytes); + bytes = 0; + bytes += qq_get8(&ret, data + bytes); + bytes += qq_get32(&qd->online_total, data + bytes); if(0 == qd->online_total) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Keep alive error")); } - - bytes += qq_getIP(&qd->my_ip, data + bytes); + + bytes += qq_getIP(&qd->my_ip, data + bytes); bytes += qq_get16(&qd->my_port, data + bytes); /* skip 2 byytes, 0x(00 3c) */ bytes += 2; @@ -1279,7 +1280,7 @@ bytes += qq_get32(&uid, data + bytes); if (uid != qd->uid) { - purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid); + purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid); } bytes += qq_getIP(&qd->my_ip, data + bytes); bytes += qq_get16(&qd->my_port, data + bytes); @@ -1468,7 +1469,7 @@ bytes += qq_get32(&uid, data + bytes); if (uid != qd->uid) { - purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid); + purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid); } bytes += qq_getIP(&qd->my_ip, data + bytes); bytes += qq_get16(&qd->my_port, data + bytes); diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/qq_define.c --- a/libpurple/protocols/qq/qq_define.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/qq_define.c Sun Dec 07 01:38:29 2008 +0000 @@ -132,65 +132,65 @@ case QQ_CMD_LOGOUT: return "QQ_CMD_LOGOUT"; case QQ_CMD_KEEP_ALIVE: - return "QQ_CMD_KEEP_ALIVE"; + return "CMD_KEEP_ALIVE"; case QQ_CMD_UPDATE_INFO: - return "QQ_CMD_UPDATE_INFO"; + return "CMD_UPDATE_INFO"; case QQ_CMD_SEARCH_USER: - return "QQ_CMD_SEARCH_USER"; + return "CMD_SEARCH_USER"; case QQ_CMD_GET_BUDDY_INFO: - return "QQ_CMD_GET_BUDDY_INFO"; + return "CMD_GET_BUDDY_INFO"; case QQ_CMD_ADD_BUDDY_NO_AUTH: - return "QQ_CMD_ADD_BUDDY_NO_AUTH"; + return "CMD_ADD_BUDDY_NO_AUTH"; case QQ_CMD_REMOVE_BUDDY: - return "QQ_CMD_REMOVE_BUDDY"; + return "CMD_REMOVE_BUDDY"; case QQ_CMD_ADD_BUDDY_AUTH: - return "QQ_CMD_ADD_BUDDY_AUTH"; + return "CMD_ADD_BUDDY_AUTH"; case QQ_CMD_CHANGE_STATUS: - return "QQ_CMD_CHANGE_STATUS"; + return "CMD_CHANGE_STATUS"; case QQ_CMD_ACK_SYS_MSG: - return "QQ_CMD_ACK_SYS_MSG"; + return "CMD_ACK_SYS_MSG"; case QQ_CMD_SEND_IM: - return "QQ_CMD_SEND_IM"; + return "CMD_SEND_IM"; case QQ_CMD_RECV_IM: - return "QQ_CMD_RECV_IM"; + return "CMD_RECV_IM"; case QQ_CMD_REMOVE_ME: - return "QQ_CMD_REMOVE_ME"; + return "CMD_REMOVE_ME"; case QQ_CMD_LOGIN: - return "QQ_CMD_LOGIN"; + return "CMD_LOGIN"; case QQ_CMD_GET_BUDDIES_LIST: - return "QQ_CMD_GET_BUDDIES_LIST"; + return "CMD_GET_BUDDIES_LIST"; case QQ_CMD_GET_BUDDIES_ONLINE: - return "QQ_CMD_GET_BUDDIES_ONLINE"; + return "CMD_GET_BUDDIES_ONLINE"; case QQ_CMD_ROOM: - return "QQ_CMD_ROOM"; + return "CMD_ROOM"; case QQ_CMD_GET_BUDDIES_AND_ROOMS: - return "QQ_CMD_GET_BUDDIES_AND_ROOMS"; + return "CMD_GET_BUDDIES_AND_ROOMS"; case QQ_CMD_GET_LEVEL: - return "QQ_CMD_GET_LEVEL"; + return "CMD_GET_LEVEL"; case QQ_CMD_TOKEN: - return "QQ_CMD_TOKEN"; + return "CMD_TOKEN"; case QQ_CMD_RECV_MSG_SYS: - return "QQ_CMD_RECV_MSG_SYS"; + return "CMD_RECV_MSG_SYS"; case QQ_CMD_BUDDY_CHANGE_STATUS: - return "QQ_CMD_BUDDY_CHANGE_STATUS"; + return "CMD_BUDDY_CHANGE_STATUS"; case QQ_CMD_GET_SERVER: - return "QQ_CMD_GET_SERVER"; + return "CMD_GET_SERVER"; case QQ_CMD_TOKEN_EX: - return "QQ_CMD_TOKEN_EX"; + return "CMD_TOKEN_EX"; case QQ_CMD_CHECK_PWD: - return "QQ_CMD_CHECK_PWD"; + return "CMD_CHECK_PWD"; case QQ_CMD_AUTH_CODE: - return "QQ_CMD_AUTH_CODE"; + return "CMD_AUTH_CODE"; case QQ_CMD_ADD_BUDDY_NO_AUTH_EX: - return "QQ_CMD_ADD_BUDDY_NO_AUTH_EX"; + return "CMD_ADD_BUDDY_NO_AUTH_EX"; case QQ_CMD_ADD_BUDDY_AUTH_EX: - return "QQ_CMD_BUDDY_ADD_AUTH_EX"; + return "CMD_BUDDY_ADD_AUTH_EX"; case QQ_CMD_BUDDY_CHECK_CODE: - return "QQ_CMD_BUDDY_CHECK_CODE"; + return "CMD_BUDDY_CHECK_CODE"; case QQ_CMD_BUDDY_QUESTION: - return "QQ_CMD_BUDDY_QUESTION"; + return "CMD_BUDDY_QUESTION"; default: - return "Unknown CMD"; + return "CMD_UNKNOW"; } } @@ -198,55 +198,55 @@ { switch (room_cmd) { case QQ_ROOM_CMD_CREATE: - return "QQ_ROOM_CMD_CREATE"; + return "ROOM_CMD_CREATE"; case QQ_ROOM_CMD_MEMBER_OPT: - return "QQ_ROOM_CMD_MEMBER_OPT"; + return "ROOM_CMD_MEMBER_OPT"; case QQ_ROOM_CMD_CHANGE_INFO: - return "QQ_ROOM_CMD_CHANGE_INFO"; + return "ROOM_CMD_CHANGE_INFO"; case QQ_ROOM_CMD_GET_INFO: - return "QQ_ROOM_CMD_GET_INFO"; + return "ROOM_CMD_GET_INFO"; case QQ_ROOM_CMD_ACTIVATE: - return "QQ_ROOM_CMD_ACTIVATE"; + return "ROOM_CMD_ACTIVATE"; case QQ_ROOM_CMD_SEARCH: - return "QQ_ROOM_CMD_SEARCH"; + return "ROOM_CMD_SEARCH"; case QQ_ROOM_CMD_JOIN: - return "QQ_ROOM_CMD_JOIN"; + return "ROOM_CMD_JOIN"; case QQ_ROOM_CMD_AUTH: - return "QQ_ROOM_CMD_AUTH"; + return "ROOM_CMD_AUTH"; case QQ_ROOM_CMD_QUIT: - return "QQ_ROOM_CMD_QUIT"; - case QQ_ROOM_CMD_SEND_MSG: - return "QQ_ROOM_CMD_SEND_MSG"; + return "ROOM_CMD_QUIT"; + case QQ_ROOM_CMD_SEND_IM: + return "ROOM_CMD_SEND_IM"; case QQ_ROOM_CMD_GET_ONLINES: - return "QQ_ROOM_CMD_GET_ONLINES"; + return "ROOM_CMD_GET_ONLINES"; case QQ_ROOM_CMD_GET_BUDDIES: - return "QQ_ROOM_CMD_GET_BUDDIES"; + return "ROOM_CMD_GET_BUDDIES"; case QQ_ROOM_CMD_CHANGE_CARD: - return "QQ_ROOM_CMD_CHANGE_CARD"; + return "ROOM_CMD_CHANGE_CARD"; case QQ_ROOM_CMD_GET_REALNAMES: - return "QQ_ROOM_CMD_GET_REALNAMES"; + return "ROOM_CMD_GET_REALNAMES"; case QQ_ROOM_CMD_GET_CARD: - return "QQ_ROOM_CMD_GET_CARD"; + return "ROOM_CMD_GET_CARD"; case QQ_ROOM_CMD_SEND_IM_EX: - return "QQ_ROOM_CMD_SEND_IM_EX"; + return "ROOM_CMD_SEND_IM_EX"; case QQ_ROOM_CMD_ADMIN: - return "QQ_ROOM_CMD_ADMIN"; + return "ROOM_CMD_ADMIN"; case QQ_ROOM_CMD_TRANSFER: - return "QQ_ROOM_CMD_TRANSFER"; + return "ROOM_CMD_TRANSFER"; case QQ_ROOM_CMD_TEMP_CREATE: - return "QQ_ROOM_CMD_TEMP_CREATE"; + return "ROOM_CMD_TEMP_CREATE"; case QQ_ROOM_CMD_TEMP_CHANGE_MEMBER: - return "QQ_ROOM_CMD_TEMP_CHANGE_MEMBER"; + return "ROOM_CMD_TEMP_CHANGE_MEMBER"; case QQ_ROOM_CMD_TEMP_QUIT: - return "QQ_ROOM_CMD_TEMP_QUIT"; + return "ROOM_CMD_TEMP_QUIT"; case QQ_ROOM_CMD_TEMP_GET_INFO: - return "QQ_ROOM_CMD_TEMP_GET_INFO"; + return "ROOM_CMD_TEMP_GET_INFO"; case QQ_ROOM_CMD_TEMP_SEND_IM: - return "QQ_ROOM_CMD_TEMP_SEND_IM"; + return "ROOM_CMD_TEMP_SEND_IM"; case QQ_ROOM_CMD_TEMP_GET_MEMBERS: - return "QQ_ROOM_CMD_TEMP_GET_MEMBERS"; + return "ROOM_CMD_TEMP_GET_MEMBERS"; default: - return "Unknown Room Command"; + return "ROOM_CMD_UNKNOW"; } } diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/qq_define.h --- a/libpurple/protocols/qq/qq_define.h Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/qq_define.h Sun Dec 07 01:38:29 2008 +0000 @@ -89,7 +89,7 @@ QQ_ROOM_CMD_JOIN = 0x07, QQ_ROOM_CMD_AUTH = 0x08, QQ_ROOM_CMD_QUIT = 0x09, - QQ_ROOM_CMD_SEND_MSG = 0x0a, + QQ_ROOM_CMD_SEND_IM = 0x0a, QQ_ROOM_CMD_GET_ONLINES = 0x0b, QQ_ROOM_CMD_GET_BUDDIES = 0x0c, diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/qq_network.c --- a/libpurple/protocols/qq/qq_network.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/qq_network.c Sun Dec 07 01:38:29 2008 +0000 @@ -302,8 +302,7 @@ update_class = qq_trans_get_class(trans); ship32 = qq_trans_get_ship(trans); if (update_class != 0 || ship32 != 0) { - purple_debug_info("QQ", "Process in Update class %d, ship32 %d\n", - update_class, ship32); + purple_debug_info("QQ", "Update class %d, ship32 %d\n", update_class, ship32); } switch (cmd) { @@ -323,10 +322,6 @@ case QQ_CMD_ROOM: room_cmd = qq_trans_get_room_cmd(trans); room_id = qq_trans_get_room_id(trans); -#if 1 - purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n", - qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); -#endif qq_proc_room_cmds(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32); break; default: @@ -630,11 +625,13 @@ } if (ret < data_len) { - purple_debug_info("TCP_SEND_OUT", - "Add %d bytes to buffer\n", data_len - ret); + purple_debug_info("TCP_SEND_OUT", "Add %d bytes to buffer\n", data_len - ret); if (conn->can_write_handler == 0) { conn->can_write_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, gc); } + if (conn->tcp_txbuf == NULL) { + conn->tcp_txbuf = purple_circ_buffer_new(4096); + } purple_circ_buffer_append(conn->tcp_txbuf, data + ret, data_len - ret); } return ret; @@ -707,7 +704,7 @@ qd->send_seq = rand() & 0xffff; qd->is_login = FALSE; - qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); + qd->uid = strtoul(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); #ifdef DEBUG memset(qd->ld.random_key, 0x01, sizeof(qd->ld.random_key)); @@ -1107,7 +1104,7 @@ #if 1 /* qq_show_packet("qq_send_cmd_encrypted", data, data_len); */ - purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n", + purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n", seq, qq_get_cmd_desc(cmd), cmd, encrypted_len); #endif @@ -1161,7 +1158,7 @@ seq = ++qd->send_seq; #if 1 - purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n", + purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n", seq, qq_get_cmd_desc(cmd), cmd, data_len); #endif return send_cmd_detail(gc, cmd, seq, data, data_len, TRUE, update_class, ship32); @@ -1186,7 +1183,7 @@ is_save2trans = FALSE; } #if 1 - purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n", + purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n", seq, qq_get_cmd_desc(cmd), cmd, data_len); #endif return send_cmd_detail(gc, cmd, seq, data, data_len, is_save2trans, 0, 0); @@ -1205,7 +1202,7 @@ g_return_val_if_fail(data != NULL && data_len > 0, -1); #if 1 - purple_debug_info("QQ", "<== [SRV-%05d], %s(0x%04X), datalen %d\n", + purple_debug_info("QQ", "<== [SRV-%05d] %s(0x%04X), datalen %d\n", seq, qq_get_cmd_desc(cmd), cmd, data_len); #endif /* at most 16 bytes more */ @@ -1244,7 +1241,7 @@ buf_len = 0; buf_len += qq_put8(buf + buf_len, room_cmd); if (room_id != 0) { - /* id 0 is for QQ Demo Group, now there are not existed*/ + /* id 0 is for QQ Demo Group, now they are closed*/ buf_len += qq_put32(buf + buf_len, room_id); } if (data != NULL && data_len > 0) { @@ -1268,7 +1265,7 @@ #if 1 /* qq_show_packet("send_room_cmd", buf, buf_len); */ purple_debug_info("QQ", - "<== [%05d], %s (0x%02X) to room %d, datalen %d\n", + "<== [%05d] %s (0x%02X) to room %d, datalen %d\n", seq, qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); #endif diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/qq_process.c --- a/libpurple/protocols/qq/qq_process.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/qq_process.c Sun Dec 07 01:38:29 2008 +0000 @@ -94,7 +94,7 @@ purple_debug_info("QQ", "OK sent IM\n"); } -static void do_server_news(guint8 *data, gint data_len, PurpleConnection *gc) +static void do_server_news(PurpleConnection *gc, guint8 *data, gint data_len) { qq_data *qd = (qq_data *) gc->proto_data; gint bytes; @@ -114,7 +114,7 @@ content = g_strdup_printf(_("Server News:\n%s\n%s\n%s"), title, brief, url); if (qd->is_show_news) { - qq_got_attention(gc, content); + qq_got_message(gc, content); } else { purple_debug_info("QQ", "QQ Server news:\n%s\n", content); } @@ -124,6 +124,40 @@ g_free(content); } +static void do_got_sms(PurpleConnection *gc, guint8 *data, gint data_len) +{ + gint bytes; + gchar *mobile = NULL; + gchar *msg = NULL; + gchar *msg_utf8 = NULL; + gchar *msg_formated; + + g_return_if_fail(data != NULL && data_len > 26); + + qq_show_packet("Rcv sms", data, data_len); + + bytes = 0; + bytes += 1; /* skip 0x00 */ + mobile = g_strndup((gchar *)data + bytes, 20); + bytes += 20; + bytes += 5; /* skip 0x(49 11 98 d5 03)*/ + if (bytes < data_len) { + msg = g_strndup((gchar *)data + bytes, data_len - bytes); + msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); + g_free(msg); + } else { + msg_utf8 = g_strdup(""); + } + + msg_formated = g_strdup_printf(_("%s:%s"), mobile, msg_utf8); + + qq_got_message(gc, msg_formated); + + g_free(msg_formated); + g_free(msg_utf8); + g_free(mobile); +} + static void do_msg_sys_30(PurpleConnection *gc, guint8 *data, gint data_len) { gint len; @@ -142,7 +176,7 @@ purple_debug_warning("QQ", "We are kicked out by QQ server\n"); msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); - qq_got_attention(gc, msg_utf8); + qq_got_message(gc, msg_utf8); } static void do_msg_sys_4c(PurpleConnection *gc, guint8 *data, gint data_len) @@ -172,7 +206,7 @@ purple_debug_warning("QQ", "Failed to read QQ_MSG_SYS_4C\n"); qq_show_packet("do_msg_sys_4c", data, data_len); } - qq_got_attention(gc, content->str); + qq_got_message(gc, content->str); g_string_free(content, FALSE); } @@ -183,8 +217,8 @@ return "QQ_MSG_TO_BUDDY"; case QQ_MSG_TO_UNKNOWN: return "QQ_MSG_TO_UNKNOWN"; - case QQ_MSG_UNKNOWN_QUN_IM: - return "QQ_MSG_UNKNOWN_QUN_IM"; + case QQ_MSG_QUN_IM_UNKNOWN: + return "QQ_MSG_QUN_IM_UNKNOWN"; case QQ_MSG_ADD_TO_QUN: return "QQ_MSG_ADD_TO_QUN"; case QQ_MSG_DEL_FROM_QUN: @@ -207,6 +241,8 @@ return "QQ_MSG_QUN_IM"; case QQ_MSG_NEWS: return "QQ_MSG_NEWS"; + case QQ_MSG_SMS: + return "QQ_MSG_SMS"; case QQ_MSG_EXTEND: return "QQ_MSG_EXTEND"; case QQ_MSG_EXTEND_85: @@ -262,7 +298,7 @@ /* im_header prepared */ if (header.uid_to != qd->uid) { /* should not happen */ - purple_debug_error("QQ", "MSG to [%d], NOT me\n", header.uid_to); + purple_debug_error("QQ", "MSG to %u, NOT me\n", header.uid_to); return; } @@ -274,7 +310,10 @@ switch (header.msg_type) { case QQ_MSG_NEWS: - do_server_news(data + bytes, data_len - bytes, gc); + do_server_news(gc, data + bytes, data_len - bytes); + break; + case QQ_MSG_SMS: + do_got_sms(gc, data + bytes, data_len - bytes); break; case QQ_MSG_EXTEND: case QQ_MSG_EXTEND_85: @@ -286,7 +325,7 @@ purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from); qq_process_im(gc, data + bytes, data_len - bytes); break; - case QQ_MSG_UNKNOWN_QUN_IM: + case QQ_MSG_QUN_IM_UNKNOWN: case QQ_MSG_TEMP_QUN_IM: case QQ_MSG_QUN_IM: purple_debug_info("QQ", "MSG from room [%d]\n", header.uid_from); @@ -327,9 +366,12 @@ do_msg_sys_4c(gc, data + bytes, data_len - bytes); break; default: - purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%04X]\n", + purple_debug_warning("QQ", "MSG from %u, unknown type %s [0x%04X]\n", header.uid_from, get_im_type_desc(header.msg_type), header.msg_type); - qq_show_packet("Unknown MSG type", data, data_len); + qq_show_packet("MSG header", data, bytes); + if (data_len - bytes > 0) { + qq_show_packet("MSG data", data + bytes, data_len - bytes); + } break; } } @@ -381,7 +423,7 @@ content = g_strdup_printf(_("Server notice From %s: \n%s"), from, msg_utf8); if (qd->is_show_notice) { - qq_got_attention(gc, content); + qq_got_message(gc, content); } else { purple_debug_info("QQ", "QQ Server notice from %s:\n%s", from, msg_utf8); } @@ -425,7 +467,7 @@ request_server_ack(gc, funct_str, from, seq); /* qq_show_packet("Server MSG", data, data_len); */ - if (strtol(to, NULL, 10) != qd->uid) { /* not to me */ + if (strtoul(to, NULL, 10) != qd->uid) { /* not to me */ purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to); g_strfreev(segments); return; @@ -512,7 +554,7 @@ msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); g_free(msg); - prim = g_strdup_printf(_("Error reply of %s(0x%02X)\nRoom %d, reply 0x%02X"), + prim = g_strdup_printf(_("Error reply of %s(0x%02X)\nRoom %u, reply 0x%02X"), qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, reply); purple_notify_error(gc, _("QQ Qun Command"), prim, msg_utf8); @@ -562,13 +604,13 @@ qd = (qq_data *) gc->proto_data; next_id = qq_room_get_next(gc, room_id); - purple_debug_info("QQ", "Update rooms, next id %d, prev id %d\n", next_id, room_id); + purple_debug_info("QQ", "Update rooms, next id %u, prev id %u\n", next_id, room_id); if (next_id <= 0) { if (room_id > 0) { is_new_turn = TRUE; next_id = qq_room_get_next(gc, 0); - purple_debug_info("QQ", "new turn, id %d\n", next_id); + purple_debug_info("QQ", "New turn, id %u\n", next_id); } else { purple_debug_info("QQ", "No room. Finished update\n"); return; @@ -798,9 +840,12 @@ case QQ_ROOM_CMD_QUIT: qq_process_group_cmd_exit_group(data + bytes, data_len - bytes, gc); break; - case QQ_ROOM_CMD_SEND_MSG: + case QQ_ROOM_CMD_SEND_IM: qq_process_room_send_im(gc, data + bytes, data_len - bytes); break; + case QQ_ROOM_CMD_SEND_IM_EX: + qq_process_room_send_im_ex(gc, data + bytes, data_len - bytes); + break; case QQ_ROOM_CMD_GET_ONLINES: qq_process_room_cmd_get_onlines(data + bytes, data_len - bytes, gc); break; @@ -934,20 +979,20 @@ if (ret_8 != QQ_LOGIN_REPLY_OK) { return ret_8; } - if (qd->client_version == 2008) { + if (qd->client_version >= 2008) { qq_request_login_2008(gc); } else { qq_request_login_2007(gc); } break; case QQ_CMD_LOGIN: - if (qd->client_version == 2008) { + if (qd->client_version >= 2008) { ret_8 = qq_process_login_2008(gc, data, data_len); if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) { qq_request_get_server(gc); return QQ_LOGIN_REPLY_OK; } - } else if (qd->client_version == 2007) { + } else if (qd->client_version >= 2007) { ret_8 = qq_process_login_2007(gc, data, data_len); if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) { qq_request_get_server(gc); @@ -961,7 +1006,7 @@ } purple_connection_update_progress(gc, _("Logging in"), QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS); - purple_debug_info("QQ", "Login repliess OK; everything is fine\n"); + purple_debug_info("QQ", "Login replies OK; everything is fine\n"); purple_connection_set_state(gc, PURPLE_CONNECTED); qd->is_login = TRUE; /* must be defined after sev_finish_login */ diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/utils.c --- a/libpurple/protocols/qq/utils.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/utils.c Sun Dec 07 01:38:29 2008 +0000 @@ -39,8 +39,6 @@ #include "util.h" #include "utils.h" -#define QQ_NAME_FORMAT "%d" - /* These functions are used only in development phase */ /* static void _qq_show_socket(gchar *desc, gint fd) { @@ -135,7 +133,7 @@ guint32 ret; g_return_val_if_fail(name != NULL, 0); - ret = strtol(name, NULL, 10); + ret = strtoul(name, NULL, 10); if (errno == ERANGE) return 0; else @@ -169,7 +167,7 @@ * the return needs to be freed */ gchar *uid_to_purple_name(guint32 uid) { - return g_strdup_printf(QQ_NAME_FORMAT, uid); + return g_strdup_printf("%u", uid); } /* try to dump the data as GBK */ @@ -339,3 +337,15 @@ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", buf, len, desc); } +void qq_filter_str(gchar *str) { + gchar *temp; + if (str == NULL) { + return; + } + + for (temp = str; *temp != 0; temp++) { + /*if (*temp == '\r' || *temp == '\n') *temp = ' ';*/ + if (*temp > 0 && *temp < 0x20) *temp = ' '; + } + g_strstrip(str); +} diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/qq/utils.h --- a/libpurple/protocols/qq/utils.h Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/qq/utils.h Sun Dec 07 01:38:29 2008 +0000 @@ -51,4 +51,5 @@ const char *format, ...); guint8 *hex_str_to_bytes(const gchar *buf, gint *out_len); +void qq_filter_str(gchar *str); #endif diff -r 21b21bffd946 -r e1331f6126ba libpurple/protocols/yahoo/yahoo.c --- a/libpurple/protocols/yahoo/yahoo.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Sun Dec 07 01:38:29 2008 +0000 @@ -2122,7 +2122,7 @@ char *url = NULL; char *fullmsg; PurpleAccount *account = gc->account; - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; + PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR; while (l) { struct yahoo_pair *pair = l->data; @@ -2136,6 +2136,10 @@ } switch (err) { + case 0: + msg = g_strdup(_("Unknown error.")); + reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; + break; case 3: msg = g_strdup(_("Invalid username.")); reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME; @@ -2160,9 +2164,11 @@ purple_account_set_password(account, NULL); msg = g_strdup(_("Incorrect password.")); + reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; break; case 14: msg = g_strdup(_("Your account is locked, please log in to the Yahoo! website.")); + reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; break; default: msg = g_strdup_printf(_("Unknown error number %d. Logging into the Yahoo! website may fix this."), err); @@ -2700,6 +2706,7 @@ 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; } @@ -2796,7 +2803,7 @@ static void yahoo_login_page_hash_iter(const char *key, const char *val, GString *url) { - if (!strcmp(key, "passwd")) + if (!strcmp(key, "passwd") || !strcmp(key, "login")) return; g_string_append_c(url, '&'); g_string_append(url, key); diff -r 21b21bffd946 -r e1331f6126ba libpurple/prpl.h --- a/libpurple/prpl.h Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/prpl.h Sun Dec 07 01:38:29 2008 +0000 @@ -274,7 +274,16 @@ PurpleMessageFlags flags); void (*set_info)(PurpleConnection *, const char *info); + + /** + * @return If this protocol requires the PURPLE_TYPING message to + * be sent repeatedly to signify that the user is still + * typing, then the PRPL should return the number of + * seconds to wait before sending a subsequent notification. + * Otherwise the PRPL should return 0. + */ unsigned int (*send_typing)(PurpleConnection *, const char *name, PurpleTypingState state); + /** * Should arrange for purple_notify_userinfo() to be called with * @a who's user info. diff -r 21b21bffd946 -r e1331f6126ba libpurple/roomlist.c --- a/libpurple/roomlist.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/roomlist.c Sun Dec 07 01:38:29 2008 +0000 @@ -173,6 +173,7 @@ PurplePluginProtocolInfo *prpl_info = NULL; g_return_val_if_fail(gc != NULL, NULL); + g_return_val_if_fail(PURPLE_CONNECTION_IS_CONNECTED(gc), NULL); prpl = purple_connection_get_prpl(gc); diff -r 21b21bffd946 -r e1331f6126ba libpurple/util.c --- a/libpurple/util.c Tue Dec 02 22:11:39 2008 +0000 +++ b/libpurple/util.c Sun Dec 07 01:38:29 2008 +0000 @@ -56,6 +56,8 @@ gsize request_written; gboolean include_headers; + gboolean is_ssl; + PurpleSslConnection *ssl_connection; PurpleProxyConnectData *connect_data; int fd; guint inpa; @@ -3443,9 +3445,6 @@ char *cmd; GHashTable *params = NULL; int len; - - g_return_if_fail(uri != NULL); - if (!(tmp = strchr(uri, ':')) || tmp == uri) { purple_debug_error("util", "Malformed protocol handler message - missing protocol.\n"); return; @@ -3518,6 +3517,7 @@ purple_url_parse(const char *url, char **ret_host, int *ret_port, char **ret_path, char **ret_user, char **ret_passwd) { + gboolean is_https = FALSE; char scan_info[255]; char port_str[6]; int f; @@ -3541,6 +3541,7 @@ } else if ((turl = purple_strcasestr(url, "https://")) != NULL) { + is_https = TRUE; turl += 8; url = turl; } @@ -3581,7 +3582,11 @@ "%%255[%s]/%%255[%s]", addr_ctrl, page_ctrl); f = sscanf(url, scan_info, host, path); - g_snprintf(port_str, sizeof(port_str), "80"); + /* Use the default port */ + if (is_https) + g_snprintf(port_str, sizeof(port_str), "443"); + else + g_snprintf(port_str, sizeof(port_str), "80"); } if (f == 0) @@ -3620,6 +3625,8 @@ } static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message); +static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond); +static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data); static gboolean parse_redirect(const char *data, size_t data_len, @@ -3686,10 +3693,16 @@ g_free(gfud->request); gfud->request = NULL; - purple_input_remove(gfud->inpa); - gfud->inpa = 0; - close(gfud->fd); - gfud->fd = -1; + if (gfud->is_ssl) { + gfud->is_ssl = FALSE; + purple_ssl_close(gfud->ssl_connection); + gfud->ssl_connection = NULL; + } else { + purple_input_remove(gfud->inpa); + gfud->inpa = 0; + close(gfud->fd); + gfud->fd = -1; + } gfud->request_written = 0; gfud->len = 0; gfud->data_len = 0; @@ -3701,11 +3714,18 @@ purple_url_parse(new_url, &gfud->website.address, &gfud->website.port, &gfud->website.page, &gfud->website.user, &gfud->website.passwd); - gfud->connect_data = purple_proxy_connect(NULL, NULL, - gfud->website.address, gfud->website.port, - url_fetch_connect_cb, gfud); - - if (gfud->connect_data == NULL) + if (purple_strcasestr(new_url, "https://") != NULL) { + gfud->is_ssl = TRUE; + gfud->ssl_connection = purple_ssl_connect(NULL, + gfud->website.address, gfud->website.port, + ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud); + } else { + gfud->connect_data = purple_proxy_connect(NULL, NULL, + gfud->website.address, gfud->website.port, + url_fetch_connect_cb, gfud); + } + + if (gfud->ssl_connection == NULL && gfud->connect_data == NULL) { purple_util_fetch_url_error(gfud, _("Unable to connect to %s"), gfud->website.address); @@ -3766,8 +3786,14 @@ char *data_cursor; gboolean got_eof = FALSE; - while((len = read(source, buf, sizeof(buf))) > 0) { - + /* + * Read data in a loop until we can't read any more! This is a + * little confusing because we read using a different function + * depending on whether the socket is ssl or cleartext. + */ + while ((gfud->is_ssl && ((len = purple_ssl_read(gfud->ssl_connection, buf, sizeof(buf))) > 0)) || + (!gfud->is_ssl && (len = read(source, buf, sizeof(buf))) > 0)) + { if(gfud->max_len != -1 && (gfud->len + len) > gfud->max_len) { purple_util_fetch_url_error(gfud, _("Error reading from %s: response too long (%d bytes limit)"), gfud->website.address, gfud->max_len); @@ -3887,6 +3913,21 @@ } } +static void ssl_url_fetch_recv_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) +{ + url_fetch_recv_cb(data, -1, cond); +} + +/* + * This function is called when the socket is available to be written + * to. + * + * @param source The file descriptor that can be written to. This can + * be an http connection or it can be the SSL connection of an + * https request. So be careful what you use it for! If it's + * an https request then use purple_ssl_write() instead of + * writing to it directly. + */ static void url_fetch_send_cb(gpointer data, gint source, PurpleInputCondition cond) { @@ -3895,53 +3936,14 @@ gfud = data; - total_len = strlen(gfud->request); - - len = write(gfud->fd, gfud->request + gfud->request_written, - total_len - gfud->request_written); - - if (len < 0 && errno == EAGAIN) - return; - else if (len < 0) { - purple_util_fetch_url_error(gfud, _("Error writing to %s: %s"), - gfud->website.address, g_strerror(errno)); - return; - } - gfud->request_written += len; - - if (gfud->request_written < total_len) - return; - - /* We're done writing our request, now start reading the response */ - purple_input_remove(gfud->inpa); - gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, url_fetch_recv_cb, - gfud); -} - -static void -url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message) -{ - PurpleUtilFetchUrlData *gfud; - - gfud = url_data; - gfud->connect_data = NULL; - - if (source == -1) + if (gfud->request == NULL) { - purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"), - (gfud->website.address ? gfud->website.address : ""), error_message); - return; - } - - gfud->fd = source; - - if (!gfud->request) { + /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1 + * clients must know how to handle the "chunked" transfer encoding. + * Purple doesn't know how to handle "chunked", so should always send + * the Host header regardless, to get around some observed problems + */ if (gfud->user_agent) { - /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1 - * clients must know how to handle the "chunked" transfer encoding. - * Purple doesn't know how to handle "chunked", so should always send - * the Host header regardless, to get around some observed problems - */ gfud->request = g_strdup_printf( "GET %s%s HTTP/%s\r\n" "Connection: close\r\n" @@ -3968,11 +3970,84 @@ purple_debug_misc("util", "Request: '%s'\n", gfud->request); + total_len = strlen(gfud->request); + + if (gfud->is_ssl) + len = purple_ssl_write(gfud->ssl_connection, gfud->request + gfud->request_written, + total_len - gfud->request_written); + else + len = write(gfud->fd, gfud->request + gfud->request_written, + total_len - gfud->request_written); + + if (len < 0 && errno == EAGAIN) + return; + else if (len < 0) { + purple_util_fetch_url_error(gfud, _("Error writing to %s: %s"), + gfud->website.address, g_strerror(errno)); + return; + } + gfud->request_written += len; + + if (gfud->request_written < total_len) + return; + + /* We're done writing our request, now start reading the response */ + if (gfud->is_ssl) { + purple_input_remove(gfud->inpa); + gfud->inpa = 0; + purple_ssl_input_add(gfud->ssl_connection, ssl_url_fetch_recv_cb, gfud); + } else { + purple_input_remove(gfud->inpa); + gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, url_fetch_recv_cb, + gfud); + } +} + +static void +url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message) +{ + PurpleUtilFetchUrlData *gfud; + + gfud = url_data; + gfud->connect_data = NULL; + + if (source == -1) + { + purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"), + (gfud->website.address ? gfud->website.address : ""), error_message); + return; + } + + gfud->fd = source; + gfud->inpa = purple_input_add(source, PURPLE_INPUT_WRITE, url_fetch_send_cb, gfud); url_fetch_send_cb(gfud, source, PURPLE_INPUT_WRITE); } +static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) +{ + PurpleUtilFetchUrlData *gfud; + + gfud = data; + + gfud->inpa = purple_input_add(ssl_connection->fd, PURPLE_INPUT_WRITE, + url_fetch_send_cb, gfud); + url_fetch_send_cb(gfud, ssl_connection->fd, PURPLE_INPUT_WRITE); +} + +static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data) +{ + PurpleUtilFetchUrlData *gfud; + + gfud = data; + gfud->ssl_connection = NULL; + + purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"), + (gfud->website.address ? gfud->website.address : ""), + purple_ssl_strerror(error)); +} + PurpleUtilFetchUrlData * purple_util_fetch_url_request(const char *url, gboolean full, const char *user_agent, gboolean http11, @@ -3985,13 +4060,6 @@ callback, user_data); } -static gboolean -url_fetch_connect_failed(gpointer data) -{ - url_fetch_connect_cb(data, -1, ""); - return FALSE; -} - PurpleUtilFetchUrlData * purple_util_fetch_url_request_len(const char *url, gboolean full, const char *user_agent, gboolean http11, @@ -4023,14 +4091,22 @@ purple_url_parse(url, &gfud->website.address, &gfud->website.port, &gfud->website.page, &gfud->website.user, &gfud->website.passwd); - gfud->connect_data = purple_proxy_connect(NULL, NULL, - gfud->website.address, gfud->website.port, - url_fetch_connect_cb, gfud); - - if (gfud->connect_data == NULL) + if (purple_strcasestr(url, "https://") != NULL) { + gfud->is_ssl = TRUE; + gfud->ssl_connection = purple_ssl_connect(NULL, + gfud->website.address, gfud->website.port, + ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud); + } else { + gfud->connect_data = purple_proxy_connect(NULL, NULL, + gfud->website.address, gfud->website.port, + url_fetch_connect_cb, gfud); + } + + if (gfud->ssl_connection == NULL && gfud->connect_data == NULL) { - /* Trigger the connect_cb asynchronously. */ - purple_timeout_add(10, url_fetch_connect_failed, gfud); + purple_util_fetch_url_error(gfud, _("Unable to connect to %s"), + gfud->website.address); + return NULL; } return gfud; @@ -4039,6 +4115,9 @@ void purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *gfud) { + if (gfud->ssl_connection != NULL) + purple_ssl_close(gfud->ssl_connection); + if (gfud->connect_data != NULL) purple_proxy_connect_cancel(gfud->connect_data); diff -r 21b21bffd946 -r e1331f6126ba pidgin/gtkaccount.c --- a/pidgin/gtkaccount.c Tue Dec 02 22:11:39 2008 +0000 +++ b/pidgin/gtkaccount.c Sun Dec 07 01:38:29 2008 +0000 @@ -2425,25 +2425,25 @@ }; static void -authorize_and_add_cb(struct auth_and_add *aa) +free_auth_and_add(struct auth_and_add *aa) { - aa->auth_cb(aa->data); - purple_blist_request_add_buddy(aa->account, aa->username, - NULL, aa->alias); - g_free(aa->username); g_free(aa->alias); g_free(aa); } static void +authorize_and_add_cb(struct auth_and_add *aa) +{ + aa->auth_cb(aa->data); + purple_blist_request_add_buddy(aa->account, aa->username, + NULL, aa->alias); +} + +static void deny_no_add_cb(struct auth_and_add *aa) { aa->deny_cb(aa->data); - - g_free(aa->username); - g_free(aa->alias); - g_free(aa); } static void * @@ -2492,7 +2492,7 @@ _("Authorize"), authorize_and_add_cb, _("Deny"), deny_no_add_cb, NULL); - g_object_set_data(G_OBJECT(alert), "auth_and_add", aa); + g_signal_connect_swapped(G_OBJECT(alert), "destroy", G_CALLBACK(free_auth_and_add), aa); } else { alert = pidgin_make_mini_dialog(gc, PIDGIN_STOCK_DIALOG_QUESTION, _("Authorize buddy?"), buffer, user_data, @@ -2501,6 +2501,8 @@ NULL); } pidgin_blist_add_alert(alert); + g_signal_connect(G_OBJECT(alert), "destroy", + G_CALLBACK(purple_account_request_close), NULL); g_free(buffer); @@ -2510,13 +2512,6 @@ static void pidgin_accounts_request_close(void *ui_handle) { - /* This is super ugly, but without API changes, this is how it works */ - struct auth_and_add *aa = g_object_get_data(G_OBJECT(ui_handle), "auth_and_add"); - if (aa != NULL) { - g_free(aa->username); - g_free(aa->alias); - g_free(aa); - } gtk_widget_destroy(GTK_WIDGET(ui_handle)); } diff -r 21b21bffd946 -r e1331f6126ba pidgin/gtkconv.c --- a/pidgin/gtkconv.c Tue Dec 02 22:11:39 2008 +0000 +++ b/pidgin/gtkconv.c Sun Dec 07 01:38:29 2008 +0000 @@ -7282,18 +7282,14 @@ if(purple_status_is_available(oldstatus) || !purple_status_is_available(newstatus)) return; - while ((l = hidden_convwin->gtkconvs) != NULL) - { + for (l = hidden_convwin->gtkconvs; l; ) { gtkconv = l->data; + l = l->next; conv = gtkconv->active_conv; - - while(l && !purple_status_is_available( - purple_account_get_active_status( - purple_conversation_get_account(conv)))) - l = l->next; - if (!l) - break; + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT || + account != purple_conversation_get_account(conv)) + continue; pidgin_conv_attach_to_conversation(conv); diff -r 21b21bffd946 -r e1331f6126ba pidgin/gtkroomlist.c --- a/pidgin/gtkroomlist.c Tue Dec 02 22:11:39 2008 +0000 +++ b/pidgin/gtkroomlist.c Sun Dec 07 01:38:29 2008 +0000 @@ -488,7 +488,7 @@ PurpleConnection *conn = purple_account_get_connection(account); PurplePluginProtocolInfo *prpl_info = NULL; - if (conn) + if (conn && PURPLE_CONNECTION_IS_CONNECTED(conn)) prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl); return (prpl_info && prpl_info->roomlist_get_list != NULL); diff -r 21b21bffd946 -r e1331f6126ba pidgin/gtkutils.c --- a/pidgin/gtkutils.c Tue Dec 02 22:11:39 2008 +0000 +++ b/pidgin/gtkutils.c Sun Dec 07 01:38:29 2008 +0000 @@ -3276,7 +3276,13 @@ static void combo_box_changed_cb(GtkComboBox *combo_box, GtkEntry *entry) { +#if GTK_CHECK_VERSION(2, 6, 0) char *text = gtk_combo_box_get_active_text(combo_box); +#else + GtkWidget *widget = gtk_bin_get_child(GTK_BIN(combo_box)); + char *text = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget))); +#endif + gtk_entry_set_text(entry, text ? text : ""); g_free(text); } diff -r 21b21bffd946 -r e1331f6126ba pidgin/pixmaps/protocols/16/facebook.png Binary file pidgin/pixmaps/protocols/16/facebook.png has changed diff -r 21b21bffd946 -r e1331f6126ba pidgin/pixmaps/protocols/22/facebook.png Binary file pidgin/pixmaps/protocols/22/facebook.png has changed diff -r 21b21bffd946 -r e1331f6126ba pidgin/pixmaps/protocols/48/facebook.png Binary file pidgin/pixmaps/protocols/48/facebook.png has changed