Mercurial > pidgin
changeset 24685:0ac34748464a
merge of '17b584b23aee26cc5553b12483ab9b550eee8103'
and '2b3429067be9bc2eb9be9e8deeeac9c35001f61d'
author | John Bailey <rekkanoryo@rekkanoryo.org> |
---|---|
date | Thu, 11 Dec 2008 06:24:14 +0000 |
parents | 8792c97479fd (current diff) 662fdd4836aa (diff) |
children | 79da937ac0f1 |
files | |
diffstat | 63 files changed, 2453 insertions(+), 1420 deletions(-) [+] |
line wrap: on
line diff
--- a/COPYRIGHT Thu Dec 11 06:24:06 2008 +0000 +++ b/COPYRIGHT Thu Dec 11 06:24:14 2008 +0000 @@ -418,6 +418,7 @@ Brian Tarricone Peter Teichman Philip Tellis +Michael Terry Arun A. Tharuvai Cestonaro Thilo Will Thompson
--- a/ChangeLog Thu Dec 11 06:24:06 2008 +0000 +++ b/ChangeLog Thu Dec 11 06:24:14 2008 +0000 @@ -8,7 +8,6 @@ --with-system-ssl-certs and GnuTLS need to include these in the system certs directory. * Corrected maximum message lengths for Yahoo! - * Enable auto-reply on Zephyr, to emulate 'zaway' (Toby Schaffer) * The Buddy State Notification plugin no longer prints duplicate notifications when the same buddy is in multiple groups (Florian Quèze) * The Buddy State Notification plugin no longer turns JID's, MSN Passport @@ -16,10 +15,18 @@ * 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) + * Fix a crash in purple_accounts_delete that happens when this function is + called before the buddy list is initialized (Florian Quèze) + * On MSN, the Games and Office media can now be set and displayed (in + addition to the previous Music media). The Media status text now shows + the album, if possible. Gadu-Gadu: * Fix some problems with Gadu-Gadu buddy icons (Adam Strzelecki) - * Gadu-Gadu now validates that UID's are valid (Adam Strzelecki) + * Gadu-Gadu now checks that UID's are valid (Adam Strzelecki) * Gadu-Gadu now does proper charset translations where needed (Adam Strzelecki) @@ -35,6 +42,13 @@ * Send "client-accepts-full-bind-result" attribute during SASL login. This will fix Google Talk login failures if the user configures the wrong domain for his/her account. + + Zephyr: + * Enable auto-reply, to emulate 'zaway' (Toby Schaffer) + * Fix a crash when an account is configured to use tzc but tzc is not + installed or the configured tzc command is invalid (Michael Terry) + * Fix a 10 second delay waiting on tzc if it is not installed or the + configured command is invalid (Michael Terry) Pidgin: * On GTK+ 2.14 and higher, we're using the gtk-tooltip-delay setting @@ -47,6 +61,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.
--- a/ChangeLog.win32 Thu Dec 11 06:24:06 2008 +0000 +++ b/ChangeLog.win32 Thu Dec 11 06:24:14 2008 +0000 @@ -1,3 +1,7 @@ +version 2.5.3 (12/??/2008): + * Upgrade SILC to use the 1.1.8 toolkit + * Updated included Meanwhile library to include patch referenced in #7563 + version 2.5.2 (10/19/2008): * Updated GTK+ to 2.12.12 This will resolve an issue with stuff in QQ appearing as "(NULL)" @@ -28,7 +32,7 @@ version 2.4.0 (02/29/2008): * Updated GTK+ to 2.12.8 - * Updated include Meanwhile library to include patches referenced at: + * Updated included Meanwhile library to include patches referenced at: https://sourceforge.net/tracker/?func=detail&atid=656718&aid=1626349&group_id=110565 * Build the xmpp protocol with SASL support (and include Cyrus SASL 2.1.22).
--- a/finch/gntaccount.c Thu Dec 11 06:24:06 2008 +0000 +++ b/finch/gntaccount.c Thu Dec 11 06:24:14 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; }
--- a/finch/gntroomlist.c Thu Dec 11 06:24:06 2008 +0000 +++ b/finch/gntroomlist.c Thu Dec 11 06:24:14 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),
--- a/libpurple/account.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/account.c Thu Dec 11 06:24:14 2008 +0000 @@ -2499,7 +2499,7 @@ purple_accounts_remove(account); /* Remove this account's buddies */ - for (gnode = purple_get_blist()->root; gnode != NULL; gnode = gnode->next) { + for (gnode = purple_blist_get_root(); gnode != NULL; gnode = gnode->next) { if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
--- a/libpurple/protocols/bonjour/bonjour_ft.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Thu Dec 11 06:24:14 2008 +0000 @@ -411,8 +411,10 @@ BonjourData *bd; PurpleXfer *xfer; - if(pc == NULL || packet == NULL || pb == NULL) - return; + g_return_if_fail(pc != NULL); + g_return_if_fail(packet != NULL); + g_return_if_fail(pb != NULL); + bd = (BonjourData*) pc->proto_data; if(bd == NULL) return; @@ -488,8 +490,9 @@ xmlnode *query; BonjourData *bd; - if(pc == NULL || packet == NULL || pb == NULL) - return; + g_return_if_fail(pc != NULL); + g_return_if_fail(packet != NULL); + g_return_if_fail(pb != NULL); bd = (BonjourData*) pc->proto_data; if(bd == NULL)
--- a/libpurple/protocols/bonjour/jabber.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Thu Dec 11 06:24:14 2008 +0000 @@ -77,7 +77,7 @@ }; static void -xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb); +xep_iq_parse(xmlnode *packet, PurpleBuddy *pb); static BonjourJabberConversation * bonjour_jabber_conv_new(PurpleBuddy *pb, PurpleAccount *account, const char *ip) { @@ -128,7 +128,7 @@ if (contents) { char *bodystart = strchr(contents, '>'); - char *bodyend = strrchr(bodystart, '<'); + char *bodyend = bodystart ? strrchr(bodystart, '<') : NULL; if (bodystart && bodyend && (bodystart + 1) != bodyend) { *bodyend = '\0'; memmove(contents, bodystart + 1, (bodyend - bodystart)); @@ -364,11 +364,36 @@ if (!strcmp(packet->name, "message")) _jabber_parse_and_write_message_to_ui(packet, pb); else if(!strcmp(packet->name, "iq")) - xep_iq_parse(packet, NULL, pb); + xep_iq_parse(packet, pb); else purple_debug_warning("bonjour", "Unknown packet: %s\n", packet->name ? packet->name : "(null)"); } +static void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) { + + /* Inform the user that the conversation has been closed */ + BonjourBuddy *bb = NULL; + + purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", bconv->pb ? bconv->pb->name : "(unknown)"); + + if(bconv->pb != NULL) + bb = bconv->pb->proto_data; +#if 0 + if(bconv->pb != NULL) { + PurpleConversation *conv; + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bconv->pb->name, bconv->pb->account); + if (conv != NULL) { + char *tmp = g_strdup_printf(_("%s has closed the conversation."), bconv->pb->name); + purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); + g_free(tmp); + } + } +#endif + /* Close the socket, clear the watcher and free memory */ + bonjour_jabber_close_conversation(bconv); + if(bb) + bb->conversation = NULL; +} static void _client_socket_handler(gpointer data, gint socket, PurpleInputCondition condition) @@ -414,35 +439,6 @@ bonjour_parser_process(bconv, message, message_length); } -void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) { - - purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", bconv->pb ? bconv->pb->name : "(unknown)"); - - /* Inform the user that the conversation has been closed */ - if (bconv != NULL) { - BonjourBuddy *bb = NULL; - - if(bconv->pb != NULL) - bb = bconv->pb->proto_data; -#if 0 - if(bconv->pb != NULL) { - PurpleConversation *conv; - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bconv->pb->name, bconv->pb->account); - if (conv != NULL) { - char *tmp = g_strdup_printf(_("%s has closed the conversation."), bconv->pb->name); - purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(tmp); - } - } -#endif - /* Close the socket, clear the watcher and free memory */ - bonjour_jabber_close_conversation(bconv); - if(bb) - bb->conversation = NULL; - } -} - - struct _stream_start_data { char *msg; }; @@ -1162,14 +1158,12 @@ check_if_blocked(PurpleBuddy *pb) { gboolean blocked = FALSE; - GSList *l = NULL; - PurpleAccount *acc = NULL; + GSList *l; + PurpleAccount *acc = purple_buddy_get_account(pb); - if(pb == NULL) + if(acc == NULL) return FALSE; - acc = pb->account; - for(l = acc->deny; l != NULL; l = l->next) { if(!purple_utf8_strcasecmp(pb->name, (char *)l->data)) { purple_debug_info("bonjour", "%s has been blocked by %s.\n", pb->name, acc->username); @@ -1181,25 +1175,19 @@ } static void -xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb) +xep_iq_parse(xmlnode *packet, PurpleBuddy *pb) { - xmlnode *child = NULL; - - if(packet == NULL || pb == NULL) - return; - - if(connection == NULL) { - if(pb->account != NULL) - connection = (pb->account)->gc; - } + xmlnode *child; if(check_if_blocked(pb)) return; if ((child = xmlnode_get_child(packet, "si")) || (child = xmlnode_get_child(packet, "error"))) - xep_si_parse(connection, packet, pb); + xep_si_parse(purple_account_get_connection(pb->account), + packet, pb); else - xep_bytestreams_parse(connection, packet, pb); + xep_bytestreams_parse(purple_account_get_connection(pb->account), + packet, pb); } int
--- a/libpurple/protocols/bonjour/jabber.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/bonjour/jabber.h Thu Dec 11 06:24:14 2008 +0000 @@ -79,8 +79,6 @@ void bonjour_jabber_stream_started(BonjourJabberConversation *bconv); -void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv); - void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet); void bonjour_jabber_stop(BonjourJabber *data);
--- a/libpurple/protocols/jabber/jabber.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.c Thu Dec 11 06:24:14 2008 +0000 @@ -200,7 +200,7 @@ if (requested_resource != NULL) { resource = xmlnode_new_child(bind, "resource"); xmlnode_insert_data(resource, requested_resource, -1); - free(requested_resource); + g_free(requested_resource); } jabber_iq_set_callback(iq, jabber_bind_result_cb, NULL);
--- a/libpurple/protocols/msn/contact.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/msn/contact.c Thu Dec 11 06:24:14 2008 +0000 @@ -176,28 +176,6 @@ return 0; } -/* get Network */ -/* QuLogic: These names don't really refer to the MsnNetwork, - * but I haven't yet written the code to properly use them. - */ -static MsnNetwork -msn_get_network(char *type) -{ - g_return_val_if_fail(type != NULL, 0); - - if (!strcmp(type,"Regular")) { - return MSN_NETWORK_PASSPORT; - } - if (!strcmp(type,"Live")) { - return MSN_NETWORK_PASSPORT; - } - if (!strcmp(type,"LivePending")) { - return MSN_NETWORK_PASSPORT; - } - - return MSN_NETWORK_UNKNOWN; -} - /* Create the AddressBook in the server, if we don't have one */ static void msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) @@ -245,9 +223,26 @@ char *type = xmlnode_get_data(xmlnode_get_child(member, "Type")); char *member_id = xmlnode_get_data(xmlnode_get_child(member, "MembershipId")); MsnUser *user = msn_userlist_find_add_user(session->userlist, passport, NULL); + xmlnode *annotation; + guint nid = MSN_NETWORK_PASSPORT; - purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s\n", - node, passport, type, member_id == NULL ? "(null)" : member_id); + for (annotation = xmlnode_get_child(member, "Annotations/Annotation"); + annotation; + annotation = xmlnode_get_next_twin(annotation)) { + char *name = xmlnode_get_data(xmlnode_get_child(annotation, "Name")); + if (name && !strcmp(name, "MSN.IM.BuddyType")) { + char *value = xmlnode_get_data(xmlnode_get_child(annotation, "Value")); + if (value != NULL) + nid = strtoul(value, NULL, 10); + g_free(value); + } + g_free(name); + } + + purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s, NetworkID: %u\n", + node, passport, type, member_id == NULL ? "(null)" : member_id, nid); + + msn_user_set_network(user, nid); if (member_id) { user->membership_id[list] = atoi(member_id); @@ -445,16 +440,16 @@ if ((groupInfo = xmlnode_get_child(group, "groupInfo")) && (groupname = xmlnode_get_child(groupInfo, "name"))) group_name = xmlnode_get_data(groupname); - msn_group_new(session->userlist, group_id, group_name); - - if (group_id == NULL){ + if (group_id == NULL) { /* Group of ungroupped buddies */ g_free(group_name); continue; } + msn_group_new(session->userlist, group_id, group_name); + purple_debug_info("msn", "AB group_id: %s, name: %s\n", group_id, group_name ? group_name : "(null)"); - if ((purple_find_group(group_name)) == NULL){ + if ((purple_find_group(group_name)) == NULL) { PurpleGroup *g = purple_group_new(group_name); purple_blist_add_group(g, NULL); } @@ -528,7 +523,6 @@ xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds, *messenger_user; xmlnode *annotation; MsnUser *user; - MsnNetwork networkId; if (!(contactId = xmlnode_get_child(contactNode,"contactId")) || !(contactInfo = xmlnode_get_child(contactNode, "contactInfo")) @@ -569,7 +563,6 @@ g_free(is_messenger_user); } - networkId = msn_get_network(type); passportName = xmlnode_get_child(contactInfo, "passportName"); if (passportName == NULL) { xmlnode *emailsNode, *contactEmailNode, *emailNode; @@ -600,7 +593,6 @@ if (msnEnabled && !strcmp(msnEnabled, "true")) { /*Messenger enabled, Get the Passport*/ purple_debug_info("msn", "AB Yahoo User %s\n", passport ? passport : "(null)"); - networkId = MSN_NETWORK_YAHOO; g_free(msnEnabled); break; } else { @@ -628,6 +620,15 @@ name = xmlnode_get_data(xmlnode_get_child(annotation, "Name")); if (!strcmp(name, "AB.NickName")) alias = xmlnode_get_data(xmlnode_get_child(annotation, "Value")); + else if (!strcmp(name, "MSN.IM.HasSharedFolder")) + ; /* Do nothing yet... */ + else if (!strcmp(name, "AB.Spouse")) + ; /* Do nothing yet... */ + else if (!strcmp(name, "MSN.Mobile.ContactId")) + ; /* Do nothing yet... */ + else + purple_debug_info("msn", + "Unknown AB contact annotation: %s\n", name); g_free(name); } @@ -639,7 +640,6 @@ user = msn_userlist_find_add_user(session->userlist, passport, Name); msn_user_set_uid(user, uid); - msn_user_set_network(user, networkId); msn_user_set_mobile_phone(user, mobile_number); groupIds = xmlnode_get_child(contactInfo, "groupIds"); @@ -870,7 +870,7 @@ gpointer data) { MsnCallbackState *state = data; - xmlnode *faultcode; + xmlnode *fault; char *faultcode_str; if (resp == NULL) { @@ -880,9 +880,9 @@ return; } - faultcode = xmlnode_get_child(resp->xml, "Body/Fault/faultcode"); + fault = xmlnode_get_child(resp->xml, "Body/Fault"); - if (faultcode == NULL) { + if (fault == NULL) { /* No errors */ if (state->cb) ((MsnSoapCallback)state->cb)(req, resp, data); @@ -890,7 +890,7 @@ return; } - faultcode_str = xmlnode_get_data(faultcode); + faultcode_str = xmlnode_get_data(xmlnode_get_child(fault, "faultcode")); if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) { purple_debug_info("msn", @@ -903,12 +903,15 @@ } else { - /* We don't know how to respond to this faultcode, so just log it */ - /* XXX: Probably should notify the user or undo the change or something? */ - char *str = xmlnode_to_str(xmlnode_get_child(resp->xml, "Body/Fault"), NULL); - purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", - msn_contact_operation_str(state->action), str); - g_free(str); + if (state->cb) { + ((MsnSoapCallback)state->cb)(req, resp, data); + } else { + /* We don't know how to respond to this faultcode, so log it */ + char *str = xmlnode_to_str(fault, NULL); + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), str); + g_free(str); + } msn_callback_state_free(state); } @@ -943,21 +946,44 @@ MsnUser *user; xmlnode *guid; + xmlnode *fault; + g_return_if_fail(session != NULL); + userlist = session->userlist; + + fault = xmlnode_get_child(resp->xml, "Body/Fault"); + if (fault != NULL) { + char *errorcode = xmlnode_get_data(xmlnode_get_child(fault, "detail/errorcode")); + if (errorcode && !strcmp(errorcode, "EmailDomainIsFederated")) { + /* Do something special! */ + purple_debug_error("msn", "Contact is from a federated domain, but don't know what to do yet!\n"); - userlist = session->userlist; + } else if (errorcode && !strcmp(errorcode, "InvalidPassportUser")) { + PurpleBuddy *buddy = purple_find_buddy(session->account, state->who); + char *str = g_strdup_printf(_("Unable to add \"%s\"."), state->who); + purple_notify_error(state->session, _("Buddy Add error"), str, + _("The username specified does not exist.")); + g_free(str); + msn_userlist_rem_buddy(userlist, state->who); + if (buddy != NULL) + purple_blist_remove_buddy(buddy); + + } else { + /* We don't know how to respond to this faultcode, so log it */ + char *fault_str = xmlnode_to_str(fault, NULL); + if (fault_str != NULL) { + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), fault_str); + g_free(fault_str); + } + } + return; + } purple_debug_info("msn", "Contact added successfully\n"); - /* the code this block is replacing didn't send ADL for yahoo contacts, - * but i haven't confirmed this is WLM's behaviour wrt yahoo contacts - */ - if ( !msn_user_is_yahoo(session->account, state->who) ) { - msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); - msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); - } - - msn_notification_send_fqy(session, state->who); + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); user = msn_userlist_find_add_user(userlist, state->who, state->who); msn_user_add_group_id(user, state->guid); @@ -976,6 +1002,7 @@ void msn_add_contact(MsnSession *session, MsnCallbackState *state, const char *passport) { + MsnUser *user; gchar *body = NULL; gchar *contact_xml = NULL; @@ -993,7 +1020,21 @@ purple_debug_info("msn", "Adding contact %s to contact list\n", passport); - contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport); + user = msn_userlist_find_user(session->userlist, passport); + if (user == NULL) { + purple_debug_warning("msn", "Unable to retrieve user %s from the userlist!\n", passport); + return; /* guess this never happened! */ + } + + if (user->networkid != MSN_NETWORK_PASSPORT) { + contact_xml = g_strdup_printf(MSN_CONTACT_EMAIL_XML, + user->networkid == MSN_NETWORK_YAHOO ? + "Messenger2" : + "Messenger3", + passport, 0); + } else { + contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport); + } body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE, contact_xml); state->body = xmlnode_from_str(body, -1); @@ -1011,11 +1052,41 @@ gpointer data) { MsnCallbackState *state = data; + MsnSession *session = state->session; MsnUserList *userlist; + xmlnode *fault; + + g_return_if_fail(session != NULL); + userlist = session->userlist; + + fault = xmlnode_get_child(resp->xml, "Body/Fault"); + if (fault != NULL) { + char *errorcode = xmlnode_get_data(xmlnode_get_child(fault, "detail/errorcode")); + if (errorcode && !strcmp(errorcode, "EmailDomainIsFederated")) { + /* Do something special! */ + purple_debug_error("msn", "Contact is from a federated domain, but don't know what to do yet!\n"); - g_return_if_fail(data != NULL); + } else if (errorcode && !strcmp(errorcode, "InvalidPassportUser")) { + PurpleBuddy *buddy = purple_find_buddy(session->account, state->who); + char *str = g_strdup_printf(_("Unable to add \"%s\"."), state->who); + purple_notify_error(session, _("Buddy Add error"), str, + _("The username specified does not exist.")); + g_free(str); + msn_userlist_rem_buddy(userlist, state->who); + if (buddy != NULL) + purple_blist_remove_buddy(buddy); - userlist = state->session->userlist; + } else { + /* We don't know how to respond to this faultcode, so log it */ + char *fault_str = xmlnode_to_str(fault, NULL); + if (fault_str != NULL) { + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), fault_str); + g_free(fault_str); + } + } + return; + } if (msn_userlist_add_buddy_to_group(userlist, state->who, state->new_group_name)) { @@ -1036,11 +1107,8 @@ g_free(uid); } - if ( !msn_user_is_yahoo(state->session->account, state->who) ) { - msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); - msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); - } - msn_notification_send_fqy(state->session, state->who); + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) { msn_del_contact_from_list(state->session, NULL, state->who, MSN_LIST_PL); @@ -1095,8 +1163,14 @@ return; /* guess this never happened! */ } - if (user != NULL && user->uid != NULL) { + if (user->uid != NULL) { contact_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid); + } else if (user->networkid != MSN_NETWORK_PASSPORT) { + contact_xml = g_strdup_printf(MSN_CONTACT_EMAIL_XML, + user->networkid == MSN_NETWORK_YAHOO ? + "Messenger2" : + "Messenger3", + passport, 0); } else { contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport); } @@ -1120,6 +1194,17 @@ MsnCallbackState *state = data; MsnUserList *userlist = state->session->userlist; MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid); + xmlnode *fault; + + /* We don't know how to respond to this faultcode, so log it */ + fault = xmlnode_get_child(resp->xml, "Body/Fault"); + if (fault != NULL) { + char *fault_str = xmlnode_to_str(fault, NULL); + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), fault_str); + g_free(fault_str); + return; + } purple_debug_info("msn", "Delete contact successful\n"); @@ -1140,8 +1225,8 @@ contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid); purple_debug_info("msn", "Deleting contact with contactId: %s\n", user->uid); } else { - contact_id_xml = g_strdup_printf(MSN_CONTACT_XML, user->passport); - purple_debug_info("msn", "Deleting contact with passport: %s\n", user->passport); + purple_debug_info("msn", "Unable to delete contact %s without a ContactId\n", user->passport); + return; } state = msn_callback_state_new(session); @@ -1165,6 +1250,17 @@ gpointer data) { MsnCallbackState *state = data; + xmlnode *fault; + + /* We don't know how to respond to this faultcode, so log it */ + fault = xmlnode_get_child(resp->xml, "Body/Fault"); + if (fault != NULL) { + char *fault_str = xmlnode_to_str(fault, NULL); + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), fault_str); + g_free(fault_str); + return; + } if (msn_userlist_rem_buddy_from_group(state->session->userlist, state->who, state->old_group_name)) { @@ -1235,6 +1331,19 @@ msn_update_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { + MsnCallbackState *state = (MsnCallbackState *)data; + xmlnode *fault; + + /* We don't know how to respond to this faultcode, so log it */ + fault = xmlnode_get_child(resp->xml, "Body/Fault"); + if (fault != NULL) { + char *fault_str = xmlnode_to_str(fault, NULL); + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), fault_str); + g_free(fault_str); + return; + } + purple_debug_info("msn", "Contact updated successfully\n"); } @@ -1312,6 +1421,17 @@ { MsnCallbackState *state = data; MsnSession *session = state->session; + xmlnode *fault; + + /* We don't know how to respond to this faultcode, so log it */ + fault = xmlnode_get_child(resp->xml, "Body/Fault"); + if (fault != NULL) { + char *fault_str = xmlnode_to_str(fault, NULL); + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), fault_str); + g_free(fault_str); + return; + } purple_debug_info("msn", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]); @@ -1339,10 +1459,13 @@ const gchar *passport, const MsnListId list) { gchar *body = NULL, *member = NULL; + const char *type = "PassportMember"; + gchar *federate = NULL; MsnSoapPartnerScenario partner_scenario; MsnUser *user; g_return_if_fail(session != NULL); + g_return_if_fail(session->userlist != NULL); g_return_if_fail(passport != NULL); g_return_if_fail(list < 5); @@ -1354,21 +1477,27 @@ msn_callback_state_set_list_id(state, list); msn_callback_state_set_who(state, passport); + user = msn_userlist_find_user(session->userlist, passport); + if (user && user->networkid != MSN_NETWORK_PASSPORT) { + type = "EmailMember"; + federate = g_strdup_printf(MSN_MEMBER_FEDERATED_ANNOTATION_XML, + user->networkid); + } + if (list == MSN_LIST_PL) { - g_return_if_fail(session->userlist != NULL); - - user = msn_userlist_find_user(session->userlist, passport); - partner_scenario = MSN_PS_CONTACT_API; - member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML, user->membership_id[MSN_LIST_PL]); + member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML, + type, user->membership_id[MSN_LIST_PL], + federate ? federate : ""); } else { /* list == MSN_LIST_AL || list == MSN_LIST_BL */ partner_scenario = MSN_PS_BLOCK_UNBLOCK; - - member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport); + member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, + type, passport, + federate ? federate : ""); } - body = g_strdup_printf(MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE, + body = g_strdup_printf(MSN_CONTACT_DELETE_FROM_LIST_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], MsnMemberRole[list], member); @@ -1378,6 +1507,7 @@ state->cb = msn_del_contact_from_list_read_cb; msn_contact_request(state); + g_free(federate); g_free(member); g_free(body); } @@ -1387,8 +1517,18 @@ gpointer data) { MsnCallbackState *state = data; + xmlnode *fault; - g_return_if_fail(state != NULL); + /* We don't know how to respond to this faultcode, so log it */ + fault = xmlnode_get_child(resp->xml, "Body/Fault"); + if (fault != NULL) { + char *fault_str = xmlnode_to_str(fault, NULL); + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), fault_str); + g_free(fault_str); + return; + } + g_return_if_fail(state->session != NULL); purple_debug_info("msn", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]); @@ -1415,7 +1555,10 @@ const gchar *passport, const MsnListId list) { gchar *body = NULL, *member = NULL; + const char *type = "PassportMember"; + gchar *federate = NULL; MsnSoapPartnerScenario partner_scenario; + MsnUser *user; g_return_if_fail(session != NULL); g_return_if_fail(passport != NULL); @@ -1429,9 +1572,16 @@ msn_callback_state_set_list_id(state, list); msn_callback_state_set_who(state, passport); - partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK; + user = msn_userlist_find_user(session->userlist, passport); + if (user && user->networkid != MSN_NETWORK_PASSPORT) { + type = "EmailMember"; + federate = g_strdup_printf(MSN_MEMBER_FEDERATED_ANNOTATION_XML, + user->networkid); + } - member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, state->who); + partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK; + member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, + type, state->who, federate ? federate : ""); body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], @@ -1443,6 +1593,7 @@ state->cb = msn_add_contact_to_list_read_cb; msn_contact_request(state); + g_free(federate); g_free(member); g_free(body); } @@ -1482,6 +1633,17 @@ MsnCallbackState *state = data; MsnSession *session; MsnUserList *userlist; + xmlnode *fault; + + /* We don't know how to respond to this faultcode, so log it */ + fault = xmlnode_get_child(resp->xml, "Body/Fault"); + if (fault != NULL) { + char *fault_str = xmlnode_to_str(fault, NULL); + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), fault_str); + g_free(fault_str); + return; + } purple_debug_info("msn", "Group request successful.\n"); @@ -1661,3 +1823,4 @@ g_free(escaped_group_name); g_free(body); } +
--- a/libpurple/protocols/msn/contact.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/msn/contact.h Thu Dec 11 06:24:14 2008 +0000 @@ -211,6 +211,27 @@ "</contactInfo>"\ "</Contact>" +#define MSN_CONTACT_ID_XML \ + "<Contact>"\ + "<contactId>%s</contactId>"\ + "</Contact>" + +#define MSN_CONTACT_EMAIL_XML \ + "<Contact>"\ + "<contactInfo>"\ + "<emails>"\ + "<ContactEmail>"\ + "<contactEmailType>%s</contactEmailType>"\ + "<email>%s</email>"\ + "<isMessengerEnabled>true</isMessengerEnabled>"\ + "<Capability>%d</Capability>"\ + "<MessengerEnabledExternally>false</MessengerEnabledExternally>"\ + "<propertiesChanged/>"\ + "</ContactEmail>"\ + "</emails>"\ + "</contactInfo>"\ + "</Contact>" + #define MSN_ADD_CONTACT_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ @@ -275,7 +296,6 @@ /* Delete a contact from the Contact List */ #define MSN_CONTACT_DEL_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactDelete" -#define MSN_CONTACT_ID_XML "<Contact><contactId>%s</contactId></Contact>" #define MSN_DEL_CONTACT_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ "<soap:Envelope"\ " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ @@ -376,22 +396,32 @@ #define MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/DeleteMember" #define MSN_MEMBER_PASSPORT_XML \ - "<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\ + "<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"%s\">"\ "<Type>Passport</Type>"\ "<State>Accepted</State>"\ "<PassportName>%s</PassportName>"\ + "%s"\ "</Member>" #define MSN_MEMBER_MEMBERSHIPID_XML \ - "<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\ + "<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"%s\">"\ "<Type>Passport</Type>"\ "<MembershipId>%u</MembershipId>"\ "<State>Accepted</State>"\ + "%s"\ "</Member>" +#define MSN_MEMBER_FEDERATED_ANNOTATION_XML \ + "<Annotations>"\ + "<Annotation>"\ + "<Name>MSN.IM.BuddyType</Name>"\ + "<Value>%02d:</Value>"\ + "</Annotation>"\ + "</Annotations>" + /* first delete contact from allow list */ -#define MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +#define MSN_CONTACT_DELETE_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ "<soap:Envelope"\ " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
--- a/libpurple/protocols/msn/msn.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/msn/msn.c Thu Dec 11 06:24:14 2008 +0000 @@ -647,25 +647,41 @@ presence = purple_buddy_get_presence(buddy); status = purple_presence_get_active_status(presence); - /* I think status message should take precedence over media */ - msg = purple_status_get_attr_string(status, "message"); - if (msg && *msg) - return g_markup_escape_text(msg, -1); - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { - const char *title, *artist; + const char *title, *game, *office; char *media, *esc; status = purple_presence_get_status(presence, "tune"); title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE); - artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST); - - media = g_strdup_printf("%s%s%s", title, artist ? " - " : "", - artist ? artist : ""); + + game = purple_status_get_attr_string(status, "game"); + office = purple_status_get_attr_string(status, "office"); + + if (title && *title) { + const char *artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST); + const char *album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM); + media = g_strdup_printf("%s%s%s%s%s%s", title, + (artist && *artist) ? " - " : "", + (artist && *artist) ? artist : "", + (album && *album) ? " (" : "", + (album && *album) ? album : "", + (album && *album) ? ")" : ""); + } + else if (game && *game) + media = g_strdup_printf("Playing %s", game); + else if (office && *office) + media = g_strdup_printf("Editing %s", office); + else + return NULL; esc = g_markup_escape_text(media, -1); g_free(media); return esc; } + /* Official client says media takes precedence over message */ + msg = purple_status_get_attr_string(status, "message"); + if (msg && *msg) + return g_markup_escape_text(msg, -1); + return NULL; } @@ -681,6 +697,7 @@ if (purple_presence_is_online(presence)) { const char *psm, *name; + const char *mediatype = NULL; char *currentmedia = NULL; char *tmp; @@ -688,10 +705,20 @@ if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { PurpleStatus *tune = purple_presence_get_status(presence, "tune"); const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE); - const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST); - const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM); - currentmedia = purple_util_format_song_info(title, artist, album, NULL); - /* We could probably just use user->media.title etc. here */ + const char *game = purple_status_get_attr_string(tune, "game"); + const char *office = purple_status_get_attr_string(tune, "office"); + if (title && *title) { + const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST); + const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM); + mediatype = _("Now Listening"); + currentmedia = purple_util_format_song_info(title, artist, album, NULL); + } else if (game && *game) { + mediatype = _("Playing a game"); + currentmedia = g_strdup(game); + } else if (office && *office) { + mediatype = _("Working"); + currentmedia = g_strdup(office); + } } if (!purple_status_is_available(status)) { @@ -745,7 +772,7 @@ } if (currentmedia) { - purple_notify_user_info_add_pair(user_info, _("Now Listening"), currentmedia); + purple_notify_user_info_add_pair(user_info, mediatype, currentmedia); g_free(currentmedia); } } @@ -840,6 +867,8 @@ PURPLE_TUNE_ARTIST, _("Artist"), purple_value_new(PURPLE_TYPE_STRING), PURPLE_TUNE_ALBUM, _("Album"), purple_value_new(PURPLE_TYPE_STRING), PURPLE_TUNE_TITLE, _("Title"), purple_value_new(PURPLE_TYPE_STRING), + "game", _("Game Title"), purple_value_new(PURPLE_TYPE_STRING), + "office", _("Office Title"), purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); @@ -1414,21 +1443,18 @@ purple_debug_info("msn", "msn_add_buddy: %s\n", who); #endif -#if 0 - /* Which is the max? */ - if (session->fl_users_count >= 150) - { - purple_debug_info("msn", "Too many buddies\n"); - /* Buddy list full */ - /* TODO: purple should be notified of this */ - return; - } -#endif - /* XXX - Would group ever be NULL here? I don't think so... * shx: Yes it should; MSN handles non-grouped buddies, and this is only * internal. */ - msn_userlist_add_buddy(userlist, who, group ? group->name : NULL); + if (msn_userlist_find_user(userlist, who) != NULL) { + /* We already know this buddy. This function takes care of users + already in the list and stuff... */ + msn_userlist_add_buddy(userlist, who, group ? group->name : NULL); + } else { + /* We need to check the network for this buddy first */ + msn_userlist_save_pending_buddy(userlist, who, group ? group->name : NULL); + msn_notification_send_fqy(session, who); + } } static void @@ -1811,7 +1837,7 @@ if (b->server_alias) { char *nicktext = g_markup_escape_text(b->server_alias, -1); - tmp = g_strdup_printf("<font sml=\"msn\">%s</font><br>", nicktext); + tmp = g_strdup_printf("<font sml=\"msn\">%s</font>", nicktext); purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp); g_free(tmp); g_free(nicktext); @@ -1917,9 +1943,8 @@ if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0) { - tmp = g_strdup_printf("<b>%s</b>", _("Error retrieving profile")); - purple_notify_user_info_add_pair(user_info, NULL, tmp); - g_free(tmp); + purple_notify_user_info_add_pair(user_info, + _("Error retrieving profile"), NULL); purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL); purple_notify_user_info_destroy(user_info); @@ -2260,21 +2285,24 @@ char *p = strstr(url_buffer, "<form id=\"profile_form\" name=\"profile_form\" action=\"http://spaces.live.com/profile.aspx?cid=0\""); PurpleBuddy *b = purple_find_buddy (purple_connection_get_account(info_data->gc), info_data->name); - purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"), - ((p && b) ? _("The user has not created a public profile.") : - (p ? _("MSN reported not being able to find the user's profile. " - "This either means that the user does not exist, " - "or that the user exists " - "but has not created a public profile.") : - _("Could not find " /* This should never happen */ - "any information in the user's profile. " - "The user most likely does not exist.")))); + purple_notify_user_info_add_pair(user_info, + _("Error retrieving profile"), NULL); + purple_notify_user_info_add_pair(user_info, NULL, + ((p && b) ? _("The user has not created a public profile.") : + (p ? _("MSN reported not being able to find the user's profile. " + "This either means that the user does not exist, " + "or that the user exists " + "but has not created a public profile.") : + _("Could not find " /* This should never happen */ + "any information in the user's profile. " + "The user most likely does not exist.")))); } /* put a link to the actual profile URL */ - tmp = g_strdup_printf("<a href=\"%s%s\">%s%s</a>", - PROFILE_URL, info_data->name, PROFILE_URL, info_data->name); - purple_notify_user_info_add_pair(user_info, _("Profile URL"), tmp); + purple_notify_user_info_add_section_break(user_info); + tmp = g_strdup_printf("<a href=\"%s%s\">%s</a>", + PROFILE_URL, info_data->name, _("View web profile")); + purple_notify_user_info_add_pair(user_info, NULL, tmp); g_free(tmp); #if PHOTO_SUPPORT
--- a/libpurple/protocols/msn/notification.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/msn/notification.c Thu Dec 11 06:24:14 2008 +0000 @@ -849,10 +849,35 @@ fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len) { - purple_debug_info("msn", "FQY payload:\n%s\n", payload); - g_return_if_fail(cmdproc->session != NULL); -/* msn_notification_post_adl(cmdproc, payload, len); */ -/* msn_get_address_book(cmdproc->session, MSN_AB_SAVE_CONTACT, NULL, NULL); */ + MsnUserList *userlist; + xmlnode *ml, *d, *c; + const char *domain; + const char *local; + const char *type; + char *passport; + MsnNetwork network = MSN_NETWORK_PASSPORT; + + userlist = cmdproc->session->userlist; + + /* FQY response: + <ml><d n="domain.com"><c n="local-node" t="network" /></d></ml> */ + ml = xmlnode_from_str(payload, len); + d = xmlnode_get_child(ml, "d"); + c = xmlnode_get_child(d, "c"); + domain = xmlnode_get_attrib(d, "n"); + local = xmlnode_get_attrib(c, "n"); + type = xmlnode_get_attrib(c, "t"); + + passport = g_strdup_printf("%s@%s", local, domain); + + if (type != NULL) + network = (MsnNetwork)strtoul(type, NULL, 10); + purple_debug_info("msn", "FQY response says %s is from network %d\n", + passport, network); + msn_userlist_add_pending_buddy(userlist, passport, network); + + g_free(passport); + xmlnode_free(ml); } static void @@ -1578,7 +1603,7 @@ MsnUser *user; const char *passport; char *psm_str, *str; - CurrentMedia media = {NULL, NULL, NULL}; + CurrentMedia media = {CURRENT_MEDIA_UNKNOWN, NULL, NULL, NULL}; session = cmdproc->session; account = session->account;
--- a/libpurple/protocols/msn/state.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/msn/state.c Thu Dec 11 06:24:14 2008 +0000 @@ -100,14 +100,15 @@ cmedia_array = g_strsplit(cmedia, "\\0", 0); /* - * 0: Media Player - * 1: 'Music' + * 0: Application + * 1: 'Music'/'Games'/'Office' * 2: '1' if enabled, '0' if not * 3: Format (eg. {0} by {1}) * 4: Title - * 5: Artist - * 6: Album - * 7: ? + * If 'Music': + * 5: Artist + * 6: Album + * 7: ? */ #if GLIB_CHECK_VERSION(2,6,0) strings = g_strv_length(cmedia_array); @@ -118,6 +119,15 @@ if (strings >= 4 && !strcmp(cmedia_array[2], "1")) { parsed = TRUE; + if (!strcmp(cmedia_array[1], "Music")) + media->type = CURRENT_MEDIA_MUSIC; + else if (!strcmp(cmedia_array[1], "Games")) + media->type = CURRENT_MEDIA_GAMES; + else if (!strcmp(cmedia_array[1], "Office")) + media->type = CURRENT_MEDIA_OFFICE; + else + media->type = CURRENT_MEDIA_UNKNOWN; + g_free(media->title); if (strings == 4) { media->title = g_strdup(cmedia_array[3]); @@ -199,21 +209,33 @@ static char * create_media_string(PurplePresence *presence) { - const char *artist, *title, *album; + const char *title, *game, *office; char *ret; PurpleStatus *status = purple_presence_get_status(presence, "tune"); if (!status || !purple_status_is_active(status)) - return g_strdup_printf("WMP\\0Music\\00\\0{0} - {1}\\0\\0\\0\\0\\0"); + return g_strdup_printf("\\0Music\\00\\0\\0"); - artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST); title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE); - album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM); + game = purple_status_get_attr_string(status, "game"); + office = purple_status_get_attr_string(status, "office"); - ret = g_strdup_printf("WMP\\0Music\\0%c\\0{0} - {1}\\0%s\\0%s\\0%s\\0\\0", - (title && *title) ? '1' : '0', - title ? title : "", - artist ? artist : "", - album ? album : ""); + if (title && *title) { + const char *artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST); + const char *album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM); + ret = g_strdup_printf("WMP\\0Music\\01\\0{0}%s%s\\0%s\\0%s\\0%s\\0", + artist ? " - {1}" : "", + album ? " ({2})" : "", + title, + artist ? artist : "", + album ? album : ""); + } + else if (game && *game) + ret = g_strdup_printf("\\0Games\\01\\0Playing {0}\\0%s\\0", game); + else if (office && *office) + ret = g_strdup_printf("\\0Office\\01\\0Editing {0}\\0%s\\0", office); + else + ret = g_strdup_printf("\\0Music\\00\\0\\0"); + return ret; }
--- a/libpurple/protocols/msn/user.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/msn/user.c Thu Dec 11 06:24:14 2008 +0000 @@ -106,12 +106,25 @@ purple_prpl_got_user_status_deactive(account, user->passport, "mobile"); } - if (!offline && user->media.title) { - purple_prpl_got_user_status(account, user->passport, "tune", - PURPLE_TUNE_ARTIST, user->media.artist, - PURPLE_TUNE_ALBUM, user->media.album, - PURPLE_TUNE_TITLE, user->media.title, - NULL); + if (!offline && user->media.type != CURRENT_MEDIA_UNKNOWN) { + if (user->media.type == CURRENT_MEDIA_MUSIC) { + purple_prpl_got_user_status(account, user->passport, "tune", + PURPLE_TUNE_ARTIST, user->media.artist, + PURPLE_TUNE_ALBUM, user->media.album, + PURPLE_TUNE_TITLE, user->media.title, + NULL); + } else if (user->media.type == CURRENT_MEDIA_GAMES) { + purple_prpl_got_user_status(account, user->passport, "tune", + "game", user->media.title, + NULL); + } else if (user->media.type == CURRENT_MEDIA_OFFICE) { + purple_prpl_got_user_status(account, user->passport, "tune", + "office", user->media.title, + NULL); + } else { + purple_debug_warning("msn", "Got CurrentMedia with unknown type %d.\n", + user->media.type); + } } else { purple_prpl_got_user_status_deactive(account, user->passport, "tune"); } @@ -191,6 +204,7 @@ g_free(user->media.album); g_free(user->media.artist); + user->media.type = media ? media->type : CURRENT_MEDIA_UNKNOWN; user->media.title = media ? g_strdup(media->title) : NULL; user->media.artist = media ? g_strdup(media->artist) : NULL; user->media.album = media ? g_strdup(media->album) : NULL; @@ -326,6 +340,20 @@ } void +msn_user_set_pending_group(MsnUser *user, const char *group) +{ + user->pending_group = g_strdup(group); +} + +char * +msn_user_remove_pending_group(MsnUser *user) +{ + char *group = user->pending_group; + user->pending_group = NULL; + return group; +} + +void msn_user_set_home_phone(MsnUser *user, const char *number) { g_return_if_fail(user != NULL);
--- a/libpurple/protocols/msn/user.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/msn/user.h Thu Dec 11 06:24:14 2008 +0000 @@ -45,11 +45,20 @@ /** * Current media. */ +typedef enum +{ + CURRENT_MEDIA_UNKNOWN, + CURRENT_MEDIA_MUSIC, + CURRENT_MEDIA_GAMES, + CURRENT_MEDIA_OFFICE +} CurrentMediaType; + typedef struct _CurrentMedia { + CurrentMediaType type; /**< Type. */ + char *title; /**< Title. */ char *artist; /**< Artist. */ char *album; /**< Album. */ - char *title; /**< Title. */ } CurrentMedia; /** @@ -82,6 +91,7 @@ gboolean mobile; /**< Signed up with MSN Mobile. */ GList *group_ids; /**< The group IDs. */ + char *pending_group; /**< A pending group to add. */ MsnObject *msnobj; /**< The user's MSN Object. */ @@ -204,6 +214,23 @@ void msn_user_remove_group_id(MsnUser *user, const char * id); /** + * Sets the pending group for a user. + * + * @param user The user. + * @param group The group name. + */ +void msn_user_set_pending_group(MsnUser *user, const char *group); + +/** + * Removes the pending group from a user. + * + * @param user The user. + * + * @return Returns the pending group name. + */ +char *msn_user_remove_pending_group(MsnUser *user); + +/** * Sets the home phone number for a user. * * @param user The user.
--- a/libpurple/protocols/msn/userlist.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/msn/userlist.c Thu Dec 11 06:24:14 2008 +0000 @@ -184,10 +184,6 @@ { msn_user_add_group_id(user, group_id); } - else - { - /* session->sync->fl_users_count++; */ - } } else if (list_id == MSN_LIST_AL) { @@ -253,10 +249,6 @@ msn_user_remove_group_id(user, group_id); return; } - else - { - /* session->sync->fl_users_count--; */ - } } else if (list_id == MSN_LIST_AL) { @@ -756,6 +748,62 @@ msn_add_contact_to_group(userlist->session, state, who, group_id); } +/* + * Save a buddy address/group until we get back response from FQY + */ +void +msn_userlist_save_pending_buddy(MsnUserList *userlist, + const char *who, + const char *group_name) +{ + MsnUser *user; + + g_return_if_fail(userlist != NULL); + + user = msn_user_new(userlist, who, NULL); + msn_user_set_pending_group(user, group_name); + msn_user_set_network(user, MSN_NETWORK_UNKNOWN); + userlist->pending = g_list_prepend(userlist->pending, user); +} + +/* + * Actually adds a buddy once we have the response from FQY + */ +void +msn_userlist_add_pending_buddy(MsnUserList *userlist, + const char *who, + /*MsnNetwork*/ int network) +{ + MsnUser *user = NULL; + GList *l; + char *group; + + for (l = userlist->pending; l != NULL; l = l->next) + { + user = (MsnUser *)l->data; + + if (!g_strcasecmp(who, user->passport)) { + userlist->pending = g_list_delete_link(userlist->pending, l); + break; + } + } + + if (user == NULL) { + purple_debug_error("msn", "Attempting to add a pending user that does not exist.\n"); + return; + } + + /* Bit of a hack, but by adding to userlist now, the rest of the code + * will know what network to use. + */ + msn_user_set_network(user, network); + msn_userlist_add_user(userlist, user); + + group = msn_user_remove_pending_group(user); + msn_userlist_add_buddy(userlist, who, group); + g_free(group); +} + void msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who, MsnListId list_id)
--- a/libpurple/protocols/msn/userlist.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/msn/userlist.h Thu Dec 11 06:24:14 2008 +0000 @@ -47,13 +47,12 @@ GList *users; /* Contains MsnUsers */ GList *groups; /* Contains MsnGroups */ + GList *pending; /* MsnUsers pending addition (waiting for FQY response) */ GQueue *buddy_icon_requests; int buddy_icon_window; guint buddy_icon_request_timer; - int fl_users_count; - }; gboolean msn_userlist_user_is_in_group(MsnUser *user, const char * group_id); @@ -93,6 +92,12 @@ void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who); void msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group_name); +void msn_userlist_save_pending_buddy(MsnUserList *userlist, + const char *who, + const char *group_name); +void msn_userlist_add_pending_buddy(MsnUserList *userlist, + const char *who, + /*MsnNetwork*/ int network); void msn_userlist_move_buddy(MsnUserList *userlist, const char *who, const char *old_group_name, const char *new_group_name);
--- a/libpurple/protocols/myspace/user.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/myspace/user.c Thu Dec 11 06:24:14 2008 +0000 @@ -107,17 +107,6 @@ uid = purple_blist_node_get_int(&user->buddy->node, "UserID"); - if (full) { - /* TODO: link to username, if available */ - if (uid) { - char *profile = g_strdup_printf("<a href=\"http://myspace.com/%d\">http://myspace.com/%d</a>", - uid, uid); - purple_notify_user_info_add_pair(user_info, _("Profile"), profile); - g_free(profile); - } - } - - /* a/s/l...the vitals */ if (user->age) { char age[16]; @@ -180,6 +169,16 @@ purple_notify_user_info_add_pair(user_info, _("Client Version"), client); g_free(client); } + + if (full && uid) { + /* TODO: link to username, if available */ + char *profile; + purple_notify_user_info_add_section_break(user_info); + profile = g_strdup_printf("<a href=\"http://myspace.com/%d\">%s</a>", + uid, _("View web profile")); + purple_notify_user_info_add_pair(user_info, NULL, profile); + g_free(profile); + } } /** Set the currently playing song artist and or title.
--- a/libpurple/protocols/null/nullprpl.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/null/nullprpl.c Thu Dec 11 06:24:14 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; }
--- a/libpurple/protocols/oscar/bstream.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/oscar/bstream.c Thu Dec 11 06:24:14 2008 +0000 @@ -302,3 +302,12 @@ return len; } + +int byte_stream_putuid(ByteStream *bs, OscarData *od) +{ + PurpleAccount *account; + + account = purple_connection_get_account(od->gc); + + return byte_stream_putle32(bs, atoi(purple_account_get_username(account))); +}
--- a/libpurple/protocols/oscar/family_auth.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/oscar/family_auth.c Thu Dec 11 06:24:14 2008 +0000 @@ -295,10 +295,9 @@ /* * No matter what, we should have a screen name. */ - memset(od->sn, 0, sizeof(od->sn)); if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) { info->sn = aim_tlv_getstr(tlvlist, 0x0001, 1); - strncpy(od->sn, info->sn, sizeof(od->sn)); + purple_connection_set_display_name(od->gc, info->sn); } /*
--- a/libpurple/protocols/oscar/family_icbm.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/oscar/family_icbm.c Thu Dec 11 06:24:14 2008 +0000 @@ -1214,7 +1214,7 @@ /* * Your UIN */ - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); /* * TLV t(type) l(strlen(message)+1) v(message+NULL)
--- a/libpurple/protocols/oscar/family_icq.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/oscar/family_icq.c Thu Dec 11 06:24:14 2008 +0000 @@ -36,7 +36,7 @@ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) return -EINVAL; - purple_debug_info("oscar", "Requesting offline messages from %s", od->sn); + purple_debug_info("oscar", "Requesting offline messages\n"); bslen = 2 + 4 + 2 + 2; @@ -49,7 +49,7 @@ byte_stream_put16(&bs, bslen); byte_stream_putle16(&bs, bslen - 2); - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x003c); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ @@ -70,7 +70,7 @@ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) return -EINVAL; - purple_debug_info("oscar", "Acknowledged receipt of offline messages from %s", od->sn); + purple_debug_info("oscar", "Acknowledged receipt of offline messages\n"); bslen = 2 + 4 + 2 + 2; @@ -83,7 +83,7 @@ byte_stream_put16(&bs, bslen); byte_stream_putle16(&bs, bslen - 2); - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x003e); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ @@ -117,7 +117,7 @@ byte_stream_put16(&bs, bslen); byte_stream_putle16(&bs, bslen - 2); - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ byte_stream_putle16(&bs, 0x0c3a); /* shrug. */ @@ -172,7 +172,7 @@ byte_stream_put16(&bs, bslen); byte_stream_putle16(&bs, bslen - 2); - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ byte_stream_putle16(&bs, 0x042e); /* shrug. */ @@ -212,7 +212,7 @@ byte_stream_put16(&bs, bslen); byte_stream_putle16(&bs, bslen - 2); - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ byte_stream_putle16(&bs, 0x04b2); /* shrug. */ @@ -259,7 +259,7 @@ byte_stream_put16(&bs, bslen); byte_stream_putle16(&bs, bslen - 2); - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ byte_stream_putle16(&bs, 0x04ba); /* shrug. */ @@ -303,7 +303,7 @@ byte_stream_put16(&bs, bslen); byte_stream_putle16(&bs, bslen - 2); - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ byte_stream_putle16(&bs, 0x051f); /* shrug. */ @@ -341,7 +341,7 @@ byte_stream_put16(&bs, bslen); byte_stream_putle16(&bs, bslen - 2); - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ byte_stream_putle16(&bs, 0x0998); /* shrug. */ @@ -377,11 +377,12 @@ int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias) { FlapConnection *conn; + PurpleAccount *account; ByteStream bs; aim_snacid_t snacid; int bslen, xmllen; char *xml; - const char *timestr; + const char *timestr, *username; time_t t; struct tm *tm; gchar *stripped; @@ -392,6 +393,9 @@ if (!name || !msg || !alias) return -EINVAL; + account = purple_connection_get_account(od->gc); + username = purple_account_get_username(account); + time(&t); tm = gmtime(&t); timestr = purple_utf8_strftime("%a, %d %b %Y %T %Z", tm); @@ -399,7 +403,7 @@ stripped = purple_markup_strip_html(msg); /* The length of xml included the null terminating character */ - xmllen = 209 + strlen(name) + strlen(stripped) + strlen(od->sn) + strlen(alias) + strlen(timestr) + 1; + xmllen = 209 + strlen(name) + strlen(stripped) + strlen(username) + strlen(alias) + strlen(timestr) + 1; xml = g_new(char, xmllen); snprintf(xml, xmllen, "<icq_sms_message>" @@ -411,7 +415,7 @@ "<delivery_receipt>Yes</delivery_receipt>" "<time>%s</time>" "</icq_sms_message>", - name, stripped, od->sn, alias, timestr); + name, stripped, username, alias, timestr); bslen = 36 + xmllen; @@ -424,7 +428,7 @@ byte_stream_put16(&bs, bslen); byte_stream_putle16(&bs, bslen - 2); - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ @@ -481,7 +485,7 @@ byte_stream_put16(&bs, bslen); byte_stream_putle16(&bs, bslen - 2); - byte_stream_putle32(&bs, atoi(od->sn)); + byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ byte_stream_putle16(&bs, 0x0fa0); /* shrug. */
--- a/libpurple/protocols/oscar/oscar.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.c Thu Dec 11 06:24:14 2008 +0000 @@ -3187,6 +3187,12 @@ } } + purple_notify_user_info_add_section_break(user_info); + tmp = g_strdup_printf("<a href=\"http://profiles.aim.com/%s\">%s</a>", + purple_normalize(account, userinfo->sn), _("View web profile")); + purple_notify_user_info_add_pair(user_info, NULL, tmp); + g_free(tmp); + purple_notify_userinfo(gc, userinfo->sn, user_info, NULL, NULL); purple_notify_user_info_destroy(user_info);
--- a/libpurple/protocols/oscar/oscar.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.h Thu Dec 11 06:24:14 2008 +0000 @@ -488,16 +488,8 @@ guint maxawaymsglen; /* max size (bytes) of posted away message */ } rights; - /* ---- Client Accessible ------------------------ */ - - /* Our screen name. */ - /* TODO: Get rid of this and use purple_account_get_username() everywhere? */ - char sn[MAXSNLEN+1]; - PurpleConnection *gc; - /* ---- Internal Use Only ------------------------ */ - void *modlistv; /* @@ -1587,6 +1579,7 @@ int byte_stream_putraw(ByteStream *bs, const guint8 *v, int len); int byte_stream_putstr(ByteStream *bs, const char *str); int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, int len); +int byte_stream_putuid(ByteStream *bs, OscarData *od); int byte_stream_putcaps(ByteStream *bs, guint32 caps); /*
--- a/libpurple/protocols/qq/ChangeLog Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/ChangeLog Thu Dec 11 06:24:14 2008 +0000 @@ -1,3 +1,35 @@ +2008.12.06 - flos <lonicerae(at)gmail.com> + * 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 <lonicerae(at)gmail.com> + * Fixed a bug after propagating + +2008.11.18 - ccpaging <ccpaging(at)gmail.com> + * 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 <ccpaging(at)gmail.com> + * 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 <ccpaging(at)gmail.com> + * Fixed a bug in group_join.c + +2008.10.30 - flos <lonicerae(at)gmail.com> + * 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 <lonicerae(at)gmail.com> * Updated AUTHORS @@ -22,15 +54,15 @@ 2008.10.10 - ccpaging <ccpaging(at)gmail.com> * Keep group_search.c/h for later use - * Update 'group' + * Update 'group' 2008.10.09 - ccpaging <ccpaging(at)gmail.com> * 20081009-1 2008.10.09 - ccpaging <ccpaging(at)gmail.com> * 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 <ccpaging(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> +2008.08.16 - ccpaging <ccpaging(at)gmail.com> * 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 <csyfek(at)gmail.com> * Commit to Pidgin -2008.08.07 - ccpaging <ecc_hy(at)hotmail.com> +2008.08.07 - ccpaging <ccpaging(at)gmail.com> * Support managing multi-connections according to simple.c -2008.08.06 - ccpaging <ecc_hy(at)hotmail.com> +2008.08.06 - ccpaging <ccpaging(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> +2008.08.04 - ccpaging <ccpaging(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> +2008.08.02 - ccpaging <ccpaging(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> +2008.07.31 - ccpaging <ccpaging(at)gmail.com> * 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 <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> +2008.07.30 - ccpaging <ccpaging(at)gmail.com>, csyfek <csyfek(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> + +2008.07.12 - ccpaging <ccpaging(at)gmail.com> * 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 <ecc_hy(at)hotmail.com>, moo <phpxcache(at)gmail.com> +2008.06.28 - ccpaging <ccpaging(at)gmail.com>, moo <phpxcache(at)gmail.com> * Patches from moo<phpxcache@gmail.com> and ccpaging<ccpaging@foxmail.com>. * Tickets: * Fixes #4956. * Fixes #2998. -2008.06.07 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> +2008.06.07 - ccpaging <ccpaging(at)gmail.com>, csyfek <csyfek(at)gmail.com> * Clean code and apply patches from QuLogic -2008.05.19 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> +2008.05.19 - ccpaging <ccpaging(at)gmail.com>, csyfek <csyfek(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> +2008.05.14 - ccpaging <ccpaging(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> +2008.05.09 - ccpaging <ccpaging(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> +2008.05.09 - ccpaging <ccpaging(at)gmail.com> * Clean code of packet_parse.c, enable PARSER_DEBUG * Rewrite send_queue -2008.05.08 - ccpaging <ecc_hy(at)hotmail.com> +2008.05.08 - ccpaging <ccpaging(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> +2008.05.05 - ccpaging <ccpaging(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> +2008.05.05 - ccpaging <ccpaging(at)gmail.com> * Add qq_hex_dump function -2008.04.25 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> +2008.04.25 - ccpaging <ccpaging(at)gmail.com>, csyfek <csyfek(at)gmail.com> * 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 <ecc_hy(at)hotmail.com> +2008.03.24 - ccpaging <ccpaging(at)gmail.com> * Remove qq_crypt function in crypt.c, use qq_crypt and qq_decrypt directly ** since pidgin-2.4.0 ***
--- a/libpurple/protocols/qq/Makefile.mingw Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.mingw Thu Dec 11 06:24:14 2008 +0000 @@ -6,6 +6,7 @@ PIDGIN_TREE_TOP := ../../.. include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak + TARGET = libqq TYPE = PLUGIN
--- a/libpurple/protocols/qq/buddy_info.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/buddy_info.c Thu Dec 11 06:24:14 2008 +0000 @@ -58,7 +58,7 @@ #define QQ_PUBLISH_SIZE 3 static const gchar *publish_types[] = { - N_("Visible"), N_("Firend Only"), N_("Private") + N_("Visible"), N_("Friend Only"), N_("Private") }; #define QQ_GENDER_SIZE 3 @@ -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) {
--- a/libpurple/protocols/qq/buddy_info.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/buddy_info.h Thu Dec 11 06:24:14 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
--- a/libpurple/protocols/qq/buddy_list.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/buddy_list.c Thu Dec 11 06:24:14 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)
--- a/libpurple/protocols/qq/buddy_opt.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/buddy_opt.c Thu Dec 11 06:24:14 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);
--- a/libpurple/protocols/qq/char_conv.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/char_conv.c Thu Dec 11 06:24:14 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 "<IMG ID=\"0\">" /* 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, - "<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">", - color_code, font_name, font_size / 3); - purple_debug_info("QQ_MESG", - "recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n", - color_code, font_name, font_size / 3); - g_string_append(encoded, msg_utf8); - - if (is_bold) { - g_string_prepend(encoded, "<b>"); - g_string_append(encoded, "</b>"); - } - if (is_italic) { - g_string_prepend(encoded, "<i>"); - g_string_append(encoded, "</i>"); - } - if (is_underline) { - g_string_prepend(encoded, "<u>"); - g_string_append(encoded, "</u>"); - } - - g_string_append(encoded, "</font></font></font>"); - 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); -}
--- a/libpurple/protocols/qq/char_conv.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/char_conv.h Thu Dec 11 06:24:14 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
--- a/libpurple/protocols/qq/group.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/group.c Thu Dec 11 06:24:14 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); }
--- a/libpurple/protocols/qq/group_im.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/group_im.c Thu Dec 11 06:24:14 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; }
--- a/libpurple/protocols/qq/group_im.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/group_im.h Thu Dec 11 06:24:14 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);
--- a/libpurple/protocols/qq/group_info.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/group_info.c Thu Dec 11 06:24:14 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);
--- a/libpurple/protocols/qq/group_internal.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/group_internal.c Thu Dec 11 06:24:14 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);
--- a/libpurple/protocols/qq/group_join.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/group_join.c Thu Dec 11 06:24:14 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(_("Succeeded joining 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, _("Succeeded joining Qun")); } } @@ -254,29 +254,29 @@ g_return_if_fail(rmd != NULL); switch (reply) { case QQ_ROOM_JOIN_OK: - purple_debug_info("QQ", "Successed in joining group \"%s\"\n", rmd->title_utf8); + purple_debug_info("QQ", "Succeeded in joining group \"%s\"\n", rmd->title_utf8); rmd->my_role = QQ_ROOM_ROLE_YES; /* this must be shown before getting online members */ qq_room_conv_open(gc, rmd); 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 from joining"), 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")); + purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), _("Join Qun, Unknown 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);
--- a/libpurple/protocols/qq/group_opt.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/group_opt.c Thu Dec 11 06:24:14 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(_("<b>Joining Qun %d is approved by admin %d for %s</b>"), + msg = g_strdup_printf(_("<b>Joinning Qun %u is approved by admin %u for %s</b>"), 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(_("<b>Removed buddy %d.</b>"), uid); + msg = g_strdup_printf(_("<b>Removed buddy %u.</b>"), 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(_("<b>New buddy %d joined.</b>"), uid); + msg = g_strdup_printf(_("<b>New buddy %u joined.</b>"), uid); qq_room_got_chat_in(gc, id, 0, msg, now); g_free(msg); }
--- a/libpurple/protocols/qq/im.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/im.c Thu Dec 11 06:24:14 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, "/<W>"}, {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, "/<L>"}, {0x56, "/fw"}, {0x56, "/blow_kiss"}, + {0x58, "/jump"}, {0x58, "/tiao"}, + {0x5a, "/shake"}, {0x5a, "/fad"}, /* after fade */ + {0x5b, "/<O>"}, {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, "/<D>"}, {0x50, "/dd"}, + {0x51, "/<J>"}, {0x51, "/mn"}, {0x51, "/beauty"}, + {0x52, "/<H>"}, {0x52, "/hl"}, + {0x53, "/<M>"}, {0x53, "/mamao"}, + {0x54, "/<QQ>"}, {0x54, "/qz"}, {0x54, "/qq"}, + {0x5d, "/<B>"}, {0x5d, "/bj"}, {0x5d, "/baijiu"}, + {0x5f, "/<U>"}, {0x5f, "/qsh"}, {0x5f, "/soda"}, + {0x69, "/<!!>"}, {0x69, "/xy"}, {0x69, "/rain"}, + {0x6a, "/<~>"}, {0x6a, "/duoy"}, {0x6a, "/cloudy"}, + {0x6c, "/<Z>"}, {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, "<IMG ID=\"0\">"); + } 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, "<font sml=\"none\">"); + g_string_append(converted, "</font>"); + } + 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, "<font color=\"#%02x%02x%02x\">", + 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, "</font>"); + + /* 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, "<font face=\"%s\">", fmt->font); + g_string_prepend(converted, tmp->str); + g_string_set_size(tmp, 0); + g_string_append(converted, "</font>"); + } + size = (fmt->attr & 0x1f) / 3; + if (size >= 0) { + g_string_append_printf(tmp, "<font size=\"%d\">", size); + g_string_prepend(converted, tmp->str); + g_string_set_size(tmp, 0); + g_string_append(converted, "</font>"); + } + if (fmt->attr & 0x20) { + /* bold */ + g_string_prepend(converted, "<b>"); + g_string_append(converted, "</b>"); + } + if (fmt->attr & 0x40) { + /* italic */ + g_string_prepend(converted, "<i>"); + g_string_append(converted, "</i>"); + } + if (fmt->attr & 0x80) { + /* underline */ + g_string_prepend(converted, "<u>"); + g_string_append(converted, "</u>"); + } + + 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; +}
--- a/libpurple/protocols/qq/im.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/im.h Thu Dec 11 06:24:14 2008 +0000 @@ -27,22 +27,13 @@ #include <glib.h> #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
--- a/libpurple/protocols/qq/packet_parse.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/packet_parse.c Thu Dec 11 06:24:14 2008 +0000 @@ -32,11 +32,9 @@ /* note: * 1, in these functions, 'b' stands for byte, 'w' stands for word, 'dw' stands for double word. * 2, we use '*cursor' and 'buf' as two addresses to calculate the length. - * 3, change '0' to '1', if want to get more info about the packet parsing. */ + * 3, change 'undef' to 'define' to get more info about the packet parsing. */ -#if 0 -#define PARSER_DEBUG -#endif +#undef PARSER_DEBUG /* read one byte from buf, * return the number of bytes read if succeeds, otherwise return -1 */
--- a/libpurple/protocols/qq/qq.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/qq.c Thu Dec 11 06:24:14 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,11 +99,14 @@ 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)); - return; + custom_server = purple_account_get_string(account, "server", NULL); + + if (custom_server != NULL) { + purple_debug_info("QQ", "Select server '%s'\n", custom_server); + if (*custom_server != '\0' && g_ascii_strcasecmp(custom_server, "auto") != 0) { + qd->servers = g_list_append(qd->servers, g_strdup(custom_server)); + return; + } } if (qd->use_tcp) { @@ -268,9 +269,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 +417,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 +440,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 +657,14 @@ g_string_append(info, "khc(at)pidgin.im<br>\n"); g_string_append(info, "qulogic(at)pidgin.im<br>\n"); g_string_append(info, "rlaager(at)pidgin.im<br>\n"); + g_string_append(info, "Huang Guan : http://home.xxsyzx.com<br>\n"); g_string_append(info, "OpenQ Google Group : http://groups.google.com/group/openq<br>\n"); g_string_append(info, "<br>\n"); g_string_append(info, _("<p><i>And, all the boys in the backroom...</i><br>\n")); g_string_append(info, _("<i>Feel free to join us!</i> :)")); g_string_append(info, "</body></html>"); - 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 +703,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 +722,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 +935,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 +973,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 +1068,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 +1085,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 +1104,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);
--- a/libpurple/protocols/qq/qq.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/qq.h Thu Dec 11 06:24:14 2008 +0000 @@ -182,6 +182,8 @@ gboolean is_show_notice; gboolean is_show_news; + + guint16 send_im_id; /* send IM sequence number */ }; #endif
--- a/libpurple/protocols/qq/qq_base.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/qq_base.c Thu Dec 11 06:24:14 2008 +0000 @@ -68,10 +68,11 @@ 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")); + _("Cannot decrypt server reply")); return QQ_LOGIN_REPLY_ERR; } @@ -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; } @@ -159,7 +160,7 @@ if (len < 11) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("Can not decrypt get server reply")); + _("Cannot decrypt server reply")); return QQ_LOGIN_REPLY_ERR; } @@ -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); @@ -423,7 +424,7 @@ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, ">>> [default] decrypt and dump"); error = g_strdup_printf( - _("Unknow reply code when login (0x%02X)"), + _("Unknown reply code when login (0x%02X)"), ret ); reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR; break; @@ -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; @@ -871,8 +872,8 @@ purple_request_field_group_add_field(group, field); purple_request_fields(account, - _("QQ Captcha Verifing"), - _("QQ Captcha Verifing"), + _("QQ Captcha Verifying"), + _("QQ Captcha Verifying"), _("Enter the text from the image"), fields, _("OK"), G_CALLBACK(captcha_input_ok_cb), @@ -1110,7 +1111,7 @@ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, ">>> [default] decrypt and dump"); error = g_strdup_printf( - _("Unknow reply code when checking password (0x%02X)"), + _("Unknown reply code when checking password (0x%02X)"), ret ); reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR; break; @@ -1256,7 +1257,7 @@ /* Missing get server before login*/ default: error = g_strdup_printf( - _("Unknow reply code when login (0x%02X):\n%s"), + _("Unknown reply code when login (0x%02X):\n%s"), ret, msg_utf8); break; } @@ -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); @@ -1445,7 +1446,7 @@ break; default: error = g_strdup_printf( - _("Unknow reply code when login (0x%02X):\n%s"), + _("Unknown reply code when login (0x%02X):\n%s"), ret, msg_utf8); break; } @@ -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);
--- a/libpurple/protocols/qq/qq_define.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/qq_define.c Thu Dec 11 06:24:14 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"; } }
--- a/libpurple/protocols/qq/qq_define.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/qq_define.h Thu Dec 11 06:24:14 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,
--- a/libpurple/protocols/qq/qq_network.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/qq_network.c Thu Dec 11 06:24:14 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: @@ -381,7 +376,7 @@ /* No worries */ return; - error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno)); + error_msg = g_strdup_printf(_("Lost connection with server: %d, %s"), errno, g_strerror(errno)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); @@ -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)); @@ -938,7 +935,7 @@ return FALSE; } - purple_connection_update_progress(gc, _("Connecting server ..."), 1, QQ_CONNECT_STEPS); + purple_connection_update_progress(gc, _("Connecting to server ..."), 1, QQ_CONNECT_STEPS); purple_debug_info("QQ", "Connect to %s:%d\n", server, port); @@ -954,7 +951,7 @@ qd->conn_data = purple_proxy_connect_udp(gc, account, server, port, connect_cb, gc); } if ( qd->conn_data == NULL ) { - purple_debug_error("QQ", _("Couldn't create socket")); + purple_debug_error("QQ", "Couldn't create socket"); return FALSE; } #else @@ -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
--- a/libpurple/protocols/qq/qq_process.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/qq_process.c Thu Dec 11 06:24:14 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; @@ -496,7 +538,7 @@ qq_process_buddy_change_status(data, data_len, gc); break; default: - process_unknow_cmd(gc, _("Unknow SERVER CMD"), data, data_len, cmd, seq); + process_unknow_cmd(gc, _("Unknown SERVER CMD"), data, data_len, cmd, seq); break; } } @@ -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; @@ -754,7 +796,7 @@ seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); } else { purple_debug_warning("QQ", - _("Not a member of room \"%s\"\n"), rmd->title_utf8); + "Not a member of room \"%s\"\n", rmd->title_utf8); rmd->my_role = QQ_ROOM_ROLE_NO; } break; @@ -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 */ @@ -974,7 +1019,7 @@ qq_update_all(gc, 0); break; default: - process_unknow_cmd(gc, _("Unknow LOGIN CMD"), data, data_len, cmd, seq); + process_unknow_cmd(gc, _("Unknown LOGIN CMD"), data, data_len, cmd, seq); return QQ_LOGIN_REPLY_ERR; } return QQ_LOGIN_REPLY_OK; @@ -1096,7 +1141,7 @@ qq_process_buddy_check_code(gc, data, data_len); break; default: - process_unknow_cmd(gc, _("Unknow CLIENT CMD"), data, data_len, cmd, seq); + process_unknow_cmd(gc, _("Unknown CLIENT CMD"), data, data_len, cmd, seq); is_unknow = TRUE; break; }
--- a/libpurple/protocols/qq/send_file.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/send_file.c Thu Dec 11 06:24:14 2008 +0000 @@ -612,7 +612,8 @@ PurpleConnection *gc; PurpleAccount *account; guint32 to_uid; - gchar *filename, *filename_without_path; + const gchar *filename; + gchar *base_filename; g_return_if_fail (xfer != NULL); account = purple_xfer_get_account(xfer); @@ -621,13 +622,14 @@ to_uid = purple_name_to_uid (xfer->who); g_return_if_fail (to_uid != 0); - filename = (gchar *) purple_xfer_get_local_filename (xfer); + filename = purple_xfer_get_local_filename (xfer); g_return_if_fail (filename != NULL); - filename_without_path = strrchr (filename, '/') + 1; + base_filename = g_path_get_basename(filename); - _qq_send_packet_file_request (gc, to_uid, filename_without_path, + _qq_send_packet_file_request (gc, to_uid, base_filename, purple_xfer_get_size(xfer)); + g_free(base_filename); } /* cancel the transfer of receiving files */ @@ -696,7 +698,7 @@ return; } */ - filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1; + filename = g_path_get_basename(purple_xfer_get_local_filename(qd->xfer)); msg = g_strdup_printf(_("%d has declined the file %s"), sender_uid, filename); @@ -704,7 +706,8 @@ purple_xfer_request_denied(qd->xfer); qd->xfer = NULL; - g_free (msg); + g_free(filename); + g_free(msg); } /* process cancel im for file transfer request */ @@ -725,7 +728,7 @@ return; } */ - filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1; + filename = g_path_get_basename(purple_xfer_get_local_filename(qd->xfer)); msg = g_strdup_printf (_("%d canceled the transfer of %s"), sender_uid, filename); @@ -734,7 +737,8 @@ purple_xfer_cancel_remote(qd->xfer); qd->xfer = NULL; - g_free (msg); + g_free(filename); + g_free(msg); } /* process accept im for file transfer request */
--- a/libpurple/protocols/qq/utils.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/utils.c Thu Dec 11 06:24:14 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); +}
--- a/libpurple/protocols/qq/utils.h Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/qq/utils.h Thu Dec 11 06:24:14 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
--- a/libpurple/protocols/yahoo/yahoo_aliases.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_aliases.c Thu Dec 11 06:24:14 2008 +0000 @@ -301,7 +301,7 @@ "<ct a=\"1\" yi='%s' nn='%s' />\n</ab>\r\n", purple_account_get_username(gc->account), who, converted_alias_jp); - free(converted_alias_jp); + g_free(converted_alias_jp); g_free(alias_jp); } else { gchar *escaped_alias = g_markup_escape_text(alias, -1); @@ -321,7 +321,7 @@ "<ct e=\"1\" yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n", purple_account_get_username(gc->account), who, cb->id, converted_alias_jp); - free(converted_alias_jp); + g_free(converted_alias_jp); g_free(alias_jp); } else { gchar *escaped_alias = g_markup_escape_text(alias, -1);
--- a/libpurple/protocols/yahoo/yahoo_profile.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_profile.c Thu Dec 11 06:24:14 2008 +0000 @@ -807,7 +807,7 @@ */ if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0) { purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"), NULL); - purple_notify_userinfo(info_data->gc, info_data->name, + purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL); purple_notify_user_info_destroy(user_info); g_free(profile_url_text); @@ -841,10 +841,10 @@ _("If you wish to view this profile, " "you will need to visit this link in your web browser:"), profile_url_text, profile_url_text); - purple_notify_user_info_add_pair(user_info, NULL, tmp); + purple_notify_user_info_add_pair(user_info, NULL, tmp); g_free(tmp); - purple_notify_userinfo(info_data->gc, info_data->name, + purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL); g_free(profile_url_text); @@ -1193,17 +1193,15 @@ if(!found) { - GString *str = g_string_new(""); + const gchar *str; - g_string_append_printf(str, "<br><b>"); - g_string_append_printf(str, _("User information for %s unavailable"), - info_data->name); - g_string_append_printf(str, "</b><br>"); + purple_notify_user_info_add_section_break(user_info); + purple_notify_user_info_add_pair(user_info, + _("Error retrieving profile"), NULL); if (profile_state == PROFILE_STATE_UNKNOWN_LANGUAGE) { - g_string_append_printf(str, "%s<br><br>", - _("Sorry, this profile seems to be in a language " - "or format that is not supported at this time.")); + str = _("This profile is in a language " + "or format that is not supported at this time."); } else if (profile_state == PROFILE_STATE_NOT_FOUND) { PurpleBuddy *b = purple_find_buddy @@ -1217,27 +1215,26 @@ */ f = yahoo_friend_find(b->account->gc, b->name); } - g_string_append_printf(str, "%s<br><br>", - f? _("Could not retrieve the user's profile. " + str = f ? _("Could not retrieve the user's profile. " "This most likely is a temporary server-side problem. " - "Please try again later."): + "Please try again later.") : _("Could not retrieve the user's profile. " "This most likely means that the user does not exist; " "however, Yahoo! sometimes does fail to find a user's " "profile. If you know that the user exists, " - "please try again later.")); + "please try again later."); } else { - g_string_append_printf(str, "%s<br><br>", - _("The user's profile is empty.")); + str = _("The user's profile is empty."); } - - purple_notify_user_info_add_pair(user_info, NULL, str->str); - g_string_free(str, TRUE); + + purple_notify_user_info_add_pair(user_info, NULL, str); } /* put a link to the actual profile URL */ - tmp = g_strdup_printf("<a href=\"%s\">%s</a>", profile_url_text, profile_url_text); - purple_notify_user_info_add_pair(user_info, _("Profile URL"), tmp); + purple_notify_user_info_add_section_break(user_info); + tmp = g_strdup_printf("<a href=\"%s\">%s</a>", + profile_url_text, _("View web profile")); + purple_notify_user_info_add_pair(user_info, NULL, tmp); g_free(tmp); g_free(stripped);
--- a/libpurple/protocols/zephyr/zephyr.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/protocols/zephyr/zephyr.c Thu Dec 11 06:24:14 2008 +0000 @@ -1607,27 +1607,21 @@ gboolean found_ps = FALSE; gchar ** tzc_cmd_array = g_strsplit(purple_account_get_string(gc->account,"tzc_command","/usr/bin/tzc -e %s")," ",0); if (close(1) == -1) { - purple_debug_error("zephyr", "stdout couldn't be closed. dying\n"); exit(-1); } if (dup2(zephyr->fromtzc[1], 1) == -1) { - purple_debug_error("zephyr", "dup2 of stdout failed \n"); exit(-1); } if (close(zephyr->fromtzc[1]) == -1) { - purple_debug_error("zephyr", "closing of piped stdout failed\n"); exit(-1); } if (close(0) == -1) { - purple_debug_error("zephyr", "stdin couldn't be closed. dying\n"); exit(-1); } if (dup2(zephyr->totzc[0], 0) == -1) { - purple_debug_error("zephyr", "dup2 of stdin failed \n"); exit(-1); } if (close(zephyr->totzc[0]) == -1) { - purple_debug_error("zephyr", "closing of piped stdin failed\n"); exit(-1); } /* tzc_command should really be of the form @@ -1651,11 +1645,11 @@ } if (!found_ps) { - purple_connection_error(gc,"Tzc command needs %s to set the exposure\n"); - return; + exit(-1); } execvp(tzc_cmd_array[0], tzc_cmd_array); + exit(-1); } else { fd_set rfds; @@ -1667,6 +1661,7 @@ int parenlevel=0; char* tempstr; int tempstridx; + int select_status; zephyr->tzc_pid = pid; /* wait till we have data to read from ssh */ @@ -1678,11 +1673,19 @@ purple_debug_info("zephyr", "about to read from tzc\n"); - select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, NULL); + if (waitpid(pid, NULL, WNOHANG) == 0) { /* Only select if tzc is still running */ + purple_debug_info("zephyr", "about to read from tzc\n"); + select_status = select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, NULL); + } + else { + purple_debug_info("zephyr", "tzc exited early\n"); + select_status = -1; + } FD_ZERO(&rfds); FD_SET(zephyr->fromtzc[ZEPHYR_FD_READ], &rfds); - while (select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, &tv)) { + while (select_status > 0 && + select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, &tv) > 0) { read(zephyr->fromtzc[ZEPHYR_FD_READ], bufcur, 1); bufcur++; if ((bufcur - buf) > (bufsize - 1)) {
--- a/libpurple/roomlist.c Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/roomlist.c Thu Dec 11 06:24:14 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);
--- a/libpurple/win32/global.mak Thu Dec 11 06:24:06 2008 +0000 +++ b/libpurple/win32/global.mak Thu Dec 11 06:24:14 2008 +0000 @@ -16,11 +16,11 @@ GTK_BIN ?= $(GTK_TOP)/bin BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.6.30 -MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa1 +MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa2 NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4 NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.11.4 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.10.0 -SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.7 +SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.8 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5 GSTREAMER_TOP ?= $(WIN32_DEV_TOP)/gstreamer-0.10.13
--- a/pidgin/gtkaccount.c Thu Dec 11 06:24:06 2008 +0000 +++ b/pidgin/gtkaccount.c Thu Dec 11 06:24:14 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)); }
--- a/pidgin/gtkblist.c Thu Dec 11 06:24:06 2008 +0000 +++ b/pidgin/gtkblist.c Thu Dec 11 06:24:14 2008 +0000 @@ -3630,6 +3630,7 @@ const char *name = NULL; char *filename, *path; PurplePresence *p; + PurpleStatus *tune; if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { if(!gtknode->contact_expanded) { @@ -3668,7 +3669,21 @@ return _pidgin_blist_get_cached_emblem(path); } - if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) { + tune = purple_presence_get_status(p, "tune"); + if (tune && purple_status_is_active(tune)) { + /* Only in MSN. + * TODO: Replace "Tune" with generalized "Media" in 3.0. */ + if (purple_status_get_attr_string(tune, "game") != NULL) { + path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "game.png", NULL); + return _pidgin_blist_get_cached_emblem(path); + } + /* Only in MSN. + * TODO: Replace "Tune" with generalized "Media" in 3.0. */ + if (purple_status_get_attr_string(tune, "office") != NULL) { + path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "office.png", NULL); + return _pidgin_blist_get_cached_emblem(path); + } + /* Regular old "tune" is the only one in all protocols. */ path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL); return _pidgin_blist_get_cached_emblem(path); }
--- a/pidgin/gtkroomlist.c Thu Dec 11 06:24:06 2008 +0000 +++ b/pidgin/gtkroomlist.c Thu Dec 11 06:24:14 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);