Mercurial > pidgin
changeset 23513:cab5420c3879
merge of '5033650a1e57437cc3f2894adc41935daa6b3269'
and '95fe62719d362e8857a89e906a9008305e31dd5e'
author | Elliott Sales de Andrade <qulogic@pidgin.im> |
---|---|
date | Wed, 09 Jul 2008 00:32:18 +0000 |
parents | 1061a9ad1ddb (current diff) f1ff35d14a95 (diff) |
children | 7e16d193bb57 |
files | libpurple/protocols/msn/slp.c libpurple/protocols/msn/soap2.c libpurple/protocols/msn/soap2.h libpurple/protocols/qq/qq_proxy.c libpurple/protocols/qq/qq_proxy.h libpurple/protocols/qq/recv_core.c libpurple/protocols/qq/recv_core.h libpurple/protocols/qq/send_core.c libpurple/protocols/qq/send_core.h libpurple/protocols/qq/sendqueue.c libpurple/protocols/qq/sendqueue.h libpurple/protocols/qq/udp_proxy_s5.c libpurple/protocols/qq/udp_proxy_s5.h |
diffstat | 92 files changed, 4666 insertions(+), 5604 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Wed Jul 09 00:27:44 2008 +0000 +++ b/ChangeLog Wed Jul 09 00:32:18 2008 +0000 @@ -5,8 +5,6 @@ * Ability to create custom smileys (currently only the MSN protocol utilizes the feature). (Thanks to Mauro Sérgio Ferreira Brasil, Marcus Lundblad, Jorge Villaseñor and other contributors) - * Yahoo! Japan now uses UTF-8, matching the behavior of official clients - and restoring compatibility with the web messenger (Yusuke Odate) * Add a configure option, --with-system-ssl-certs to allow packagers to specify a system-wide SSL CA certificates directory. When set, we don't install our SSL CA certs, so it's important that the @@ -31,6 +29,12 @@ * Added '/msgcolor' command to change colors of different classes of messages in a conversation. See '/help msgcolor' for details. +version 2.4.3 (??/??/2008): + libpurple: + * Yahoo! Japan now uses UTF-8, matching the behavior of official clients + and restoring compatibility with the web messenger (Yusuke Odate) + * Setting your buddy icon once again works for Yahoo! accounts. + version 2.4.2 (05/17/2008): libpurple: * In MySpaceIM, messages from spambots are discarded (Justin Williams)
--- a/libpurple/idle.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/idle.c Wed Jul 09 00:32:18 2008 +0000 @@ -252,7 +252,7 @@ PurpleAccount *account; account = purple_connection_get_account(gc); - idled_accts = g_list_remove(idled_accts, account); + set_account_unidle(account); } static void
--- a/libpurple/internal.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/internal.h Wed Jul 09 00:32:18 2008 +0000 @@ -140,6 +140,14 @@ # define G_MAXUINT32 ((guint32) 0xffffffff) #endif +#ifndef G_MAXSIZE +# if GLIB_SIZEOF_LONG == 8 +# define G_MAXSIZE ((gsize) 0xffffffffffffffff) +# else +# define G_MAXSIZE ((gsize) 0xffffffff) +# endif +#endif + #if GLIB_CHECK_VERSION(2,6,0) # include <glib/gstdio.h> #endif
--- a/libpurple/protocols/msn/Makefile.am Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/Makefile.am Wed Jul 09 00:32:18 2008 +0000 @@ -50,10 +50,8 @@ slpmsg.h \ slpsession.c \ slpsession.h \ - soap.c\ - soap.h\ - soap2.c \ - soap2.h \ + soap.c \ + soap.h \ state.c \ state.h \ switchboard.c \
--- a/libpurple/protocols/msn/contact.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/contact.c Wed Jul 09 00:32:18 2008 +0000 @@ -28,7 +28,7 @@ #include "contact.h" #include "xmlnode.h" #include "group.h" -#include "soap2.h" +#include "soap.h" #include "nexus.h" const char *MsnSoapPartnerScenarioText[] = @@ -78,7 +78,7 @@ /* The rest should be made new */ return new_state; -} +} void msn_callback_state_free(MsnCallbackState *state)
--- a/libpurple/protocols/msn/contact.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/contact.h Wed Jul 09 00:32:18 2008 +0000 @@ -27,6 +27,8 @@ #include "session.h" +#define MSN_APPLICATION_ID "CFE80F9D-180F-4399-82AB-413F33A1FA11" + #define MSN_CONTACT_SERVER "contacts.msn.com" /* Get Contact List */ @@ -43,7 +45,7 @@ "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ "<soap:Header xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration xmlns=\"http://www.msn.com/webservices/AddressBook\">false</IsMigration>"\ "<PartnerScenario xmlns=\"http://www.msn.com/webservices/AddressBook\">%s</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -85,7 +87,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>Initial</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -126,7 +128,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>%s</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -155,7 +157,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>Initial</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -215,7 +217,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>ContactSave</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -244,7 +246,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>ContactSave</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -281,7 +283,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>Timer</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -308,7 +310,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>Timer</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -341,7 +343,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>Timer</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -396,7 +398,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>%s</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -432,7 +434,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>%s</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -476,7 +478,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>GroupSave</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -518,7 +520,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>Timer</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -549,7 +551,7 @@ " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>Timer</PartnerScenario>"\ "</ABApplicationHeader>"\ @@ -670,7 +672,5 @@ void msn_del_contact_from_list(MsnSession *session, MsnCallbackState *state, const gchar *passport, const MsnListId list); -void msn_contact_connect_init(MsnSoapConn *soapconn); - #endif /* _MSN_CONTACT_H_ */
--- a/libpurple/protocols/msn/group.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/group.h Wed Jul 09 00:32:18 2008 +0000 @@ -30,16 +30,8 @@ #include "session.h" #include "user.h" -#include "soap.h" #include "userlist.h" -#define MSN_ADD_GROUPS "<GroupInfo><name>test111</name><groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType><fMessenger>false</fMessenger><annotations><Annotation><Name>MSN.IM.Display</Name><Value>1</Value></Annotation></annotations></GroupInfo>" - -#define MSN_ADD_GROUP_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\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>GroupSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupAddOptions><fRenameOnMsgrConflict>false</fRenameOnMsgrConflict></groupAddOptions><groupInfo>%s</groupInfo></ABGroupAdd></soap:Body></soap:Envelope>" - -#define MSN_GROUP_IDS "<guid>9e57e654-59f0-44d1-aedc-0a7500b7e51f</guid>" -#define MSN_DELETE_GROUP_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\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupFilter><groupIds>%s</groupIds></groupFilter></ABGroupDelete></soap:Body></soap:Envelope>" - #define MSN_INDIVIDUALS_GROUP_ID "1983" #define MSN_INDIVIDUALS_GROUP_NAME "Other Contacts" @@ -52,7 +44,6 @@ struct _MsnGroup { MsnSession *session; /**< The MSN session. */ - MsnSoapConn *soapconn; char *id; /**< The group ID. */ char *name; /**< The name of the group. */
--- a/libpurple/protocols/msn/httpconn.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/httpconn.c Wed Jul 09 00:32:18 2008 +0000 @@ -588,7 +588,8 @@ if (httpconn->virgin) { - host = "gateway.messenger.hotmail.com"; + /* QuLogic: This doesn't look right to me, but it still seems to work */ + host = MSN_HTTPCONN_SERVER; /* The first time servconn->host is the host we should connect to. */ params = g_strdup_printf("Action=open&Server=%s&IP=%s",
--- a/libpurple/protocols/msn/msn.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/msn.c Wed Jul 09 00:32:18 2008 +0000 @@ -2551,11 +2551,11 @@ PurpleAccountOption *option; option = purple_account_option_string_new(_("Server"), "server", - WLM_SERVER); + MSN_SERVER); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_int_new(_("Port"), "port", WLM_PORT); + option = purple_account_option_int_new(_("Port"), "port", MSN_PORT); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
--- a/libpurple/protocols/msn/msn.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/msn.h Wed Jul 09 00:32:18 2008 +0000 @@ -57,15 +57,10 @@ #define MSN_BUF_LEN 8192 -#define USEROPT_MSNSERVER 3 +/* Windows Live Messenger Server*/ #define MSN_SERVER "messenger.hotmail.com" #define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com" -#define USEROPT_MSNPORT 4 #define MSN_PORT 1863 - -/* Windows Live Messenger Server*/ -#define WLM_SERVER "muser.messenger.hotmail.com" -#define WLM_PORT 1863 #define WLM_PROT_VER 15 #define WLM_MAX_PROTOCOL 15 @@ -74,16 +69,13 @@ #define MSN_TYPING_RECV_TIMEOUT 6 #define MSN_TYPING_SEND_TIMEOUT 4 -#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"w3 -#define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login=" #define PROFILE_URL "http://spaces.live.com/profile.aspx?mem=" #define PHOTO_URL " contactparams:photopreauthurl=\"" -#define USEROPT_HOTMAIL 0 - #define BUDDY_ALIAS_MAXLEN 387 -#define MSN_FT_GUID "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" +#define MSN_FT_GUID "5D3E02AB-6190-11D3-BBBB-00C04F795683" +#define MSN_OBJ_GUID "A4268EEC-FEC5-49E5-95C3-F126696BDBF6" #define MSN_CLIENTINFO \ "Client-Name: Purple/" VERSION "\r\n" \
--- a/libpurple/protocols/msn/nexus.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/nexus.c Wed Jul 09 00:32:18 2008 +0000 @@ -22,7 +22,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "msn.h" -#include "soap2.h" +#include "soap.h" #include "nexus.h" #include "notification.h"
--- a/libpurple/protocols/msn/nexus.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/nexus.h Wed Jul 09 00:32:18 2008 +0000 @@ -24,8 +24,6 @@ #ifndef _MSN_NEXUS_H_ #define _MSN_NEXUS_H_ -#include "soap.h" - /* Index into ticket_tokens in nexus.c Keep updated! */ typedef enum {
--- a/libpurple/protocols/msn/notification.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/notification.c Wed Jul 09 00:32:18 2008 +0000 @@ -132,7 +132,7 @@ servconn = notification->servconn; msn_servconn_set_connect_cb(servconn, connect_cb); - notification->in_use = msn_servconn_connect(servconn, host, port); + notification->in_use = msn_servconn_connect(servconn, host, port, TRUE); return notification->in_use; } @@ -328,7 +328,9 @@ static void out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - if (!g_ascii_strcasecmp(cmd->params[0], "OTH")) + if (cmd->param_count == 0) + msn_session_set_error(cmdproc->session, -1, NULL); + else if (!g_ascii_strcasecmp(cmd->params[0], "OTH")) msn_session_set_error(cmdproc->session, MSN_ERROR_SIGN_OTHER, NULL); else if (!g_ascii_strcasecmp(cmd->params[0], "SSD")) @@ -360,7 +362,7 @@ msg = msn_message_new_from_cmd(cmdproc->session, cmd); - msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM); + msn_message_parse_payload(msg, payload, len, MSG_LINE_DEM, MSG_BODY_DEM); #ifdef MSN_DEBUG_NS msn_message_show_readable(msg, "Notification", TRUE); #endif @@ -374,18 +376,16 @@ msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { purple_debug_info("MSNP14","Processing MSG... \n"); - if(cmd->payload_len == 0){ + if (cmd->payload_len == 0) { return; } /* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued * command and we are processing it */ - if (cmd->payload == NULL) - { + if (cmd->payload == NULL) { cmdproc->last_cmd->payload_cb = msg_cmd_post; cmdproc->servconn->payload_len = atoi(cmd->params[2]); - } - else - { + + } else { g_return_if_fail(cmd->payload_cb != NULL); #if 0 /* glib on win32 doesn't correctly support precision modifiers for a string */ @@ -418,6 +418,7 @@ msn_cmdproc_send_trans(cmdproc, trans); } +#if 0 static void ubm_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len) @@ -491,25 +492,26 @@ } msn_message_destroy(msg); } +#endif /*Yahoo msg process*/ static void ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { purple_debug_info("MSNP14","Processing UBM... \n"); - if(cmd->payload_len == 0){ + if (cmd->payload_len == 0) { return; } /* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued * command and we are processing it */ - if (cmd->payload == NULL){ - cmdproc->last_cmd->payload_cb = ubm_cmd_post; - cmdproc->servconn->payload_len = atoi(cmd->params[2]); - }else{ + if (cmd->payload == NULL ){ + cmdproc->last_cmd->payload_cb = msg_cmd_post; + cmdproc->servconn->payload_len = atoi(cmd->params[4]); + } else { g_return_if_fail(cmd->payload_cb != NULL); purple_debug_info("MSNP14", "UBM payload:{%.*s}\n", (guint)(cmd->payload_len), cmd->payload); - ubm_cmd_post(cmdproc, cmd, cmd->payload, cmd->payload_len); + msg_cmd_post(cmdproc, cmd, cmd->payload, cmd->payload_len); } }
--- a/libpurple/protocols/msn/oim.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/oim.c Wed Jul 09 00:32:18 2008 +0000 @@ -24,7 +24,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "msn.h" -#include "soap2.h" +#include "soap.h" #include "oim.h" #include "msnutils.h"
--- a/libpurple/protocols/msn/oim.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/oim.h Wed Jul 09 00:32:18 2008 +0000 @@ -138,10 +138,8 @@ { MsnSession *session; - MsnSoapConn *retrieveconn; GList * oim_list; - MsnSoapConn *sendconn; char *challenge; char *run_id; gint send_seq;
--- a/libpurple/protocols/msn/servconn.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/servconn.c Wed Jul 09 00:32:18 2008 +0000 @@ -203,7 +203,7 @@ } gboolean -msn_servconn_connect(MsnServConn *servconn, const char *host, int port) +msn_servconn_connect(MsnServConn *servconn, const char *host, int port, gboolean force) { MsnSession *session; @@ -223,7 +223,7 @@ { /* HTTP Connection. */ - if (!servconn->httpconn->connected) + if (!servconn->httpconn->connected || force) if (!msn_httpconn_connect(servconn->httpconn, host, port)) return FALSE;
--- a/libpurple/protocols/msn/servconn.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/servconn.h Wed Jul 09 00:32:18 2008 +0000 @@ -115,8 +115,10 @@ * @param servconn The connection. * @param host The host. * @param port The port. + * @param force Force this servconn to connect to a new server. */ -gboolean msn_servconn_connect(MsnServConn *servconn, const char *host, int port); +gboolean msn_servconn_connect(MsnServConn *servconn, const char *host, int port, + gboolean force); /** * Disconnects.
--- a/libpurple/protocols/msn/slp.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/slp.c Wed Jul 09 00:32:18 2008 +0000 @@ -251,7 +251,7 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch, const char *euf_guid, const char *context) { - if (!strcmp(euf_guid, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6")) + if (!strcmp(euf_guid, MSN_OBJ_GUID)) { /* Emoticon or UserDisplay */ char *content; @@ -332,7 +332,7 @@ msn_slplink_queue_slpmsg(slplink, slpmsg); purple_imgstore_unref(img); } - else if (!strcmp(euf_guid, "5D3E02AB-6190-11D3-BBBB-00C04F795683")) + else if (!strcmp(euf_guid, MSN_FT_GUID)) { /* File Transfer */ PurpleAccount *account; @@ -384,7 +384,8 @@ purple_xfer_request(xfer); } - } + } else + purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid); } void @@ -781,16 +782,13 @@ got_emoticon(MsnSlpCall *slpcall, const guchar *data, gsize size) { - PurpleConversation *conv; - PurpleConnection *gc; - const char *who; + MsnSwitchBoard *swboard; - gc = slpcall->slplink->session->account->gc; - who = slpcall->slplink->remote_user; + swboard = slpcall->slplink->swboard; + conv = swboard->conv; - if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, gc->account))) { - + if (conv) { /* FIXME: it would be better if we wrote the data as we received it instead of all at once, calling write multiple times and close once at the very end @@ -808,6 +806,7 @@ { MsnSession *session; MsnSlpLink *slplink; + MsnSwitchBoard *swboard; MsnObject *obj; char **tokens; char *smile, *body_str; @@ -847,8 +846,9 @@ slplink = msn_session_get_slplink(session, who); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, - session->account); + swboard = cmdproc->data; + slplink->swboard = swboard; + conv = swboard->conv; /* If the conversation doesn't exist then this is a custom smiley * used in the first message in a MSN conversation: we need to create
--- a/libpurple/protocols/msn/slplink.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/slplink.c Wed Jul 09 00:32:18 2008 +0000 @@ -593,7 +593,7 @@ } else if (slpmsg->size) { - if ((offset + len) > slpmsg->size) + if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size) { purple_debug_error("msn", "Oversized slpmsg - msgsize=%lld offset=%" G_GSIZE_FORMAT " len=%" G_GSIZE_FORMAT "\n", @@ -774,8 +774,7 @@ context = gen_context(fn, fp); - msn_slp_call_invite(slpcall, "5D3E02AB-6190-11D3-BBBB-00C04F795683", 2, - context); + msn_slp_call_invite(slpcall, MSN_FT_GUID, 2, context); g_free(context); } @@ -805,8 +804,7 @@ slpcall->cb = cb; slpcall->end_cb = end_cb; - msn_slp_call_invite(slpcall, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6", 1, - msnobj_base64); + msn_slp_call_invite(slpcall, MSN_OBJ_GUID, 1, msnobj_base64); g_free(msnobj_base64); }
--- a/libpurple/protocols/msn/soap.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/soap.c Wed Jul 09 00:32:18 2008 +0000 @@ -1,8 +1,7 @@ /** * @file soap.c - * SOAP connection related process - * Author - * MaYuan<mayuan2006@gmail.com> + * C file for SOAP connection related process + * * purple * * Purple is the legal property of its developers, whose names are too numerous @@ -23,854 +22,659 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "msn.h" + +#include "internal.h" + #include "soap.h" -#define MSN_SOAP_DEBUG -/*local function prototype*/ -void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step); +#include "session.h" + +#include "debug.h" +#include "xmlnode.h" + +#include <glib.h> +#if !defined(_WIN32) || !defined(_WINERROR_) +#include <error.h> +#endif + +#define SOAP_TIMEOUT (5 * 60) -/*setup the soap process step*/ -void -msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step) -{ -#ifdef MSN_SOAP_DEBUG - const char *MsnSoapStepText[] = - { - "Unconnected", - "Connecting", - "Connected", - "Processing", - "Connected Idle" - }; +typedef struct _MsnSoapRequest { + char *path; + MsnSoapMessage *message; + MsnSoapCallback cb; + gpointer cb_data; +} MsnSoapRequest; - purple_debug_info("MSN SOAP", "Setting SOAP process step to %s\n", MsnSoapStepText[step]); -#endif - soapconn->step = step; -} +typedef struct _MsnSoapConnection { + MsnSession *session; + char *host; -/*new a soap connection*/ -MsnSoapConn * -msn_soap_new(MsnSession *session,gpointer data, gboolean ssl) -{ - MsnSoapConn *soapconn; - - soapconn = g_new0(MsnSoapConn, 1); - soapconn->session = session; - soapconn->parent = data; - soapconn->ssl_conn = ssl; + time_t last_used; + PurpleSslConnection *ssl; + gboolean connected; - soapconn->gsc = NULL; - soapconn->input_handler = 0; - soapconn->output_handler = 0; - - msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED); - soapconn->soap_queue = g_queue_new(); + guint event_handle; + GString *buf; + gsize handled_len; + gsize body_len; + int response_code; + gboolean headers_done; + gboolean close_when_done; - return soapconn; -} + MsnSoapMessage *message; -/*ssl soap connect callback*/ -void -msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, - PurpleInputCondition cond) -{ - MsnSoapConn * soapconn; - MsnSession *session; - gboolean soapconn_is_valid = FALSE; + GQueue *queue; + MsnSoapRequest *current_request; +} MsnSoapConnection; + +static void msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data); +static gboolean msn_soap_connection_run(gpointer data); - purple_debug_misc("MSN SOAP","SOAP server connection established!\n"); - - soapconn = data; - g_return_if_fail(soapconn != NULL); +static MsnSoapConnection *msn_soap_connection_new(MsnSession *session, + const char *host); +static void msn_soap_connection_handle_next(MsnSoapConnection *conn); +static void msn_soap_connection_destroy(MsnSoapConnection *conn); - session = soapconn->session; - g_return_if_fail(session != NULL); - - soapconn->gsc = gsc; +static void msn_soap_message_send_internal(MsnSession *session, + MsnSoapMessage *message, const char *host, const char *path, + MsnSoapCallback cb, gpointer cb_data, gboolean first); - msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTED); +static void msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message); +static void msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect); +static gboolean msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond, gboolean initial); +static void msn_soap_process(MsnSoapConnection *conn); - /*connection callback*/ - if (soapconn->connect_cb != NULL) { - soapconn_is_valid = soapconn->connect_cb(soapconn, gsc); - } +static gboolean +msn_soap_cleanup_each(gpointer key, gpointer value, gpointer data) +{ + MsnSoapConnection *conn = value; + time_t *t = data; - if (!soapconn_is_valid) { - return; + if ((*t - conn->last_used) > SOAP_TIMEOUT * 2) { + purple_debug_info("soap", "cleaning up soap conn %p\n", conn); + return TRUE; } - /*we do the SOAP request here*/ - msn_soap_post_head_request(soapconn); -} - -/*ssl soap error callback*/ -static void -msn_soap_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data) -{ - MsnSoapConn * soapconn = data; - - g_return_if_fail(data != NULL); - - purple_debug_warning("MSN SOAP","Soap connection error!\n"); - - msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED); - - /*error callback*/ - if (soapconn->error_cb != NULL) { - soapconn->error_cb(soapconn, gsc, error); - } else { - msn_soap_post(soapconn, NULL); - } -} - -/*init the soap connection*/ -void -msn_soap_init(MsnSoapConn *soapconn,char * host, gboolean ssl, - MsnSoapSslConnectCbFunction connect_cb, - MsnSoapSslErrorCbFunction error_cb) -{ - purple_debug_misc("MSN SOAP","Initializing SOAP connection\n"); - g_free(soapconn->login_host); - soapconn->login_host = g_strdup(host); - soapconn->ssl_conn = ssl; - soapconn->connect_cb = connect_cb; - soapconn->error_cb = error_cb; + return FALSE; } -/*connect the soap connection*/ -void -msn_soap_connect(MsnSoapConn *soapconn) +static gboolean +msn_soap_cleanup_for_session(gpointer data) { - if (soapconn->ssl_conn) { - purple_ssl_connect(soapconn->session->account, soapconn->login_host, - PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, msn_soap_error_cb, - soapconn); - } else { + MsnSession *sess = data; + time_t t = time(NULL); + + purple_debug_info("soap", "session cleanup timeout\n"); + + if (sess->soap_table) { + g_hash_table_foreach_remove(sess->soap_table, msn_soap_cleanup_each, + &t); + + if (g_hash_table_size(sess->soap_table) == 0) { + purple_timeout_remove(sess->soap_cleanup_handle); + sess->soap_cleanup_handle = 0; + } } - msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTING); -} - - -static void -msn_soap_close_handler(guint *handler) -{ - if (*handler > 0) { - purple_input_remove(*handler); - *handler = 0; - } -#ifdef MSN_SOAP_DEBUG - else { - purple_debug_misc("MSN SOAP", "Handler inactive, not removing\n"); - } -#endif - -} - - -/*close the soap connection*/ -void -msn_soap_close(MsnSoapConn *soapconn) -{ - if (soapconn->ssl_conn) { - if (soapconn->gsc != NULL) { - purple_ssl_close(soapconn->gsc); - soapconn->gsc = NULL; - } - } else { - } - msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED); + return TRUE; } -/*clean the unhandled SOAP request*/ -void -msn_soap_clean_unhandled_requests(MsnSoapConn *soapconn) +static MsnSoapConnection * +msn_soap_get_connection(MsnSession *session, const char *host) { - MsnSoapReq *request; - - g_return_if_fail(soapconn != NULL); - - soapconn->body = NULL; - - while ((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){ - if (soapconn->read_cb) { - soapconn->read_cb(soapconn); - } - msn_soap_request_free(request); - } -} - -/*destroy the soap connection*/ -void -msn_soap_destroy(MsnSoapConn *soapconn) -{ - g_free(soapconn->login_host); - - g_free(soapconn->login_path); + MsnSoapConnection *conn = NULL; - /*remove the write handler*/ - if (soapconn->output_handler > 0){ - purple_input_remove(soapconn->output_handler); - soapconn->output_handler = 0; - } - /*remove the read handler*/ - if (soapconn->input_handler > 0){ - purple_input_remove(soapconn->input_handler); - soapconn->input_handler = 0; - } - msn_soap_free_read_buf(soapconn); - msn_soap_free_write_buf(soapconn); - - /*close ssl connection*/ - msn_soap_close(soapconn); - - /*process the unhandled soap request*/ - msn_soap_clean_unhandled_requests(soapconn); - - g_queue_free(soapconn->soap_queue); - g_free(soapconn); -} - -/*check the soap is connected? - * if connected return 1 - */ -int -msn_soap_connected(MsnSoapConn *soapconn) -{ - if (soapconn->ssl_conn) { - return (soapconn->gsc == NULL ? 0 : 1); - } - return (soapconn->fd > 0 ? 1 : 0); -} - -/*read and append the content to the buffer*/ -static gssize -msn_soap_read(MsnSoapConn *soapconn) -{ - gssize len, requested_len; - char temp_buf[MSN_SOAP_READ_BUFF_SIZE]; - - if ( soapconn->need_to_read == 0 || soapconn->need_to_read > MSN_SOAP_READ_BUFF_SIZE) { - requested_len = MSN_SOAP_READ_BUFF_SIZE; - } - else { - requested_len = soapconn->need_to_read; + if (session->soap_table) { + conn = g_hash_table_lookup(session->soap_table, host); + } else { + session->soap_table = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, (GDestroyNotify)msn_soap_connection_destroy); } - if ( soapconn->ssl_conn ) { - len = purple_ssl_read(soapconn->gsc, temp_buf, requested_len); - } else { - len = read(soapconn->fd, temp_buf, requested_len); + if (session->soap_cleanup_handle == 0) + session->soap_cleanup_handle = purple_timeout_add(SOAP_TIMEOUT * 1000, + msn_soap_cleanup_for_session, session); + + if (conn == NULL) { + conn = msn_soap_connection_new(session, host); + g_hash_table_insert(session->soap_table, conn->host, conn); } + conn->last_used = time(NULL); - if ( len <= 0 ) { - switch (errno) { + return conn; +} - case 0: - case EBADF: /* we are sometimes getting this in Windows */ - case EAGAIN: return len; +static MsnSoapConnection * +msn_soap_connection_new(MsnSession *session, const char *host) +{ + MsnSoapConnection *conn = g_new0(MsnSoapConnection, 1); + conn->session = session; + conn->host = g_strdup(host); + conn->queue = g_queue_new(); + return conn; +} - default : purple_debug_error("MSN SOAP", "Read error!" - "read len: %" G_GSSIZE_FORMAT ", error = %s\n", - len, g_strerror(errno)); - purple_input_remove(soapconn->input_handler); - //soapconn->input_handler = 0; - g_free(soapconn->read_buf); - soapconn->read_buf = NULL; - soapconn->read_len = 0; - /* TODO: error handling */ - return len; - } - } - else { - soapconn->read_buf = g_realloc(soapconn->read_buf, - soapconn->read_len + len + 1); - if ( soapconn->read_buf != NULL ) { - memcpy(soapconn->read_buf + soapconn->read_len, temp_buf, len); - soapconn->read_len += len; - soapconn->read_buf[soapconn->read_len] = '\0'; - } - else { - purple_debug_error("MSN SOAP", - "Failure re-allocating %" G_GSIZE_FORMAT " bytes of memory!\n", - soapconn->read_len + len + 1); - exit(EXIT_FAILURE); - } +static void +msn_soap_connected_cb(gpointer data, PurpleSslConnection *ssl, + PurpleInputCondition cond) +{ + MsnSoapConnection *conn = data; + + conn->connected = TRUE; - } + if (conn->event_handle == 0) + conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); +} -#if defined(MSN_SOAP_DEBUG) - if (len > 0) - purple_debug_info("MSN SOAP", - "Read %" G_GSIZE_FORMAT " bytes from SOAP server:\n%s\n", len, - soapconn->read_buf + soapconn->read_len - len); -#endif +static void +msn_soap_error_cb(PurpleSslConnection *ssl, PurpleSslErrorType error, + gpointer data) +{ + MsnSoapConnection *conn = data; - return len; + /* sslconn already frees the connection in case of error */ + conn->ssl = NULL; + + g_hash_table_remove(conn->session->soap_table, conn->host); } -/*read the whole SOAP server response*/ -static void -msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond) +static gboolean +msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url) { - MsnSoapConn *soapconn = data; - MsnSession *session; - int len; - char * body_start,*body_len; - char *length_start,*length_end; -#ifdef MSN_SOAP_DEBUG -#if !defined(_WIN32) - gchar * formattedxml = NULL; - gchar * http_headers = NULL; - xmlnode * node = NULL; -#endif - purple_debug_misc("MSN SOAP", "msn_soap_read_cb()\n"); -#endif - session = soapconn->session; - g_return_if_fail(session != NULL); + char *host; + char *path; + + if (purple_url_parse(url, &host, NULL, &path, NULL, NULL)) { + msn_soap_message_send_internal(conn->session, + conn->current_request->message, host, path, + conn->current_request->cb, conn->current_request->cb_data, TRUE); - - /*read the request header*/ - len = msn_soap_read(soapconn); + msn_soap_request_destroy(conn->current_request, TRUE); + conn->current_request = NULL; - if ( len < 0 ) - return; + g_free(host); + g_free(path); - if (soapconn->read_buf == NULL) { - return; + return TRUE; } - if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL) - || ( strstr(soapconn->read_buf, "HTTP/1.1 301") != NULL ) ) - { - /* Redirect. */ - char *location, *c; + return FALSE; +} + +static gboolean +msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response) +{ + xmlnode *body = xmlnode_get_child(response->xml, "Body"); + xmlnode *fault = xmlnode_get_child(response->xml, "Fault"); + + if (fault) { + xmlnode *faultcode = xmlnode_get_child(fault, "faultcode"); + + if (faultcode != NULL) { + char *faultdata = xmlnode_get_data(faultcode); + + if (g_str_equal(faultdata, "psf:Redirect")) { + xmlnode *url = xmlnode_get_child(fault, "redirectUrl"); - purple_debug_info("MSN SOAP", "HTTP Redirect\n"); - location = strstr(soapconn->read_buf, "Location: "); - if (location == NULL) - { - c = (char *) g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n"); - if (c != NULL) { - /* we have read the whole HTTP headers and found no Location: */ - msn_soap_free_read_buf(soapconn); - msn_soap_post(soapconn, NULL); + if (url) { + char *urldata = xmlnode_get_data(url); + msn_soap_handle_redirect(conn, urldata); + g_free(urldata); + } + + g_free(faultdata); + msn_soap_message_destroy(response); + return TRUE; + } else if (g_str_equal(faultdata, "wsse:FailedAuthentication")) { + xmlnode *reason = xmlnode_get_child(fault, "faultstring"); + char *reasondata = xmlnode_get_data(reason); + + msn_soap_connection_sanitize(conn, TRUE); + msn_session_set_error(conn->session, MSN_ERROR_AUTH, + reasondata); + + g_free(reasondata); + g_free(faultdata); + msn_soap_message_destroy(response); + return FALSE; } - return; - } - location = strchr(location, ' ') + 1; - - if ((c = strchr(location, '\r')) != NULL) - *c = '\0'; - else - return; - - /* Skip the http:// */ - if ((c = strchr(location, '/')) != NULL) - location = c + 2; - - if ((c = strchr(location, '/')) != NULL) - { - g_free(soapconn->login_path); - soapconn->login_path = g_strdup(c); - - *c = '\0'; - } - - g_free(soapconn->login_host); - soapconn->login_host = g_strdup(location); - - msn_soap_close_handler( &(soapconn->input_handler) ); - msn_soap_close(soapconn); - - if (purple_ssl_connect(session->account, soapconn->login_host, - PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, - msn_soap_error_cb, soapconn) == NULL) { - - purple_debug_error("MSN SOAP", "Unable to connect to %s !\n", soapconn->login_host); - // dispatch next request - msn_soap_post(soapconn, NULL); - } - } - /* Another case of redirection, active on May, 2007 - See http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener#Redirect - */ - else if (strstr(soapconn->read_buf, - "<faultcode>psf:Redirect</faultcode>") != NULL) - { - char *location, *c; - - if ( (location = strstr(soapconn->read_buf, "<psf:redirectUrl>") ) == NULL) - return; - - /* Omit the tag preceding the URL */ - location += strlen("<psf:redirectUrl>"); - if (location > soapconn->read_buf + soapconn->read_len) - return; - if ( (location = strstr(location, "://")) == NULL) - return; - - location += strlen("://"); /* Skip http:// or https:// */ - - if ( (c = strstr(location, "</psf:redirectUrl>")) != NULL ) - *c = '\0'; - else - return; - - if ( (c = strstr(location, "/")) != NULL ) - { - g_free(soapconn->login_path); - soapconn->login_path = g_strdup(c); - *c = '\0'; - } - - g_free(soapconn->login_host); - soapconn->login_host = g_strdup(location); - - msn_soap_close_handler( &(soapconn->input_handler) ); - msn_soap_close(soapconn); - - if (purple_ssl_connect(session->account, soapconn->login_host, - PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, - msn_soap_error_cb, soapconn) == NULL) { - - purple_debug_error("MSN SOAP", "Unable to connect to %s !\n", soapconn->login_host); - // dispatch next request - msn_soap_post(soapconn, NULL); + g_free(faultdata); } } - else if (strstr(soapconn->read_buf, "HTTP/1.1 401 Unauthorized") != NULL) - { - const char *error; - - purple_debug_error("MSN SOAP", "Received HTTP error 401 Unauthorized\n"); - if ((error = strstr(soapconn->read_buf, "WWW-Authenticate")) != NULL) - { - if ((error = strstr(error, "cbtxt=")) != NULL) - { - const char *c; - char *temp; - - error += strlen("cbtxt="); - - if ((c = strchr(error, '\n')) == NULL) - c = error + strlen(error); - - temp = g_strndup(error, c - error); - error = purple_url_decode(temp); - g_free(temp); - } - } - - msn_session_set_error(session, MSN_ERROR_AUTH, error); - } - /* Handle Passport 3.0 authentication failures. - * Further info: http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener - */ - else if (strstr(soapconn->read_buf, - "<faultcode>wsse:FailedAuthentication</faultcode>") != NULL) - { - gchar *faultstring; - - faultstring = strstr(soapconn->read_buf, "<faultstring>"); - - if (faultstring != NULL) - { - gchar *c; - faultstring += strlen("<faultstring>"); - if (faultstring < soapconn->read_buf + soapconn->read_len) { - c = strstr(soapconn->read_buf, "</faultstring>"); - if (c != NULL) { - *c = '\0'; - msn_session_set_error(session, MSN_ERROR_AUTH, faultstring); - } - } - } - - } - else if (strstr(soapconn->read_buf, "HTTP/1.1 503 Service Unavailable")) - { - msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL); - } - else if ((strstr(soapconn->read_buf, "HTTP/1.1 200 OK")) - ||(strstr(soapconn->read_buf, "HTTP/1.1 500"))) - { - gboolean soapconn_is_valid = FALSE; - - /*OK! process the SOAP body*/ - body_start = (char *)g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n"); - if (!body_start) { - return; - } - body_start += 4; - - if (body_start > soapconn->read_buf + soapconn->read_len) - return; - - /* we read the content-length*/ - if ( (length_start = g_strstr_len(soapconn->read_buf, soapconn->read_len, "Content-Length: ")) != NULL) - length_start += strlen("Content-Length: "); - - if (length_start > soapconn->read_buf + soapconn->read_len) - return; - if ( (length_end = strstr(length_start, "\r\n")) == NULL ) - return; - - body_len = g_strndup(length_start, length_end - length_start); - - /*setup the conn body */ - soapconn->body = body_start; - soapconn->body_len = atoi(body_len); - g_free(body_len); -#ifdef MSN_SOAP_DEBUG - purple_debug_misc("MSN SOAP", - "SOAP bytes read so far: %" G_GSIZE_FORMAT ", Content-Length: %d\n", - soapconn->read_len, soapconn->body_len); -#endif - soapconn->need_to_read = (body_start - soapconn->read_buf + soapconn->body_len) - soapconn->read_len; - if ( soapconn->need_to_read > 0 ) { - return; - } - -#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32) - - node = xmlnode_from_str(soapconn->body, soapconn->body_len); - - if (node != NULL) { - formattedxml = xmlnode_to_formatted_str(node, NULL); - http_headers = g_strndup(soapconn->read_buf, soapconn->body - soapconn->read_buf); - - purple_debug_info("MSN SOAP","Data with XML payload received from the SOAP server:\n%s%s\n", http_headers, formattedxml); - g_free(http_headers); - g_free(formattedxml); - xmlnode_free(node); - } - else - purple_debug_info("MSN SOAP","Data received from the SOAP server:\n%s\n", soapconn->read_buf); -#endif + if (fault || body) { + MsnSoapRequest *request = conn->current_request; + conn->current_request = NULL; + request->cb(request->message, response, + request->cb_data); + msn_soap_message_destroy(response); + msn_soap_request_destroy(request, FALSE); + } - /*remove the read handler*/ - msn_soap_close_handler( &(soapconn->input_handler) ); -// purple_input_remove(soapconn->input_handler); -// soapconn->input_handler = 0; - /* - * close the soap connection,if more soap request came, - * Just reconnect to do it, - * - * To solve the problem described below: - * When I post the soap request in one socket one after the other, - * The first read is ok, But the second soap read always got 0 bytes, - * Weird! - * */ - msn_soap_close(soapconn); - - /*call the read callback*/ - if ( soapconn->read_cb != NULL ) { - soapconn_is_valid = soapconn->read_cb(soapconn); - } - - if (!soapconn_is_valid) { - return; - } - - /* dispatch next request in queue */ - msn_soap_post(soapconn, NULL); - } - return; -} - -void -msn_soap_free_read_buf(MsnSoapConn *soapconn) -{ - g_return_if_fail(soapconn != NULL); - - if (soapconn->read_buf) { - g_free(soapconn->read_buf); - } - soapconn->read_buf = NULL; - soapconn->read_len = 0; - soapconn->need_to_read = 0; + return TRUE; } -void -msn_soap_free_write_buf(MsnSoapConn *soapconn) +static void +msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond) { - g_return_if_fail(soapconn != NULL); - - if (soapconn->write_buf) { - g_free(soapconn->write_buf); - } - soapconn->write_buf = NULL; - soapconn->written_len = 0; -} + MsnSoapConnection *conn = data; + int count = 0, cnt, perrno; + /* This buffer needs to be larger than any packets received from + login.live.com or Adium will fail to receive the packet + (something weird with the login.live.com server). With NSS it works + fine, so I believe it's some bug with OS X */ + char buf[16 * 1024]; -/*Soap write process func*/ -static void -msn_soap_write_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnSoapConn *soapconn = data; - int len, total_len; - - g_return_if_fail(soapconn != NULL); - if ( soapconn->write_buf == NULL ) { - purple_debug_error("MSN SOAP","SOAP write buffer is NULL\n"); - // msn_soap_check_conn_errors(soapconn); - // purple_input_remove(soapconn->output_handler); - // soapconn->output_handler = 0; - msn_soap_close_handler( &(soapconn->output_handler) ); - return; + if (conn->message == NULL) { + conn->message = msn_soap_message_new(NULL, NULL); } - total_len = strlen(soapconn->write_buf); - - /* - * write the content to SSL server, - */ - len = purple_ssl_write(soapconn->gsc, - soapconn->write_buf + soapconn->written_len, - total_len - soapconn->written_len); - - if (len < 0 && errno == EAGAIN) - return; - else if (len <= 0){ - /*SSL write error!*/ -// msn_soap_check_conn_errors(soapconn); - msn_soap_close_handler( &(soapconn->output_handler) ); -// purple_input_remove(soapconn->output_handler); -// soapconn->output_handler = 0; - - msn_soap_close(soapconn); + if (conn->buf == NULL) { + conn->buf = g_string_new_len(buf, 0); + } + + while ((cnt = purple_ssl_read(conn->ssl, buf, sizeof(buf))) > 0) { + purple_debug_info("soap", "read %d bytes\n", cnt); + count += cnt; + g_string_append_len(conn->buf, buf, cnt); + } - /* TODO: notify of the error */ - purple_debug_error("MSN SOAP", "Error writing to SSL connection!\n"); - msn_soap_post(soapconn, NULL); - return; - } - soapconn->written_len += len; - - if (soapconn->written_len < total_len) + /* && count is necessary for Adium, on OS X the last read always + return an error, so we want to proceed anyway. See #5212 for + discussion on this and the above buffer size issues */ + if(cnt < 0 && errno == EAGAIN && count == 0) return; - msn_soap_close_handler( &(soapconn->output_handler) ); -// purple_input_remove(soapconn->output_handler); -// soapconn->output_handler = 0; - - /*clear the write buff*/ - msn_soap_free_write_buf(soapconn); - - /* Write finish! - * callback for write done - */ - if(soapconn->written_cb != NULL){ - soapconn->written_cb(soapconn); - } - /*maybe we need to read the input?*/ - if ( soapconn->input_handler == 0 ) { - soapconn->input_handler = purple_input_add(soapconn->gsc->fd, - PURPLE_INPUT_READ, msn_soap_read_cb, soapconn); - } -} - -/*write the buffer to SOAP connection*/ -void -msn_soap_write(MsnSoapConn * soapconn, char *write_buf, MsnSoapWrittenCbFunction written_cb) -{ - if (soapconn == NULL) { - return; - } - - msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING); - - /* Ideally this wouldn't ever be necessary, but i believe that it is leaking the previous value */ - g_free(soapconn->write_buf); - soapconn->write_buf = write_buf; - soapconn->written_len = 0; - soapconn->written_cb = written_cb; - - msn_soap_free_read_buf(soapconn); - - /*clear the read buffer first*/ - /*start the write*/ - soapconn->output_handler = purple_input_add(soapconn->gsc->fd, PURPLE_INPUT_WRITE, - msn_soap_write_cb, soapconn); - msn_soap_write_cb(soapconn, soapconn->gsc->fd, PURPLE_INPUT_WRITE); -} - -/* New a soap request*/ -MsnSoapReq * -msn_soap_request_new(const char *host,const char *post_url,const char *soap_action, - const char *body, const gpointer data_cb, - MsnSoapReadCbFunction read_cb, - MsnSoapWrittenCbFunction written_cb, - MsnSoapConnectInitFunction connect_init) -{ - MsnSoapReq *request; - - request = g_new0(MsnSoapReq, 1); - request->id = 0; - - request->login_host = g_strdup(host); - request->login_path = g_strdup(post_url); - request->soap_action = g_strdup(soap_action); - request->body = g_strdup(body); - request->data_cb = data_cb; - request->read_cb = read_cb; - request->written_cb = written_cb; - request->connect_init = connect_init; - - return request; -} - -/*free a soap request*/ -void -msn_soap_request_free(MsnSoapReq *request) -{ - g_return_if_fail(request != NULL); - - g_free(request->login_host); - g_free(request->login_path); - g_free(request->soap_action); - g_free(request->body); - request->read_cb = NULL; - request->written_cb = NULL; - request->connect_init = NULL; - - g_free(request); -} - -/*post the soap request queue's head request*/ -void -msn_soap_post_head_request(MsnSoapConn *soapconn) -{ - g_return_if_fail(soapconn != NULL); - g_return_if_fail(soapconn->soap_queue != NULL); - - if (soapconn->step == MSN_SOAP_CONNECTED || - soapconn->step == MSN_SOAP_CONNECTED_IDLE) { - - purple_debug_info("MSN SOAP", "Posting new request from head of the queue\n"); - - if ( !g_queue_is_empty(soapconn->soap_queue) ) { - MsnSoapReq *request; - - if ( (request = g_queue_pop_head(soapconn->soap_queue)) != NULL ) { - msn_soap_post_request(soapconn,request); - } - } else { - purple_debug_info("MSN SOAP", "No requests to process found.\n"); - msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTED_IDLE); + // msn_soap_process could alter errno + perrno = errno; + msn_soap_process(conn); + + if (cnt < 0 && perrno != EAGAIN) { + purple_debug_info("soap", "read: %s\n", g_strerror(perrno)); + // It's possible msn_soap_process closed the ssl connection + if (conn->ssl) { + purple_ssl_close(conn->ssl); + conn->ssl = NULL; + msn_soap_connection_handle_next(conn); } } } -/*post the soap request , - * if not connected, Connected first. - */ -void -msn_soap_post(MsnSoapConn *soapconn, MsnSoapReq *request) -{ - MsnSoapReq *head_request; +static void +msn_soap_process(MsnSoapConnection *conn) { + gboolean handled = FALSE; + char *cursor; + char *linebreak; + + purple_debug_info("soap", "current %s\n", conn->buf->str); + + cursor = conn->buf->str + conn->handled_len; + + if (!conn->headers_done) { + while ((linebreak = strstr(cursor, "\r\n")) != NULL) { + conn->handled_len = linebreak - conn->buf->str + 2; + + if (conn->response_code == 0) { + if (sscanf(cursor, "HTTP/1.1 %d", &conn->response_code) != 1) { + /* something horribly wrong */ + purple_ssl_close(conn->ssl); + conn->ssl = NULL; + msn_soap_connection_handle_next(conn); + handled = TRUE; + break; + } else if (conn->response_code == 503) { + msn_soap_connection_sanitize(conn, TRUE); + msn_session_set_error(conn->session, MSN_ERROR_SERV_UNAVAILABLE, NULL); + return; + } + } else if (cursor == linebreak) { + /* blank line */ + conn->headers_done = TRUE; + cursor = conn->buf->str + conn->handled_len; + break; + } else { + char *line = g_strndup(cursor, linebreak - cursor); + char *sep = strstr(line, ": "); + char *key = line; + char *value; - if (soapconn == NULL) - return; + if (sep == NULL) { + purple_debug_info("soap", "ignoring malformed line: %s\n", line); + g_free(line); + goto loop_end; + } + + value = sep + 2; + *sep = '\0'; + msn_soap_message_add_header(conn->message, key, value); + + if ((conn->response_code == 301 || conn->response_code == 300) + && strcmp(key, "Location") == 0) { + + msn_soap_handle_redirect(conn, value); + + handled = TRUE; + g_free(line); + break; + } else if (conn->response_code == 401 && + strcmp(key, "WWW-Authenticate") == 0) { + char *error = strstr(value, "cbtxt="); - if (request != NULL) { -#ifdef MSN_SOAP_DEBUG - purple_debug_misc("MSN SOAP", "Request added to the queue\n"); -#endif - g_queue_push_tail(soapconn->soap_queue, request); + if (error) { + error += strlen("cbtxt="); + } + + msn_soap_connection_sanitize(conn, TRUE); + msn_session_set_error(conn->session, MSN_ERROR_AUTH, + error ? purple_url_decode(error) : NULL); + + g_free(line); + return; + } else if (strcmp(key, "Content-Length") == 0) { + conn->body_len = atoi(value); + } else if (strcmp(key, "Connection") == 0) { + if (strcmp(value, "close") == 0) { + conn->close_when_done = TRUE; + } + } + g_free(line); + } + + loop_end: + cursor = conn->buf->str + conn->handled_len; + } } - if ( !g_queue_is_empty(soapconn->soap_queue)) { - - /* we may have to reinitialize the soap connection, so avoid - * reusing the connection for now */ + if (!handled && conn->headers_done) { + if (conn->buf->len - conn->handled_len >= + conn->body_len) { + xmlnode *node = xmlnode_from_str(cursor, conn->body_len); - if (soapconn->step == MSN_SOAP_CONNECTED_IDLE) { - purple_debug_misc("MSN SOAP","Already connected to SOAP server, re-initializing\n"); - msn_soap_close_handler( &(soapconn->input_handler) ); - msn_soap_close_handler( &(soapconn->output_handler) ); - msn_soap_close(soapconn); - } + if (node == NULL) { + purple_debug_info("soap", "Malformed SOAP response: %s\n", + cursor); + } else { + MsnSoapMessage *message = conn->message; + conn->message = NULL; + message->xml = node; - if (!msn_soap_connected(soapconn) && (soapconn->step == MSN_SOAP_UNCONNECTED)) { - - /*not connected?and we have something to process connect it first*/ - purple_debug_misc("MSN SOAP","No connection to SOAP server. Connecting...\n"); - head_request = g_queue_peek_head(soapconn->soap_queue); - - if (head_request == NULL) { - purple_debug_error("MSN SOAP", "Queue is not empty, but failed to peek the head request!\n"); - return; + if (!msn_soap_handle_body(conn, message)) { + return; + } } - if (head_request->connect_init != NULL) { - head_request->connect_init(soapconn); - } - msn_soap_connect(soapconn); - return; + msn_soap_connection_handle_next(conn); } -#ifdef MSN_SOAP_DEBUG - purple_debug_info("MSN SOAP", "Currently processing another SOAP request\n"); - } else { - purple_debug_info("MSN SOAP", "No requests left to dispatch\n"); -#endif + return; + } + + if (handled) { + msn_soap_connection_handle_next(conn); + } +} + +static void +msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond) +{ + msn_soap_write_cb_internal(data, fd, cond, FALSE); +} + +static gboolean +msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond, + gboolean initial) +{ + MsnSoapConnection *conn = data; + int written; + + if (cond != PURPLE_INPUT_WRITE) return TRUE; + + written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len, + conn->buf->len - conn->handled_len); + + if (written < 0 && errno == EAGAIN) + return TRUE; + else if (written <= 0) { + purple_ssl_close(conn->ssl); + conn->ssl = NULL; + if (!initial) msn_soap_connection_handle_next(conn); + return FALSE; } + conn->handled_len += written; + + if (conn->handled_len < conn->buf->len) + return TRUE; + + /* we are done! */ + g_string_free(conn->buf, TRUE); + conn->buf = NULL; + conn->handled_len = 0; + conn->body_len = 0; + conn->response_code = 0; + conn->headers_done = FALSE; + conn->close_when_done = FALSE; + + purple_input_remove(conn->event_handle); + conn->event_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ, + msn_soap_read_cb, conn); + return TRUE; +} + +static gboolean +msn_soap_connection_run(gpointer data) +{ + MsnSoapConnection *conn = data; + MsnSoapRequest *req = g_queue_peek_head(conn->queue); + + conn->event_handle = 0; + + if (req) { + if (conn->ssl == NULL) { + conn->ssl = purple_ssl_connect(conn->session->account, conn->host, + 443, msn_soap_connected_cb, msn_soap_error_cb, conn); + } else if (conn->connected) { + int len = -1; + char *body = xmlnode_to_str(req->message->xml, &len); + GSList *iter; + + g_queue_pop_head(conn->queue); + + conn->buf = g_string_new(""); + + g_string_append_printf(conn->buf, + "POST /%s HTTP/1.1\r\n" + "SOAPAction: %s\r\n" + "Content-Type:text/xml; charset=utf-8\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n" + "Accept: */*\r\n" + "Host: %s\r\n" + "Content-Length: %d\r\n" + "Connection: Keep-Alive\r\n" + "Cache-Control: no-cache\r\n", + req->path, req->message->action ? req->message->action : "", + conn->host, len); + + for (iter = req->message->headers; iter; iter = iter->next) { + g_string_append(conn->buf, (char *)iter->data); + g_string_append(conn->buf, "\r\n"); + } + + g_string_append(conn->buf, "\r\n"); + g_string_append(conn->buf, body); + + purple_debug_info("soap", "%s\n", conn->buf->str); + + conn->handled_len = 0; + conn->current_request = req; + + conn->event_handle = purple_input_add(conn->ssl->fd, + PURPLE_INPUT_WRITE, msn_soap_write_cb, conn); + if (!msn_soap_write_cb_internal(conn, conn->ssl->fd, PURPLE_INPUT_WRITE, TRUE)) { + /* Not connected => reconnect and retry */ + purple_debug_info("soap", "not connected, reconnecting\n"); + + conn->connected = FALSE; + conn->current_request = NULL; + msn_soap_connection_sanitize(conn, FALSE); + + g_queue_push_head(conn->queue, req); + conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); + } + + g_free(body); + } + } + + return FALSE; +} + +void +msn_soap_message_send(MsnSession *session, MsnSoapMessage *message, + const char *host, const char *path, + MsnSoapCallback cb, gpointer cb_data) +{ + msn_soap_message_send_internal(session, message, host, path, cb, cb_data, + FALSE); } -/*Post the soap request action*/ -void -msn_soap_post_request(MsnSoapConn *soapconn, MsnSoapReq *request) +static void +msn_soap_message_send_internal(MsnSession *session, + MsnSoapMessage *message, const char *host, const char *path, + MsnSoapCallback cb, gpointer cb_data, gboolean first) { - char * request_str = NULL; -#ifdef MSN_SOAP_DEBUG -#if !defined(_WIN32) - xmlnode * node; -#endif - purple_debug_misc("MSN SOAP","msn_soap_post_request()\n"); -#endif + MsnSoapConnection *conn = msn_soap_get_connection(session, host); + MsnSoapRequest *req = g_new0(MsnSoapRequest, 1); + + req->path = g_strdup(path); + req->message = message; + req->cb = cb; + req->cb_data = cb_data; + + if (first) { + g_queue_push_head(conn->queue, req); + } else { + g_queue_push_tail(conn->queue, req); + } + + if (conn->event_handle == 0) + conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, + conn); +} + +static void +msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect) +{ + if (conn->event_handle) { + purple_input_remove(conn->event_handle); + conn->event_handle = 0; + } - msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING); - request_str = g_strdup_printf( - "POST %s HTTP/1.1\r\n" - "SOAPAction: %s\r\n" - "Content-Type:text/xml; charset=utf-8\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n" - "Accept: */*\r\n" - "Host: %s\r\n" - "Content-Length: %" G_GSIZE_FORMAT "\r\n" - "Connection: Keep-Alive\r\n" - "Cache-Control: no-cache\r\n\r\n" - "%s", - request->login_path, - request->soap_action, - request->login_host, - strlen(request->body), - request->body - ); + if (conn->message) { + msn_soap_message_destroy(conn->message); + conn->message = NULL; + } + + if (conn->buf) { + g_string_free(conn->buf, TRUE); + conn->buf = NULL; + } + + if (conn->ssl && (disconnect || conn->close_when_done)) { + purple_ssl_close(conn->ssl); + conn->ssl = NULL; + } -#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32) - node = xmlnode_from_str(request->body, -1); - if (node != NULL) { - char *formattedstr = xmlnode_to_formatted_str(node, NULL); - purple_debug_info("MSN SOAP","Posting request to SOAP server:\n%s%s\n",request_str, formattedstr); - g_free(formattedstr); - xmlnode_free(node); + if (conn->current_request) { + msn_soap_request_destroy(conn->current_request, FALSE); + conn->current_request = NULL; } - else - purple_debug_info("MSN SOAP","Failed to parse SOAP request being sent:\n%s\n", request_str); -#endif +} + +static void +msn_soap_connection_handle_next(MsnSoapConnection *conn) +{ + msn_soap_connection_sanitize(conn, FALSE); - /*free read buffer*/ - // msn_soap_free_read_buf(soapconn); - /*post it to server*/ - soapconn->data_cb = request->data_cb; - msn_soap_write(soapconn, request_str, request->written_cb); + conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); + + if (conn->current_request) { + MsnSoapRequest *req = conn->current_request; + conn->current_request = NULL; + msn_soap_connection_destroy_foreach_cb(req, conn); + } } +static void +msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data) +{ + MsnSoapRequest *req = item; + + if (req->cb) + req->cb(req->message, NULL, req->cb_data); + + msn_soap_request_destroy(req, FALSE); +} + +static void +msn_soap_connection_destroy(MsnSoapConnection *conn) +{ + if (conn->current_request) { + MsnSoapRequest *req = conn->current_request; + conn->current_request = NULL; + msn_soap_connection_destroy_foreach_cb(req, conn); + } + + msn_soap_connection_sanitize(conn, TRUE); + g_queue_foreach(conn->queue, msn_soap_connection_destroy_foreach_cb, conn); + g_queue_free(conn->queue); + + g_free(conn->host); + g_free(conn); +} + +MsnSoapMessage * +msn_soap_message_new(const char *action, xmlnode *xml) +{ + MsnSoapMessage *message = g_new0(MsnSoapMessage, 1); + + message->action = g_strdup(action); + message->xml = xml; + + return message; +} + +void +msn_soap_message_destroy(MsnSoapMessage *message) +{ + if (message) { + g_slist_foreach(message->headers, (GFunc)g_free, NULL); + g_slist_free(message->headers); + g_free(message->action); + if (message->xml) + xmlnode_free(message->xml); + g_free(message); + } +} + +void +msn_soap_message_add_header(MsnSoapMessage *message, + const char *name, const char *value) +{ + char *header = g_strdup_printf("%s: %s\r\n", name, value); + + message->headers = g_slist_prepend(message->headers, header); +} + +static void +msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message) +{ + g_free(req->path); + if (!keep_message) + msn_soap_message_destroy(req->message); + g_free(req); +} +
--- a/libpurple/protocols/msn/soap.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/soap.h Wed Jul 09 00:32:18 2008 +0000 @@ -1,8 +1,7 @@ /** * @file soap.h * header file for SOAP connection related process - * Author - * MaYuan<mayuan2006@gmail.com> + * * purple * * Purple is the legal property of its developers, whose names are too numerous @@ -23,144 +22,35 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _MSN_SOAP_H_ -#define _MSN_SOAP_H_ -#define MSN_SOAP_READ_BUFF_SIZE 8192 - -/* define this to debug the communications with the SOAP server */ -/* #define MSN_SOAP_DEBUG */ - -#define MSN_SOAP_READ 1 -#define MSN_SOAP_WRITE 2 +#ifndef _MSN_SOAP_H +#define _MSN_SOAP_H -typedef enum -{ - MSN_SOAP_UNCONNECTED, - MSN_SOAP_CONNECTING, - MSN_SOAP_CONNECTED, - MSN_SOAP_PROCESSING, - MSN_SOAP_CONNECTED_IDLE -}MsnSoapStep; - -/* MSN SoapRequest structure*/ -typedef struct _MsnSoapReq MsnSoapReq; +#include "session.h" +#include "sslconn.h" +#include "xmlnode.h" -/* MSN Https connection structure*/ -typedef struct _MsnSoapConn MsnSoapConn; - -typedef void (*MsnSoapConnectInitFunction)(MsnSoapConn *); -typedef gboolean (*MsnSoapReadCbFunction)(MsnSoapConn *); -typedef void (*MsnSoapWrittenCbFunction)(MsnSoapConn *); - -typedef gboolean (*MsnSoapSslConnectCbFunction)(MsnSoapConn *, PurpleSslConnection *); -typedef void (*MsnSoapSslErrorCbFunction)(MsnSoapConn *, PurpleSslConnection *, PurpleSslErrorType); - +#include <glib.h> -struct _MsnSoapReq{ - /*request sequence*/ - int id; +typedef struct _MsnSoapMessage MsnSoapMessage; +typedef void (*MsnSoapCallback)(MsnSoapMessage *request, + MsnSoapMessage *response, gpointer cb_data); - char *login_host; - char *login_path; - char *soap_action; - - char *body; - - gpointer data_cb; - MsnSoapReadCbFunction read_cb; - MsnSoapWrittenCbFunction written_cb; - MsnSoapConnectInitFunction connect_init; +struct _MsnSoapMessage { + char *action; + xmlnode *xml; + GSList *headers; }; -struct _MsnSoapConn{ - MsnSession *session; - gpointer parent; - - char *login_host; - char *login_path; - char *soap_action; - - MsnSoapStep step; - /*ssl connection?*/ - gboolean ssl_conn; - /*normal connection*/ - guint fd; - /*SSL connection*/ - PurpleSslConnection *gsc; - /*ssl connection callback*/ - MsnSoapSslConnectCbFunction connect_cb; - /*ssl error callback*/ - MsnSoapSslErrorCbFunction error_cb; +MsnSoapMessage *msn_soap_message_new(const char *action, xmlnode *xml); - /*read handler*/ - guint input_handler; - /*write handler*/ - guint output_handler; - - /*Queue of SOAP request to send*/ - int soap_id; - GQueue *soap_queue; - - /*write buffer*/ - char *write_buf; - gsize written_len; - MsnSoapWrittenCbFunction written_cb; - - /*read buffer*/ - char *read_buf; - gsize read_len; - gsize need_to_read; - MsnSoapReadCbFunction read_cb; - - gpointer data_cb; +void msn_soap_message_add_header(MsnSoapMessage *req, + const char *name, const char *value); - /*HTTP reply body part*/ - char *body; - int body_len; -}; - - -/*Function Prototype*/ -/*Soap Request Function */ -MsnSoapReq *msn_soap_request_new(const char *host, const char *post_url, - const char *soap_action, const char *body, - const gpointer data_cb, - MsnSoapReadCbFunction read_cb, - MsnSoapWrittenCbFunction written_cb, - MsnSoapConnectInitFunction connect_init); - -void msn_soap_request_free(MsnSoapReq *request); -void msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request); -void msn_soap_post_head_request(MsnSoapConn *soapconn); - -/*new a soap conneciton */ -MsnSoapConn *msn_soap_new(MsnSession *session, gpointer data, gboolean ssl); - -/*destroy */ -void msn_soap_destroy(MsnSoapConn *soapconn); +void msn_soap_message_send(MsnSession *session, + MsnSoapMessage *message, const char *host, const char *path, + MsnSoapCallback cb, gpointer cb_data); -/*init a soap conneciton */ -void msn_soap_init(MsnSoapConn *soapconn, char * host, gboolean ssl, - MsnSoapSslConnectCbFunction connect_cb, - MsnSoapSslErrorCbFunction error_cb); -void msn_soap_connect(MsnSoapConn *soapconn); -void msn_soap_close(MsnSoapConn *soapconn); - -/*write to soap*/ -void msn_soap_write(MsnSoapConn * soapconn, char *write_buf, MsnSoapWrittenCbFunction written_cb); -void msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request); +void msn_soap_message_destroy(MsnSoapMessage *message); -void msn_soap_free_read_buf(MsnSoapConn *soapconn); -void msn_soap_free_write_buf(MsnSoapConn *soapconn); -void msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond); - -/*clean the unhandled requests*/ -void msn_soap_clean_unhandled_requests(MsnSoapConn *soapconn); - -/*check if the soap connection is connected*/ -int msn_soap_connected(MsnSoapConn *soapconn); -void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step); - -#endif/*_MSN_SOAP_H_*/ - +#endif
--- a/libpurple/protocols/msn/soap2.c Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,680 +0,0 @@ -/** - * @file soap2.c - * C file for SOAP connection related process - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "internal.h" - -#include "soap2.h" - -#include "session.h" - -#include "debug.h" -#include "xmlnode.h" - -#include <glib.h> -#if !defined(_WIN32) || !defined(_WINERROR_) -#include <error.h> -#endif - -#define SOAP_TIMEOUT (5 * 60) - -typedef struct _MsnSoapRequest { - char *path; - MsnSoapMessage *message; - MsnSoapCallback cb; - gpointer cb_data; -} MsnSoapRequest; - -typedef struct _MsnSoapConnection { - MsnSession *session; - char *host; - - time_t last_used; - PurpleSslConnection *ssl; - gboolean connected; - - guint event_handle; - GString *buf; - gsize handled_len; - gsize body_len; - int response_code; - gboolean headers_done; - gboolean close_when_done; - - MsnSoapMessage *message; - - GQueue *queue; - MsnSoapRequest *current_request; -} MsnSoapConnection; - -static void msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data); -static gboolean msn_soap_connection_run(gpointer data); - -static MsnSoapConnection *msn_soap_connection_new(MsnSession *session, - const char *host); -static void msn_soap_connection_handle_next(MsnSoapConnection *conn); -static void msn_soap_connection_destroy(MsnSoapConnection *conn); - -static void msn_soap_message_send_internal(MsnSession *session, - MsnSoapMessage *message, const char *host, const char *path, - MsnSoapCallback cb, gpointer cb_data, gboolean first); - -static void msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message); -static void msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect); -static gboolean msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond, gboolean initial); -static void msn_soap_process(MsnSoapConnection *conn); - -static gboolean -msn_soap_cleanup_each(gpointer key, gpointer value, gpointer data) -{ - MsnSoapConnection *conn = value; - time_t *t = data; - - if ((*t - conn->last_used) > SOAP_TIMEOUT * 2) { - purple_debug_info("soap", "cleaning up soap conn %p\n", conn); - return TRUE; - } - - return FALSE; -} - -static gboolean -msn_soap_cleanup_for_session(gpointer data) -{ - MsnSession *sess = data; - time_t t = time(NULL); - - purple_debug_info("soap", "session cleanup timeout\n"); - - if (sess->soap_table) { - g_hash_table_foreach_remove(sess->soap_table, msn_soap_cleanup_each, - &t); - - if (g_hash_table_size(sess->soap_table) == 0) { - purple_timeout_remove(sess->soap_cleanup_handle); - sess->soap_cleanup_handle = 0; - } - } - - return TRUE; -} - -static MsnSoapConnection * -msn_soap_get_connection(MsnSession *session, const char *host) -{ - MsnSoapConnection *conn = NULL; - - if (session->soap_table) { - conn = g_hash_table_lookup(session->soap_table, host); - } else { - session->soap_table = g_hash_table_new_full(g_str_hash, g_str_equal, - NULL, (GDestroyNotify)msn_soap_connection_destroy); - } - - if (session->soap_cleanup_handle == 0) - session->soap_cleanup_handle = purple_timeout_add(SOAP_TIMEOUT * 1000, - msn_soap_cleanup_for_session, session); - - if (conn == NULL) { - conn = msn_soap_connection_new(session, host); - g_hash_table_insert(session->soap_table, conn->host, conn); - } - - conn->last_used = time(NULL); - - return conn; -} - -static MsnSoapConnection * -msn_soap_connection_new(MsnSession *session, const char *host) -{ - MsnSoapConnection *conn = g_new0(MsnSoapConnection, 1); - conn->session = session; - conn->host = g_strdup(host); - conn->queue = g_queue_new(); - return conn; -} - -static void -msn_soap_connected_cb(gpointer data, PurpleSslConnection *ssl, - PurpleInputCondition cond) -{ - MsnSoapConnection *conn = data; - - conn->connected = TRUE; - - if (conn->event_handle == 0) - conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); -} - -static void -msn_soap_error_cb(PurpleSslConnection *ssl, PurpleSslErrorType error, - gpointer data) -{ - MsnSoapConnection *conn = data; - - /* sslconn already frees the connection in case of error */ - conn->ssl = NULL; - - g_hash_table_remove(conn->session->soap_table, conn->host); -} - -static gboolean -msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url) -{ - char *host; - char *path; - - if (purple_url_parse(url, &host, NULL, &path, NULL, NULL)) { - msn_soap_message_send_internal(conn->session, - conn->current_request->message, host, path, - conn->current_request->cb, conn->current_request->cb_data, TRUE); - - msn_soap_request_destroy(conn->current_request, TRUE); - conn->current_request = NULL; - - g_free(host); - g_free(path); - - return TRUE; - } - - return FALSE; -} - -static gboolean -msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response) -{ - xmlnode *body = xmlnode_get_child(response->xml, "Body"); - xmlnode *fault = xmlnode_get_child(response->xml, "Fault"); - - if (fault) { - xmlnode *faultcode = xmlnode_get_child(fault, "faultcode"); - - if (faultcode != NULL) { - char *faultdata = xmlnode_get_data(faultcode); - - if (g_str_equal(faultdata, "psf:Redirect")) { - xmlnode *url = xmlnode_get_child(fault, "redirectUrl"); - - if (url) { - char *urldata = xmlnode_get_data(url); - msn_soap_handle_redirect(conn, urldata); - g_free(urldata); - } - - g_free(faultdata); - msn_soap_message_destroy(response); - return TRUE; - } else if (g_str_equal(faultdata, "wsse:FailedAuthentication")) { - xmlnode *reason = xmlnode_get_child(fault, "faultstring"); - char *reasondata = xmlnode_get_data(reason); - - msn_soap_connection_sanitize(conn, TRUE); - msn_session_set_error(conn->session, MSN_ERROR_AUTH, - reasondata); - - g_free(reasondata); - g_free(faultdata); - msn_soap_message_destroy(response); - return FALSE; - } - - g_free(faultdata); - } - } - - if (fault || body) { - MsnSoapRequest *request = conn->current_request; - conn->current_request = NULL; - request->cb(request->message, response, - request->cb_data); - msn_soap_message_destroy(response); - msn_soap_request_destroy(request, FALSE); - } - - return TRUE; -} - -static void -msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond) -{ - MsnSoapConnection *conn = data; - int count = 0, cnt, perrno; - /* This buffer needs to be larger than any packets received from - login.live.com or Adium will fail to receive the packet - (something weird with the login.live.com server). With NSS it works - fine, so I believe it's some bug with OS X */ - char buf[16 * 1024]; - - if (conn->message == NULL) { - conn->message = msn_soap_message_new(NULL, NULL); - } - - if (conn->buf == NULL) { - conn->buf = g_string_new_len(buf, 0); - } - - while ((cnt = purple_ssl_read(conn->ssl, buf, sizeof(buf))) > 0) { - purple_debug_info("soap", "read %d bytes\n", cnt); - count += cnt; - g_string_append_len(conn->buf, buf, cnt); - } - - /* && count is necessary for Adium, on OS X the last read always - return an error, so we want to proceed anyway. See #5212 for - discussion on this and the above buffer size issues */ - if(cnt < 0 && errno == EAGAIN && count == 0) - return; - - // msn_soap_process could alter errno - perrno = errno; - msn_soap_process(conn); - - if (cnt < 0 && perrno != EAGAIN) { - purple_debug_info("soap", "read: %s\n", g_strerror(perrno)); - // It's possible msn_soap_process closed the ssl connection - if (conn->ssl) { - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - msn_soap_connection_handle_next(conn); - } - } -} - -static void -msn_soap_process(MsnSoapConnection *conn) { - gboolean handled = FALSE; - char *cursor; - char *linebreak; - - purple_debug_info("soap", "current %s\n", conn->buf->str); - - cursor = conn->buf->str + conn->handled_len; - - if (!conn->headers_done) { - while ((linebreak = strstr(cursor, "\r\n")) != NULL) { - conn->handled_len = linebreak - conn->buf->str + 2; - - if (conn->response_code == 0) { - if (sscanf(cursor, "HTTP/1.1 %d", &conn->response_code) != 1) { - /* something horribly wrong */ - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - msn_soap_connection_handle_next(conn); - handled = TRUE; - break; - } else if (conn->response_code == 503) { - msn_soap_connection_sanitize(conn, TRUE); - msn_session_set_error(conn->session, MSN_ERROR_SERV_UNAVAILABLE, NULL); - return; - } - } else if (cursor == linebreak) { - /* blank line */ - conn->headers_done = TRUE; - cursor = conn->buf->str + conn->handled_len; - break; - } else { - char *line = g_strndup(cursor, linebreak - cursor); - char *sep = strstr(line, ": "); - char *key = line; - char *value; - - if (sep == NULL) { - purple_debug_info("soap", "ignoring malformed line: %s\n", line); - g_free(line); - goto loop_end; - } - - value = sep + 2; - *sep = '\0'; - msn_soap_message_add_header(conn->message, key, value); - - if ((conn->response_code == 301 || conn->response_code == 300) - && strcmp(key, "Location") == 0) { - - msn_soap_handle_redirect(conn, value); - - handled = TRUE; - g_free(line); - break; - } else if (conn->response_code == 401 && - strcmp(key, "WWW-Authenticate") == 0) { - char *error = strstr(value, "cbtxt="); - - if (error) { - error += strlen("cbtxt="); - } - - msn_soap_connection_sanitize(conn, TRUE); - msn_session_set_error(conn->session, MSN_ERROR_AUTH, - error ? purple_url_decode(error) : NULL); - - g_free(line); - return; - } else if (strcmp(key, "Content-Length") == 0) { - conn->body_len = atoi(value); - } else if (strcmp(key, "Connection") == 0) { - if (strcmp(value, "close") == 0) { - conn->close_when_done = TRUE; - } - } - g_free(line); - } - - loop_end: - cursor = conn->buf->str + conn->handled_len; - } - } - - if (!handled && conn->headers_done) { - if (conn->buf->len - conn->handled_len >= - conn->body_len) { - xmlnode *node = xmlnode_from_str(cursor, conn->body_len); - - if (node == NULL) { - purple_debug_info("soap", "Malformed SOAP response: %s\n", - cursor); - } else { - MsnSoapMessage *message = conn->message; - conn->message = NULL; - message->xml = node; - - if (!msn_soap_handle_body(conn, message)) { - return; - } - } - - msn_soap_connection_handle_next(conn); - } - - return; - } - - if (handled) { - msn_soap_connection_handle_next(conn); - } -} - -static void -msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond) -{ - msn_soap_write_cb_internal(data, fd, cond, FALSE); -} - -static gboolean -msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond, - gboolean initial) -{ - MsnSoapConnection *conn = data; - int written; - - if (cond != PURPLE_INPUT_WRITE) return TRUE; - - written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len, - conn->buf->len - conn->handled_len); - - if (written < 0 && errno == EAGAIN) - return TRUE; - else if (written <= 0) { - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - if (!initial) msn_soap_connection_handle_next(conn); - return FALSE; - } - - conn->handled_len += written; - - if (conn->handled_len < conn->buf->len) - return TRUE; - - /* we are done! */ - g_string_free(conn->buf, TRUE); - conn->buf = NULL; - conn->handled_len = 0; - conn->body_len = 0; - conn->response_code = 0; - conn->headers_done = FALSE; - conn->close_when_done = FALSE; - - purple_input_remove(conn->event_handle); - conn->event_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ, - msn_soap_read_cb, conn); - return TRUE; -} - -static gboolean -msn_soap_connection_run(gpointer data) -{ - MsnSoapConnection *conn = data; - MsnSoapRequest *req = g_queue_peek_head(conn->queue); - - conn->event_handle = 0; - - if (req) { - if (conn->ssl == NULL) { - conn->ssl = purple_ssl_connect(conn->session->account, conn->host, - 443, msn_soap_connected_cb, msn_soap_error_cb, conn); - } else if (conn->connected) { - int len = -1; - char *body = xmlnode_to_str(req->message->xml, &len); - GSList *iter; - - g_queue_pop_head(conn->queue); - - conn->buf = g_string_new(""); - - g_string_append_printf(conn->buf, - "POST /%s HTTP/1.1\r\n" - "SOAPAction: %s\r\n" - "Content-Type:text/xml; charset=utf-8\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n" - "Accept: */*\r\n" - "Host: %s\r\n" - "Content-Length: %d\r\n" - "Connection: Keep-Alive\r\n" - "Cache-Control: no-cache\r\n", - req->path, req->message->action ? req->message->action : "", - conn->host, len); - - for (iter = req->message->headers; iter; iter = iter->next) { - g_string_append(conn->buf, (char *)iter->data); - g_string_append(conn->buf, "\r\n"); - } - - g_string_append(conn->buf, "\r\n"); - g_string_append(conn->buf, body); - - purple_debug_info("soap", "%s\n", conn->buf->str); - - conn->handled_len = 0; - conn->current_request = req; - - conn->event_handle = purple_input_add(conn->ssl->fd, - PURPLE_INPUT_WRITE, msn_soap_write_cb, conn); - if (!msn_soap_write_cb_internal(conn, conn->ssl->fd, PURPLE_INPUT_WRITE, TRUE)) { - /* Not connected => reconnect and retry */ - purple_debug_info("soap", "not connected, reconnecting"); - - conn->connected = FALSE; - conn->current_request = NULL; - msn_soap_connection_sanitize(conn, FALSE); - - g_queue_push_head(conn->queue, req); - conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); - } - - g_free(body); - } - } - - return FALSE; -} - -void -msn_soap_message_send(MsnSession *session, MsnSoapMessage *message, - const char *host, const char *path, - MsnSoapCallback cb, gpointer cb_data) -{ - msn_soap_message_send_internal(session, message, host, path, cb, cb_data, - FALSE); -} - -static void -msn_soap_message_send_internal(MsnSession *session, - MsnSoapMessage *message, const char *host, const char *path, - MsnSoapCallback cb, gpointer cb_data, gboolean first) -{ - MsnSoapConnection *conn = msn_soap_get_connection(session, host); - MsnSoapRequest *req = g_new0(MsnSoapRequest, 1); - - req->path = g_strdup(path); - req->message = message; - req->cb = cb; - req->cb_data = cb_data; - - if (first) { - g_queue_push_head(conn->queue, req); - } else { - g_queue_push_tail(conn->queue, req); - } - - if (conn->event_handle == 0) - conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, - conn); -} - -static void -msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect) -{ - if (conn->event_handle) { - purple_input_remove(conn->event_handle); - conn->event_handle = 0; - } - - if (conn->message) { - msn_soap_message_destroy(conn->message); - conn->message = NULL; - } - - if (conn->buf) { - g_string_free(conn->buf, TRUE); - conn->buf = NULL; - } - - if (conn->ssl && (disconnect || conn->close_when_done)) { - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - } - - if (conn->current_request) { - msn_soap_request_destroy(conn->current_request, FALSE); - conn->current_request = NULL; - } -} - -static void -msn_soap_connection_handle_next(MsnSoapConnection *conn) -{ - msn_soap_connection_sanitize(conn, FALSE); - - conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); - - if (conn->current_request) { - MsnSoapRequest *req = conn->current_request; - conn->current_request = NULL; - msn_soap_connection_destroy_foreach_cb(req, conn); - } -} - -static void -msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data) -{ - MsnSoapRequest *req = item; - - if (req->cb) - req->cb(req->message, NULL, req->cb_data); - - msn_soap_request_destroy(req, FALSE); -} - -static void -msn_soap_connection_destroy(MsnSoapConnection *conn) -{ - if (conn->current_request) { - MsnSoapRequest *req = conn->current_request; - conn->current_request = NULL; - msn_soap_connection_destroy_foreach_cb(req, conn); - } - - msn_soap_connection_sanitize(conn, TRUE); - g_queue_foreach(conn->queue, msn_soap_connection_destroy_foreach_cb, conn); - g_queue_free(conn->queue); - - g_free(conn->host); - g_free(conn); -} - -MsnSoapMessage * -msn_soap_message_new(const char *action, xmlnode *xml) -{ - MsnSoapMessage *message = g_new0(MsnSoapMessage, 1); - - message->action = g_strdup(action); - message->xml = xml; - - return message; -} - -void -msn_soap_message_destroy(MsnSoapMessage *message) -{ - if (message) { - g_slist_foreach(message->headers, (GFunc)g_free, NULL); - g_slist_free(message->headers); - g_free(message->action); - if (message->xml) - xmlnode_free(message->xml); - g_free(message); - } -} - -void -msn_soap_message_add_header(MsnSoapMessage *message, - const char *name, const char *value) -{ - char *header = g_strdup_printf("%s: %s\r\n", name, value); - - message->headers = g_slist_prepend(message->headers, header); -} - -static void -msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message) -{ - g_free(req->path); - if (!keep_message) - msn_soap_message_destroy(req->message); - g_free(req); -} -
--- a/libpurple/protocols/msn/soap2.h Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * @file soap2.h - * header file for SOAP connection related process - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _MSN_SOAP2_H -#define _MSN_SOAP2_H - -#include "session.h" -#include "sslconn.h" -#include "xmlnode.h" - -#include <glib.h> - -typedef struct _MsnSoapMessage MsnSoapMessage; -typedef void (*MsnSoapCallback)(MsnSoapMessage *request, - MsnSoapMessage *response, gpointer cb_data); - -struct _MsnSoapMessage { - char *action; - xmlnode *xml; - GSList *headers; -}; - -MsnSoapMessage *msn_soap_message_new(const char *action, xmlnode *xml); - -void msn_soap_message_add_header(MsnSoapMessage *req, - const char *name, const char *value); - -void msn_soap_message_send(MsnSession *session, - MsnSoapMessage *message, const char *host, const char *path, - MsnSoapCallback cb, gpointer cb_data); - -void msn_soap_message_destroy(MsnSoapMessage *message); - -#endif
--- a/libpurple/protocols/msn/switchboard.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msn/switchboard.c Wed Jul 09 00:32:18 2008 +0000 @@ -960,17 +960,40 @@ } static void -nudge_msg(MsnCmdProc *cmdproc, MsnMessage *msg) +datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { - MsnSwitchBoard *swboard; - PurpleAccount *account; - const char *user; + GHashTable *body; + const char *id; + body = msn_message_get_hashtable_from_body(msg); + + id = g_hash_table_lookup(body, "ID"); + + if (!strcmp(id, "1")) { + /* Nudge */ + MsnSwitchBoard *swboard; + PurpleAccount *account; + const char *user; - swboard = cmdproc->data; - account = cmdproc->session->account; - user = msg->remote_user; + swboard = cmdproc->data; + account = cmdproc->session->account; + user = msg->remote_user; + + serv_got_attention(account->gc, user, MSN_NUDGE); + + } else if (!strcmp(id, "2")) { + /* Wink */ - serv_got_attention(account->gc, user, MSN_NUDGE); + } else if (!strcmp(id, "3")) { + /* Voiceclip */ + + } else if (!strcmp(id, "4")) { + /* Action */ + + } else { + purple_debug_warning("msn", "Got unknown datacast with ID %s.\n", id); + } + + g_hash_table_destroy(body); } /************************************************************************** @@ -1059,7 +1082,7 @@ msn_servconn_set_connect_cb(swboard->servconn, connect_cb); msn_servconn_set_disconnect_cb(swboard->servconn, disconnect_cb); - return msn_servconn_connect(swboard->servconn, host, port); + return msn_servconn_connect(swboard->servconn, host, port, FALSE); } void @@ -1114,13 +1137,9 @@ } purple_debug_warning("msn", "cal_error: command %s gave error %i\n", trans->command, error); - purple_debug_warning("msn", "Will Use Offline Message to sendit\n"); - -// cal_error_helper(trans, reason); - /*offline Message send Process*/ while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL){ - purple_debug_warning("MSNP14","offline msg to send:{%s}\n",msg->body); + purple_debug_warning("MSNP14", "Unable to send msg: {%s}\n", msg->body); /* The messages could not be sent due to a switchboard error */ swboard->error = MSN_SB_ERROR_USER_OFFLINE; msg_error_helper(swboard->cmdproc, msg, @@ -1313,7 +1332,7 @@ msn_table_add_msg_type(cbs_table, "text/x-mms-animemoticon", msn_emoticon_msg); msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast", - nudge_msg); + datacast_msg); #if 0 msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite", msn_invite_msg);
--- a/libpurple/protocols/msnp9/slplink.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/msnp9/slplink.c Wed Jul 09 00:32:18 2008 +0000 @@ -597,7 +597,7 @@ } else if (slpmsg->size) { - if ((offset + len) > slpmsg->size) + if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size) { purple_debug_error("msn", "Oversized slpmsg\n"); g_return_if_reached();
--- a/libpurple/protocols/oscar/family_auth.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/oscar/family_auth.c Wed Jul 09 00:32:18 2008 +0000 @@ -81,12 +81,9 @@ static int aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest) { - PurpleCipher *cipher; PurpleCipherContext *context; - cipher = purple_ciphers_find_cipher("md5"); - - context = purple_cipher_context_new(cipher, NULL); + context = purple_cipher_context_new_by_name("md5", NULL); purple_cipher_context_append(context, (const guchar *)key, strlen(key)); purple_cipher_context_append(context, (const guchar *)password, password_len); purple_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
--- a/libpurple/protocols/oscar/family_oservice.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/oscar/family_oservice.c Wed Jul 09 00:32:18 2008 +0000 @@ -952,13 +952,10 @@ byte_stream_putraw(&bs, buf, 0x10); } else if (buf && (len > 0)) { /* use input buffer */ - PurpleCipher *cipher; PurpleCipherContext *context; guchar digest[16]; - cipher = purple_ciphers_find_cipher("md5"); - - context = purple_cipher_context_new(cipher, NULL); + context = purple_cipher_context_new_by_name("md5", NULL); purple_cipher_context_append(context, buf, len); purple_cipher_context_digest(context, 16, digest, NULL); purple_cipher_context_destroy(context); @@ -966,7 +963,6 @@ byte_stream_putraw(&bs, digest, 0x10); } else if (len == 0) { /* no length, just hash NULL (buf is optional) */ - PurpleCipher *cipher; PurpleCipherContext *context; guchar digest[16]; guint8 nil = '\0'; @@ -975,9 +971,7 @@ * I'm not sure if we really need the empty append with the * new MD5 functions, so I'll leave it in, just in case. */ - cipher = purple_ciphers_find_cipher("md5"); - - context = purple_cipher_context_new(cipher, NULL); + context = purple_cipher_context_new_by_name("md5", NULL); purple_cipher_context_append(context, &nil, 0); purple_cipher_context_digest(context, 16, digest, NULL); purple_cipher_context_destroy(context);
--- a/libpurple/protocols/oscar/oscar.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.c Wed Jul 09 00:32:18 2008 +0000 @@ -6424,15 +6424,12 @@ if (img == NULL) { aim_ssi_delicon(od); } else { - PurpleCipher *cipher; PurpleCipherContext *context; guchar md5[16]; gconstpointer data = purple_imgstore_get_data(img); size_t len = purple_imgstore_get_size(img); - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); + context = purple_cipher_context_new_by_name("md5", NULL); purple_cipher_context_append(context, data, len); purple_cipher_context_digest(context, 16, md5, NULL); purple_cipher_context_destroy(context);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/ChangeLog Wed Jul 09 00:32:18 2008 +0000 @@ -0,0 +1,49 @@ +2008.06.07 - ccpaging <ecc_hy(at)hotmail.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> + * 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> + * 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> + * 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> + * Clean code of packet_parse.c, enable PARSER_DEBUG + * Rewrite send_queue + +2008.05.08 - ccpaging <ecc_hy(at)hotmail.com> + * Rewrite qq_network + * Add srv resolve function when qq_login + * 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> + * 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> + * Add qq_hex_dump function + +2008.04.25 - ccpaging <ecc_hy(at)hotmail.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> + * Remove qq_crypt function in crypt.c, use qq_crypt and qq_decrypt directly + +** since pidgin-2.4.0 ***
--- a/libpurple/protocols/qq/Makefile.am Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.am Wed Jul 09 00:32:18 2008 +0000 @@ -52,20 +52,14 @@ packet_parse.h \ qq.c \ qq.h \ - qq_proxy.c \ - qq_proxy.h \ - recv_core.c \ - recv_core.h \ - send_core.c \ - send_core.h \ + qq_network.c \ + qq_network.h \ send_file.c \ send_file.h \ - sendqueue.c \ - sendqueue.h \ + qq_trans.c \ + qq_trans.h \ sys_msg.c \ sys_msg.h \ - udp_proxy_s5.c \ - udp_proxy_s5.h \ utils.c \ utils.h
--- a/libpurple/protocols/qq/Makefile.mingw Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.mingw Wed Jul 09 00:32:18 2008 +0000 @@ -63,13 +63,10 @@ login_logout.c \ packet_parse.c \ qq.c \ - qq_proxy.c \ - recv_core.c \ - send_core.c \ + qq_network.c \ send_file.c \ - sendqueue.c \ + qq_trans.c \ sys_msg.c \ - udp_proxy_s5.c \ utils.c OBJECTS = $(C_SRC:%.c=%.o)
--- a/libpurple/protocols/qq/buddy_info.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/buddy_info.c Wed Jul 09 00:32:18 2008 +0000 @@ -34,7 +34,7 @@ #include "crypt.h" #include "header_info.h" #include "keep_alive.h" -#include "send_core.h" +#include "qq_network.h" #define QQ_PRIMARY_INFORMATION _("Primary Information") #define QQ_ADDITIONAL_INFORMATION _("Additional Information") @@ -94,6 +94,46 @@ gboolean modify_info; } qq_info_query; +typedef struct _contact_info { + gchar *uid; + gchar *nick; + gchar *country; + gchar *province; + gchar *zipcode; + gchar *address; + gchar *tel; + gchar *age; + gchar *gender; + gchar *name; + gchar *email; + gchar *pager_sn; + gchar *pager_num; + gchar *pager_sp; + gchar *pager_base_num; + gchar *pager_type; + gchar *occupation; + gchar *homepage; + gchar *auth_type; + gchar *unknown1; + gchar *unknown2; + gchar *face; + gchar *hp_num; + gchar *hp_type; + gchar *intro; + gchar *city; + gchar *unknown3; + gchar *unknown4; + gchar *unknown5; + gchar *is_open_hp; + gchar *is_open_contact; + gchar *college; + gchar *horoscope; + gchar *zodiac; + gchar *blood; + gchar *qq_show; + gchar *unknown6; /* always 0x2D */ +} contact_info; + /* We get an info packet on ourselves before we modify our information. * Even though not all of the information is modifiable, it still * all needs to be there when we send out the modify info packet */ @@ -137,7 +177,7 @@ } else { return NULL; } - /* else ASCIIized index */ + /* else ASCIIized index */ } else { if (strcmp(choice[index], "-") != 0) return g_strdup(choice[index]); @@ -161,14 +201,14 @@ if (value != NULL) { purple_notify_user_info_add_pair(user_info, title, value); g_free(value); - + return TRUE; } - + return FALSE; } -static PurpleNotifyUserInfo * + static PurpleNotifyUserInfo * info_to_notify_user_info(const contact_info *info) { PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); @@ -209,25 +249,25 @@ /* for debugging */ /* - g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous"); - append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0); - append_field_value(info_text, info->pager_num, "pager_num", NULL, 0); - append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0); - append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0); - append_field_value(info_text, info->pager_type, "pager_type", NULL, 0); - append_field_value(info_text, info->auth_type, "auth_type", NULL, 0); - append_field_value(info_text, info->unknown1, "unknown1", NULL, 0); - append_field_value(info_text, info->unknown2, "unknown2", NULL, 0); - append_field_value(info_text, info->face, "face", NULL, 0); - append_field_value(info_text, info->hp_type, "hp_type", NULL, 0); - append_field_value(info_text, info->unknown3, "unknown3", NULL, 0); - append_field_value(info_text, info->unknown4, "unknown4", NULL, 0); - append_field_value(info_text, info->unknown5, "unknown5", NULL, 0); - append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0); - append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0); - append_field_value(info_text, info->qq_show, "qq_show", NULL, 0); - append_field_value(info_text, info->unknown6, "unknown6", NULL, 0); - */ + g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous"); + append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0); + append_field_value(info_text, info->pager_num, "pager_num", NULL, 0); + append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0); + append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0); + append_field_value(info_text, info->pager_type, "pager_type", NULL, 0); + append_field_value(info_text, info->auth_type, "auth_type", NULL, 0); + append_field_value(info_text, info->unknown1, "unknown1", NULL, 0); + append_field_value(info_text, info->unknown2, "unknown2", NULL, 0); + append_field_value(info_text, info->face, "face", NULL, 0); + append_field_value(info_text, info->hp_type, "hp_type", NULL, 0); + append_field_value(info_text, info->unknown3, "unknown3", NULL, 0); + append_field_value(info_text, info->unknown4, "unknown4", NULL, 0); + append_field_value(info_text, info->unknown5, "unknown5", NULL, 0); + append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0); + append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0); + append_field_value(info_text, info->qq_show, "qq_show", NULL, 0); + append_field_value(info_text, info->unknown6, "unknown6", NULL, 0); + */ return user_info; } @@ -243,7 +283,7 @@ qd = (qq_data *) gc->proto_data; g_snprintf(uid_str, sizeof(uid_str), "%d", uid); - qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, TRUE, 0, TRUE, (guint8 *) uid_str, strlen(uid_str)); + qq_send_cmd(qd, QQ_CMD_GET_USER_INFO, (guint8 *) uid_str, strlen(uid_str)); query = g_new0(qq_info_query, 1); query->uid = uid; @@ -271,27 +311,141 @@ } /* send packet to modify personal information */ -static void qq_send_packet_modify_info(PurpleConnection *gc, gchar **segments) +static void qq_send_packet_modify_info(PurpleConnection *gc, contact_info *info) { - gint i; - guint8 *raw_data, *cursor, bar; + qq_data *qd = (qq_data *) gc->proto_data; + gint bytes = 0; + guint8 raw_data[MAX_PACKET_SIZE - 128] = {0}; + guint8 bar; - g_return_if_fail(segments != NULL); + g_return_if_fail(info != NULL); bar = 0x1f; - raw_data = g_newa(guint8, MAX_PACKET_SIZE - 128); - cursor = raw_data; - create_packet_b(raw_data, &cursor, bar); + bytes += qq_put8(raw_data + bytes, bar); /* important! skip the first uid entry */ - for (i = 1; i < QQ_CONTACT_FIELDS; i++) { - create_packet_b(raw_data, &cursor, bar); - create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i])); - } - create_packet_b(raw_data, &cursor, bar); + /* + for (i = 1; i < QQ_CONTACT_FIELDS; i++) { + create_packet_b(raw_data, &cursor, bar); + create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i])); + } + */ + /* uid */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->uid, strlen(info->uid)); + /* nick */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->nick, strlen(info->nick)); + /* country */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->country, strlen(info->country)); + /* province */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->province, strlen(info->province)); + /* zipcode */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zipcode, strlen(info->zipcode)); + /* address */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->address, strlen(info->address)); + /* tel */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->tel, strlen(info->tel)); + /* age */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->age, strlen(info->age)); + /* gender */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->gender, strlen(info->gender)); + /* name */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->name, strlen(info->name)); + /* email */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->email, strlen(info->email)); + /* pager_sn */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sn, strlen(info->pager_sn)); + /* pager_num */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_num, strlen(info->pager_num)); + /* pager_sp */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sp, strlen(info->pager_sp)); + /* pager_base_num */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_base_num, strlen(info->pager_base_num)); + /* pager_type */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_type, strlen(info->pager_type)); + /* occupation */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->occupation, strlen(info->occupation)); + /* homepage */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->homepage, strlen(info->homepage)); + /* auth_type */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->auth_type, strlen(info->auth_type)); + /* unknown1 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown1, strlen(info->unknown1)); + /* unknown2 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown2, strlen(info->unknown2)); + /* face */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->face, strlen(info->face)); + /* hp_num */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_num, strlen(info->hp_num)); + /* hp_type */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_type, strlen(info->hp_type)); + /* intro */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->intro, strlen(info->intro)); + /* city */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->city, strlen(info->city)); + /* unknown3 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown3, strlen(info->unknown3)); + /* unknown4 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown4, strlen(info->unknown4)); + /* unknown5 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown5, strlen(info->unknown5)); + /* is_open_hp */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_hp, strlen(info->is_open_hp)); + /* is_open_contact */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_contact, strlen(info->is_open_contact)); + /* college */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->college, strlen(info->college)); + /* horoscope */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->horoscope, strlen(info->horoscope)); + /* zodiac */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zodiac, strlen(info->zodiac)); + /* blood */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->blood, strlen(info->blood)); + /* qq_show */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->qq_show, strlen(info->qq_show)); + /* unknown6 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown6, strlen(info->unknown6)); - qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, TRUE, 0, TRUE, raw_data, cursor - raw_data); + bytes += qq_put8(raw_data + bytes, bar); + + qq_send_cmd(qd, QQ_CMD_UPDATE_INFO, raw_data, bytes); } @@ -407,8 +561,11 @@ groups = groups->next; } - /* This casting looks like a horrible idea to me -DAA */ - qq_send_packet_modify_info(gc, (gchar **) info); + /* This casting looks like a horrible idea to me -DAA + * yes, rewritten -s3e + * qq_send_packet_modify_info(gc, (gchar **) info); + */ + qq_send_packet_modify_info(gc, info); g_strfreev((gchar **) mid->info); g_free(mid); @@ -520,11 +677,11 @@ mid->info->unknown6 = g_strdup(info->unknown6); purple_request_fields(gc, _("Modify my information"), - _("Modify my information"), NULL, fields, - _("Update my information"), G_CALLBACK(modify_info_ok_cb), - _("Cancel"), G_CALLBACK(modify_info_cancel_cb), - purple_connection_get_account(gc), NULL, NULL, - mid); + _("Modify my information"), NULL, fields, + _("Update my information"), G_CALLBACK(modify_info_ok_cb), + _("Cancel"), G_CALLBACK(modify_info_cancel_cb), + purple_connection_get_account(gc), NULL, NULL, + mid); } } @@ -578,10 +735,9 @@ gchar *data; gsize len; - if (!g_file_get_contents(iconfile, &data, &len, NULL)) + if (!g_file_get_contents(iconfile, &data, &len, NULL)) { g_return_if_reached(); - else - { + } else { purple_buddy_icons_set_for_user(account, who, data, len, icon_num); } } @@ -608,10 +764,10 @@ /* make sure we're using an appropriate icon */ if (!(g_ascii_strncasecmp(icon_path, buddy_icon_dir, dir_len) == 0 - && icon_path[dir_len] == G_DIR_SEPARATOR - && g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0 - && g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0 - && icon_len <= 3)) { + && icon_path[dir_len] == G_DIR_SEPARATOR + && g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0 + && g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0 + && icon_len <= 3)) { if (icon_global) purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", errmsg); else @@ -650,13 +806,13 @@ old_icon_num = purple_buddy_icons_get_checksum_for_user(buddy); if (old_icon_num == NULL || - strcmp(icon_num_str, old_icon_num)) + strcmp(icon_num_str, old_icon_num)) { gchar *icon_path; icon_path = g_strconcat(qq_buddy_icon_dir(), G_DIR_SEPARATOR_S, - QQ_ICON_PREFIX, icon_num_str, - QQ_ICON_SUFFIX, NULL); + QQ_ICON_PREFIX, icon_num_str, + QQ_ICON_SUFFIX, NULL); qq_set_buddy_icon_for_user(account, name, icon_num_str, icon_path); g_free(icon_path); @@ -665,7 +821,7 @@ } /* after getting info or modify myself, refresh the buddy list accordingly */ -void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc) +static void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc) { PurpleBuddy *b; qq_data *qd; @@ -728,7 +884,7 @@ qd->modifying_face = FALSE; g_free(info->face); info->face = icon; - qq_send_packet_modify_info(gc, segments); + qq_send_packet_modify_info(gc, (contact_info *)segments); } qq_refresh_buddy_and_myself(info, gc); @@ -777,39 +933,41 @@ void qq_send_packet_get_level(PurpleConnection *gc, guint32 uid) { - guint8 buf[5]; - guint32 tmp = g_htonl(uid); - buf[0] = 0; - memcpy(buf+1, &tmp, 4); - qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, 5); + qq_data *qd = (qq_data *) gc->proto_data; + guint8 buf[16] = {0}; + gint bytes = 0; + + bytes += qq_put8(buf + bytes, 0x00); + bytes += qq_put32(buf + bytes, uid); + + qd = (qq_data *) gc->proto_data; + qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, bytes); } void qq_send_packet_get_buddies_levels(PurpleConnection *gc) { - guint8 *buf, *tmp; + guint8 *buf; guint16 size; qq_buddy *q_bud; qq_data *qd = (qq_data *) gc->proto_data; GList *node = qd->buddies; + gint bytes = 0; if (qd->buddies) { /* server only sends back levels for online buddies, no point - * in asking for anyone else */ - size = 4*g_list_length(qd->buddies) + 1; + * in asking for anyone else */ + size = 4 * g_list_length(qd->buddies) + 1; buf = g_new0(guint8, size); - tmp = buf + 1; + bytes += 1; - while (node != NULL) { - guint32 tmp4; + while (NULL != node) { q_bud = (qq_buddy *) node->data; - if (q_bud != NULL) { - tmp4 = g_htonl(q_bud->uid); - memcpy(tmp, &tmp4, 4); - tmp += 4; + if (NULL != q_bud) { + bytes += qq_put32(buf + bytes, q_bud->uid); } node = node->next; } - qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, size); + qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, size); g_free(buf); } } @@ -822,10 +980,11 @@ PurpleBuddy *b; qq_buddy *q_bud; gint decr_len, i; - guint8 *decr_buf, *tmp; + guint8 *decr_buf; PurpleAccount *account = purple_connection_get_account(gc); qq_data *qd = (qq_data *) gc->proto_data; - + gint bytes = 0; + decr_len = buf_len; decr_buf = g_new0(guint8, buf_len); if (!qq_decrypt(buf, buf_len, qd->session_key, decr_buf, &decr_len)) { @@ -835,28 +994,23 @@ decr_len--; if (decr_len % 12 != 0) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12); + "Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12); decr_len -= (decr_len % 12); } - - tmp = decr_buf + 1; + + bytes += 1; /* this byte seems random */ /* - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]); - */ + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]); + */ for (i = 0; i < decr_len; i += 12) { - uid = g_ntohl(*(guint32 *) tmp); - tmp += 4; - onlineTime = g_ntohl(*(guint32 *) tmp); - tmp += 4; - level = g_ntohs(*(guint16 *) tmp); - tmp += 2; - timeRemainder = g_ntohs(*(guint16 *) tmp); - tmp += 2; - /* - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Level packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %d\n", + bytes += qq_get32(&uid, decr_buf + bytes); + bytes += qq_get32(&onlineTime, decr_buf + bytes); + bytes += qq_get16(&level, decr_buf + bytes); + bytes += qq_get16(&timeRemainder, decr_buf + bytes); + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Level packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %d\n", uid, onlineTime, level, timeRemainder); - */ purple_name = uid_to_purple_name(uid); b = purple_find_buddy(account, purple_name); q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; @@ -872,7 +1026,7 @@ } } else { purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Got an online buddy %d, but not in my buddy list\n", uid); + "Got an online buddy %d, but not in my buddy list\n", uid); } g_free(purple_name); }
--- a/libpurple/protocols/qq/buddy_info.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/buddy_info.h Wed Jul 09 00:32:18 2008 +0000 @@ -44,47 +44,6 @@ #define QQ_ICON_PREFIX "qq_" #define QQ_ICON_SUFFIX ".png" -typedef struct _contact_info { - gchar *uid; - gchar *nick; - gchar *country; - gchar *province; - gchar *zipcode; - gchar *address; - gchar *tel; - gchar *age; - gchar *gender; - gchar *name; - gchar *email; - gchar *pager_sn; - gchar *pager_num; - gchar *pager_sp; - gchar *pager_base_num; - gchar *pager_type; - gchar *occupation; - gchar *homepage; - gchar *auth_type; - gchar *unknown1; - gchar *unknown2; - gchar *face; - gchar *hp_num; - gchar *hp_type; - gchar *intro; - gchar *city; - gchar *unknown3; - gchar *unknown4; - gchar *unknown5; - gchar *is_open_hp; - gchar *is_open_contact; - gchar *college; - gchar *horoscope; - gchar *zodiac; - gchar *blood; - gchar *qq_show; - gchar *unknown6; /* always 0x2D */ -} contact_info; - -void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc); void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window); void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img); void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile);
--- a/libpurple/protocols/qq/buddy_list.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/buddy_list.c Wed Jul 09 00:32:18 2008 +0000 @@ -38,13 +38,12 @@ #include "crypt.h" #include "header_info.h" #include "keep_alive.h" -#include "send_core.h" #include "group.h" #include "group_find.h" #include "group_internal.h" #include "group_info.h" -#include "qq_proxy.h" +#include "qq_network.h" #define QQ_GET_ONLINE_BUDDY_02 0x02 #define QQ_GET_ONLINE_BUDDY_03 0x03 /* unknown function */ @@ -64,25 +63,25 @@ void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position) { qq_data *qd; - guint8 *raw_data, *cursor; + guint8 *raw_data; + gint bytes = 0; qd = (qq_data *) gc->proto_data; raw_data = g_newa(guint8, 5); - cursor = raw_data; /* 000-000 get online friends cmd * only 0x02 and 0x03 returns info from server, other valuse all return 0xff * I can also only send the first byte (0x02, or 0x03) * and the result is the same */ - create_packet_b(raw_data, &cursor, QQ_GET_ONLINE_BUDDY_02); + bytes += qq_put8(raw_data + bytes, QQ_GET_ONLINE_BUDDY_02); /* 001-001 seems it supports 255 online buddies at most */ - create_packet_b(raw_data, &cursor, position); + bytes += qq_put8(raw_data + bytes, position); /* 002-002 */ - create_packet_b(raw_data, &cursor, 0x00); + bytes += qq_put8(raw_data + bytes, 0x00); /* 003-004 */ - create_packet_w(raw_data, &cursor, 0x0000); + bytes += qq_put16(raw_data + bytes, 0x0000); - qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_ONLINE, TRUE, 0, TRUE, raw_data, 5); + qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_ONLINE, raw_data, 5); qd->last_get_online = time(NULL); } @@ -90,42 +89,38 @@ * server may return a position tag if list is too long for one packet */ void qq_send_packet_get_buddies_list(PurpleConnection *gc, guint16 position) { - guint8 *raw_data, *cursor; - gint data_len; + qq_data *qd = (qq_data *) gc->proto_data; + guint8 raw_data[16] = {0}; + gint bytes = 0; - data_len = 3; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; /* 000-001 starting position, can manually specify */ - create_packet_w(raw_data, &cursor, position); + bytes += qq_put16(raw_data + bytes, position); /* before Mar 18, 2004, any value can work, and we sent 00 * I do not know what data QQ server is expecting, as QQ2003iii 0304 itself * even can sending packets 00 and get no response. * Now I tested that 00,00,00,00,00,01 work perfectly * March 22, found the 00,00,00 starts to work as well */ - create_packet_b(raw_data, &cursor, 0x00); + bytes += qq_put8(raw_data + bytes, 0x00); - qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_LIST, TRUE, 0, TRUE, raw_data, data_len); + qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_LIST, raw_data, bytes); } /* get all list, buddies & Quns with groupsid support */ void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position) { - guint8 *raw_data, *cursor; - gint data_len; + qq_data *qd = (qq_data *) gc->proto_data; + guint8 raw_data[16] = {0}; + gint bytes = 0; - data_len = 10; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; /* 0x01 download, 0x02, upload */ - create_packet_b(raw_data, &cursor, 0x01); + bytes += qq_put8(raw_data + bytes, 0x01); /* unknown 0x02 */ - create_packet_b(raw_data, &cursor, 0x02); + bytes += qq_put8(raw_data + bytes, 0x02); /* unknown 00 00 00 00 */ - create_packet_dw(raw_data, &cursor, 0x00000000); - create_packet_dw(raw_data, &cursor, position); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put32(raw_data + bytes, position); - qq_send_cmd(gc, QQ_CMD_GET_ALL_LIST_WITH_GROUP, TRUE, 0, TRUE, raw_data, data_len); + qq_send_cmd(qd, QQ_CMD_GET_ALL_LIST_WITH_GROUP, raw_data, bytes); } static void _qq_buddies_online_reply_dump_unclear(qq_friends_online_entry *fe) @@ -151,8 +146,8 @@ void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) { qq_data *qd; - gint len, bytes; - guint8 *data, *cursor, position; + gint len, bytes, bytes_buddy; + guint8 *data, position; PurpleBuddy *b; qq_buddy *q_bud; qq_friends_online_entry *fe; @@ -162,96 +157,100 @@ qd = (qq_data *) gc->proto_data; len = buf_len; data = g_newa(guint8, len); - cursor = data; purple_debug(PURPLE_DEBUG_INFO, "QQ", "processing get_buddies_online_reply\n"); - - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - _qq_show_packet("Get buddies online reply packet", data, len); - - read_packet_b(data, &cursor, len, &position); - - fe = g_newa(qq_friends_online_entry, 1); - fe->s = g_newa(qq_buddy_status, 1); + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online"); + return; + } - while (cursor < (data + len)) { - /* based on one online buddy entry */ - bytes = 0; - /* 000-030 qq_buddy_status */ - bytes += qq_buddy_status_read(data, &cursor, len, fe->s); - /* 031-032: unknown4 */ - bytes += read_packet_w(data, &cursor, len, &fe->unknown1); - /* 033-033: flag1 */ - bytes += read_packet_b(data, &cursor, len, &fe->flag1); - /* 034-034: comm_flag */ - bytes += read_packet_b(data, &cursor, len, &fe->comm_flag); - /* 035-036: */ - bytes += read_packet_w(data, &cursor, len, &fe->unknown2); - /* 037-037: */ - bytes += read_packet_b(data, &cursor, len, &fe->ending); /* 0x00 */ + qq_show_packet("Get buddies online reply packet", data, len); + + bytes = 0; + bytes += qq_get8(&position, data + bytes); + + fe = g_newa(qq_friends_online_entry, 1); + fe->s = g_newa(qq_buddy_status, 1); - if (fe->s->uid == 0 || bytes != QQ_ONLINE_BUDDY_ENTRY_LEN) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "uid=0 or entry complete len(%d) != %d", - bytes, QQ_ONLINE_BUDDY_ENTRY_LEN); - g_free(fe->s->ip); - g_free(fe->s->unknown_key); - continue; - } /* check if it is a valid entry */ - - if (QQ_DEBUG) - _qq_buddies_online_reply_dump_unclear(fe); + while (bytes < len) { + /* set flag */ + bytes_buddy = bytes; + /* based on one online buddy entry */ + /* ATTTENTION! NEWED in the sub function, but FREED here */ + /* 000-030 qq_buddy_status */ + bytes += qq_buddy_status_read(fe->s, data + bytes); + /* 031-032: unknown4 */ + bytes += qq_get16(&fe->unknown1, data + bytes); + /* 033-033: flag1 */ + bytes += qq_get8(&fe->flag1, data + bytes); + /* 034-034: comm_flag */ + bytes += qq_get8(&fe->comm_flag, data + bytes); + /* 035-036: */ + bytes += qq_get16(&fe->unknown2, data + bytes); + /* 037-037: */ + bytes += qq_get8(&fe->ending, data + bytes); /* 0x00 */ - /* update buddy information */ - b = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(fe->s->uid)); - q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; - - if (q_bud != NULL) { /* we find one and update qq_buddy */ - if(0 != fe->s->client_version) - q_bud->client_version = fe->s->client_version; - g_memmove(q_bud->ip, fe->s->ip, 4); - q_bud->port = fe->s->port; - q_bud->status = fe->s->status; - q_bud->flag1 = fe->flag1; - q_bud->comm_flag = fe->comm_flag; - qq_update_buddy_contact(gc, q_bud); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Got an online buddy %d, but not in my buddy list\n", fe->s->uid); - } - + if (fe->s->uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "uid=0 or entry complete len(%d) != %d", + (bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN); g_free(fe->s->ip); g_free(fe->s->unknown_key); - } - - if(cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n"); + continue; + } /* check if it is a valid entry */ + + if (QQ_DEBUG) { + _qq_buddies_online_reply_dump_unclear(fe); } - if (position != QQ_FRIENDS_ONLINE_POSITION_END) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Has more online buddies, position from %d\n", position); + /* update buddy information */ + b = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(fe->s->uid)); + q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; - qq_send_packet_get_buddies_online(gc, position); + if (q_bud != NULL) { /* we find one and update qq_buddy */ + if(0 != fe->s->client_version) + q_bud->client_version = fe->s->client_version; + g_memmove(q_bud->ip, fe->s->ip, 4); + q_bud->port = fe->s->port; + q_bud->status = fe->s->status; + q_bud->flag1 = fe->flag1; + q_bud->comm_flag = fe->comm_flag; + qq_update_buddy_contact(gc, q_bud); } else { - qq_send_packet_get_buddies_levels(gc); - qq_refresh_all_buddy_status(gc); + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Got an online buddy %d, but not in my buddy list\n", fe->s->uid); } + g_free(fe->s->ip); + g_free(fe->s->unknown_key); + } + + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n"); + } + + if (position != QQ_FRIENDS_ONLINE_POSITION_END) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Has more online buddies, position from %d\n", position); + + qq_send_packet_get_buddies_online(gc, position); } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online"); + qq_send_packet_get_buddies_levels(gc); + qq_refresh_all_buddy_status(gc); } } + /* process reply for get_buddies_list */ void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) { qq_data *qd; qq_buddy *q_bud; - gint len, bytes, bytes_expected, i; + gint len, bytes_expected, i; + gint bytes, buddy_bytes; guint16 position, unknown; - guint8 *data, *cursor, pascal_len; + guint8 *data, pascal_len; gchar *name; PurpleBuddy *b; @@ -260,81 +259,84 @@ qd = (qq_data *) gc->proto_data; len = buf_len; data = g_newa(guint8, len); - cursor = data; + + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list"); + return; + } + bytes = 0; + bytes += qq_get16(&position, data + bytes); + /* the following data is buddy list in this packet */ + i = 0; + while (bytes < len) { + q_bud = g_new0(qq_buddy, 1); + /* set flag */ + buddy_bytes = bytes; + /* 000-003: uid */ + bytes += qq_get32(&q_bud->uid, data + bytes); + /* 004-005: icon index (1-255) */ + bytes += qq_get16(&q_bud->face, data + bytes); + /* 006-006: age */ + bytes += qq_get8(&q_bud->age, data + bytes); + /* 007-007: gender */ + bytes += qq_get8(&q_bud->gender, data + bytes); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - read_packet_w(data, &cursor, len, &position); - /* the following data is buddy list in this packet */ - i = 0; - while (cursor < (data + len)) { - q_bud = g_new0(qq_buddy, 1); - bytes = 0; - /* 000-003: uid */ - bytes += read_packet_dw(data, &cursor, len, &q_bud->uid); - /* 004-005: icon index (1-255) */ - bytes += read_packet_w(data, &cursor, len, &q_bud->face); - /* 006-006: age */ - bytes += read_packet_b(data, &cursor, len, &q_bud->age); - /* 007-007: gender */ - bytes += read_packet_b(data, &cursor, len, &q_bud->gender); - pascal_len = convert_as_pascal_string(cursor, &q_bud->nickname, QQ_CHARSET_DEFAULT); - cursor += pascal_len; - bytes += pascal_len; - bytes += read_packet_w(data, &cursor, len, &unknown); - /* flag1: (0-7) - * bit1 => qq show - * comm_flag: (0-7) - * bit1 => member - * bit4 => TCP mode - * bit5 => open mobile QQ - * bit6 => bind to mobile - * bit7 => whether having a video - */ - bytes += read_packet_b(data, &cursor, len, &q_bud->flag1); - bytes += read_packet_b(data, &cursor, len, &q_bud->comm_flag); + pascal_len = convert_as_pascal_string(data + bytes, &q_bud->nickname, QQ_CHARSET_DEFAULT); + bytes += pascal_len; - bytes_expected = 12 + pascal_len; - - if (q_bud->uid == 0 || bytes != bytes_expected) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes); - g_free(q_bud->nickname); - g_free(q_bud); - continue; - } else { - i++; - } + bytes += qq_get16(&unknown, data + bytes); + /* flag1: (0-7) + * bit1 => qq show + * comm_flag: (0-7) + * bit1 => member + * bit4 => TCP mode + * bit5 => open mobile QQ + * bit6 => bind to mobile + * bit7 => whether having a video + */ + bytes += qq_get8(&q_bud->flag1, data + bytes); + bytes += qq_get8(&q_bud->comm_flag, data + bytes); - if (QQ_DEBUG) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x\n", - q_bud->uid, q_bud->flag1, q_bud->comm_flag); - } + bytes_expected = 12 + pascal_len; - name = uid_to_purple_name(q_bud->uid); - b = purple_find_buddy(gc->account, name); - g_free(name); - - if (b == NULL) - b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE); - - b->proto_data = q_bud; - qd->buddies = g_list_append(qd->buddies, q_bud); - qq_update_buddy_contact(gc, q_bud); + if (q_bud->uid == 0 || (bytes - buddy_bytes) != bytes_expected) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes); + g_free(q_bud->nickname); + g_free(q_bud); + continue; + } else { + i++; } - if(cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!"); - } - if (position == QQ_FRIENDS_LIST_POSITION_END) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get friends list done, %d buddies\n", i); - qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START); - } else { - qq_send_packet_get_buddies_list(gc, position); + if (QQ_DEBUG) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x\n", + q_bud->uid, q_bud->flag1, q_bud->comm_flag); + } + + name = uid_to_purple_name(q_bud->uid); + b = purple_find_buddy(gc->account, name); + g_free(name); + + if (b == NULL) { + b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE); } + + b->proto_data = q_bud; + qd->buddies = g_list_append(qd->buddies, q_bud); + qq_update_buddy_contact(gc, q_bud); + } + + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!"); + } + if (position == QQ_FRIENDS_LIST_POSITION_END) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get friends list done, %d buddies\n", i); + qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START); } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list"); + qq_send_packet_get_buddies_list(gc, position); } } @@ -342,7 +344,8 @@ { qq_data *qd; gint len, i, j; - guint8 *data, *cursor; + gint bytes = 0; + guint8 *data; guint8 sub_cmd, reply_code; guint32 unknown, position; guint32 uid; @@ -354,62 +357,66 @@ qd = (qq_data *) gc->proto_data; len = buf_len; data = g_newa(guint8, len); - cursor = data; + + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group"); + return; + } + + bytes += qq_get8(&sub_cmd, data + bytes); + g_return_if_fail(sub_cmd == 0x01); + + bytes += qq_get8(&reply_code, data + bytes); + if(0 != reply_code) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Get all list with group reply, reply_code(%d) is not zero", reply_code); + } - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - read_packet_b(data, &cursor, len, &sub_cmd); - g_return_if_fail(sub_cmd == 0x01); - read_packet_b(data, &cursor, len, &reply_code); - if(0 != reply_code) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Get all list with group reply, reply_code(%d) is not zero", reply_code); + bytes += qq_get32(&unknown, data + bytes); + bytes += qq_get32(&position, data + bytes); + /* the following data is all list in this packet */ + i = 0; + j = 0; + while (bytes < len) { + /* 00-03: uid */ + bytes += qq_get32(&uid, data + bytes); + /* 04: type 0x1:buddy 0x4:Qun */ + bytes += qq_get8(&type, data + bytes); + /* 05: groupid*4 */ /* seems to always be 0 */ + bytes += qq_get8(&groupid, data + bytes); + /* + purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid); + groupid >>= 2; + */ + if (uid == 0 || (type != 0x1 && type != 0x4)) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Buddy entry, uid=%d, type=%d", uid, type); + continue; + } + if(0x1 == type) { /* a buddy */ + /* don't do anything but count - buddies are handled by + * qq_send_packet_get_buddies_list */ + ++i; + } else { /* a group */ + group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID); + if(group == NULL) { + qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE); + group = g_newa(qq_group, 1); + group->internal_group_id = uid; + qq_send_cmd_group_get_group_info(gc, group); + } else { + group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; + qq_group_refresh(gc, group); + qq_send_cmd_group_get_group_info(gc, group); + } + ++j; } - read_packet_dw(data, &cursor, len, &unknown); - read_packet_dw(data, &cursor, len, &position); - /* the following data is all list in this packet */ - i = 0; - j = 0; - while (cursor < (data + len)) { - /* 00-03: uid */ - read_packet_dw(data, &cursor, len, &uid); - /* 04: type 0x1:buddy 0x4:Qun */ - read_packet_b(data, &cursor, len, &type); - /* 05: groupid*4 */ /* seems to always be 0 */ - read_packet_b(data, &cursor, len, &groupid); - /* - purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid); - groupid >>= 2; - */ - if (uid == 0 || (type != 0x1 && type != 0x4)) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Buddy entry, uid=%d, type=%d", uid, type); - continue; - } - if(0x1 == type) { /* a buddy */ - /* don't do anything but count - buddies are handled by - * qq_send_packet_get_buddies_list */ - ++i; - } else { /* a group */ - group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID); - if(group == NULL) { - qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE); - group = g_newa(qq_group, 1); - group->internal_group_id = uid; - qq_send_cmd_group_get_group_info(gc, group); - } else { - group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; - qq_group_refresh(gc, group); - qq_send_cmd_group_get_group_info(gc, group); - } - ++j; - } - } - if(cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!"); - } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get all list done, %d buddies and %d Quns\n", i, j); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group"); } + + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!"); + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get all list done, %d buddies and %d Quns\n", i, j); }
--- a/libpurple/protocols/qq/buddy_opt.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/buddy_opt.c Wed Jul 09 00:32:18 2008 +0000 @@ -36,7 +36,7 @@ #include "im.h" #include "keep_alive.h" #include "packet_parse.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" #define PURPLE_GROUP_QQ_FORMAT "QQ (%s)" @@ -61,33 +61,33 @@ /* send packet to remove a buddy from my buddy list */ static void _qq_send_packet_remove_buddy(PurpleConnection *gc, guint32 uid) { + qq_data *qd = (qq_data *) gc->proto_data; gchar uid_str[11]; g_return_if_fail(uid > 0); g_snprintf(uid_str, sizeof(uid_str), "%d", uid); - qq_send_cmd(gc, QQ_CMD_DEL_FRIEND, TRUE, 0, - TRUE, (guint8 *) uid_str, strlen(uid_str)); + qq_send_cmd(qd, QQ_CMD_DEL_FRIEND, (guint8 *) uid_str, strlen(uid_str)); } /* try to remove myself from someone's buddy list */ static void _qq_send_packet_remove_self_from(PurpleConnection *gc, guint32 uid) { - guint8 *raw_data, *cursor; + qq_data *qd = (qq_data *) gc->proto_data; + guint8 raw_data[16] = {0}; + gint bytes = 0; g_return_if_fail(uid > 0); - raw_data = g_newa(guint8, 4); - cursor = raw_data; - create_packet_dw(raw_data, &cursor, uid); + bytes += qq_put32(raw_data + bytes, uid); - qq_send_cmd(gc, QQ_CMD_REMOVE_SELF, TRUE, 0, TRUE, raw_data, 4); + qq_send_cmd(qd, QQ_CMD_REMOVE_SELF, raw_data, bytes); } /* try to add a buddy without authentication */ static void _qq_send_packet_add_buddy(PurpleConnection *gc, guint32 uid) { - qq_data *qd; + qq_data *qd = (qq_data *) gc->proto_data; qq_add_buddy_request *req; gchar uid_str[11]; @@ -95,11 +95,9 @@ /* we need to send the ascii code of this uid to qq server */ g_snprintf(uid_str, sizeof(uid_str), "%d", uid); - qq_send_cmd(gc, QQ_CMD_ADD_FRIEND_WO_AUTH, TRUE, 0, - TRUE, (guint8 *) uid_str, strlen(uid_str)); + qq_send_cmd(qd, QQ_CMD_ADD_FRIEND_WO_AUTH, (guint8 *) uid_str, strlen(uid_str)); /* must be set after sending packet to get the correct send_seq */ - qd = (qq_data *) gc->proto_data; req = g_new0(qq_add_buddy_request, 1); req->seq = qd->send_seq; req->uid = uid; @@ -109,28 +107,29 @@ /* this buddy needs authentication, text conversion is done at lowest level */ static void _qq_send_packet_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text) { + qq_data *qd = (qq_data *) gc->proto_data; gchar *text_qq, uid_str[11]; - guint8 bar, *cursor, *raw_data; + guint8 bar, *raw_data; + gint bytes = 0; g_return_if_fail(uid != 0); g_snprintf(uid_str, sizeof(uid_str), "%d", uid); bar = 0x1f; raw_data = g_newa(guint8, QQ_MSG_IM_MAX); - cursor = raw_data; - create_packet_data(raw_data, &cursor, (guint8 *) uid_str, strlen(uid_str)); - create_packet_b(raw_data, &cursor, bar); - create_packet_b(raw_data, &cursor, response); + 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); - create_packet_b(raw_data, &cursor, bar); - create_packet_data(raw_data, &cursor, (guint8 *) text_qq, strlen(text_qq)); + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *) text_qq, strlen(text_qq)); g_free(text_qq); } - qq_send_cmd(gc, QQ_CMD_BUDDY_AUTH, TRUE, 0, TRUE, raw_data, cursor - raw_data); + qq_send_cmd(qd, QQ_CMD_BUDDY_AUTH, raw_data, bytes); } static void _qq_send_packet_add_buddy_auth_with_gc_and_uid(gc_and_uid *g, const gchar *text) @@ -210,10 +209,10 @@ nombre = uid_to_purple_name(uid); purple_request_input(gc, _("Reject request"), msg1, msg2, - _("Sorry, you are not my type..."), TRUE, FALSE, - NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL, - purple_connection_get_account(gc), nombre, NULL, - g2); + _("Sorry, you are not my type..."), TRUE, FALSE, + NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL, + purple_connection_get_account(gc), nombre, NULL, + g2); g_free(nombre); } @@ -257,7 +256,8 @@ { qq_data *qd; gint len; - guint8 *data, *cursor, reply; + gint bytes = 0; + guint8 *data, reply; gchar **segments, *msg_utf8; g_return_if_fail(buf != NULL && buf_len != 0); @@ -265,22 +265,23 @@ qd = (qq_data *) gc->proto_data; len = buf_len; data = g_newa(guint8, len); - cursor = data; + + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n"); + } + + bytes += qq_get8(&reply, data + bytes); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - read_packet_b(data, &cursor, len, &reply); - if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n"); - if (NULL == (segments = split_data(data, len, "\x1f", 2))) - return; - msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); - purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8); - g_free(msg_utf8); - } else { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n"); + if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n"); + if (NULL == (segments = split_data(data, len, "\x1f", 2))) { + return; } + msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); + purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8); + g_free(msg_utf8); } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n"); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n"); } } @@ -289,7 +290,8 @@ { qq_data *qd; gint len; - guint8 *data, *cursor, reply; + gint bytes = 0; + guint8 *data, reply; g_return_if_fail(buf != NULL && buf_len != 0); @@ -297,20 +299,20 @@ len = buf_len; data = g_newa(guint8, len); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - cursor = data; - read_packet_b(data, &cursor, len, &reply); - if (reply != QQ_REMOVE_BUDDY_REPLY_OK) { - /* there is no reason return from server */ - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n"); - } else { /* if reply */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n"); - /* TODO: We don't really need to notify the user about this, do we? */ - purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL); - } - } else { + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove buddy reply\n"); } + + bytes += qq_get8(&reply, data + bytes); + + if (reply != QQ_REMOVE_BUDDY_REPLY_OK) { + /* there is no reason return from server */ + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n"); + } else { /* if reply */ + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n"); + /* TODO: We don't really need to notify the user about this, do we? */ + purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL); + } } /* process the server reply for my request to remove myself from a buddy */ @@ -318,7 +320,8 @@ { qq_data *qd; gint len; - guint8 *data, *cursor, reply; + gint bytes = 0; + guint8 *data, reply; g_return_if_fail(buf != NULL && buf_len != 0); @@ -326,20 +329,20 @@ len = buf_len; data = g_newa(guint8, len); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - cursor = data; - read_packet_b(data, &cursor, len, &reply); - if (reply != QQ_REMOVE_SELF_REPLY_OK) - /* there is no reason return from server */ - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n"); - else { /* if reply */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n"); - /* TODO: Does the user really need to be notified about this? */ - purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL); - } - } else { + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove self reply\n"); } + + bytes += qq_get8(&reply, data + bytes); + + if (reply != QQ_REMOVE_SELF_REPLY_OK) { + /* there is no reason return from server */ + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n"); + } else { /* if reply */ + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n"); + /* TODO: Does the user really need to be notified about this? */ + purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL); + } } void qq_process_add_buddy_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc) @@ -403,14 +406,14 @@ g->uid = for_uid; msg = g_strdup_printf(_("User %d needs authentication"), for_uid); purple_request_input(gc, NULL, msg, - _("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */ - _("Would you be my friend?"), - TRUE, FALSE, NULL, _("Send"), - G_CALLBACK - (_qq_send_packet_add_buddy_auth_with_gc_and_uid), - _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid), - purple_connection_get_account(gc), nombre, NULL, - g); + _("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */ + _("Would you be my friend?"), + TRUE, FALSE, NULL, _("Send"), + G_CALLBACK + (_qq_send_packet_add_buddy_auth_with_gc_and_uid), + _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid), + purple_connection_get_account(gc), nombre, NULL, + g); g_free(msg); g_free(nombre); } else { /* add OK */ @@ -457,7 +460,7 @@ g_return_val_if_fail(a != NULL && uid != 0, NULL); group_name = is_known ? - g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN); + g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN); g = qq_get_purple_group(group_name); @@ -512,8 +515,8 @@ if (b != NULL) purple_blist_remove_buddy(b); purple_notify_error(gc, NULL, - _("QQid Error"), - _("Invalid QQid")); + _("QQid Error"), + _("Invalid QQid")); } }
--- a/libpurple/protocols/qq/buddy_status.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/buddy_status.c Wed Jul 09 00:32:18 2008 +0000 @@ -3,7 +3,7 @@ * * purple * - * Purple is the legal property of its developers, whose names are too numerous + * Purple is the legal property ofr its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * @@ -33,10 +33,9 @@ #include "header_info.h" #include "keep_alive.h" #include "packet_parse.h" -#include "send_core.h" #include "utils.h" -#include "qq_proxy.h" +#include "qq_network.h" #define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001 #define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 0x30 /* ASCII value of "0" */ @@ -57,7 +56,7 @@ g_string_append_printf(dump, "013-014: %04x (client_version)\n", s->client_version); /* g_string_append_printf(dump, "015-030: %s (unknown key)\n", s->unknown_key); */ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Buddy status entry, %s", dump->str); - _qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH); + qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH); g_string_free(dump, TRUE); } @@ -66,35 +65,33 @@ * using different accounts to get info. */ /* parse the data into qq_buddy_status */ -gint qq_buddy_status_read(guint8 *data, guint8 **cursor, gint len, qq_buddy_status *s) +gint qq_buddy_status_read(qq_buddy_status *s, guint8 *data) { - gint bytes; + gint bytes = 0; - g_return_val_if_fail(data != NULL && *cursor != NULL && s != NULL, -1); - - bytes = 0; + g_return_val_if_fail(data != NULL && s != NULL, -1); /* 000-003: uid */ - bytes += read_packet_dw(data, cursor, len, &s->uid); + bytes += qq_get32(&s->uid, data + bytes); /* 004-004: 0x01 */ - bytes += read_packet_b(data, cursor, len, &s->unknown1); + bytes += qq_get8(&s->unknown1, data + bytes); /* this is no longer the IP, it seems QQ (as of 2006) no longer sends * the buddy's IP in this packet. all 0s */ /* 005-008: ip */ s->ip = g_new0(guint8, 4); - bytes += read_packet_data(data, cursor, len, s->ip, 4); + bytes += qq_getdata(s->ip, 4, data + bytes); /* port info is no longer here either */ /* 009-010: port */ - bytes += read_packet_w(data, cursor, len, &s->port); + bytes += qq_get16(&s->port, data + bytes); /* 011-011: 0x00 */ - bytes += read_packet_b(data, cursor, len, &s->unknown2); + bytes += qq_get8(&s->unknown2, data + bytes); /* 012-012: status */ - bytes += read_packet_b(data, cursor, len, &s->status); + bytes += qq_get8(&s->status, data + bytes); /* 013-014: client_version */ - bytes += read_packet_w(data, cursor, len, &s->client_version); + bytes += qq_get16(&s->client_version, data + bytes); /* 015-030: unknown key */ s->unknown_key = g_new0(guint8, QQ_KEY_LENGTH); - bytes += read_packet_data(data, cursor, len, s->unknown_key, QQ_KEY_LENGTH); + bytes += qq_getdata(s->unknown_key, QQ_KEY_LENGTH, data + bytes); if (s->uid == 0 || bytes != 31) return -1; @@ -106,17 +103,17 @@ gboolean is_online(guint8 status) { switch(status) { - case QQ_BUDDY_ONLINE_NORMAL: - case QQ_BUDDY_ONLINE_AWAY: - case QQ_BUDDY_ONLINE_INVISIBLE: - return TRUE; - case QQ_BUDDY_ONLINE_OFFLINE: - return FALSE; + case QQ_BUDDY_ONLINE_NORMAL: + case QQ_BUDDY_ONLINE_AWAY: + case QQ_BUDDY_ONLINE_INVISIBLE: + return TRUE; + case QQ_BUDDY_ONLINE_OFFLINE: + return FALSE; } return FALSE; } - /* Help calculate the correct icon index to tell the server. */ +/* Help calculate the correct icon index to tell the server. */ gint get_icon_offset(PurpleConnection *gc) { PurpleAccount *account; @@ -131,7 +128,7 @@ || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY) || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) { return 1; - } else { + } else { return 0; } } @@ -140,7 +137,9 @@ void qq_send_packet_change_status(PurpleConnection *gc) { qq_data *qd; - guint8 *raw_data, *cursor, away_cmd; + guint8 raw_data[16] = {0}; + gint bytes = 0; + guint8 away_cmd; guint32 misc_status; gboolean fake_video; PurpleAccount *account; @@ -163,28 +162,24 @@ away_cmd = QQ_BUDDY_ONLINE_NORMAL; } - raw_data = g_new0(guint8, 5); - cursor = raw_data; misc_status = 0x00000000; - fake_video = purple_prefs_get_bool("/plugins/prpl/qq/show_fake_video"); if (fake_video) misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO; - create_packet_b(raw_data, &cursor, away_cmd); - create_packet_dw(raw_data, &cursor, misc_status); + bytes = 0; + bytes += qq_put8(raw_data + bytes, away_cmd); + bytes += qq_put32(raw_data + bytes, misc_status); - qq_send_cmd(gc, QQ_CMD_CHANGE_ONLINE_STATUS, TRUE, 0, TRUE, raw_data, 5); - - g_free(raw_data); + qq_send_cmd(qd, QQ_CMD_CHANGE_ONLINE_STATUS, raw_data, bytes); } /* parse the reply packet for change_status */ void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) { qq_data *qd; - gint len; - guint8 *data, *cursor, reply; + gint len, bytes; + guint8 *data, reply; PurpleBuddy *b; qq_buddy *q_bud; gchar *name; @@ -195,21 +190,22 @@ len = buf_len; data = g_newa(guint8, len); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - cursor = data; - read_packet_b(data, &cursor, len, &reply); - if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail\n"); - } else { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n"); - name = uid_to_purple_name(qd->uid); - b = purple_find_buddy(gc->account, name); - g_free(name); - q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; - qq_update_buddy_contact(gc, q_bud); - } + if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n"); + return; + } + + bytes = 0; + bytes = qq_get8(&reply, data + bytes); + if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail\n"); } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n"); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n"); + name = uid_to_purple_name(qd->uid); + b = purple_find_buddy(gc->account, name); + g_free(name); + q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; + qq_update_buddy_contact(gc, q_bud); } } @@ -219,7 +215,7 @@ qq_data *qd; gint len, bytes; guint32 my_uid; - guint8 *data, *cursor; + guint8 *data; PurpleBuddy *b; qq_buddy *q_bud; qq_buddy_status *s; @@ -230,51 +226,53 @@ qd = (qq_data *) gc->proto_data; len = buf_len; data = g_newa(guint8, len); - cursor = data; - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - s = g_new0(qq_buddy_status, 1); - bytes = 0; - /* 000-030: qq_buddy_status */ - bytes += qq_buddy_status_read(data, &cursor, len, s); - /* 031-034: my uid */ - /* This has a value of 0 when we've changed our status to - * QQ_BUDDY_ONLINE_INVISIBLE */ - bytes += read_packet_dw(data, &cursor, len, &my_uid); - - if (bytes != 35) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "bytes(%d) != 35\n", bytes); - g_free(s->ip); - g_free(s->unknown_key); - g_free(s); - return; - } + if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n"); + return; + } - name = uid_to_purple_name(s->uid); - b = purple_find_buddy(gc->account, name); - g_free(name); - q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; - if (q_bud) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid); - if(0 != *((guint32 *)s->ip)) { - g_memmove(q_bud->ip, s->ip, 4); - q_bud->port = s->port; - } - q_bud->status = s->status; - if(0 != s->client_version) - q_bud->client_version = s->client_version; - if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) - qq_send_packet_get_level(gc, q_bud->uid); - qq_update_buddy_contact(gc, q_bud); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "got information of unknown buddy %d\n", s->uid); - } + s = g_new0(qq_buddy_status, 1); + bytes = 0; + /* 000-030: qq_buddy_status */ + bytes += qq_buddy_status_read(s, data + bytes); + /* 031-034: my uid */ + /* This has a value of 0 when we've changed our status to + * QQ_BUDDY_ONLINE_INVISIBLE */ + bytes += qq_get32(&my_uid, data + bytes); + if (bytes != 35) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "bytes(%d) != 35\n", bytes); g_free(s->ip); g_free(s->unknown_key); g_free(s); + return; + } + + name = uid_to_purple_name(s->uid); + b = purple_find_buddy(gc->account, name); + g_free(name); + q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; + if (q_bud) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid); + if(0 != *((guint32 *)s->ip)) { + g_memmove(q_bud->ip, s->ip, 4); + q_bud->port = s->port; + } + q_bud->status = s->status; + if(0 != s->client_version) { + q_bud->client_version = s->client_version; + } + if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) { + qq_send_packet_get_level(gc, q_bud->uid); + } + qq_update_buddy_contact(gc, q_bud); } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n"); + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "got information of unknown buddy %d\n", s->uid); } + + g_free(s->ip); + g_free(s->unknown_key); + g_free(s); }
--- a/libpurple/protocols/qq/buddy_status.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/buddy_status.h Wed Jul 09 00:32:18 2008 +0000 @@ -52,7 +52,7 @@ void qq_buddy_status_dump_unclear(qq_buddy_status *s); gboolean is_online(guint8 status); -gint qq_buddy_status_read(guint8 *data, guint8 **cursor, gint len, qq_buddy_status *s); +gint qq_buddy_status_read(qq_buddy_status *s, guint8 *data); gint get_icon_offset(PurpleConnection *gc); void qq_send_packet_change_status(PurpleConnection *gc);
--- a/libpurple/protocols/qq/char_conv.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/char_conv.c Wed Jul 09 00:32:18 2008 +0000 @@ -39,9 +39,6 @@ #define QQ_NULL_MSG "(NULL)" /* return this if conversion fails */ #define QQ_NULL_SMILEY "(SM)" /* return this if smiley conversion fails */ -/* a debug function */ -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len); - const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = { 0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x73, @@ -111,16 +108,19 @@ ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error); - if (error == NULL) + if (error == NULL) { return ret; /* conversion is OK */ - else { /* conversion error */ - gchar *failed = hex_dump_to_str((guint8 *) str, (len == -1) ? strlen(str) : len); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message); - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Dump failed text\n%s", failed); - g_free(failed); - g_error_free(error); - return g_strdup(QQ_NULL_MSG); } + + /* conversion error */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message); + + qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", + (guint8 *) str, (len == -1) ? strlen(str) : len, + "Dump failed text"); + + g_error_free(error); + return g_strdup(QQ_NULL_MSG); } /* take the input as a pascal string and return a converted c-string in UTF-8 @@ -142,22 +142,23 @@ gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg) { GString *encoded; - guint8 font_attr, font_size, color[3], bar, *cursor; + 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; - cursor = data; - _qq_show_packet("QQ_MESG recv for font style", data, len); + /* checked qq_show_packet OK */ + qq_show_packet("QQ_MESG recv for font style", data, len); - read_packet_b(data, &cursor, len, &font_attr); - read_packet_data(data, &cursor, len, color, 3); /* red,green,blue */ + 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]); - read_packet_b(data, &cursor, len, &bar); /* skip, not sure of its use */ - read_packet_w(data, &cursor, len, &charset_code); + bytes += qq_get8(&bar, data + bytes); /* skip, not sure of its use */ + bytes += qq_get16(&charset_code, data + bytes); - tmp = g_strndup((gchar *) cursor, data + len - cursor); + tmp = g_strndup((gchar *)(data + bytes), len - bytes); font_name = qq_to_utf8(tmp, QQ_CHARSET_DEFAULT); g_free(tmp); @@ -177,11 +178,11 @@ /* 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); + "<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">", + color_code, font_name, font_size / 3); purple_debug(PURPLE_DEBUG_INFO, "QQ_MESG", - "recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n", - color_code, font_name, font_size / 3); + "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) {
--- a/libpurple/protocols/qq/crypt.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/crypt.c Wed Jul 09 00:32:18 2008 +0000 @@ -296,3 +296,20 @@ } return 1; } + +/* return 1 is succeed, otherwise return 0 +gint qq_crypt(gint flag, + const guint8 *const instr, gint instrlen, + const guint8 *const key, + guint8 *outstr, gint *outstrlen_ptr) +{ + if (flag == DECRYPT) + return qq_decrypt(instr, instrlen, key, outstr, outstrlen_ptr); + else if (flag == ENCRYPT) + qq_encrypt(instr, instrlen, key, outstr, outstrlen_ptr); + else + return 0; + + return 1; +} +*/
--- a/libpurple/protocols/qq/crypt.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/crypt.h Wed Jul 09 00:32:18 2008 +0000 @@ -35,4 +35,13 @@ const guint8 *const key, guint8 *outstr, gint *outstrlen_ptr); +/* +#define DECRYPT 0x00 +#define ENCRYPT 0x01 + +gint qq_crypt(gint flag, + const guint8 *const instr, gint instrlen, + const guint8 *const key, + guint8 *outstr, gint *outstrlen_ptr); +*/ #endif
--- a/libpurple/protocols/qq/file_trans.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/file_trans.c Wed Jul 09 00:32:18 2008 +0000 @@ -38,12 +38,11 @@ #include "im.h" #include "packet_parse.h" #include "proxy.h" -#include "send_core.h" +#include "qq_network.h" #include "send_file.h" #include "utils.h" struct _qq_file_header { - guint8 tag; guint16 client_ver; guint8 file_key; guint32 sender_uid; @@ -58,11 +57,11 @@ key = seed | (seed << 8) | (seed << 16) | (seed << 24); return key; } - + static guint32 _gen_file_key(void) { guint8 seed; - + seed = random(); return _get_file_key(seed); } @@ -126,16 +125,17 @@ purple_cipher_context_destroy(context); } -static void _qq_get_file_header(guint8 *buf, guint8 **cursor, gint buflen, qq_file_header *fh) +static gint _qq_get_file_header(qq_file_header *fh, guint8 *buf) { - read_packet_b(buf, cursor, buflen, &(fh->tag)); - read_packet_w(buf, cursor, buflen, &(fh->client_ver)); - read_packet_b(buf, cursor, buflen, &fh->file_key); - read_packet_dw(buf, cursor, buflen, &(fh->sender_uid)); - read_packet_dw(buf, cursor, buflen, &(fh->receiver_uid)); + gint bytes = 0; + bytes += qq_get16(&(fh->client_ver), buf + bytes); + bytes += qq_get8(&fh->file_key, buf + bytes); + bytes += qq_get32(&(fh->sender_uid), buf + bytes); + bytes += qq_get32(&(fh->receiver_uid), buf + bytes); fh->sender_uid = _decrypt_qq_uid(fh->sender_uid, _get_file_key(fh->file_key)); fh->receiver_uid = _decrypt_qq_uid(fh->receiver_uid, _get_file_key(fh->file_key)); + return bytes; } static const gchar *qq_get_file_cmd_desc(gint type) @@ -190,7 +190,7 @@ fd = open(purple_xfer_get_local_filename(xfer), O_RDWR|O_CREAT, 0644); info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0); } - + if (info->buffer == NULL) { return - 1; } @@ -258,8 +258,8 @@ static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16 packet_type, guint32 to_uid) { - gint bytes; - guint8 *cursor, *buf; + guint8 *raw_data; + gint bytes = 0; guint32 file_key; qq_data *qd; ft_info *info; @@ -267,21 +267,19 @@ qd = (qq_data *) gc->proto_data; g_return_val_if_fail(qd->session_key != NULL, -1); info = (ft_info *) qd->xfer->data; - bytes = 0; - buf = g_newa(guint8, MAX_PACKET_SIZE); - cursor = buf; + raw_data = g_newa(guint8, MAX_PACKET_SIZE); file_key = _gen_file_key(); - bytes += create_packet_b(buf, &cursor, packet_type); - bytes += create_packet_w(buf, &cursor, QQ_CLIENT); - bytes += create_packet_b(buf, &cursor, file_key & 0xff); - bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(qd->uid, file_key)); - bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(to_uid, file_key)); - bytes += create_packet_data(buf, &cursor, data, len); + bytes += qq_put8(raw_data + bytes, packet_type); + bytes += qq_put16(raw_data + bytes, QQ_CLIENT); + bytes += qq_put8(raw_data + bytes, file_key & 0xff); + bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(qd->uid, file_key)); + bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(to_uid, file_key)); + bytes += qq_putdata(raw_data + bytes, data, len); if (bytes == len + 12) { - _qq_xfer_write(buf, bytes, qd->xfer); + _qq_xfer_write(raw_data, bytes, qd->xfer); } else purple_debug(PURPLE_DEBUG_INFO, "QQ", "send_file: want %d but got %d\n", len + 12, bytes); return bytes; @@ -292,57 +290,56 @@ { qq_data *qd; gint bytes, bytes_expected, encrypted_len; - guint8 *raw_data, *cursor, *encrypted_data; + guint8 *raw_data, *encrypted_data; time_t now; ft_info *info; - + qd = (qq_data *) gc->proto_data; info = (ft_info *) qd->xfer->data; - raw_data = g_new0 (guint8, 61); - cursor = raw_data; - + raw_data = g_newa (guint8, 61); bytes = 0; + now = time(NULL); - bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16); - bytes += create_packet_w(raw_data, &cursor, packet_type); + bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); + bytes += qq_put16(raw_data + bytes, packet_type); switch (packet_type) { case QQ_FILE_CMD_SENDER_SAY_HELLO: case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: case QQ_FILE_CMD_NOTIFY_IP_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO: - bytes += create_packet_w(raw_data, &cursor, info->send_seq); + bytes += qq_put16(raw_data + bytes, info->send_seq); break; default: - bytes += create_packet_w(raw_data, &cursor, ++qd->send_seq); + bytes += qq_put16(raw_data + bytes, ++qd->send_seq); } - bytes += create_packet_dw(raw_data, &cursor, (guint32) now); - bytes += create_packet_b(raw_data, &cursor, 0x00); - bytes += create_packet_b(raw_data, &cursor, qd->my_icon); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); - bytes += create_packet_w(raw_data, &cursor, 0x0000); - bytes += create_packet_b(raw_data, &cursor, 0x00); + bytes += qq_put32(raw_data + bytes, (guint32) now); + bytes += qq_put8(raw_data + bytes, 0x00); + bytes += qq_put8(raw_data + bytes, qd->my_icon); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put16(raw_data + bytes, 0x0000); + bytes += qq_put8(raw_data + bytes, 0x00); /* 0x65: send a file, 0x6b: send a custom face */ - bytes += create_packet_b(raw_data, &cursor, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */ + bytes += qq_put8(raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */ switch (packet_type) { case QQ_FILE_CMD_SENDER_SAY_HELLO: case QQ_FILE_CMD_RECEIVER_SAY_HELLO: case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: - bytes += create_packet_b(raw_data, &cursor, 0x00); - bytes += create_packet_b(raw_data, &cursor, hellobyte); + bytes += qq_put8(raw_data + bytes, 0x00); + bytes += qq_put8(raw_data + bytes, hellobyte); bytes_expected = 48; break; case QQ_FILE_CMD_PING: case QQ_FILE_CMD_PONG: case QQ_FILE_CMD_NOTIFY_IP_ACK: - bytes += qq_fill_conn_info(raw_data, &cursor, info); + bytes += qq_fill_conn_info(raw_data, info); bytes_expected = 61; break; default: @@ -350,51 +347,53 @@ packet_type); bytes_expected = 0; } - - if (bytes == bytes_expected) { - gchar *hex_dump = hex_dump_to_str(raw_data, bytes); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "sending packet[%s]: \n%s", qq_get_file_cmd_desc(packet_type), hex_dump); - g_free(hex_dump); - encrypted_len = bytes + 16; - encrypted_data = g_newa(guint8, encrypted_len); - qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len); - /*debug: try to decrypt it */ - /* - if (QQ_DEBUG) { - guint8 *buf; - int buflen; - hex_dump = hex_dump_to_str(encrypted_data, encrypted_len); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump); - g_free(hex_dump); - buf = g_newa(guint8, MAX_PACKET_SIZE); - buflen = encrypted_len; - if (qq_decrypt(encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n"); - if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0) - purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n"); - hex_dump = hex_dump_to_str(buf, buflen); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump); - g_free(hex_dump); - } else { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n"); - } - } - */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type)); - _qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid); - } - else + if (bytes != bytes_expected) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d", bytes_expected, bytes); + return; + } + + qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", + raw_data, bytes, + "sending packet[%s]:", qq_get_file_cmd_desc(packet_type)); + + encrypted_len = bytes + 16; + encrypted_data = g_newa(guint8, encrypted_len); + qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len); + /*debug: try to decrypt it */ + /* + if (QQ_DEBUG) { + guint8 *buf; + int buflen; + hex_dump = hex_dump_to_str(encrypted_data, encrypted_len); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump); + g_free(hex_dump); + buf = g_newa(guint8, MAX_PACKET_SIZE); + buflen = encrypted_len; + if (qq_crypt(DECRYPT, encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n"); + if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0) + purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n"); + hex_dump = hex_dump_to_str(buf, buflen); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump); + g_free(hex_dump); + } else { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n"); + } + } + */ + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type)); + _qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid); } /* send a file to udp channel with QQ_FILE_DATA_PACKET_TAG */ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type, guint32 fragment_index, guint16 seq, guint8 *data, gint len) { + guint8 *raw_data, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH]; gint bytes; - guint8 *raw_data, *cursor, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH]; guint32 fragment_size = 1000; gchar *filename; gint filename_len, filesize; @@ -408,17 +407,16 @@ filesize = purple_xfer_get_size(qd->xfer); raw_data = g_newa(guint8, MAX_PACKET_SIZE); - cursor = raw_data; bytes = 0; - bytes += create_packet_b(raw_data, &cursor, 0x00); - bytes += create_packet_w(raw_data, &cursor, packet_type); + bytes += qq_put8(raw_data + bytes, 0x00); + bytes += qq_put16(raw_data + bytes, packet_type); switch (packet_type) { case QQ_FILE_BASIC_INFO: case QQ_FILE_DATA_INFO: case QQ_FILE_EOF: - bytes += create_packet_w(raw_data, &cursor, 0x0000); - bytes += create_packet_b(raw_data, &cursor, 0x00); + bytes += qq_put16(raw_data + bytes, 0x0000); + bytes += qq_put8(raw_data + bytes, 0x00); break; case QQ_FILE_CMD_FILE_OP: switch(sub_type) @@ -437,44 +435,44 @@ "start transfering data, %d fragments with %d length each\n", info->fragment_num, info->fragment_len); /* Unknown */ - bytes += create_packet_w(raw_data, &cursor, 0x0000); + bytes += qq_put16(raw_data + bytes, 0x0000); /* Sub-operation type */ - bytes += create_packet_b(raw_data, &cursor, sub_type); + bytes += qq_put8(raw_data + bytes, sub_type); /* Length of file */ - bytes += create_packet_dw(raw_data, &cursor, filesize); + bytes += qq_put32(raw_data + bytes, filesize); /* Number of fragments */ - bytes += create_packet_dw(raw_data, &cursor, info->fragment_num); + bytes += qq_put32(raw_data + bytes, info->fragment_num); /* Length of a single fragment */ - bytes += create_packet_dw(raw_data, &cursor, info->fragment_len); - bytes += create_packet_data(raw_data, &cursor, file_md5, 16); - bytes += create_packet_data(raw_data, &cursor, filename_md5, 16); + bytes += qq_put32(raw_data + bytes, info->fragment_len); + bytes += qq_putdata(raw_data + bytes, file_md5, 16); + bytes += qq_putdata(raw_data + bytes, filename_md5, 16); /* Length of filename */ - bytes += create_packet_w(raw_data, &cursor, filename_len); + bytes += qq_put16(raw_data + bytes, filename_len); /* 8 unknown bytes */ - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); /* filename */ - bytes += create_packet_data(raw_data, &cursor, (guint8 *) filename, + bytes += qq_putdata(raw_data + bytes, (guint8 *) filename, filename_len); break; case QQ_FILE_DATA_INFO: purple_debug(PURPLE_DEBUG_INFO, "QQ", "sending %dth fragment with length %d, offset %d\n", fragment_index, len, (fragment_index-1)*fragment_size); - /* bytes += create_packet_w(raw_data, &cursor, ++(qd->send_seq)); */ - bytes += create_packet_w(raw_data, &cursor, info->send_seq); - bytes += create_packet_b(raw_data, &cursor, sub_type); - /* bytes += create_packet_dw(raw_data, &cursor, fragment_index); */ - bytes += create_packet_dw(raw_data, &cursor, fragment_index - 1); - bytes += create_packet_dw(raw_data, &cursor, (fragment_index - 1) * fragment_size); - bytes += create_packet_w(raw_data, &cursor, len); - bytes += create_packet_data(raw_data, &cursor, data, len); + /* bytes += qq_put16(raw_data + bytes, ++(qd->send_seq)); */ + bytes += qq_put16(raw_data + bytes, info->send_seq); + bytes += qq_put8(raw_data + bytes, sub_type); + /* bytes += qq_put32(raw_data + bytes, fragment_index); */ + bytes += qq_put32(raw_data + bytes, fragment_index - 1); + bytes += qq_put32(raw_data + bytes, (fragment_index - 1) * fragment_size); + bytes += qq_put16(raw_data + bytes, len); + bytes += qq_putdata(raw_data + bytes, data, len); break; case QQ_FILE_EOF: purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of sending data\n"); - /* bytes += create_packet_w(raw_data, &cursor, info->fragment_num + 1); */ - bytes += create_packet_w(raw_data, &cursor, info->fragment_num); - bytes += create_packet_b(raw_data, &cursor, sub_type); + /* bytes += qq_put16(raw_data + bytes, info->fragment_num + 1); */ + bytes += qq_put16(raw_data + bytes, info->fragment_num); + bytes += qq_put8(raw_data + bytes, sub_type); /* purple_xfer_set_completed(qd->xfer, TRUE); */ } break; @@ -482,18 +480,18 @@ switch (sub_type) { case QQ_FILE_BASIC_INFO: - bytes += create_packet_w(raw_data, &cursor, 0x0000); - bytes += create_packet_b(raw_data, &cursor, sub_type); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); + bytes += qq_put16(raw_data + bytes, 0x0000); + bytes += qq_put8(raw_data + bytes, sub_type); + bytes += qq_put32(raw_data + bytes, 0x00000000); break; case QQ_FILE_DATA_INFO: - bytes += create_packet_w(raw_data, &cursor, seq); - bytes += create_packet_b(raw_data, &cursor, sub_type); - bytes += create_packet_dw(raw_data, &cursor, fragment_index); + bytes += qq_put16(raw_data + bytes, seq); + bytes += qq_put8(raw_data + bytes, sub_type); + bytes += qq_put32(raw_data + bytes, fragment_index); break; case QQ_FILE_EOF: - bytes += create_packet_w(raw_data, &cursor, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2); - bytes += create_packet_b(raw_data, &cursor, sub_type); + bytes += qq_put16(raw_data + bytes, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2); + bytes += qq_put8(raw_data + bytes, sub_type); break; } } @@ -520,9 +518,11 @@ */ -static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, guint8 *cursor, - gint len, qq_file_header *fh) +static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint len) { + gint bytes ; + gint decryped_bytes; + qq_file_header fh; guint8 *decrypted_data; gint decrypted_len; qq_data *qd = (qq_data *) gc->proto_data; @@ -531,60 +531,65 @@ guint8 hellobyte; ft_info *info = (ft_info *) qd->xfer->data; + bytes = 0; + bytes += _qq_get_file_header(&fh, data + bytes); + decrypted_data = g_newa(guint8, len); decrypted_len = len; - if (qq_decrypt(cursor, len - (cursor - data), qd->session_md5, decrypted_data, &decrypted_len)) { - gchar *hex_dump; - cursor = decrypted_data + 16; /* skip md5 section */ - read_packet_w(decrypted_data, &cursor, decrypted_len, &packet_type); - read_packet_w(decrypted_data, &cursor, decrypted_len, &seq); - cursor += 4+1+1+19+1; - purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type)); - hex_dump = hex_dump_to_str(decrypted_data, decrypted_len); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted control packet received: \n%s", hex_dump); - g_free(hex_dump); - switch (packet_type) { - case QQ_FILE_CMD_NOTIFY_IP_ACK: - cursor = decrypted_data; - qq_get_conn_info(decrypted_data, &cursor, decrypted_len, info); -/* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */ - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0); - break; - case QQ_FILE_CMD_SENDER_SAY_HELLO: - /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */ - cursor += 47; - read_packet_b(decrypted_data, &cursor, - decrypted_len, &hellobyte); + if ( !qq_decrypt(data, len, qd->session_md5, decrypted_data, &decrypted_len) ) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rcv file ctrl packet\n"); + return; + } + + /* only for debug info */ + decryped_bytes = 16; /* skip md5 section */ + decryped_bytes += qq_get16(&packet_type, decrypted_data + decryped_bytes); + decryped_bytes += qq_get16(&seq, decrypted_data + decryped_bytes); + decryped_bytes += 4+1+1+19+1; /* skip something */ + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type)); + qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", + decrypted_data, decrypted_len, + "decrypted control packet received:"); - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh->sender_uid, hellobyte); - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh->sender_uid, 0); - break; - case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: - /* I'm sender, do nothing */ - break; - case QQ_FILE_CMD_RECEIVER_SAY_HELLO: - /* I'm sender, ack the hello packet and send the first data */ - cursor += 47; - read_packet_b(decrypted_data, &cursor, - decrypted_len, &hellobyte); - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh->sender_uid, hellobyte); - _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0); - break; - case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: - /* I'm receiver, do nothing */ - break; - case QQ_FILE_CMD_PING: - /* I'm receiver, ack the PING */ - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh->sender_uid, 0); - break; - case QQ_FILE_CMD_PONG: - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0); - break; - default: - purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type); - } - } + switch (packet_type) { + case QQ_FILE_CMD_NOTIFY_IP_ACK: + decryped_bytes = 0; + qq_get_conn_info(info, decrypted_data + decryped_bytes); + /* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */ + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0); + break; + case QQ_FILE_CMD_SENDER_SAY_HELLO: + /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */ + decryped_bytes += 47; + decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes); + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh.sender_uid, hellobyte); + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh.sender_uid, 0); + break; + case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: + /* I'm sender, do nothing */ + break; + case QQ_FILE_CMD_RECEIVER_SAY_HELLO: + /* I'm sender, ack the hello packet and send the first data */ + decryped_bytes += 47; + decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes); + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh.sender_uid, hellobyte); + _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0); + break; + case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: + /* I'm receiver, do nothing */ + break; + case QQ_FILE_CMD_PING: + /* I'm receiver, ack the PING */ + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh.sender_uid, 0); + break; + case QQ_FILE_CMD_PONG: + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0); + break; + default: + purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type); + } } static void _qq_recv_file_progess(PurpleConnection *gc, guint8 *buffer, guint16 len, guint32 index, guint32 offset) @@ -609,15 +614,15 @@ purple_debug(PURPLE_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", index+1); return; } - + info->window |= mask; _qq_xfer_write_file(buffer, index, len, xfer); - + xfer->bytes_sent += len; xfer->bytes_remaining -= len; purple_xfer_update_progress(xfer); - + mask = 0x1 << (info->max_fragment_index % sizeof(info->window)); while (info->window & mask) { @@ -639,7 +644,7 @@ guint8 *buffer; guint i; gint readbytes; - + if (purple_xfer_get_bytes_remaining(xfer) <= 0) return; if (info->window == 0 && info->max_fragment_index == 0) { @@ -655,7 +660,7 @@ readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + i, info->fragment_len, xfer); if (readbytes > 0) _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO, - info->max_fragment_index + i + 1, 0, buffer, readbytes); + info->max_fragment_index + i + 1, 0, buffer, readbytes); } if (mask & 0x8000) mask = 0x0001; else mask = mask << 1; @@ -706,8 +711,8 @@ info->fragment_len, xfer); if (readbytes > 0) _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO, - info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes); - + info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes); + info->max_fragment_index ++; if (mask & 0x8000) mask = 0x0001; else mask = mask << 1; @@ -718,9 +723,10 @@ fragment_index, info->window, info->max_fragment_index); } -static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, guint8 *cursor, - gint len, guint32 to_uid) +static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, gint len) { + gint bytes ; + qq_file_header fh; guint16 packet_type; guint16 packet_seq; guint8 sub_type; @@ -729,24 +735,27 @@ guint32 fragment_offset; qq_data *qd = (qq_data *) gc->proto_data; ft_info *info = (ft_info *) qd->xfer->data; - - cursor += 1; /* skip an unknown byte */ - read_packet_w(data, &cursor, len, &packet_type); + + bytes = 0; + bytes += _qq_get_file_header(&fh, data + bytes); + + bytes += 1; /* skip an unknown byte */ + bytes += qq_get16(&packet_type, data + bytes); switch(packet_type) { case QQ_FILE_CMD_FILE_OP: - read_packet_w(data, &cursor, len, &packet_seq); - read_packet_b(data, &cursor, len, &sub_type); + bytes += qq_get16(&packet_seq, data + bytes); + bytes += qq_get8(&sub_type, data + bytes); switch (sub_type) { case QQ_FILE_BASIC_INFO: - cursor += 4; /* file length, we have already known it from xfer */ - read_packet_dw(data, &cursor, len, &info->fragment_num); - read_packet_dw(data, &cursor, len, &info->fragment_len); + bytes += 4; /* file length, we have already known it from xfer */ + bytes += qq_get32(&info->fragment_num, data + bytes); + bytes += qq_get32(&info->fragment_len, data + bytes); - /* FIXME: We must check the md5 here, if md5 doesn't match - * we will ignore the packet or send sth as error number - */ + /* FIXME: We must check the md5 here, + * if md5 doesn't match we will ignore + * the packet or send sth as error number */ info->max_fragment_index = 0; info->window = 0; @@ -757,27 +766,27 @@ 0, 0, NULL, 0); break; case QQ_FILE_DATA_INFO: - read_packet_dw(data, &cursor, len, &fragment_index); - read_packet_dw(data, &cursor, len, &fragment_offset); - read_packet_w(data, &cursor, len, &fragment_len); + bytes += qq_get32(&fragment_index, data + bytes); + bytes += qq_get32(&fragment_offset, data + bytes); + bytes += qq_get16(&fragment_len, data + bytes); purple_debug(PURPLE_DEBUG_INFO, "QQ", "received %dth fragment with length %d, offset %d\n", fragment_index, fragment_len, fragment_offset); - + _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type, fragment_index, packet_seq, NULL, 0); - _qq_recv_file_progess(gc, cursor, fragment_len, fragment_index, fragment_offset); + _qq_recv_file_progess(gc, data + bytes, fragment_len, fragment_index, fragment_offset); break; case QQ_FILE_EOF: purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of receiving\n"); _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type, - 0, 0, NULL, 0); + 0, 0, NULL, 0); break; } break; case QQ_FILE_CMD_FILE_OP_ACK: - read_packet_w(data, &cursor, len, &packet_seq); - read_packet_b(data, &cursor, len, &sub_type); + bytes += qq_get16(&packet_seq, data + bytes); + bytes += qq_get8(&sub_type, data + bytes); switch (sub_type) { case QQ_FILE_BASIC_INFO: @@ -787,16 +796,16 @@ _qq_send_file_progess(gc); break; case QQ_FILE_DATA_INFO: - read_packet_dw(data, &cursor, len, &fragment_index); + bytes += qq_get32(&fragment_index, data + bytes); _qq_update_send_progess(gc, fragment_index); if (purple_xfer_is_completed(qd->xfer)) _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF, 0, 0, NULL, 0); - /* else + /* else _qq_send_file_progess(gc); */ break; case QQ_FILE_EOF: /* FIXME: OK, we can end the connection successfully */ - + _qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0); purple_xfer_set_completed(qd->xfer, TRUE); break; @@ -820,21 +829,21 @@ void qq_process_recv_file(PurpleConnection *gc, guint8 *data, gint len) { - guint8 *cursor; - qq_file_header fh; + gint bytes; + guint8 tag; qq_data *qd; qd = (qq_data *) gc->proto_data; - cursor = data; - _qq_get_file_header(data, &cursor, len, &fh); + bytes = 0; + bytes += qq_get8(&tag, data + bytes); - switch (fh.tag) { + switch (tag) { case QQ_FILE_CONTROL_PACKET_TAG: - _qq_process_recv_file_ctl_packet(gc, data, cursor, len, &fh); + _qq_process_recv_file_ctl_packet(gc, data + bytes, len - bytes); break; case QQ_FILE_DATA_PACKET_TAG: - _qq_process_recv_file_data(gc, data, cursor, len, fh.sender_uid); + _qq_process_recv_file_data(gc, data + bytes, len - bytes); break; default: purple_debug(PURPLE_DEBUG_INFO, "QQ", "unknown packet tag");
--- a/libpurple/protocols/qq/group_im.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_im.c Wed Jul 09 00:32:18 2008 +0000 @@ -58,7 +58,7 @@ void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg) { gint data_len, bytes; - guint8 *raw_data, *cursor, *send_im_tail; + guint8 *raw_data, *send_im_tail; guint16 msg_len; gchar *msg_filtered; @@ -67,19 +67,19 @@ msg_filtered = purple_markup_strip_html(msg); purple_debug_info("QQ_MESG", "filterd qq qun mesg: %s\n", msg_filtered); msg_len = strlen(msg_filtered); + data_len = 7 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN; raw_data = g_newa(guint8, data_len); - cursor = raw_data; bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEND_MSG); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - bytes += create_packet_w(raw_data, &cursor, msg_len + QQ_SEND_IM_AFTER_MSG_LEN); - bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len); + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEND_MSG); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); + 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 += create_packet_data(raw_data, &cursor, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN); + 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); @@ -87,11 +87,11 @@ qq_send_group_cmd(gc, group, raw_data, data_len); else purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes); + "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes); } /* this is the ACK */ -void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc) { /* return should be the internal group id * but we have nothing to do with it */ @@ -99,29 +99,26 @@ } /* receive an application to join the group */ -void qq_process_recv_group_im_apply_join - (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc) +void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc) { guint32 external_group_id, user_uid; guint8 group_type; gchar *reason_utf8, *msg, *reason; group_member_opt *g; gchar *nombre; + gint bytes = 0; g_return_if_fail(internal_group_id > 0 && data != NULL && len > 0); - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg apply_join is empty\n"); - return; - } + /* FIXME: check length here */ - read_packet_dw(data, cursor, len, &external_group_id); - read_packet_b(data, cursor, len, &group_type); - read_packet_dw(data, cursor, len, &user_uid); + bytes += qq_get32(&external_group_id, data + bytes); + bytes += qq_get8(&group_type, data + bytes); + bytes += qq_get32(&user_uid, data + bytes); g_return_if_fail(external_group_id > 0 && user_uid > 0); - convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); + bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT); msg = g_strdup_printf(_("User %d requested to join group %d"), user_uid, external_group_id); reason = g_strdup_printf(_("Reason: %s"), reason_utf8); @@ -134,17 +131,17 @@ nombre = uid_to_purple_name(user_uid); purple_request_action(gc, _("QQ Qun Operation"), - msg, reason, - PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(gc), nombre, NULL, - g, 3, - _("Approve"), - G_CALLBACK - (qq_group_approve_application_with_struct), - _("Reject"), - G_CALLBACK - (qq_group_reject_application_with_struct), - _("Search"), G_CALLBACK(qq_group_search_application_with_struct)); + msg, reason, + PURPLE_DEFAULT_ACTION_NONE, + purple_connection_get_account(gc), nombre, NULL, + g, 3, + _("Approve"), + G_CALLBACK + (qq_group_approve_application_with_struct), + _("Reject"), + G_CALLBACK + (qq_group_reject_application_with_struct), + _("Search"), G_CALLBACK(qq_group_search_application_with_struct)); g_free(nombre); g_free(reason); @@ -153,31 +150,28 @@ } /* the request to join a group is rejected */ -void qq_process_recv_group_im_been_rejected - (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc) +void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc) { guint32 external_group_id, admin_uid; guint8 group_type; gchar *reason_utf8, *msg, *reason; qq_group *group; + gint bytes = 0; g_return_if_fail(data != NULL && len > 0); - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_rejected is empty\n"); - return; - } + /* FIXME: check length here */ - read_packet_dw(data, cursor, len, &external_group_id); - read_packet_b(data, cursor, len, &group_type); - read_packet_dw(data, cursor, len, &admin_uid); + bytes += qq_get32(&external_group_id, data + bytes); + bytes += qq_get8(&group_type, data + bytes); + bytes += qq_get32(&admin_uid, data + bytes); g_return_if_fail(external_group_id > 0 && admin_uid > 0); - convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); + bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT); msg = g_strdup_printf - (_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid); + (_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid); reason = g_strdup_printf(_("Reason: %s"), reason_utf8); purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason); @@ -194,31 +188,28 @@ } /* the request to join a group is approved */ -void qq_process_recv_group_im_been_approved - (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc) +void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc) { guint32 external_group_id, admin_uid; guint8 group_type; gchar *reason_utf8, *msg; qq_group *group; + gint bytes = 0; g_return_if_fail(data != NULL && len > 0); - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_approved is empty\n"); - return; - } + /* FIXME: check length here */ - read_packet_dw(data, cursor, len, &external_group_id); - read_packet_b(data, cursor, len, &group_type); - read_packet_dw(data, cursor, len, &admin_uid); + bytes += qq_get32(&external_group_id, data + bytes); + bytes += qq_get8(&group_type, data + bytes); + bytes += qq_get32(&admin_uid, data + bytes); g_return_if_fail(external_group_id > 0 && admin_uid > 0); /* it is also a "无" here, so do not display */ - convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); + bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT); msg = g_strdup_printf - (_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid); + (_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid); purple_notify_warning(gc, _("QQ Qun Operation"), msg, NULL); @@ -233,24 +224,21 @@ } /* process the packet when removed from a group */ -void qq_process_recv_group_im_been_removed - (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc) +void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc) { guint32 external_group_id, uid; guint8 group_type; gchar *msg; qq_group *group; + gint bytes = 0; g_return_if_fail(data != NULL && len > 0); - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_removed is empty\n"); - return; - } + /* FIXME: check length here */ - read_packet_dw(data, cursor, len, &external_group_id); - read_packet_b(data, cursor, len, &group_type); - read_packet_dw(data, cursor, len, &uid); + bytes += qq_get32(&external_group_id, data + bytes); + bytes += qq_get8(&group_type, data + bytes); + bytes += qq_get32(&uid, data + bytes); g_return_if_fail(external_group_id > 0 && uid > 0); @@ -267,24 +255,21 @@ } /* process the packet when added to a group */ -void qq_process_recv_group_im_been_added - (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc) +void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc) { guint32 external_group_id, uid; guint8 group_type; qq_group *group; gchar *msg; + gint bytes = 0; g_return_if_fail(data != NULL && len > 0); - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_added is empty\n"); - return; - } + /* FIXME: check length here */ - read_packet_dw(data, cursor, len, &external_group_id); - read_packet_b(data, cursor, len, &group_type); - read_packet_dw(data, cursor, len, &uid); + bytes += qq_get32(&external_group_id, data + bytes); + bytes += qq_get8(&group_type, data + bytes); + bytes += qq_get32(&uid, data + bytes); g_return_if_fail(external_group_id > 0 && uid > 0); @@ -307,10 +292,9 @@ } /* recv an IM from a group chat */ -void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len, - guint32 internal_group_id, PurpleConnection *gc, guint16 im_type) +void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type) { - gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name, *hex_dump; + gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name; guint16 unknown; guint32 unknown4; PurpleConversation *conv; @@ -319,32 +303,32 @@ qq_group *group; qq_recv_group_im *im_group; gint skip_len; + gint bytes = 0; g_return_if_fail(data != NULL && data_len > 0); + + /* FIXME: check length here */ + qd = (qq_data *) gc->proto_data; - hex_dump = hex_dump_to_str(*cursor, data_len - (*cursor - data)); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "group im hex dump\n%s\n", hex_dump); - - if (*cursor >= (data + data_len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group im_group is empty\n"); - return; - } + qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", + data, data_len, + "group im hex dump"); im_group = g_newa(qq_recv_group_im, 1); - read_packet_dw(data, cursor, data_len, &(im_group->external_group_id)); - read_packet_b(data, cursor, data_len, &(im_group->group_type)); + bytes += qq_get32(&(im_group->external_group_id), data + bytes); + bytes += qq_get8(&(im_group->group_type), data + bytes); if(QQ_RECV_IM_TEMP_QUN_IM == im_type) { - read_packet_dw(data, cursor, data_len, &(internal_group_id)); + bytes += qq_get32(&(internal_group_id), data + bytes); } - read_packet_dw(data, cursor, data_len, &(im_group->member_uid)); - read_packet_w(data, cursor, data_len, &unknown); /* 0x0001? */ - read_packet_w(data, cursor, data_len, &(im_group->msg_seq)); - read_packet_time(data, cursor, data_len, &im_group->send_time); - read_packet_dw(data, cursor, data_len, &unknown4); /* versionID */ + bytes += qq_get32(&(im_group->member_uid), bytes + data); + bytes += qq_get16(&unknown, data + bytes); /* 0x0001? */ + bytes += qq_get16(&(im_group->msg_seq), data + bytes); + bytes += qq_getime(&im_group->send_time, data + bytes); + bytes += qq_get32(&unknown4, data + bytes); /* versionID */ /* * length includes font_attr * this msg_len includes msg and font_attr @@ -355,7 +339,7 @@ * 3. font_attr */ - read_packet_w(data, cursor, data_len, &(im_group->msg_len)); + bytes += qq_get16(&(im_group->msg_len), data + bytes); g_return_if_fail(im_group->msg_len > 0); /* @@ -371,14 +355,14 @@ skip_len = 10; else skip_len = 0; - *cursor += skip_len; + bytes += skip_len; - im_group->msg = g_strdup((gchar *) *cursor); - *cursor += strlen(im_group->msg) + 1; + im_group->msg = g_strdup((gchar *) data + bytes); + bytes += strlen(im_group->msg) + 1; /* there might not be any font_attr, check it */ im_group->font_attr_len = im_group->msg_len - strlen(im_group->msg) - 1 - skip_len; if (im_group->font_attr_len > 0) - im_group->font_attr = g_memdup(*cursor, im_group->font_attr_len); + im_group->font_attr = g_memdup(data + bytes, im_group->font_attr_len); else im_group->font_attr = NULL; @@ -386,7 +370,7 @@ msg_with_purple_smiley = qq_smiley_to_purple(im_group->msg); if (im_group->font_attr_len > 0) msg_utf8_encoded = qq_encode_to_purple(im_group->font_attr, - im_group->font_attr_len, msg_with_purple_smiley); + im_group->font_attr_len, msg_with_purple_smiley); else msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); @@ -406,11 +390,10 @@ else im_src_name = g_strdup(member->nickname); serv_got_chat_in(gc, - purple_conv_chat_get_id(PURPLE_CONV_CHAT - (conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time); + purple_conv_chat_get_id(PURPLE_CONV_CHAT + (conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time); g_free(im_src_name); } - g_free(hex_dump); g_free(msg_with_purple_smiley); g_free(msg_utf8_encoded); g_free(im_group->msg);
--- a/libpurple/protocols/qq/group_im.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_im.h Wed Jul 09 00:32:18 2008 +0000 @@ -30,17 +30,31 @@ #include "group.h" void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg); -void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_process_recv_group_im(guint8 *data, - guint8 **cursor, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type); -void qq_process_recv_group_im_apply_join(guint8 *data, - guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc); -void qq_process_recv_group_im_been_rejected(guint8 *data, - guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc); -void qq_process_recv_group_im_been_approved(guint8 *data, - guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc); -void qq_process_recv_group_im_been_removed(guint8 *data, - guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc); -void qq_process_recv_group_im_been_added(guint8 *data, - guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc); + +/* void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); */ +void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc); + +/* void qq_process_recv_group_im(guint8 *data, guint8 **cursor, + * gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type); */ +void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type); + +/* void qq_process_recv_group_im_apply_join(guint8 *data, guint8 **cursor, gint len, + * guint32 internal_group_id, PurpleConnection *gc); */ +void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc); + +/* void qq_process_recv_group_im_been_rejected(guint8 *data, guint8 **cursor, gint len, + * guint32 internal_group_id, PurpleConnection *gc); */ +void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc); + +/* void qq_process_recv_group_im_been_approved(guint8 *data, guint8 **cursor, gint len, + * guint32 internal_group_id, PurpleConnection *gc); */ +void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc); + +/* void qq_process_recv_group_im_been_removed(guint8 *data, guint8 **cursor, gint len, + * guint32 internal_group_id, PurpleConnection *gc); */ +void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc); + +/* void qq_process_recv_group_im_been_added(guint8 *data, guint8 **cursor, gint len, + * guint32 internal_group_id, PurpleConnection *gc); */ +void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc); #endif
--- a/libpurple/protocols/qq/group_info.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_info.c Wed Jul 09 00:32:18 2008 +0000 @@ -43,7 +43,7 @@ { g_return_val_if_fail(member != NULL, FALSE); return (member->nickname == NULL) || - (time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL; + (time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL; } /* this is done when we receive the reply to get_online_members sub_cmd @@ -65,100 +65,83 @@ /* send packet to get detailed information of one group */ void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group) { - guint8 *raw_data, *cursor; - gint bytes, data_len; + guint8 raw_data[16] = {0}; + gint bytes = 0; g_return_if_fail(group != NULL); - data_len = 5; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_GROUP_INFO); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); - bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_GROUP_INFO); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - - if (bytes != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_GROUP_INFO)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + qq_send_group_cmd(gc, group, raw_data, bytes); } /* send packet to get online group member, called by keep_alive */ void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group) { - guint8 *raw_data, *cursor; - gint bytes, data_len; + guint8 raw_data[16] = {0}; + gint bytes = 0; g_return_if_fail(group != NULL); /* only get online members when conversation window is on */ if (NULL == purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,group->group_name_utf8, purple_connection_get_account(gc))) { purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Conv windows for \"%s\" is not on, do not get online members\n", group->group_name_utf8); + "Conv windows for \"%s\" is not on, do not get online members\n", group->group_name_utf8); return; } - data_len = 5; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_ONLINE_MEMBER); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); - bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_ONLINE_MEMBER); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - - if (bytes != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_ONLINE_MEMBER)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + qq_send_group_cmd(gc, group, raw_data, bytes); } /* send packet to get info for each group member */ void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group) { - guint8 *raw_data, *cursor; - gint bytes, data_len, i; + guint8 *raw_data; + gint bytes, num, data_len; GList *list; qq_buddy *member; g_return_if_fail(group != NULL); - for (i = 0, list = group->members; list != NULL; list = list->next) { + for (num = 0, list = group->members; list != NULL; list = list->next) { member = (qq_buddy *) list->data; if (_is_group_member_need_update_info(member)) - i++; + num++; } - if (i <= 0) { + if (num <= 0) { purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member needs to to update info now.\n"); return; } - data_len = 5 + 4 * i; + data_len = 5 + 4 * num; raw_data = g_newa(guint8, data_len); - cursor = raw_data; bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_MEMBER_INFO); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_MEMBER_INFO); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); list = group->members; while (list != NULL) { member = (qq_buddy *) list->data; if (_is_group_member_need_update_info(member)) - bytes += create_packet_dw(raw_data, &cursor, member->uid); + bytes += qq_put32(raw_data + bytes, member->uid); list = list->next; } - if (bytes != data_len) + if (bytes != data_len) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO)); + return; + } + + qq_send_group_cmd(gc, group, raw_data, bytes); } -void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc) { qq_group *group; qq_buddy *member; @@ -168,16 +151,18 @@ guint16 unknown, max_members; guint32 member_uid, internal_group_id, external_group_id; GSList *pending_id; - gint pascal_len, i; guint32 unknown4; guint8 unknown1; + gint bytes, num; g_return_if_fail(data != NULL && len > 0); qd = (qq_data *) gc->proto_data; - read_packet_dw(data, cursor, len, &(internal_group_id)); + bytes = 0; + bytes += qq_get32(&(internal_group_id), data + bytes); g_return_if_fail(internal_group_id > 0); - read_packet_dw(data, cursor, len, &(external_group_id)); + + bytes += qq_get32(&(external_group_id), data + bytes); g_return_if_fail(internal_group_id > 0); pending_id = qq_get_pending_id(qd->adding_groups_from_server, internal_group_id); @@ -189,32 +174,30 @@ group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); g_return_if_fail(group != NULL); - read_packet_b(data, cursor, len, &(group->group_type)); - read_packet_dw(data, cursor, len, &unknown4); /* unknown 4 bytes */ - read_packet_dw(data, cursor, len, &(group->creator_uid)); - read_packet_b(data, cursor, len, &(group->auth_type)); - read_packet_dw(data, cursor, len, &unknown4); /* oldCategory */ - read_packet_w(data, cursor, len, &unknown); - read_packet_dw(data, cursor, len, &(group->group_category)); - read_packet_w(data, cursor, len, &max_members); - read_packet_b(data, cursor, len, &unknown1); - read_packet_dw(data, cursor, len, &(unknown4)); /* versionID */ + bytes += qq_get8(&(group->group_type), data + bytes); + bytes += qq_get32(&unknown4, data + bytes); /* unknown 4 bytes */ + bytes += qq_get32(&(group->creator_uid), data + bytes); + bytes += qq_get8(&(group->auth_type), data + bytes); + bytes += qq_get32(&unknown4, data + bytes); /* oldCategory */ + bytes += qq_get16(&unknown, data + bytes); + bytes += qq_get32(&(group->group_category), data + bytes); + bytes += qq_get16(&max_members, data + bytes); + bytes += qq_get8(&unknown1, data + bytes); + bytes += qq_get32(&(unknown4), data + bytes); /* versionID */ - pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT); - *cursor += pascal_len; - read_packet_w(data, cursor, len, &(unknown)); /* 0x0000 */ - pascal_len = convert_as_pascal_string(*cursor, &(group->notice_utf8), QQ_CHARSET_DEFAULT); - *cursor += pascal_len; - pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT); - *cursor += pascal_len; + /* strlen + <str content> */ + bytes += convert_as_pascal_string(data + bytes, &(group->group_name_utf8), QQ_CHARSET_DEFAULT); + bytes += qq_get16(&unknown, data + bytes); /* 0x0000 */ + bytes += convert_as_pascal_string(data + bytes, &(group->notice_utf8), QQ_CHARSET_DEFAULT); + bytes += convert_as_pascal_string(data + bytes, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT); - i = 0; + num = 0; /* now comes the member list separated by 0x00 */ - while (*cursor < data + len) { - read_packet_dw(data, cursor, len, &member_uid); - i++; - read_packet_b(data, cursor, len, &organization); - read_packet_b(data, cursor, len, &role); + while (bytes < len) { + bytes += qq_get32(&member_uid, data + bytes); + num++; + bytes += qq_get8(&organization, data + bytes); + bytes += qq_get8(&role, data + bytes); if(organization != 0 || role != 0) { purple_debug(PURPLE_DEBUG_INFO, "QQ", "group member %d: organization=%d, role=%d\n", member_uid, organization, role); @@ -223,11 +206,11 @@ if (member != NULL) member->role = role; } - if(*cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!"); - } + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!"); + } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, i); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, num); if (group->creator_uid == qd->uid) group->my_status = QQ_GROUP_MEMBER_STATUS_IS_ADMIN; @@ -237,33 +220,32 @@ purple_conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->group_name_utf8, purple_connection_get_account(gc)); if(NULL == purple_conv) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Conv windows for \"%s\" is not on, do not set topic\n", group->group_name_utf8); + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Conv windows for \"%s\" is not on, do not set topic\n", group->group_name_utf8); } else { purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8); } } -void qq_process_group_cmd_get_online_members(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc) { guint32 internal_group_id, member_uid; guint8 unknown; - gint bytes, i; + gint bytes, num; qq_group *group; qq_buddy *member; g_return_if_fail(data != NULL && len > 0); - if (data + len - *cursor < 4) { + if (len <= 3) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Invalid group online member reply, discard it!\n"); return; } bytes = 0; - i = 0; - bytes += read_packet_dw(data, cursor, len, &internal_group_id); - bytes += read_packet_b(data, cursor, len, &unknown); /* 0x3c ?? */ + bytes += qq_get32(&internal_group_id, data + bytes); + bytes += qq_get8(&unknown, data + bytes); /* 0x3c ?? */ g_return_if_fail(internal_group_id > 0); group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); @@ -275,61 +257,63 @@ /* set all offline first, then update those online */ _qq_group_set_members_all_offline(group); - while (*cursor < data + len) { - bytes += read_packet_dw(data, cursor, len, &member_uid); - i++; + num = 0; + while (bytes < len) { + bytes += qq_get32(&member_uid, data + bytes); + num++; member = qq_group_find_or_add_member(gc, group, member_uid); if (member != NULL) member->status = QQ_BUDDY_ONLINE_NORMAL; } - if(*cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!"); - } + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!"); + } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, i); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, num); } /* process the reply to get_members_info packet */ -void qq_process_group_cmd_get_members_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; + gint num; guint32 internal_group_id, member_uid; guint16 unknown; - gint pascal_len, i; qq_group *group; qq_buddy *member; g_return_if_fail(data != NULL && len > 0); - read_packet_dw(data, cursor, len, &internal_group_id); + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); g_return_if_fail(internal_group_id > 0); group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); g_return_if_fail(group != NULL); - i = 0; + num = 0; /* now starts the member info, as get buddy list reply */ - while (*cursor < data + len) { - read_packet_dw(data, cursor, len, &member_uid); + while (bytes < len) { + bytes += qq_get32(&member_uid, data + bytes); g_return_if_fail(member_uid > 0); member = qq_group_find_member_by_uid(group, member_uid); g_return_if_fail(member != NULL); - i++; - read_packet_w(data, cursor, len, &(member->face)); - read_packet_b(data, cursor, len, &(member->age)); - read_packet_b(data, cursor, len, &(member->gender)); - pascal_len = convert_as_pascal_string(*cursor, &(member->nickname), QQ_CHARSET_DEFAULT); - *cursor += pascal_len; - read_packet_w(data, cursor, len, &unknown); - read_packet_b(data, cursor, len, &(member->flag1)); - read_packet_b(data, cursor, len, &(member->comm_flag)); + num++; + bytes += qq_get16(&(member->face), data + bytes); + bytes += qq_get8(&(member->age), data + bytes); + bytes += qq_get8(&(member->gender), data + bytes); + bytes += convert_as_pascal_string(data + bytes, &(member->nickname), QQ_CHARSET_DEFAULT); + bytes += qq_get16(&unknown, data + bytes); + bytes += qq_get8(&(member->flag1), data + bytes); + bytes += qq_get8(&(member->comm_flag), data + bytes); member->last_refresh = time(NULL); } - if(*cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!"); - } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, i); + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!"); + } + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, num); }
--- a/libpurple/protocols/qq/group_info.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_info.h Wed Jul 09 00:32:18 2008 +0000 @@ -32,8 +32,8 @@ void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group); void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group); void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group); -void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_process_group_cmd_get_online_members(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_process_group_cmd_get_members_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); +void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc); +void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc); +void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc); #endif
--- a/libpurple/protocols/qq/group_join.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_join.c Wed Jul 09 00:32:18 2008 +0000 @@ -64,8 +64,8 @@ /* send packet to join a group without auth */ void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group) { - guint8 *raw_data, *cursor; - gint bytes, data_len; + guint8 raw_data[16] = {0}; + gint bytes = 0; g_return_if_fail(group != NULL); @@ -86,19 +86,11 @@ break; } - data_len = 5; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; + bytes = 0; + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); - bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_JOIN_GROUP); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - - if (bytes != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + qq_send_group_cmd(gc, group, raw_data, bytes); } static void _qq_group_join_auth_with_gc_and_id(gc_and_uid *g, const gchar *reason_utf8) @@ -145,7 +137,7 @@ void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8) { - guint8 *raw_data, *cursor; + guint8 *raw_data; gchar *reason_qq; gint bytes, data_len; @@ -164,50 +156,42 @@ data_len = 10 + strlen(reason_qq) + 1; raw_data = g_newa(guint8, data_len); - cursor = raw_data; bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_JOIN_GROUP_AUTH); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - bytes += create_packet_b(raw_data, &cursor, opt); - bytes += create_packet_dw(raw_data, &cursor, uid); - bytes += create_packet_b(raw_data, &cursor, strlen(reason_qq)); - bytes += create_packet_data(raw_data, &cursor, (guint8 *) reason_qq, strlen(reason_qq)); + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP_AUTH); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); + bytes += qq_put8(raw_data + bytes, opt); + bytes += qq_put32(raw_data + bytes, uid); + bytes += qq_put8(raw_data + bytes, strlen(reason_qq)); + bytes += qq_putdata(raw_data + bytes, (guint8 *) reason_qq, strlen(reason_qq)); - if (bytes != data_len) + if (bytes != data_len) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP_AUTH)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + return; + } + + qq_send_group_cmd(gc, group, raw_data, data_len); } /* send a packet to exit a group */ void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group) { - guint8 *raw_data, *cursor; - gint bytes, data_len; + guint8 raw_data[16] = {0}; + gint bytes = 0; g_return_if_fail(group != NULL); - data_len = 5; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_EXIT_GROUP); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); - bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_EXIT_GROUP); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - - if (bytes != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_EXIT_GROUP)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + qq_send_group_cmd(gc, group, raw_data, bytes); } /* If comes here, cmd is OK already */ -void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc) { - gint bytes, expected_bytes; + gint bytes; guint32 internal_group_id; PurpleChat *chat; qq_group *group; @@ -216,96 +200,94 @@ g_return_if_fail(data != NULL && len > 0); qd = (qq_data *) gc->proto_data; - bytes = 0; - expected_bytes = 4; - bytes += read_packet_dw(data, cursor, len, &internal_group_id); + if (len < 4) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Invalid exit group reply, expect %d bytes, read %d bytes\n", 4, len); + return; + } - if (bytes == expected_bytes) { - group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); - if (group != NULL) { - chat = - purple_blist_find_chat + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); + + group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); + if (group != NULL) { + chat = purple_blist_find_chat (purple_connection_get_account(gc), g_strdup_printf("%d", group->external_group_id)); - if (chat != NULL) - purple_blist_remove_chat(chat); - qq_group_delete_internal_record(qd, internal_group_id); - } - purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Invalid exit group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes); + if (chat != NULL) + purple_blist_remove_chat(chat); + qq_group_delete_internal_record(qd, internal_group_id); } + purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL); } /* Process the reply to group_auth subcmd */ -void qq_process_group_cmd_join_group_auth(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc) { - gint bytes, expected_bytes; + gint bytes; guint32 internal_group_id; qq_data *qd; g_return_if_fail(data != NULL && len > 0); qd = (qq_data *) gc->proto_data; + if (len < 4) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Invalid join group reply, expect %d bytes, read %d bytes\n", 4, len); + return; + } bytes = 0; - expected_bytes = 4; - bytes += read_packet_dw(data, cursor, len, &internal_group_id); + bytes += qq_get32(&internal_group_id, data + bytes); g_return_if_fail(internal_group_id > 0); - if (bytes == expected_bytes) - purple_notify_info - (gc, _("QQ Group Auth"), + purple_notify_info(gc, _("QQ Group Auth"), _("Your authorization request has been accepted by the QQ server"), NULL); - else - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes); } /* process group cmd reply "join group" */ -void qq_process_group_cmd_join_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc) { - gint bytes, expected_bytes; + gint bytes; guint32 internal_group_id; guint8 reply; qq_group *group; g_return_if_fail(data != NULL && len > 0); - bytes = 0; - expected_bytes = 5; - bytes += read_packet_dw(data, cursor, len, &internal_group_id); - bytes += read_packet_b(data, cursor, len, &reply); - - if (bytes != expected_bytes) { + if (len < 5) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes); + "Invalid join group reply, expect %d bytes, read %d bytes\n", 5, len); return; - } else { /* join group OK */ - group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); - /* need to check if group is NULL or not. */ - g_return_if_fail(group != NULL); - switch (reply) { - case QQ_GROUP_JOIN_OK: - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8); - group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; - qq_group_refresh(gc, group); - /* this must be shown before getting online members */ - qq_group_conv_show_window(gc, group); - qq_send_cmd_group_get_group_info(gc, group); - break; - case QQ_GROUP_JOIN_NEED_AUTH: - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Fail joining group [%d] %s, needs authentication\n", - group->external_group_id, group->group_name_utf8); - group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER; - qq_group_refresh(gc, group); - _qq_group_join_auth(gc, group); - break; - default: - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Error joining group [%d] %s, unknown reply: 0x%02x\n", - group->external_group_id, group->group_name_utf8, reply); - } + } + + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); + bytes += qq_get8(&reply, data + bytes); + + /* join group OK */ + group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); + /* need to check if group is NULL or not. */ + g_return_if_fail(group != NULL); + switch (reply) { + case QQ_GROUP_JOIN_OK: + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8); + group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; + qq_group_refresh(gc, group); + /* this must be shown before getting online members */ + qq_group_conv_show_window(gc, group); + qq_send_cmd_group_get_group_info(gc, group); + break; + case QQ_GROUP_JOIN_NEED_AUTH: + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Fail joining group [%d] %s, needs authentication\n", + group->external_group_id, group->group_name_utf8); + group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER; + qq_group_refresh(gc, group); + _qq_group_join_auth(gc, group); + break; + default: + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Error joining group [%d] %s, unknown reply: 0x%02x\n", + group->external_group_id, group->group_name_utf8, reply); } }
--- a/libpurple/protocols/qq/group_join.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_join.h Wed Jul 09 00:32:18 2008 +0000 @@ -46,8 +46,8 @@ void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group); void qq_group_exit(PurpleConnection *gc, GHashTable *data); void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group); -void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_process_group_cmd_join_group_auth(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_process_group_cmd_join_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); +void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc); +void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc); +void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc); #endif
--- a/libpurple/protocols/qq/group_network.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_network.c Wed Jul 09 00:32:18 2008 +0000 @@ -39,7 +39,7 @@ #include "group_opt.h" #include "group_search.h" #include "header_info.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" enum { @@ -81,12 +81,12 @@ } /* default process of reply error */ -static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *cursor, gint len, PurpleConnection *gc) +static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *data, gint len, PurpleConnection *gc) { gchar *msg, *msg_utf8; - g_return_if_fail(cursor != NULL && len > 0); + g_return_if_fail(data != NULL && len > 0); - msg = g_strndup((gchar *) cursor, len); /* it will append 0x00 */ + msg = g_strndup((gchar *) data, len); /* it will append 0x00 */ msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); g_free(msg); msg = g_strdup_printf(_("Code [0x%02X]: %s"), reply, msg_utf8); @@ -96,14 +96,13 @@ } /* default process, dump only */ -static void _qq_process_group_cmd_reply_default(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +static void _qq_process_group_cmd_reply_default(guint8 *data, gint len, PurpleConnection *gc) { - gchar *hex_dump; g_return_if_fail(data != NULL && len > 0); - hex_dump = hex_dump_to_str(data, len); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Dump unprocessed group cmd reply:\n%s", hex_dump); - g_free(hex_dump); + qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", + data, len, + "Dump unprocessed group cmd reply:"); } /* The lower layer command of send group cmd */ @@ -116,7 +115,7 @@ qd = (qq_data *) gc->proto_data; - qq_send_cmd(gc, QQ_CMD_GROUP_CMD, TRUE, 0, TRUE, raw_data, data_len); + qq_send_cmd(qd, QQ_CMD_GROUP_CMD, raw_data, data_len); p = g_new0(group_packet, 1); @@ -136,7 +135,7 @@ qq_data *qd; gint len, bytes; guint32 internal_group_id; - guint8 *data, *cursor, sub_cmd, reply; + guint8 *data, sub_cmd, reply; g_return_if_fail(buf != NULL && buf_len != 0); @@ -149,102 +148,101 @@ return; } - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - if (len <= 2) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len); - return; - } + if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n"); + return; + } - bytes = 0; - cursor = data; - bytes += read_packet_b(data, &cursor, len, &sub_cmd); - bytes += read_packet_b(data, &cursor, len, &reply); - - group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); + if (len <= 2) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len); + return; + } - if (reply != QQ_GROUP_CMD_REPLY_OK) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd)); + bytes = 0; + bytes += qq_get8(&sub_cmd, data + bytes); + bytes += qq_get8(&reply, data + bytes); - if (group != NULL) - qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE); + group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); - switch (reply) { /* this should be all errors */ - case QQ_GROUP_CMD_REPLY_NOT_MEMBER: - if (group != NULL) { - purple_debug(PURPLE_DEBUG_WARNING, - "QQ", - "You are not a member of group \"%s\"\n", group->group_name_utf8); - group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER; - qq_group_refresh(gc, group); - } - break; - case QQ_GROUP_CMD_REPLY_SEARCH_ERROR: - if (qd->roomlist != NULL) { - if (purple_roomlist_get_in_progress(qd->roomlist)) - purple_roomlist_set_in_progress(qd->roomlist, FALSE); - } - _qq_process_group_cmd_reply_error_default(reply, cursor, len - bytes, gc); - break; - default: - _qq_process_group_cmd_reply_error_default(reply, cursor, len - bytes, gc); - } - return; - } + if (reply != QQ_GROUP_CMD_REPLY_OK) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd)); + + if (group != NULL) + qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE); - /* seems ok so far, so we process the reply according to sub_cmd */ - switch (sub_cmd) { - case QQ_GROUP_CMD_GET_GROUP_INFO: - qq_process_group_cmd_get_group_info(data, &cursor, len, gc); + switch (reply) { /* this should be all errors */ + case QQ_GROUP_CMD_REPLY_NOT_MEMBER: if (group != NULL) { - qq_send_cmd_group_get_members_info(gc, group); - qq_send_cmd_group_get_online_members(gc, group); + purple_debug(PURPLE_DEBUG_WARNING, + "QQ", + "You are not a member of group \"%s\"\n", group->group_name_utf8); + group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER; + qq_group_refresh(gc, group); } break; - case QQ_GROUP_CMD_CREATE_GROUP: - qq_group_process_create_group_reply(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_MODIFY_GROUP_INFO: - qq_group_process_modify_info_reply(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_MEMBER_OPT: - qq_group_process_modify_members_reply(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_ACTIVATE_GROUP: - qq_group_process_activate_group_reply(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_SEARCH_GROUP: - qq_process_group_cmd_search_group(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_JOIN_GROUP: - qq_process_group_cmd_join_group(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_JOIN_GROUP_AUTH: - qq_process_group_cmd_join_group_auth(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_EXIT_GROUP: - qq_process_group_cmd_exit_group(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_SEND_MSG: - qq_process_group_cmd_im(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_GET_ONLINE_MEMBER: - qq_process_group_cmd_get_online_members(data, &cursor, len, gc); - if (group != NULL) - qq_group_conv_refresh_online_member(gc, group); - break; - case QQ_GROUP_CMD_GET_MEMBER_INFO: - qq_process_group_cmd_get_members_info(data, &cursor, len, gc); - if (group != NULL) - qq_group_conv_refresh_online_member(gc, group); + case QQ_GROUP_CMD_REPLY_SEARCH_ERROR: + if (qd->roomlist != NULL) { + if (purple_roomlist_get_in_progress(qd->roomlist)) + purple_roomlist_set_in_progress(qd->roomlist, FALSE); + } + _qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc); break; default: - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd)); - _qq_process_group_cmd_reply_default(data, &cursor, len, gc); + _qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc); + } + return; + } + + /* seems ok so far, so we process the reply according to sub_cmd */ + switch (sub_cmd) { + case QQ_GROUP_CMD_GET_GROUP_INFO: + qq_process_group_cmd_get_group_info(data + bytes, len - bytes, gc); + if (group != NULL) { + qq_send_cmd_group_get_members_info(gc, group); + qq_send_cmd_group_get_online_members(gc, group); } - - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n"); + break; + case QQ_GROUP_CMD_CREATE_GROUP: + qq_group_process_create_group_reply(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_MODIFY_GROUP_INFO: + qq_group_process_modify_info_reply(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_MEMBER_OPT: + qq_group_process_modify_members_reply(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_ACTIVATE_GROUP: + qq_group_process_activate_group_reply(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_SEARCH_GROUP: + qq_process_group_cmd_search_group(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_JOIN_GROUP: + qq_process_group_cmd_join_group(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_JOIN_GROUP_AUTH: + qq_process_group_cmd_join_group_auth(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_EXIT_GROUP: + qq_process_group_cmd_exit_group(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_SEND_MSG: + qq_process_group_cmd_im(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_GET_ONLINE_MEMBER: + qq_process_group_cmd_get_online_members(data + bytes, len - bytes, gc); + if (group != NULL) + qq_group_conv_refresh_online_member(gc, group); + break; + case QQ_GROUP_CMD_GET_MEMBER_INFO: + qq_process_group_cmd_get_members_info(data + bytes, len - bytes, gc); + if (group != NULL) + qq_group_conv_refresh_online_member(gc, group); + break; + default: + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd)); + _qq_process_group_cmd_reply_default(data + bytes, len, gc); } }
--- a/libpurple/protocols/qq/group_opt.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_opt.c Wed Jul 09 00:32:18 2008 +0000 @@ -57,22 +57,24 @@ static void _qq_group_member_opt(PurpleConnection *gc, qq_group *group, gint operation, guint32 *members) { - guint8 *data, *cursor; + guint8 *data; gint i, count, data_len; + gint bytes; g_return_if_fail(members != NULL); - for (i = 0; members[i] != 0xffffffff; i++) {; + for (count = 0; members[count] != 0xffffffff; count++) {; } - count = i; data_len = 6 + count * 4; data = g_newa(guint8, data_len); - cursor = data; - create_packet_b(data, &cursor, QQ_GROUP_CMD_MEMBER_OPT); - create_packet_dw(data, &cursor, group->internal_group_id); - create_packet_b(data, &cursor, operation); + + bytes = 0; + bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MEMBER_OPT); + bytes += qq_put32(data + bytes, group->internal_group_id); + bytes += qq_put8(data + bytes, operation); for (i = 0; i < count; i++) - create_packet_dw(data, &cursor, members[i]); - qq_send_group_cmd(gc, group, data, data_len); + bytes += qq_put32(data + bytes, members[i]); + + qq_send_group_cmd(gc, group, data, bytes); } static void _qq_group_do_nothing_with_struct(group_member_opt *g) @@ -97,11 +99,11 @@ qq_send_packet_get_info(g->gc, g->member, TRUE); /* we want to see window */ purple_request_action(g->gc, NULL, _("Do you want to approve the request?"), "", - PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(g->gc), NULL, NULL, - g, 2, - _("Reject"), G_CALLBACK(qq_group_reject_application_with_struct), - _("Approve"), G_CALLBACK(qq_group_approve_application_with_struct)); + PURPLE_DEFAULT_ACTION_NONE, + purple_connection_get_account(g->gc), NULL, NULL, + g, 2, + _("Reject"), G_CALLBACK(qq_group_reject_application_with_struct), + _("Approve"), G_CALLBACK(qq_group_approve_application_with_struct)); } void qq_group_reject_application_with_struct(group_member_opt *g) @@ -193,13 +195,15 @@ _qq_group_member_opt(gc, group, QQ_GROUP_MEMBER_ADD, add_members); } -void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; guint32 internal_group_id; qq_group *group; g_return_if_fail(data != NULL); - read_packet_dw(data, cursor, len, &internal_group_id); + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); g_return_if_fail(internal_group_id > 0); /* we should have its info locally */ @@ -213,8 +217,9 @@ void qq_group_modify_info(PurpleConnection *gc, qq_group *group) { - gint data_len, data_written; - guint8 *data, *cursor; + guint8 *data; + gint data_len; + gint bytes; gchar *group_name, *group_desc, *notice; g_return_if_fail(group != NULL); @@ -228,47 +233,50 @@ + 1 + strlen(notice); data = g_newa(guint8, data_len); - cursor = data; - data_written = 0; + bytes = 0; /* 000-000 */ - data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_MODIFY_GROUP_INFO); + bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MODIFY_GROUP_INFO); /* 001-004 */ - data_written += create_packet_dw(data, &cursor, group->internal_group_id); + bytes += qq_put32(data + bytes, group->internal_group_id); /* 005-005 */ - data_written += create_packet_b(data, &cursor, 0x01); + bytes += qq_put8(data + bytes, 0x01); /* 006-006 */ - data_written += create_packet_b(data, &cursor, group->auth_type); + bytes += qq_put8(data + bytes, group->auth_type); /* 007-008 */ - data_written += create_packet_w(data, &cursor, 0x0000); + bytes += qq_put16(data + bytes, 0x0000); /* 009-010 */ - data_written += create_packet_w(data, &cursor, group->group_category); + bytes += qq_put16(data + bytes, group->group_category); - data_written += create_packet_b(data, &cursor, strlen(group_name)); - data_written += create_packet_data(data, &cursor, (guint8 *) group_name, strlen(group_name)); + bytes += qq_put8(data + bytes, strlen(group_name)); + bytes += qq_putdata(data + bytes, (guint8 *) group_name, strlen(group_name)); - data_written += create_packet_w(data, &cursor, 0x0000); + bytes += qq_put16(data + bytes, 0x0000); - data_written += create_packet_b(data, &cursor, strlen(notice)); - data_written += create_packet_data(data, &cursor, (guint8 *) notice, strlen(notice)); + bytes += qq_put8(data + bytes, strlen(notice)); + bytes += qq_putdata(data+ bytes, (guint8 *) notice, strlen(notice)); - data_written += create_packet_b(data, &cursor, strlen(group_desc)); - data_written += create_packet_data(data, &cursor, (guint8 *) group_desc, strlen(group_desc)); + bytes += qq_put8(data + bytes, strlen(group_desc)); + bytes += qq_putdata(data + bytes, (guint8 *) group_desc, strlen(group_desc)); - if (data_written != data_len) + if (bytes != data_len) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail to create group_modify_info packet, expect %d bytes, wrote %d bytes\n", - data_len, data_written); - else - qq_send_group_cmd(gc, group, data, data_len); + data_len, bytes); + return; + } + + qq_send_group_cmd(gc, group, data, bytes); } -void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; guint32 internal_group_id; qq_group *group; g_return_if_fail(data != NULL); - read_packet_dw(data, cursor, len, &internal_group_id); + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); g_return_if_fail(internal_group_id > 0); /* we should have its info locally */ @@ -284,42 +292,44 @@ /* we create a very simple group first, and then let the user to modify */ void qq_group_create_with_name(PurpleConnection *gc, const gchar *name) { - gint data_len, data_written; - guint8 *data, *cursor; + gint data_len; + guint8 *data; + gint bytes; qq_data *qd; g_return_if_fail(name != NULL); qd = (qq_data *) gc->proto_data; data_len = 7 + 1 + strlen(name) + 2 + 1 + 1 + 4; data = g_newa(guint8, data_len); - cursor = data; - data_written = 0; + bytes = 0; /* we create the simpleset group, only group name is given */ /* 000 */ - data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_CREATE_GROUP); + bytes += qq_put8(data + bytes, QQ_GROUP_CMD_CREATE_GROUP); /* 001 */ - data_written += create_packet_b(data, &cursor, QQ_GROUP_TYPE_PERMANENT); + bytes += qq_put8(data + bytes, QQ_GROUP_TYPE_PERMANENT); /* 002 */ - data_written += create_packet_b(data, &cursor, QQ_GROUP_AUTH_TYPE_NEED_AUTH); + bytes += qq_put8(data + bytes, QQ_GROUP_AUTH_TYPE_NEED_AUTH); /* 003-004 */ - data_written += create_packet_w(data, &cursor, 0x0000); + bytes += qq_put16(data + bytes, 0x0000); /* 005-006 */ - data_written += create_packet_w(data, &cursor, 0x0003); + bytes += qq_put16(data + bytes, 0x0003); /* 007 */ - data_written += create_packet_b(data, &cursor, strlen(name)); - data_written += create_packet_data(data, &cursor, (guint8 *) name, strlen(name)); - data_written += create_packet_w(data, &cursor, 0x0000); - data_written += create_packet_b(data, &cursor, 0x00); /* no group notice */ - data_written += create_packet_b(data, &cursor, 0x00); /* no group desc */ - data_written += create_packet_dw(data, &cursor, qd->uid); /* I am member of coz */ + bytes += qq_put8(data + bytes, strlen(name)); + bytes += qq_putdata(data + bytes, (guint8 *) name, strlen(name)); + bytes += qq_put16(data + bytes, 0x0000); + bytes += qq_put8(data + bytes, 0x00); /* no group notice */ + bytes += qq_put8(data + bytes, 0x00); /* no group desc */ + bytes += qq_put32(data + bytes, qd->uid); /* I am member of coz */ - if (data_written != data_len) + if (bytes != data_len) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create create_group packet, expect %d bytes, written %d bytes\n", - data_len, data_written); - else - qq_send_group_cmd(gc, NULL, data, data_len); + data_len, bytes); + return; + } + + qq_send_group_cmd(gc, NULL, data, bytes); } static void qq_group_setup_with_gc_and_uid(gc_and_uid *g) @@ -335,8 +345,9 @@ g_free(g); } -void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; guint32 internal_group_id, external_group_id; qq_group *group; gc_and_uid *g; @@ -346,8 +357,9 @@ g_return_if_fail(gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; - read_packet_dw(data, cursor, len, &internal_group_id); - read_packet_dw(data, cursor, len, &external_group_id); + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); + bytes += qq_get32(&external_group_id, data + bytes); g_return_if_fail(internal_group_id > 0 && external_group_id); group = qq_group_create_internal_record(gc, internal_group_id, external_group_id, NULL); @@ -378,36 +390,29 @@ /* we have to activate group after creation, otherwise the group can not be searched */ void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id) { - gint data_len, data_written; - guint8 *data, *cursor; + guint8 data[16] = {0}; + gint bytes = 0; g_return_if_fail(internal_group_id > 0); - data_len = 5; - data = g_newa(guint8, data_len); - cursor = data; - - data_written = 0; + bytes = 0; /* we create the simplest group, only group name is given */ /* 000 */ - data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_ACTIVATE_GROUP); + bytes += qq_put8(data + bytes, QQ_GROUP_CMD_ACTIVATE_GROUP); /* 001-005 */ - data_written += create_packet_dw(data, &cursor, internal_group_id); + bytes += qq_put32(data + bytes, internal_group_id); - if (data_written != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create activate_group packet, expect %d bytes, written %d bytes\n", - data_len, data_written); - else - qq_send_group_cmd(gc, NULL, data, data_len); + qq_send_group_cmd(gc, NULL, data, bytes); } -void qq_group_process_activate_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; guint32 internal_group_id; qq_group *group; g_return_if_fail(data != NULL); - read_packet_dw(data, cursor, len, &internal_group_id); + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); g_return_if_fail(internal_group_id > 0); /* we should have its info locally */
--- a/libpurple/protocols/qq/group_opt.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_opt.h Wed Jul 09 00:32:18 2008 +0000 @@ -54,12 +54,12 @@ void qq_group_reject_application_with_struct(group_member_opt *g); void qq_group_search_application_with_struct(group_member_opt *g); -void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); +void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc); +void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc); void qq_group_manage_group(PurpleConnection *gc, GHashTable *data); void qq_group_create_with_name(PurpleConnection *gc, const gchar *name); void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id); -void qq_group_process_activate_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); +void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc); +void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc); #endif
--- a/libpurple/protocols/qq/group_search.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_search.c Wed Jul 09 00:32:18 2008 +0000 @@ -43,24 +43,18 @@ /* send packet to search for qq_group */ void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id) { - guint8 *raw_data, *cursor, type; - gint bytes, data_len; + guint8 raw_data[16] = {0}; + gint bytes = 0; + guint8 type; - data_len = 6; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; type = (external_group_id == 0x00000000) ? QQ_GROUP_SEARCH_TYPE_DEMO : QQ_GROUP_SEARCH_TYPE_BY_ID; bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEARCH_GROUP); - bytes += create_packet_b(raw_data, &cursor, type); - bytes += create_packet_dw(raw_data, &cursor, external_group_id); + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEARCH_GROUP); + bytes += qq_put8(raw_data + bytes, type); + bytes += qq_put32(raw_data + bytes, external_group_id); - if (bytes != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_SEARCH_GROUP)); - else - qq_send_group_cmd(gc, NULL, raw_data, data_len); + qq_send_group_cmd(gc, NULL, raw_data, bytes); } static void _qq_setup_roomlist(qq_data *qd, qq_group *group) @@ -89,55 +83,50 @@ } /* process group cmd reply "search group" */ -void qq_process_group_cmd_search_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; guint8 search_type; guint16 unknown; - gint bytes, pascal_len; + qq_group group; qq_data *qd; - qq_group *group; GSList *pending_id; g_return_if_fail(data != NULL && len > 0); qd = (qq_data *) gc->proto_data; - read_packet_b(data, cursor, len, &search_type); - group = g_newa(qq_group, 1); + bytes = 0; + bytes += qq_get8(&search_type, data + bytes); /* now it starts with group_info_entry */ - bytes = 0; - bytes += read_packet_dw(data, cursor, len, &(group->internal_group_id)); - bytes += read_packet_dw(data, cursor, len, &(group->external_group_id)); - bytes += read_packet_b(data, cursor, len, &(group->group_type)); - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_dw(data, cursor, len, &(group->creator_uid)); - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_dw(data, cursor, len, &(group->group_category)); - pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT); - bytes += pascal_len; - *cursor += pascal_len; - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_b(data, cursor, len, &(group->auth_type)); - pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT); - bytes += pascal_len; - *cursor += pascal_len; + bytes += qq_get32(&(group.internal_group_id), data + bytes); + bytes += qq_get32(&(group.external_group_id), data + bytes); + bytes += qq_get8(&(group.group_type), data + bytes); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get32(&(group.creator_uid), data + bytes); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get32(&(group.group_category), data + bytes); + bytes += convert_as_pascal_string(data + bytes, &(group.group_name_utf8), QQ_CHARSET_DEFAULT); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get8(&(group.auth_type), data + bytes); + bytes += convert_as_pascal_string(data + bytes, &(group.group_desc_utf8), QQ_CHARSET_DEFAULT); /* end of one qq_group */ - if(*cursor != (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!"); - } + if(bytes != len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!"); + } - pending_id = qq_get_pending_id(qd->joining_groups, group->external_group_id); + pending_id = qq_get_pending_id(qd->joining_groups, group.external_group_id); if (pending_id != NULL) { - qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE); - if (qq_group_find_by_id(gc, group->internal_group_id, QQ_INTERNAL_ID) == NULL) + qq_set_pending_id(&qd->joining_groups, group.external_group_id, FALSE); + if (qq_group_find_by_id(gc, group.internal_group_id, QQ_INTERNAL_ID) == NULL) qq_group_create_internal_record(gc, - group->internal_group_id, group->external_group_id, group->group_name_utf8); - qq_send_cmd_group_join_group(gc, group); + group.internal_group_id, group.external_group_id, group.group_name_utf8); + qq_send_cmd_group_join_group(gc, &group); } else { - _qq_setup_roomlist(qd, group); + _qq_setup_roomlist(qd, &group); } }
--- a/libpurple/protocols/qq/group_search.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/group_search.h Wed Jul 09 00:32:18 2008 +0000 @@ -29,6 +29,6 @@ #include "connection.h" void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id); -void qq_process_group_cmd_search_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); +void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc); #endif
--- a/libpurple/protocols/qq/im.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/im.c Wed Jul 09 00:32:18 2008 +0000 @@ -40,19 +40,16 @@ #include "header_info.h" #include "im.h" #include "packet_parse.h" -#include "send_core.h" +#include "qq_network.h" #include "send_file.h" #include "utils.h" #define QQ_SEND_IM_REPLY_OK 0x00 #define DEFAULT_FONT_NAME_LEN 4 -/* a debug function */ -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len); - enum { - QQ_NORMAL_IM_TEXT = 0x000b, + QQ_NORMAL_IM_TEXT = 0x000b, QQ_NORMAL_IM_FILE_REQUEST_TCP = 0x0001, QQ_NORMAL_IM_FILE_APPROVE_TCP = 0x0003, QQ_NORMAL_IM_FILE_REJECT_TCP = 0x0005, @@ -121,9 +118,9 @@ #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) + 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; @@ -141,7 +138,7 @@ 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); + 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; @@ -182,39 +179,39 @@ 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); + qq_show_packet("QQ_MESG", send_im_tail, tail_len); return (guint8 *) send_im_tail; } static const gchar *qq_get_recv_im_type_str(gint type) { switch (type) { - case QQ_RECV_IM_TO_BUDDY: - return "QQ_RECV_IM_TO_BUDDY"; - case QQ_RECV_IM_TO_UNKNOWN: - return "QQ_RECV_IM_TO_UNKNOWN"; - case QQ_RECV_IM_UNKNOWN_QUN_IM: - return "QQ_RECV_IM_UNKNOWN_QUN_IM"; - case QQ_RECV_IM_ADD_TO_QUN: - return "QQ_RECV_IM_ADD_TO_QUN"; - case QQ_RECV_IM_DEL_FROM_QUN: - return "QQ_RECV_IM_DEL_FROM_QUN"; - case QQ_RECV_IM_APPLY_ADD_TO_QUN: - return "QQ_RECV_IM_APPLY_ADD_TO_QUN"; - case QQ_RECV_IM_CREATE_QUN: - return "QQ_RECV_IM_CREATE_QUN"; - case QQ_RECV_IM_SYS_NOTIFICATION: - return "QQ_RECV_IM_SYS_NOTIFICATION"; - case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN: - return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN"; - case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN: - return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN"; - case QQ_RECV_IM_TEMP_QUN_IM: - return "QQ_RECV_IM_TEMP_QUN_IM"; - case QQ_RECV_IM_QUN_IM: - return "QQ_RECV_IM_QUN_IM"; - default: - return "QQ_RECV_IM_UNKNOWN"; + case QQ_RECV_IM_TO_BUDDY: + return "QQ_RECV_IM_TO_BUDDY"; + case QQ_RECV_IM_TO_UNKNOWN: + return "QQ_RECV_IM_TO_UNKNOWN"; + case QQ_RECV_IM_UNKNOWN_QUN_IM: + return "QQ_RECV_IM_UNKNOWN_QUN_IM"; + case QQ_RECV_IM_ADD_TO_QUN: + return "QQ_RECV_IM_ADD_TO_QUN"; + case QQ_RECV_IM_DEL_FROM_QUN: + return "QQ_RECV_IM_DEL_FROM_QUN"; + case QQ_RECV_IM_APPLY_ADD_TO_QUN: + return "QQ_RECV_IM_APPLY_ADD_TO_QUN"; + case QQ_RECV_IM_CREATE_QUN: + return "QQ_RECV_IM_CREATE_QUN"; + case QQ_RECV_IM_SYS_NOTIFICATION: + return "QQ_RECV_IM_SYS_NOTIFICATION"; + case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN: + return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN"; + case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN: + return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN"; + case QQ_RECV_IM_TEMP_QUN_IM: + return "QQ_RECV_IM_TEMP_QUN_IM"; + case QQ_RECV_IM_QUN_IM: + return "QQ_RECV_IM_QUN_IM"; + default: + return "QQ_RECV_IM_UNKNOWN"; } } @@ -222,27 +219,29 @@ * we send an ACK which is the first 16 bytes of incoming packet */ static void _qq_send_packet_recv_im_ack(PurpleConnection *gc, guint16 seq, guint8 *data) { - qq_send_cmd(gc, QQ_CMD_RECV_IM, FALSE, seq, FALSE, data, 16); + qq_data *qd; + + qd = (qq_data *) gc->proto_data; + qq_send_cmd_detail(qd, QQ_CMD_RECV_IM, seq, FALSE, data, 16); } /* read the common parts of the normal_im, * returns the bytes read if succeed, or -1 if there is any error */ -static gint _qq_normal_im_common_read(guint8 *data, guint8 **cursor, gint len, qq_recv_normal_im_common *common) +static gint _qq_normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common) { gint bytes; g_return_val_if_fail(data != NULL && len != 0 && common != NULL, -1); bytes = 0; /* now push data into common header */ - bytes += read_packet_w(data, cursor, len, &(common->sender_ver)); - bytes += read_packet_dw(data, cursor, len, &(common->sender_uid)); - bytes += read_packet_dw(data, cursor, len, &(common->receiver_uid)); + bytes += qq_get16(&(common->sender_ver), data + bytes); + bytes += qq_get32(&(common->sender_uid), data + bytes); + bytes += qq_get32(&(common->receiver_uid), data + bytes); - common->session_md5 = g_memdup(*cursor, QQ_KEY_LENGTH); + common->session_md5 = g_memdup(data + bytes, QQ_KEY_LENGTH); bytes += QQ_KEY_LENGTH; - *cursor += QQ_KEY_LENGTH; - bytes += read_packet_w(data, cursor, len, &(common->normal_im_type)); + bytes += qq_get16(&(common->normal_im_type), data + bytes); if (bytes != 28) { /* read common place fail */ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Expect 28 bytes, read %d bytes\n", bytes); @@ -253,8 +252,7 @@ } /* process received normal text IM */ -static void _qq_process_recv_normal_im_text - (guint8 *data, guint8 **cursor, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc) +static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc) { guint16 purple_msg_type; gchar *name; @@ -262,50 +260,52 @@ gchar *msg_utf8_encoded; qq_data *qd; qq_recv_normal_im_text *im_text; + gint bytes = 0; g_return_if_fail(common != NULL); qd = (qq_data *) gc->proto_data; /* now it is QQ_NORMAL_IM_TEXT */ - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n"); - return; - } else - im_text = g_newa(qq_recv_normal_im_text, 1); + /* + if (*cursor >= (data + len - 1)) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n"); + return; + } else + */ + im_text = g_newa(qq_recv_normal_im_text, 1); im_text->common = common; /* push data into im_text */ - read_packet_w(data, cursor, len, &(im_text->msg_seq)); - read_packet_dw(data, cursor, len, &(im_text->send_time)); - read_packet_w(data, cursor, len, &(im_text->sender_icon)); - read_packet_data(data, cursor, len, (guint8 *) & (im_text->unknown2), 3); - read_packet_b(data, cursor, len, &(im_text->is_there_font_attr)); + 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(); + * fragmentSequence = buf.get() & 255; + * messageId = buf.getChar(); */ - read_packet_data(data, cursor, len, (guint8 *) & (im_text->unknown3), 4); - read_packet_b(data, cursor, len, &(im_text->msg_type)); + bytes += qq_getdata((guint8 *) & (im_text->unknown3), 4, data + bytes); + bytes += qq_get8(&(im_text->msg_type), data + bytes); /* 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 */ - im_text->msg = g_strndup(*(gchar **) cursor, data + len - *cursor); + 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 **) cursor); - *cursor += strlen(im_text->msg) + 1; - im_text->font_attr_len = data + len - *cursor; - im_text->font_attr = g_memdup(*cursor, im_text->font_attr_len); + 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 **) cursor, data + len - *cursor); + im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes); } /* if im_text->msg_type */ - _qq_show_packet("QQ_MESG recv", data, *cursor - data); name = uid_to_purple_name(common->sender_uid); if (purple_find_buddy(gc->account, name) == NULL) @@ -315,9 +315,9 @@ 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) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); + qq_encode_to_purple(im_text->font_attr, + im_text->font_attr_len, + msg_with_purple_smiley) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); /* send encoded to purple, note that we use im_text->send_time, * not the time we receive the message @@ -333,81 +333,68 @@ } /* it is a normal IM, maybe text or video request */ -static void _qq_process_recv_normal_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +static void _qq_process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc) { - gint bytes; + gint bytes = 0; qq_recv_normal_im_common *common; qq_recv_normal_im_unprocessed *im_unprocessed; - gchar *hex_dump; g_return_if_fail (data != NULL && len != 0); - if (*cursor >= (data + len - 1)) { - purple_debug (PURPLE_DEBUG_WARNING, "QQ", - "Received normal IM is empty\n"); - return; - } - else - common = g_newa (qq_recv_normal_im_common, 1); + common = g_newa (qq_recv_normal_im_common, 1); - bytes = _qq_normal_im_common_read (data, cursor, len, common); + bytes = _qq_normal_im_common_read(data, len, common); if (bytes < 0) { purple_debug (PURPLE_DEBUG_ERROR, "QQ", - "Fail read the common part of normal IM\n"); + "Fail read the common part of normal IM\n"); return; } switch (common->normal_im_type) { - case QQ_NORMAL_IM_TEXT: - purple_debug (PURPLE_DEBUG_INFO, - "QQ", - "Normal IM, text type:\n [%d] => [%d], src: %s\n", - common->sender_uid, common->receiver_uid, - qq_get_source_str (common->sender_ver)); - _qq_process_recv_normal_im_text (data, cursor, len, common, - gc); - break; - case QQ_NORMAL_IM_FILE_REJECT_UDP: - qq_process_recv_file_reject (data, cursor, len, - common->sender_uid, gc); - break; - case QQ_NORMAL_IM_FILE_APPROVE_UDP: - qq_process_recv_file_accept (data, cursor, len, - common->sender_uid, gc); - break; - case QQ_NORMAL_IM_FILE_REQUEST_UDP: - qq_process_recv_file_request (data, cursor, len, - common->sender_uid, gc); - break; - case QQ_NORMAL_IM_FILE_CANCEL: - qq_process_recv_file_cancel (data, cursor, len, - common->sender_uid, gc); - break; - case QQ_NORMAL_IM_FILE_NOTIFY: - qq_process_recv_file_notify (data, cursor, len, - common->sender_uid, gc); - break; - default: - im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1); - im_unprocessed->common = common; - im_unprocessed->unknown = *cursor; - im_unprocessed->length = data + len - *cursor; - /* a simple process here, maybe more later */ - purple_debug (PURPLE_DEBUG_WARNING, "QQ", - "Normal IM, unprocessed type [0x%04x]\n", - common->normal_im_type); - hex_dump = hex_dump_to_str(im_unprocessed->unknown, im_unprocessed->length); - purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Dump unknown part.\n%s", hex_dump); - g_free(hex_dump); - g_free (common->session_md5); - return; + case QQ_NORMAL_IM_TEXT: + purple_debug (PURPLE_DEBUG_INFO, "QQ", + "Normal IM, text type:\n [%d] => [%d], src: %s\n", + common->sender_uid, common->receiver_uid, + qq_get_source_str (common->sender_ver)); + if (bytes >= len - 1) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n"); + return; + } + _qq_process_recv_normal_im_text(data + bytes, len - bytes, common, gc); + break; + case QQ_NORMAL_IM_FILE_REJECT_UDP: + qq_process_recv_file_reject(data + bytes, len - bytes, common->sender_uid, gc); + break; + case QQ_NORMAL_IM_FILE_APPROVE_UDP: + qq_process_recv_file_accept(data + bytes, len - bytes, common->sender_uid, gc); + break; + case QQ_NORMAL_IM_FILE_REQUEST_UDP: + qq_process_recv_file_request(data + bytes, len - bytes, common->sender_uid, gc); + break; + case QQ_NORMAL_IM_FILE_CANCEL: + qq_process_recv_file_cancel(data + bytes, len - bytes, common->sender_uid, gc); + break; + case QQ_NORMAL_IM_FILE_NOTIFY: + qq_process_recv_file_notify(data + bytes, len - bytes, common->sender_uid, gc); + break; + default: + im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1); + im_unprocessed->common = common; + im_unprocessed->unknown = data + bytes; + im_unprocessed->length = len - bytes; + /* a simple process here, maybe more later */ + purple_debug (PURPLE_DEBUG_WARNING, "QQ", + "Normal IM, unprocessed type [0x%04x], unknown [0x%02x], len %d\n", + common->normal_im_type, im_unprocessed->unknown, im_unprocessed->length); + g_free (common->session_md5); + return; } g_free (common->session_md5); } /* process im from system administrator */ -static void _qq_process_recv_sys_im(guint8 *data, guint8 **cursor, gint data_len, PurpleConnection *gc) +static void _qq_process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc) { gint len; guint8 reply; @@ -415,14 +402,9 @@ g_return_if_fail(data != NULL && data_len != 0); - if (*cursor >= (data + data_len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received sys IM is empty\n"); - return; - } + len = data_len; - len = data + data_len - *cursor; - - if (NULL == (segments = split_data(*cursor, len, "\x2f", 2))) + if (NULL == (segments = split_data(data, len, "\x2f", 2))) return; reply = strtol(segments[0], NULL, 10); @@ -436,7 +418,7 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type) { qq_data *qd; - guint8 *cursor, *raw_data, *send_im_tail; + guint8 *raw_data, *send_im_tail; guint16 client_tag, normal_im_type; gint msg_len, raw_len, font_name_len, tail_len, bytes; time_t now; @@ -500,52 +482,51 @@ raw_len = QQ_SEND_IM_BEFORE_MSG_LEN + msg_len + tail_len; raw_data = g_newa(guint8, raw_len); - cursor = raw_data; bytes = 0; /* 000-003: receiver uid */ - bytes += create_packet_dw(raw_data, &cursor, qd->uid); + bytes += qq_put32(raw_data + bytes, qd->uid); /* 004-007: sender uid */ - bytes += create_packet_dw(raw_data, &cursor, to_uid); + bytes += qq_put32(raw_data + bytes, to_uid); /* 008-009: sender client version */ - bytes += create_packet_w(raw_data, &cursor, client_tag); + bytes += qq_put16(raw_data + bytes, client_tag); /* 010-013: receiver uid */ - bytes += create_packet_dw(raw_data, &cursor, qd->uid); + bytes += qq_put32(raw_data + bytes, qd->uid); /* 014-017: sender uid */ - bytes += create_packet_dw(raw_data, &cursor, to_uid); + bytes += qq_put32(raw_data + bytes, to_uid); /* 018-033: md5 of (uid+session_key) */ - bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16); + bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); /* 034-035: message type */ - bytes += create_packet_w(raw_data, &cursor, normal_im_type); + bytes += qq_put16(raw_data + bytes, normal_im_type); /* 036-037: sequence number */ - bytes += create_packet_w(raw_data, &cursor, qd->send_seq); + bytes += qq_put16(raw_data + bytes, qd->send_seq); /* 038-041: send time */ - bytes += create_packet_dw(raw_data, &cursor, (guint32) now); + bytes += qq_put32(raw_data + bytes, (guint32) now); /* 042-043: sender icon */ - bytes += create_packet_w(raw_data, &cursor, qd->my_icon); + bytes += qq_put16(raw_data + bytes, qd->my_icon); /* 044-046: always 0x00 */ - bytes += create_packet_w(raw_data, &cursor, 0x0000); - bytes += create_packet_b(raw_data, &cursor, 0x00); + bytes += qq_put16(raw_data + bytes, 0x0000); + bytes += qq_put8(raw_data + bytes, 0x00); /* 047-047: we use font attr */ - bytes += create_packet_b(raw_data, &cursor, 0x01); + bytes += qq_put8(raw_data + bytes, 0x01); /* 048-051: always 0x00 */ - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); /* 052-052: text message type (normal/auto-reply) */ - bytes += create_packet_b(raw_data, &cursor, type); + bytes += qq_put8(raw_data + bytes, type); /* 053- : msg ends with 0x00 */ - bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len); + 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_MESG debug", send_im_tail, tail_len); - bytes += create_packet_data(raw_data, &cursor, send_im_tail, tail_len); + is_italic, is_underline, tail_len); + qq_show_packet("QQ_send_im_tail debug", send_im_tail, tail_len); + bytes += qq_putdata(raw_data + bytes, send_im_tail, tail_len); - _qq_show_packet("QQ_MESG raw", raw_data, cursor - raw_data); + qq_show_packet("QQ_raw_data debug", raw_data, bytes); if (bytes == raw_len) /* create packet OK */ - qq_send_cmd(gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, cursor - raw_data); + qq_send_cmd(qd, QQ_CMD_SEND_IM, raw_data, bytes); else purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes); + "Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes); if (font_color) g_free(font_color); @@ -560,7 +541,8 @@ { qq_data *qd; gint len; - guint8 *data, *cursor, reply; + guint8 *data, reply; + gint bytes = 0; g_return_if_fail(buf != NULL && buf_len != 0); @@ -569,8 +551,7 @@ data = g_newa(guint8, len); if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - cursor = data; - read_packet_b(data, &cursor, len, &reply); + bytes += qq_get8(&reply, data + bytes); if (reply != QQ_SEND_IM_REPLY_OK) { purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Send IM fail\n"); purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL); @@ -588,7 +569,7 @@ { qq_data *qd; gint len, bytes; - guint8 *data, *cursor; + guint8 *data; qq_recv_im_header *im_header; g_return_if_fail(buf != NULL && buf_len != 0); @@ -597,98 +578,107 @@ len = buf_len; data = g_newa(guint8, len); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - if (len < 16) { /* we need to ack with the first 16 bytes */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n"); - return; - } else - _qq_send_packet_recv_im_ack(gc, seq, data); + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n"); + } + + if (len < 16) { /* we need to ack with the first 16 bytes */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n"); + return; + } else { + _qq_send_packet_recv_im_ack(gc, seq, data); + } + + /* check len first */ + if (len < 20) { /* length of im_header */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Fail read recv IM header, len should longer than 20 bytes, read %d bytes\n", len); + return; + } - cursor = data; - bytes = 0; - im_header = g_newa(qq_recv_im_header, 1); - bytes += read_packet_dw(data, &cursor, len, &(im_header->sender_uid)); - bytes += read_packet_dw(data, &cursor, len, &(im_header->receiver_uid)); - bytes += read_packet_dw(data, &cursor, len, &(im_header->server_im_seq)); - /* if the message is delivered via server, it is server IP/port */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) & (im_header->sender_ip), 4); - bytes += read_packet_w(data, &cursor, len, &(im_header->sender_port)); - bytes += read_packet_w(data, &cursor, len, &(im_header->im_type)); + bytes = 0; + im_header = g_newa(qq_recv_im_header, 1); + bytes += qq_get32(&(im_header->sender_uid), data + bytes); + bytes += qq_get32(&(im_header->receiver_uid), data + bytes); + bytes += qq_get32(&(im_header->server_im_seq), data + bytes); + /* if the message is delivered via server, it is server IP/port */ + bytes += qq_getdata((guint8 *) & (im_header->sender_ip), 4, data + bytes); + bytes += qq_get16(&(im_header->sender_port), data + bytes); + bytes += qq_get16(&(im_header->im_type), data + bytes); + /* im_header prepared */ - if (bytes != 20) { /* length of im_header */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail read recv IM header, expect 20 bytes, read %d bytes\n", bytes); - return; - } + if (im_header->receiver_uid != qd->uid) { /* should not happen */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid); + return; + } - if (im_header->receiver_uid != qd->uid) { /* should not happen */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid); - return; - } + /* check bytes */ + if (bytes >= len - 1) { + purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received IM is empty\n"); + return; + } - switch (im_header->im_type) { + switch (im_header->im_type) { case QQ_RECV_IM_TO_BUDDY: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid); - _qq_process_recv_normal_im(data, &cursor, len, gc); + "IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid); + _qq_process_recv_normal_im(data + bytes, len - bytes, gc); /* position and rest length */ break; case QQ_RECV_IM_TO_UNKNOWN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid); - _qq_process_recv_normal_im(data, &cursor, len, gc); + "IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid); + _qq_process_recv_normal_im(data + bytes, len - bytes, gc); break; case QQ_RECV_IM_UNKNOWN_QUN_IM: case QQ_RECV_IM_TEMP_QUN_IM: case QQ_RECV_IM_QUN_IM: purple_debug(PURPLE_DEBUG_INFO, "QQ", "IM from group, internal_id [%d]\n", im_header->sender_uid); /* sender_uid is in fact internal_group_id */ - qq_process_recv_group_im(data, &cursor, len, im_header->sender_uid, gc, im_header->im_type); + qq_process_recv_group_im(data + bytes, len - bytes, im_header->sender_uid, gc, im_header->im_type); break; case QQ_RECV_IM_ADD_TO_QUN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from group, added by group internal_id [%d]\n", im_header->sender_uid); + "IM from group, added by group internal_id [%d]\n", im_header->sender_uid); /* sender_uid is in fact internal_group_id * we need this to create a dummy group and add to blist */ - qq_process_recv_group_im_been_added(data, &cursor, len, im_header->sender_uid, gc); + qq_process_recv_group_im_been_added(data + bytes, len - bytes, im_header->sender_uid, gc); break; case QQ_RECV_IM_DEL_FROM_QUN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid); + "IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid); /* sender_uid is in fact internal_group_id */ - qq_process_recv_group_im_been_removed(data, &cursor, len, im_header->sender_uid, gc); + qq_process_recv_group_im_been_removed(data + bytes, len - bytes, im_header->sender_uid, gc); break; case QQ_RECV_IM_APPLY_ADD_TO_QUN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid); + "IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid); /* sender_uid is in fact internal_group_id */ - qq_process_recv_group_im_apply_join(data, &cursor, len, im_header->sender_uid, gc); + qq_process_recv_group_im_apply_join(data + bytes, len - bytes, im_header->sender_uid, gc); break; case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM for group system info, approved by group internal_id [%d]\n", - im_header->sender_uid); + "IM for group system info, approved by group internal_id [%d]\n", + im_header->sender_uid); /* sender_uid is in fact internal_group_id */ - qq_process_recv_group_im_been_approved(data, &cursor, len, im_header->sender_uid, gc); + qq_process_recv_group_im_been_approved(data + bytes, len - bytes, im_header->sender_uid, gc); break; case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM for group system info, rejected by group internal_id [%d]\n", - im_header->sender_uid); + "IM for group system info, rejected by group internal_id [%d]\n", + im_header->sender_uid); /* sender_uid is in fact internal_group_id */ - qq_process_recv_group_im_been_rejected(data, &cursor, len, im_header->sender_uid, gc); + qq_process_recv_group_im_been_rejected(data + bytes, len - bytes, im_header->sender_uid, gc); break; case QQ_RECV_IM_SYS_NOTIFICATION: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from [%d], should be a system administrator\n", im_header->sender_uid); - _qq_process_recv_sys_im(data, &cursor, len, gc); + "IM from [%d], should be a system administrator\n", im_header->sender_uid); + _qq_process_recv_sys_im(data + bytes, len - bytes, gc); break; default: purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "IM from [%d], [0x%02x] %s is not processed\n", - im_header->sender_uid, - im_header->im_type, qq_get_recv_im_type_str(im_header->im_type)); - } - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n"); + "IM from [%d], [0x%02x] %s is not processed\n", + im_header->sender_uid, + im_header->im_type, qq_get_recv_im_type_str(im_header->im_type)); } } +
--- a/libpurple/protocols/qq/keep_alive.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/keep_alive.c Wed Jul 09 00:32:18 2008 +0000 @@ -40,7 +40,7 @@ #include "header_info.h" #include "keep_alive.h" #include "packet_parse.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" #define QQ_UPDATE_ONLINE_INTERVAL 300 /* in sec */ @@ -49,18 +49,17 @@ void qq_send_packet_keep_alive(PurpleConnection *gc) { qq_data *qd; - guint8 *raw_data, *cursor; + guint8 raw_data[16] = {0}; + gint bytes= 0; qd = (qq_data *) gc->proto_data; - raw_data = g_newa(guint8, 4); - cursor = raw_data; /* 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 */ - create_packet_dw(raw_data, &cursor, qd->uid); + bytes += qq_put32(raw_data + bytes, qd->uid); - qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, TRUE, 0, TRUE, raw_data, 4); + qq_send_cmd(qd, QQ_CMD_KEEP_ALIVE, raw_data, 4); } /* parse the return of keep-alive packet, it includes some system information */
--- a/libpurple/protocols/qq/login_logout.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/login_logout.c Wed Jul 09 00:32:18 2008 +0000 @@ -25,6 +25,7 @@ #include "debug.h" #include "internal.h" #include "server.h" +#include "cipher.h" #include "buddy_info.h" #include "buddy_list.h" @@ -36,8 +37,7 @@ #include "login_logout.h" #include "packet_parse.h" #include "qq.h" -#include "qq_proxy.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" #define QQ_LOGIN_DATA_LENGTH 416 @@ -70,32 +70,36 @@ */ /* for QQ 2005? copy from lumaqq */ -static const gint8 login_23_51[29] = { - 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, -122, - -52, 76, 53, 44, -45, 115, 108, 20, -10, -10, - -81, -61, -6, 51, -92, 1 +/* FIXME: change to guint8 */ +static const guint8 login_23_51[29] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35, + 0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf, + 0xc3, 0xfa, 0x33, 0xa4, 0x01 }; -static const gint8 login_53_68[16] = { - -115, -117, -6, -20, -43, 82, 23, 74, -122, -7, - -89, 117, -26, 50, -47, 109 +static const guint8 login_53_68[16] = { + 0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A, + 0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D }; -static const gint8 login_100_bytes[100] = { - 64, - 11, 4, 2, 0, 1, 0, 0, 0, 0, 0, - 3, 9, 0, 0, 0, 0, 0, 0, 0, 1, - -23, 3, 1, 0, 0, 0, 0, 0, 1, -13, - 3, 0, 0, 0, 0, 0, 0, 1, -19, 3, - 0, 0, 0, 0, 0, 0, 1, -20, 3, 0, - 0, 0, 0, 0, 0, 3, 5, 0, 0, 0, - 0, 0, 0, 0, 3, 7, 0, 0, 0, 0, - 0, 0, 0, 1, -18, 3, 0, 0, 0, 0, - 0, 0, 1, -17, 3, 0, 0, 0, 0, 0, - 0, 1, -21, 3, 0, 0, 0, 0, 0 +static const guint8 login_100_bytes[100] = { + 0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + /* fixed value, not affected by version, or mac address */ /* static const guint8 login_53_68[16] = { @@ -138,74 +142,80 @@ guint16 new_server_port; }; -extern gint /* defined in send_core.c */ - _create_packet_head_seq(guint8 *buf, - guint8 **cursor, PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq); -extern gint /* defined in send_core.c */ - _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd); +/* generate a md5 key using uid and session_key */ +static guint8 *gen_session_md5(gint uid, guint8 *session_key) +{ + guint8 *src, md5_str[QQ_KEY_LENGTH]; + PurpleCipher *cipher; + PurpleCipherContext *context; -/* It is fixed to 16 bytes 0x01 for QQ2003, - * Any value works (or a random 16 bytes string) */ -static guint8 *_gen_login_key(void) -{ - return (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01); + src = g_newa(guint8, 20); + /* bug found by QuLogic */ + memcpy(src, &uid, sizeof(uid)); + memcpy(src + sizeof(uid), session_key, QQ_KEY_LENGTH); + + cipher = purple_ciphers_find_cipher("md5"); + context = purple_cipher_context_new(cipher, NULL); + purple_cipher_context_append(context, src, 20); + purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL); + purple_cipher_context_destroy(context); + + return g_memdup(md5_str, QQ_KEY_LENGTH); } /* process login reply which says OK */ static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len) { gint bytes; - guint8 *cursor; qq_data *qd; qq_login_reply_ok_packet lrop; qd = (qq_data *) gc->proto_data; - cursor = data; + /* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */ bytes = 0; /* 000-000: reply code */ - bytes += read_packet_b(data, &cursor, len, &lrop.result); + bytes += qq_get8(&lrop.result, data + bytes); /* 001-016: session key */ - lrop.session_key = g_memdup(cursor, QQ_KEY_LENGTH); - cursor += QQ_KEY_LENGTH; + lrop.session_key = g_memdup(data + bytes, QQ_KEY_LENGTH); bytes += QQ_KEY_LENGTH; purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get session_key done\n"); /* 017-020: login uid */ - bytes += read_packet_dw(data, &cursor, len, &lrop.uid); + bytes += qq_get32(&lrop.uid, data + bytes); /* 021-024: server detected user public IP */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.client_ip, 4); + bytes += qq_getdata((guint8 *) &lrop.client_ip, 4, data + bytes); /* 025-026: server detected user port */ - bytes += read_packet_w(data, &cursor, len, &lrop.client_port); + bytes += qq_get16(&lrop.client_port, data + bytes); /* 027-030: server detected itself ip 127.0.0.1 ? */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.server_ip, 4); + bytes += qq_getdata((guint8 *) &lrop.server_ip, 4, data + bytes); /* 031-032: server listening port */ - bytes += read_packet_w(data, &cursor, len, &lrop.server_port); + bytes += qq_get16(&lrop.server_port, data + bytes); /* 033-036: login time for current session */ - bytes += read_packet_time(data, &cursor, len, &lrop.login_time); + bytes += qq_getime(&lrop.login_time, data + bytes); /* 037-062: 26 bytes, unknown */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown1, 26); + bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes); /* 063-066: unknown server1 ip address */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server1_ip, 4); + bytes += qq_getdata((guint8 *) &lrop.unknown_server1_ip, 4, data + bytes); /* 067-068: unknown server1 port */ - bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server1_port); + bytes += qq_get16(&lrop.unknown_server1_port, data + bytes); /* 069-072: unknown server2 ip address */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server2_ip, 4); + bytes += qq_getdata((guint8 *) &lrop.unknown_server2_ip, 4, data + bytes); /* 073-074: unknown server2 port */ - bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server2_port); + bytes += qq_get16(&lrop.unknown_server2_port, data + bytes); /* 075-076: 2 bytes unknown */ - bytes += read_packet_w(data, &cursor, len, &lrop.unknown2); + bytes += qq_get16(&lrop.unknown2, data + bytes); /* 077-078: 2 bytes unknown */ - bytes += read_packet_w(data, &cursor, len, &lrop.unknown3); + bytes += qq_get16(&lrop.unknown3, data + bytes); /* 079-110: 32 bytes unknown */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown4, 32); + bytes += qq_getdata((guint8 *) &lrop.unknown4, 32, data + bytes); /* 111-122: 12 bytes unknown */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown5, 12); + bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes); /* 123-126: login IP of last session */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.last_client_ip, 4); + bytes += qq_getdata((guint8 *) &lrop.last_client_ip, 4, data + bytes); /* 127-130: login time of last session */ - bytes += read_packet_time(data, &cursor, len, &lrop.last_login_time); + bytes += qq_getime(&lrop.last_login_time, data + bytes); /* 131-138: 8 bytes unknown */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown6, 8); + bytes += qq_getdata((guint8 *) &lrop.unknown6, 8, data + bytes); if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) { /* fail parsing login info */ purple_debug(PURPLE_DEBUG_WARNING, "QQ", @@ -213,9 +223,15 @@ QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes); } /* but we still go on as login OK */ + g_return_val_if_fail(qd->session_key == NULL, QQ_LOGIN_REPLY_MISC_ERROR); qd->session_key = lrop.session_key; - qd->session_md5 = _gen_session_md5(qd->uid, qd->session_key); + + g_return_val_if_fail(qd->session_md5 == NULL, QQ_LOGIN_REPLY_MISC_ERROR); + qd->session_md5 = gen_session_md5(qd->uid, qd->session_key); + + g_return_val_if_fail(qd->my_ip == NULL, QQ_LOGIN_REPLY_MISC_ERROR); qd->my_ip = gen_ip_str(lrop.client_ip); + qd->my_port = lrop.client_port; qd->login_time = lrop.login_time; qd->last_login_time = lrop.last_login_time; @@ -247,34 +263,40 @@ static gint _qq_process_login_redirect(PurpleConnection *gc, guint8 *data, gint len) { gint bytes, ret; - guint8 *cursor; - gchar *new_server_str; qq_data *qd; qq_login_reply_redirect_packet lrrp; qd = (qq_data *) gc->proto_data; - cursor = data; bytes = 0; /* 000-000: reply code */ - bytes += read_packet_b(data, &cursor, len, &lrrp.result); + bytes += qq_get8(&lrrp.result, data + bytes); /* 001-004: login uid */ - bytes += read_packet_dw(data, &cursor, len, &lrrp.uid); + bytes += qq_get32(&lrrp.uid, data + bytes); /* 005-008: redirected new server IP */ - bytes += read_packet_data(data, &cursor, len, lrrp.new_server_ip, 4); + bytes += qq_getdata(lrrp.new_server_ip, 4, data + bytes); /* 009-010: redirected new server port */ - bytes += read_packet_w(data, &cursor, len, &lrrp.new_server_port); + bytes += qq_get16(&lrrp.new_server_port, data + bytes); if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n", QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes); ret = QQ_LOGIN_REPLY_MISC_ERROR; - } else { /* start new connection */ - new_server_str = gen_ip_str(lrrp.new_server_ip); + } else { + /* redirect to new server, do not disconnect or connect here + * those connect should be called at packet_process */ + if (qd->real_hostname) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); + g_free(qd->real_hostname); + qd->real_hostname = NULL; + } + qd->real_hostname = gen_ip_str(lrrp.new_server_ip); + qd->real_port = lrrp.new_server_port; + qd->is_redirect = TRUE; + purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Redirected to new server: %s:%d\n", new_server_str, lrrp.new_server_port); - qq_connect(gc->account, new_server_str, lrrp.new_server_port, qd->use_tcp, TRUE); - g_free(new_server_str); + "Redirected to new server: %s:%d\n", qd->real_hostname, qd->real_port); + ret = QQ_LOGIN_REPLY_REDIRECT; } @@ -299,89 +321,78 @@ void qq_send_packet_request_login_token(PurpleConnection *gc) { qq_data *qd; - guint8 *buf, *cursor; - guint16 seq_ret; - gint bytes; - - qd = (qq_data *) gc->proto_data; - buf = g_newa(guint8, MAX_PACKET_SIZE); + guint8 buf[16] = {0}; + gint bytes = 0; - cursor = buf; - bytes = 0; - bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_REQUEST_LOGIN_TOKEN, TRUE, &seq_ret); - bytes += create_packet_dw(buf, &cursor, qd->uid); - bytes += create_packet_b(buf, &cursor, 0); - bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL); + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; - if (bytes == (cursor - buf)) /* packet creation OK */ - _qq_send_packet(gc, buf, bytes, QQ_CMD_REQUEST_LOGIN_TOKEN); - else - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create request login token packet\n"); + bytes += qq_put8(buf + bytes, 0); + + qq_send_data(qd, QQ_CMD_REQUEST_LOGIN_TOKEN, buf, bytes); } /* send login packet to QQ server */ static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guint8 *token) { qq_data *qd; - guint8 *buf, *cursor, *raw_data, *encrypted_data; - guint16 seq_ret; - gint encrypted_len, bytes; - gint pos; + guint8 *buf, *raw_data; + gint bytes; + guint8 *encrypted_data; + gint encrypted_len; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; - buf = g_newa(guint8, MAX_PACKET_SIZE); + raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH); + memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH); + encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16); /* 16 bytes more */ - qd->inikey = _gen_login_key(); + if (qd->inikey) { + g_free(qd->inikey); + } + qd->inikey = (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01); + bytes = 0; /* now generate the encrypted data * 000-015 use pwkey as key to encrypt empty string */ - qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data, &encrypted_len); + qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data + bytes, &encrypted_len); + bytes += 16; /* 016-016 */ - raw_data[16] = 0x00; + bytes += qq_put8(raw_data + bytes, 0x00); /* 017-020, used to be IP, now zero */ - *((guint32 *) (raw_data + 17)) = 0x00000000; + bytes += qq_put32(raw_data + bytes, 0x00000000); /* 021-022, used to be port, now zero */ - *((guint16 *) (raw_data + 21)) = 0x0000; + bytes += qq_put16(raw_data + bytes, 0x0000); /* 023-051, fixed value, unknown */ - g_memmove(raw_data + 23, login_23_51, 29); + bytes += qq_putdata(raw_data + bytes, login_23_51, 29); /* 052-052, login mode */ - raw_data[52] = qd->login_mode; + bytes += qq_put8(raw_data + bytes, qd->login_mode); /* 053-068, fixed value, maybe related to per machine */ - g_memmove(raw_data + 53, login_53_68, 16); - + bytes += qq_putdata(raw_data + bytes, login_53_68, 16); /* 069, login token length */ - raw_data[69] = token_length; - pos = 70; + bytes += qq_put8(raw_data + bytes, token_length); /* 070-093, login token, normally 24 bytes */ - g_memmove(raw_data + pos, token, token_length); - pos += token_length; + bytes += qq_putdata(raw_data + bytes, token, token_length); /* 100 bytes unknown */ - g_memmove(raw_data + pos, login_100_bytes, 100); - pos += 100; + bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100); /* all zero left */ - memset(raw_data+pos, 0, QQ_LOGIN_DATA_LENGTH - pos); qq_encrypt(raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len); - cursor = buf; + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); bytes = 0; - bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_LOGIN, TRUE, &seq_ret); - bytes += create_packet_dw(buf, &cursor, qd->uid); - bytes += create_packet_data(buf, &cursor, qd->inikey, QQ_KEY_LENGTH); - bytes += create_packet_data(buf, &cursor, encrypted_data, encrypted_len); - bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL); + bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH); + bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len); - if (bytes == (cursor - buf)) /* packet creation OK */ - _qq_send_packet(gc, buf, bytes, QQ_CMD_LOGIN); - else - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create login packet\n"); + qq_send_data(qd, QQ_CMD_LOGIN, buf, bytes); } void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) { qq_data *qd; - gchar *hex_dump; + gchar *error_msg; g_return_if_fail(buf != NULL && buf_len != 0); @@ -394,20 +405,24 @@ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Attempting to proceed with the actual packet length.\n"); } - hex_dump = hex_dump_to_str(buf+2, buf_len-2); - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "<<< got a token with %d bytes -> [default] decrypt and dump\n%s", buf_len-2, hex_dump); + qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", + buf+2, buf_len-2, + "<<< got a token -> [default] decrypt and dump"); qq_send_packet_login(gc, buf_len-2, buf+2); } else { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown request login token reply code : %d\n", buf[0]); - hex_dump = hex_dump_to_str(buf, buf_len); - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - ">>> %d bytes -> [default] decrypt and dump\n%s", - buf_len, hex_dump); - try_dump_as_gbk(buf, buf_len); - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Error requesting login token")); + qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", + buf, buf_len, + ">>> [default] decrypt and dump"); + error_msg = try_dump_as_gbk(buf, buf_len); + if (error_msg) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); + g_free(error_msg); + } else { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Error requesting login token")); + } } - g_free(hex_dump); } /* send logout packets to QQ server */ @@ -418,7 +433,7 @@ qd = (qq_data *) gc->proto_data; for (i = 0; i < 4; i++) - qq_send_cmd(gc, QQ_CMD_LOGOUT, FALSE, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH); + qq_send_cmd_detail(qd, QQ_CMD_LOGOUT, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH); qd->logged_in = FALSE; /* update login status AFTER sending logout packets */ } @@ -429,7 +444,7 @@ gint len, ret, bytes; guint8 *data; qq_data *qd; - gchar *hex_dump; + gchar* error_msg; g_return_if_fail(buf != NULL && buf_len != 0); @@ -462,13 +477,14 @@ break; default: purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: %d\n", data[0]); - hex_dump = hex_dump_to_str(data, len); - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - ">>> %d bytes -> [default] decrypt and dump\n%s", - buf_len, hex_dump); - g_free(hex_dump); - try_dump_as_gbk(data, len); - + qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", + data, len, + ">>> [default] decrypt and dump"); + error_msg = try_dump_as_gbk(data, len); + if (error_msg) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); + g_free(error_msg); + } ret = QQ_LOGIN_REPLY_MISC_ERROR; } } else { /* no idea how to decrypt */
--- a/libpurple/protocols/qq/packet_parse.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/packet_parse.c Wed Jul 09 00:32:18 2008 +0000 @@ -25,119 +25,147 @@ #include <string.h> #include "packet_parse.h" +#include "debug.h" + + +/*------------------------------------------------PUT------------------------------------------------*/ + +/* 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. */ + +#if 0 +#define PARSER_DEBUG +#endif /* read one byte from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b) +gint qq_get8(guint8 *b, guint8 *buf) { - if (*cursor <= buf + buflen - sizeof(*b)) { - *b = **(guint8 **) cursor; - *cursor += sizeof(*b); - return sizeof(*b); - } else { - return -1; - } + guint8 b_dest; + memcpy(&b_dest, buf, sizeof(b_dest)); + *b = b_dest; +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] b_dest 0x%2x, *b 0x%02x\n", b_dest, *b); +#endif + return sizeof(b_dest); } + /* read two bytes as "guint16" from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w) +gint qq_get16(guint16 *w, guint8 *buf) { - if (*cursor <= buf + buflen - sizeof(*w)) { - *w = g_ntohs(**(guint16 **) cursor); - *cursor += sizeof(*w); - return sizeof(*w); - } else { - return -1; - } + guint16 w_dest; + memcpy(&w_dest, buf, sizeof(w_dest)); + *w = g_ntohs(w_dest); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] w_dest 0x%04x, *w 0x%04x\n", w_dest, *w); +#endif + return sizeof(w_dest); } + /* read four bytes as "guint32" from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw) +gint qq_get32(guint32 *dw, guint8 *buf) { - if (*cursor <= buf + buflen - sizeof(*dw)) { - *dw = g_ntohl(**(guint32 **) cursor); - *cursor += sizeof(*dw); - return sizeof(*dw); - } else { - return -1; - } + guint32 dw_dest; + memcpy(&dw_dest, buf, sizeof(dw_dest)); + *dw = g_ntohl(dw_dest); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] dw_dest 0x%08x, *dw 0x%08x\n", dw_dest, *dw); +#endif + return sizeof(dw_dest); } + +/* read datalen bytes from buf, + * return the number of bytes read if succeeds, otherwise return -1 */ +gint qq_getdata(guint8 *data, gint datalen, guint8 *buf) +{ + memcpy(data, buf, datalen); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getdata] buf %p\n", (void *)buf); +#endif + return datalen; +} + + /* read four bytes as "time_t" from buf, * return the number of bytes read if succeeds, otherwise return -1 * This function is a wrapper around read_packet_dw() to avoid casting. */ -gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t) +gint qq_getime(time_t *t, guint8 *buf) { - guint32 time; - gint ret = read_packet_dw(buf, cursor, buflen, &time); - if (ret != -1 ) { - *t = time; - } - return ret; + guint32 dw_dest; + memcpy(&dw_dest, buf, sizeof(dw_dest)); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest before 0x%08x\n", dw_dest); +#endif + dw_dest = g_ntohl(dw_dest); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest); +#endif + memcpy(t, &dw_dest, sizeof(dw_dest)); + return sizeof(dw_dest); } -/* read datalen bytes from buf, - * return the number of bytes read if succeeds, otherwise return -1 */ -gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen) { - if (*cursor <= buf + buflen - datalen) { - g_memmove(data, *cursor, datalen); - *cursor += datalen; - return datalen; - } else { - return -1; - } +/*------------------------------------------------PUT------------------------------------------------*/ +/* pack one byte into buf + * return the number of bytes packed, otherwise return -1 */ +gint qq_put8(guint8 *buf, guint8 b) +{ + memcpy(buf, &b, sizeof(b)); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] b 0x%02x\n", b); +#endif + return sizeof(b); } -/* pack one byte into buf - * return the number of bytes packed, otherwise return -1 */ -gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b) -{ - if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint8)) { - **(guint8 **) cursor = b; - *cursor += sizeof(guint8); - return sizeof(guint8); - } else { - return -1; - } -} /* pack two bytes as "guint16" into buf * return the number of bytes packed, otherwise return -1 */ -gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w) +gint qq_put16(guint8 *buf, guint16 w) { - if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint16)) { - **(guint16 **) cursor = g_htons(w); - *cursor += sizeof(guint16); - return sizeof(guint16); - } else { - return -1; - } + guint16 w_porter; + w_porter = g_htons(w); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] w 0x%04x, w_porter 0x%04x\n", w, w_porter); +#endif + memcpy(buf, &w_porter, sizeof(w_porter)); + return sizeof(w_porter); } + /* pack four bytes as "guint32" into buf * return the number of bytes packed, otherwise return -1 */ -gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw) +gint qq_put32(guint8 *buf, guint32 dw) { - if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint32)) { - **(guint32 **) cursor = g_htonl(dw); - *cursor += sizeof(guint32); - return sizeof(guint32); - } else { - return -1; - } + guint32 dw_porter; + dw_porter = g_htonl(dw); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter); +#endif + memcpy(buf, &dw_porter, sizeof(dw_porter)); + return sizeof(dw_porter); } + /* pack datalen bytes into buf * return the number of bytes packed, otherwise return -1 */ -gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen) +gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen) { - if (*cursor <= buf + MAX_PACKET_SIZE - datalen) { - g_memmove(*cursor, data, datalen); - *cursor += datalen; - return datalen; - } else { - return -1; - } + memcpy(buf, data, datalen); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][putdata] buf %p\n", (void *)buf); +#endif + return datalen; }
--- a/libpurple/protocols/qq/packet_parse.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/packet_parse.h Wed Jul 09 00:32:18 2008 +0000 @@ -37,14 +37,28 @@ */ #define MAX_PACKET_SIZE 65535 +gint qq_get8(guint8 *b, guint8 *buf); +gint qq_get16(guint16 *w, guint8 *buf); +gint qq_get32(guint32 *dw, guint8 *buf); +gint qq_getime(time_t *t, guint8 *buf); +gint qq_getdata(guint8 *data, gint datalen, guint8 *buf); + +gint qq_put8(guint8 *buf, guint8 b); +gint qq_put16(guint8 *buf, guint16 w); +gint qq_put32(guint8 *buf, guint32 dw); +gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen); + +/* gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b); gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w); gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw); gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t); gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen); + gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b); gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w); gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw); gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen); +*/ #endif
--- a/libpurple/protocols/qq/qq.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/qq.c Wed Jul 09 00:32:18 2008 +0000 @@ -55,48 +55,94 @@ #include "login_logout.h" #include "packet_parse.h" #include "qq.h" -#include "qq_proxy.h" -#include "send_core.h" +#include "qq_network.h" #include "send_file.h" #include "utils.h" #include "version.h" #define OPENQ_AUTHOR "Puzzlebird" #define OPENQ_WEBSITE "http://openq.sourceforge.net" -#define QQ_TCP_QUERY_PORT "8000" -#define QQ_UDP_PORT "8000" + +#define QQ_TCP_PORT 8000 +#define QQ_UDP_PORT 8000 + +static void server_list_create(PurpleAccount *account) { + PurpleConnection *gc; + qq_data *qd; + const gchar *user_server; + int port; + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create server list\n"); + gc = purple_account_get_connection(account); + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = gc->proto_data; -const gchar *udp_server_list[] = { - "sz.tencent.com", - "sz2.tencent.com", - "sz3.tencent.com", - "sz4.tencent.com", - "sz5.tencent.com", - "sz6.tencent.com", - "sz7.tencent.com", - "sz8.tencent.com", - "sz9.tencent.com" -}; -const gint udp_server_amount = (sizeof(udp_server_list) / sizeof(udp_server_list[0])); + qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE); + port = purple_account_get_int(account, "port", 0); + if (port == 0) { + if (qd->use_tcp) { + port = QQ_TCP_PORT; + } else { + port = QQ_UDP_PORT; + } + } + qd->user_port = port; + g_return_if_fail(qd->user_server == NULL); + user_server = purple_account_get_string(account, "server", NULL); + if (user_server != NULL && strlen(user_server) > 0) { + qd->user_server = g_strdup(user_server); + } -const gchar *tcp_server_list[] = { - "tcpconn.tencent.com", - "tcpconn2.tencent.com", - "tcpconn3.tencent.com", - "tcpconn4.tencent.com", - "tcpconn5.tencent.com", - "tcpconn6.tencent.com" -}; -const gint tcp_server_amount = (sizeof(tcp_server_list) / sizeof(tcp_server_list[0])); + if (qd->user_server != NULL) { + qd->servers = g_list_append(qd->servers, qd->user_server); + return; + } + if (qd->use_tcp) { + qd->servers = g_list_append(qd->servers, "tcpconn.tencent.com"); + qd->servers = g_list_append(qd->servers, "tcpconn2.tencent.com"); + qd->servers = g_list_append(qd->servers, "tcpconn3.tencent.com"); + qd->servers = g_list_append(qd->servers, "tcpconn4.tencent.com"); + qd->servers = g_list_append(qd->servers, "tcpconn5.tencent.com"); + qd->servers = g_list_append(qd->servers, "tcpconn6.tencent.com"); + return; + } + + qd->servers = g_list_append(qd->servers, "sz.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz2.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz3.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz4.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz5.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz6.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz7.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz8.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz9.tencent.com"); +} -static void _qq_login(PurpleAccount *account) +static void server_list_remove_all(qq_data *qd) { + g_return_if_fail(qd != NULL); + + if (qd->real_hostname) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); + g_free(qd->real_hostname); + qd->real_hostname = NULL; + } + + if (qd->user_server != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free user_server\n"); + g_free(qd->user_server); + qd->user_server = NULL; + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free server list\n"); + g_list_free(qd->servers); +} + +static void qq_login(PurpleAccount *account) { - const gchar *qq_server, *qq_port; + PurpleConnection *gc; qq_data *qd; - PurpleConnection *gc; PurplePresence *presence; - gboolean use_tcp; g_return_if_fail(account != NULL); @@ -109,13 +155,7 @@ qd->gc = gc; gc->proto_data = qd; - qq_server = purple_account_get_string(account, "server", NULL); - qq_port = purple_account_get_string(account, "port", NULL); - use_tcp = purple_account_get_bool(account, "use_tcp", FALSE); presence = purple_account_get_presence(account); - - qd->use_tcp = use_tcp; - if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) { qd->login_mode = QQ_LOGIN_MODE_HIDDEN; } else if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY) @@ -125,26 +165,28 @@ qd->login_mode = QQ_LOGIN_MODE_NORMAL; } - if (qq_server == NULL || strlen(qq_server) == 0) - qq_server = use_tcp ? - tcp_server_list[random() % tcp_server_amount] : - udp_server_list[random() % udp_server_amount]; + server_list_create(account); + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Server list has %d\n", g_list_length(qd->servers)); - if (qq_port == NULL || strtol(qq_port, NULL, 10) == 0) - qq_port = use_tcp ? QQ_TCP_QUERY_PORT : QQ_UDP_PORT; - - purple_connection_update_progress(gc, _("Connecting"), 0, QQ_CONNECT_STEPS); - - if (qq_connect(account, qq_server, strtol(qq_port, NULL, 10), use_tcp, FALSE) < 0) - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect.")); + qq_connect(account); } -/* directly goes for qq_disconnect */ -static void _qq_close(PurpleConnection *gc) +/* clean up the given QQ connection and free all resources */ +static void qq_close(PurpleConnection *gc) { - g_return_if_fail(gc != NULL); + qq_data *qd; + + g_return_if_fail(gc != NULL && gc->proto_data); + qd = gc->proto_data; + qq_disconnect(gc); + + server_list_remove_all(qd); + + g_free(qd); + + gc->proto_data = NULL; } /* returns the icon name for a buddy or protocol */ @@ -442,8 +484,9 @@ g_string_append(info, "<hr>\n"); + g_string_append_printf(info, _("<b>Server</b>: %s: %d<br>\n"), qd->server_name, qd->real_port); g_string_append_printf(info, _("<b>Connection Mode</b>: %s<br>\n"), qd->use_tcp ? "TCP" : "UDP"); - g_string_append_printf(info, _("<b>Server IP</b>: %s: %d<br>\n"), qd->server_ip, qd->server_port); + g_string_append_printf(info, _("<b>Real hostname</b>: %s: %d<br>\n"), qd->real_hostname, qd->real_port); g_string_append_printf(info, _("<b>My Public IP</b>: %s<br>\n"), qd->my_ip); g_string_append(info, "<hr>\n"); @@ -594,7 +637,7 @@ } -static void _qq_keep_alive(PurpleConnection *gc) +static void qq_keep_alive(PurpleConnection *gc) { qq_group *group; qq_data *qd; @@ -651,8 +694,8 @@ _qq_buddy_menu, /* blist_node_menu */ qq_chat_info, /* chat_info */ qq_chat_info_defaults, /* chat_info_defaults */ - _qq_login, /* login */ - _qq_close, /* close */ + qq_login, /* open */ + qq_close, /* close */ _qq_send_im, /* send_im */ NULL, /* set_info */ NULL, /* send_typing */ @@ -676,7 +719,7 @@ NULL, /* chat_leave */ NULL, /* chat_whisper */ _qq_chat_send, /* chat_send */ - _qq_keep_alive, /* keepalive */ + qq_keep_alive, /* keepalive */ NULL, /* register_user */ _qq_get_chat_buddy_info, /* get_cb_info */ NULL, /* get_cb_away */ @@ -751,13 +794,13 @@ { PurpleAccountOption *option; - option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_string_new(_("Server"), "server", NULL); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_string_new(_("Port"), "port", NULL); + option = purple_account_option_int_new(_("Port"), "port", 0); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + + option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", TRUE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); my_protocol = plugin;
--- a/libpurple/protocols/qq/qq.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/qq.h Wed Jul 09 00:32:18 2008 +0000 @@ -28,6 +28,9 @@ #include <glib.h> #include "internal.h" #include "ft.h" +#include "circbuffer.h" +#include "dnsquery.h" +#include "dnssrv.h" #include "proxy.h" #include "roomlist.h" @@ -66,7 +69,39 @@ }; struct _qq_data { - gint fd; /* socket file handler */ + PurpleConnection *gc; + + /* common network resource */ + GList *servers; + gchar *user_server; + gint user_port; + gboolean use_tcp; /* network in tcp or udp */ + + gchar *server_name; + gboolean is_redirect; + gchar *real_hostname; /* from real connction */ + guint16 real_port; + guint reconnect_timeout; + gint reconnect_times; + + PurpleProxyConnectData *connect_data; + gint fd; /* socket file handler */ + gint tx_handler; /* socket can_write handle, use in udp connecting and tcp send out */ + + GList *send_trans; /* check ack packet and resend */ + guint resend_timeout; + + guint8 rcv_window[1 << 13]; /* windows for check duplicate packet */ + GQueue *rcv_trans; /* queue to store packet can not process before login */ + + /* tcp related */ + PurpleCircBuffer *tcp_txbuf; + guint8 *tcp_rxqueue; + int tcp_rxlen; + + /* udp related */ + PurpleDnsQueryData *udp_query_data; + guint32 uid; /* QQ number */ guint8 *inikey; /* initial key to encrypt login packet */ guint8 *pwkey; /* password in md5 (or md5' md5) */ @@ -76,17 +111,9 @@ guint16 send_seq; /* send sequence number */ guint8 login_mode; /* online of invisible */ gboolean logged_in; /* used by qq-add_buddy */ - gboolean use_tcp; /* network in tcp or udp */ - - PurpleProxyType proxy_type; - PurpleConnection *gc; PurpleXfer *xfer; /* file transfer handler */ - struct sockaddr_in dest_sin; - /* from real connction */ - gchar *server_ip; - guint16 server_port; /* get from login reply packet */ time_t login_time; time_t last_login_time; @@ -99,9 +126,6 @@ guint32 all_online; /* the number of online QQ users */ time_t last_get_online; /* last time send get_friends_online packet */ - guint8 window[1 << 13]; /* check up for duplicated packet */ - gint sendqueue_timeout; - PurpleRoomlist *roomlist; gint channel; /* the id for opened chat conversation */ @@ -112,16 +136,12 @@ GList *buddies; GList *contact_info_window; GList *group_info_window; - GList *sendqueue; GList *info_query; GList *add_buddy_request; - GQueue *before_login_packets; /* TODO pass qq_send_packet_get_info() a callback and use signals to get rid of these */ gboolean modifying_info; gboolean modifying_face; }; -void qq_function_not_implemented(PurpleConnection *gc); - #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_network.c Wed Jul 09 00:32:18 2008 +0000 @@ -0,0 +1,1258 @@ +/** + * @file qq_network.c + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "cipher.h" +#include "debug.h" +#include "internal.h" + +#ifdef _WIN32 +#define random rand +#define srandom srand +#endif + +#include "buddy_info.h" +#include "buddy_list.h" +#include "buddy_opt.h" +#include "buddy_status.h" +#include "group_free.h" +#include "char_conv.h" +#include "crypt.h" +#include "group_network.h" +#include "header_info.h" +#include "keep_alive.h" +#include "im.h" +#include "login_logout.h" +#include "packet_parse.h" +#include "qq_network.h" +#include "qq_trans.h" +#include "sys_msg.h" +#include "utils.h" + +/* set QQ_RECONNECT_MAX to 1, when test reconnecting */ +#define QQ_RECONNECT_MAX 4 +#define QQ_RECONNECT_INTERVAL 5000 + +static gboolean set_new_server(qq_data *qd) +{ + gint count; + gint index; + GList *it = NULL; + + g_return_val_if_fail(qd != NULL, FALSE); + + if (qd->servers == NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list is NULL\n"); + return FALSE; + } + + if (qd->real_hostname) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); + g_free(qd->real_hostname); + qd->real_hostname = NULL; + } + + /* remove server used before */ + if (qd->server_name != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Remove previous server [%s]\n", qd->server_name); + qd->servers = g_list_remove(qd->servers, qd->server_name); + qd->server_name = NULL; + } + + count = g_list_length(qd->servers); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list has %d\n", count); + if (count <= 0) { + /* no server left, disconnect when result is false */ + qd->servers = NULL; + return FALSE; + } + + /* get new server */ + index = random() % count; + it = g_list_nth(qd->servers, index); + qd->server_name = it->data; /* do not free server_name */ + if (qd->server_name == NULL || strlen(qd->server_name) <= 0 ) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server name at %d is empty\n", index); + return FALSE; + } + + qd->real_hostname = g_strdup(qd->server_name); + qd->real_port = qd->user_port; + + qd->reconnect_times = QQ_RECONNECT_MAX; + + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "set new server to %s:%d\n", qd->real_hostname, qd->real_port); + return TRUE; +} + +/* QQ 2003iii uses double MD5 for the pwkey to get the session key */ +static guint8 *encrypt_account_password(const gchar *pwd) +{ + PurpleCipher *cipher; + PurpleCipherContext *context; + + guchar pwkey_tmp[QQ_KEY_LENGTH]; + + cipher = purple_ciphers_find_cipher("md5"); + context = purple_cipher_context_new(cipher, NULL); + purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd)); + purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); + purple_cipher_context_destroy(context); + context = purple_cipher_context_new(cipher, NULL); + purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH); + purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); + purple_cipher_context_destroy(context); + + return g_memdup(pwkey_tmp, QQ_KEY_LENGTH); +} + +/* default process, decrypt and dump */ +static void process_cmd_unknow(PurpleConnection *gc, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq) +{ + qq_data *qd; + guint8 *data; + gint data_len; + gchar *msg_utf8 = NULL; + + g_return_if_fail(buf != NULL && buf_len != 0); + + qq_show_packet("Processing unknown packet", buf, buf_len); + + qd = (qq_data *) gc->proto_data; + + data_len = buf_len; + data = g_newa(guint8, data_len); + memset(data, 0, data_len); + if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len )) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n"); + return; + } + + qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", + data, data_len, + ">>> [%d] %s -> [default] decrypt and dump", + seq, qq_get_cmd_desc(cmd)); + + msg_utf8 = try_dump_as_gbk(data, data_len); + if (msg_utf8) { + g_free(msg_utf8); + } +} + +static gint packet_get_header(guint8 *header_tag, guint16 *source_tag, + guint16 *cmd, guint16 *seq, guint8 *buf) +{ + gint bytes = 0; + bytes += qq_get8(header_tag, buf + bytes); + bytes += qq_get16(source_tag, buf + bytes); + bytes += qq_get16(cmd, buf + bytes); + bytes += qq_get16(seq, buf + bytes); + return bytes; +} + +/* check whether one sequence number is duplicated or not + * return TRUE if it is duplicated, otherwise FALSE */ +static gboolean packet_is_dup(qq_data *qd, guint16 seq) +{ + guint8 *byte, mask; + + g_return_val_if_fail(qd != NULL, FALSE); + + byte = &(qd->rcv_window[seq / 8]); + mask = (1 << (seq % 8)); + + if ((*byte) & mask) + return TRUE; /* check mask */ + (*byte) |= mask; + return FALSE; /* set mask */ +} + +static gboolean packet_check_ack(qq_data *qd, guint16 seq) +{ + gpointer trans; + + g_return_val_if_fail(qd != NULL, FALSE); + + trans = qq_send_trans_find(qd, seq); + if (trans == NULL) { + return FALSE; + } + + qq_send_trans_remove(qd, trans); + return TRUE; +} + +static gboolean reconnect_later_cb(gpointer data) +{ + PurpleConnection *gc; + qq_data *qd; + + gc = (PurpleConnection *) data; + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); + qd = (qq_data *) gc->proto_data; + + qd->reconnect_timeout = 0; + + qq_connect(gc->account); + return FALSE; /* timeout callback stops */ +} + +static void reconnect_later(PurpleConnection *gc) +{ + qq_data *qd; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + qd->reconnect_times--; + if (qd->reconnect_times < 0) { + if ( set_new_server(qd) != TRUE) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Failed to connect server")); + return; + } + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Reconnect to server %s:%d next retries %d in %d ms\n", + qd->real_hostname, qd->real_port, + qd->reconnect_times, QQ_RECONNECT_INTERVAL); + + qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL, + reconnect_later_cb, gc); +} + +static void process_cmd_server( + PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) +{ + /* now process the packet */ + switch (cmd) { + case QQ_CMD_RECV_IM: + qq_process_recv_im(data, data_len, seq, gc); + break; + case QQ_CMD_RECV_MSG_SYS: + qq_process_msg_sys(data, data_len, seq, gc); + break; + case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: + qq_process_friend_change_status(data, data_len, gc); + break; + default: + process_cmd_unknow(gc, data, data_len, cmd, seq); + break; + } +} + +static void process_cmd_reply( + PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) +{ + /* now process the packet */ + switch (cmd) { + case QQ_CMD_KEEP_ALIVE: + qq_process_keep_alive_reply(data, data_len, gc); + break; + case QQ_CMD_UPDATE_INFO: + qq_process_modify_info_reply(data, data_len, gc); + break; + case QQ_CMD_ADD_FRIEND_WO_AUTH: + qq_process_add_buddy_reply(data, data_len, seq, gc); + break; + case QQ_CMD_DEL_FRIEND: + qq_process_remove_buddy_reply(data, data_len, gc); + break; + case QQ_CMD_REMOVE_SELF: + qq_process_remove_self_reply(data, data_len, gc); + break; + case QQ_CMD_BUDDY_AUTH: + qq_process_add_buddy_auth_reply(data, data_len, gc); + break; + case QQ_CMD_GET_USER_INFO: + qq_process_get_info_reply(data, data_len, gc); + break; + case QQ_CMD_CHANGE_ONLINE_STATUS: + qq_process_change_status_reply(data, data_len, gc); + break; + case QQ_CMD_SEND_IM: + qq_process_send_im_reply(data, data_len, gc); + break; + case QQ_CMD_LOGIN: + qq_process_login_reply(data, data_len, gc); + break; + case QQ_CMD_GET_FRIENDS_LIST: + qq_process_get_buddies_list_reply(data, data_len, gc); + break; + case QQ_CMD_GET_FRIENDS_ONLINE: + qq_process_get_buddies_online_reply(data, data_len, gc); + break; + case QQ_CMD_GROUP_CMD: + qq_process_group_cmd_reply(data, data_len, seq, gc); + break; + case QQ_CMD_GET_ALL_LIST_WITH_GROUP: + qq_process_get_all_list_with_group_reply(data, data_len, gc); + break; + case QQ_CMD_GET_LEVEL: + qq_process_get_level_reply(data, data_len, gc); + break; + case QQ_CMD_REQUEST_LOGIN_TOKEN: + qq_process_request_login_token_reply(data, data_len, gc); + break; + default: + process_cmd_unknow(gc, data, data_len, cmd, seq); + break; + } +} + +/* process the incoming packet from qq_pending */ +static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) +{ + qq_data *qd; + gint bytes, bytes_not_read; + + gboolean prev_login_status; + guint8 *new_data; + gint new_data_len; + + guint8 header_tag; + guint16 source_tag; + guint16 cmd; + guint16 seq; /* May be ack_seq or send_seq, depends on cmd */ + + gboolean is_reply; + + g_return_if_fail(buf != NULL && buf_len > 0); + + qd = (qq_data *) gc->proto_data; + + prev_login_status = qd->logged_in; + + /* Len, header and tail tag have been checked before */ + bytes = 0; + bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); + + if (QQ_DEBUG) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "==> [%05d] 0x%04X %s, from (0x%04X %s)\n", + seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_source_str(source_tag)); + } + + bytes_not_read = buf_len - bytes - 1; + + /* ack packet, we need to update send tranactions */ + /* we do not check duplication for server ack */ + is_reply = packet_check_ack(qd, seq); + if ( !is_reply ) { + if ( !qd->logged_in ) { + /* packets before login */ + qq_rcv_trans_push(qd, cmd, seq, buf + bytes, bytes_not_read); + return; /* do not process it now */ + } + + /* server intiated packet, we need to send ack and check duplicaion + * this must be put after processing b4_packet + * as these packets will be passed in twice */ + if (packet_is_dup(qd, seq)) { + purple_debug(PURPLE_DEBUG_WARNING, + "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); + return; + } + process_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); + return; + } + + /* this is the length of all the encrypted data (also remove tail tag */ + process_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read); + + /* check is redirect or not, and do it now */ + if (qd->is_redirect) { + /* free resource except real_hostname and port */ + qq_disconnect(gc); + qd->reconnect_times = QQ_RECONNECT_MAX; + reconnect_later(gc); + return; + } + + if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) { + /* logged_in, but we have packets before login */ + new_data = g_newa(guint8, MAX_PACKET_SIZE); + while (1) { + memset(new_data, 0, MAX_PACKET_SIZE); + new_data_len = qq_rcv_trans_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE); + if (new_data_len < 0) { + break; + } + if (new_data_len == 0) { + continue; + } + process_cmd_reply(gc, seq, cmd, new_data, new_data_len); + } + } +} + +static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleConnection *gc; + qq_data *qd; + guint8 buf[1024]; /* set to 16 when test tcp_rxqueue */ + gint buf_len; + gint bytes; + + guint8 *pkt; + guint16 pkt_len; + + gchar *error_msg; + guint8 *jump; + gint jump_len; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + if(cond != PURPLE_INPUT_READ) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Socket error")); + return; + } + + qd = (qq_data *) gc->proto_data; + + /* test code, not using tcp_rxqueue + memset(pkt,0, sizeof(pkt)); + buf_len = read(qd->fd, pkt, sizeof(pkt)); + if (buf_len > 2) { + packet_process(gc, pkt + 2, buf_len - 2); + } + return; + */ + + buf_len = read(qd->fd, buf, sizeof(buf)); + if (buf_len < 0) { + if (errno == EAGAIN) + /* No worries */ + return; + + error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno)); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); + g_free(error_msg); + return; + } else if (buf_len == 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Server closed the connection.")); + return; + } + + gc->last_received = time(NULL); + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen); + qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen); + memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len); + qd->tcp_rxlen += buf_len; + + pkt = g_newa(guint8, MAX_PACKET_SIZE); + while (1) { + if (qd->tcp_rxlen < QQ_TCP_HEADER_LENGTH) { + break; + } + + bytes = 0; + bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes); + if (qd->tcp_rxlen < pkt_len) { + break; + } + + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen); + + if ( pkt_len < QQ_TCP_HEADER_LENGTH + || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG + || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { + /* HEY! This isn't even a QQ. What are you trying to pull? */ + + purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", + "Packet error, failed to check header and tail tag\n"); + + jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1); + if ( !jump ) { + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Failed to find next QQ_PACKET_TAIL, clear receive buffer\n"); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = NULL; + qd->tcp_rxlen = 0; + return; + } + + /* jump and over QQ_PACKET_TAIL */ + jump_len = (jump - qd->tcp_rxqueue) + 1; + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1); + g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len); + qd->tcp_rxlen -= jump_len; + continue; + } + + memset(pkt, 0, MAX_PACKET_SIZE); + g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes); + + /* jump to next packet */ + qd->tcp_rxlen -= pkt_len; + if (qd->tcp_rxlen) { + purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", + "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen); + jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = jump; + } else { + purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", + "free tcp_rxqueue\n"); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = NULL; + } + + if (pkt == NULL) { + continue; + } + /* do not call packet_process before jump + * packet_process may call disconnect and destory tcp_rxqueue */ + packet_process(gc, pkt, pkt_len - bytes); + } +} + +static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleConnection *gc; + qq_data *qd; + guint8 *buf; + gint buf_len; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + if(cond != PURPLE_INPUT_READ) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Socket error")); + return; + } + + qd = (qq_data *) gc->proto_data; + g_return_if_fail(qd->fd >= 0); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + + /* here we have UDP proxy suppport */ + buf_len = read(qd->fd, buf, MAX_PACKET_SIZE); + if (buf_len <= 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to read from socket")); + return; + } + + gc->last_received = time(NULL); + + if (buf_len < QQ_UDP_HEADER_LENGTH) { + if (buf[0] != QQ_PACKET_TAG || buf[buf_len - 1] != QQ_PACKET_TAIL) { + qq_hex_dump(PURPLE_DEBUG_ERROR, "UDP_PENDING", + buf, buf_len, + "Received packet is too short, or no header and tail tag"); + return; + } + } + + packet_process(gc, buf, buf_len); +} + +static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len) +{ + gint ret; + + g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Send %d bytes to socket %d\n", data_len, qd->fd); + + errno = 0; + ret = send(qd->fd, data, data_len, 0); + if (ret < 0 && errno == EAGAIN) { + return ret; + } + + if (ret < 0) { + /* TODO: what to do here - do we really have to disconnect? */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Send failed: %d, %s\n", errno, g_strerror(errno)); + purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); + } + return ret; +} + +static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond) +{ + qq_data *qd = data; + int ret, writelen; + + writelen = purple_circ_buffer_get_max_read(qd->tcp_txbuf); + if (writelen == 0) { + purple_input_remove(qd->tx_handler); + qd->tx_handler = 0; + return; + } + + ret = write(qd->fd, qd->tcp_txbuf->outptr, writelen); + purple_debug(PURPLE_DEBUG_ERROR, "TCP_CAN_WRITE", + "total %d bytes is sent %d\n", writelen, ret); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret < 0) { + /* TODO: what to do here - do we really have to disconnect? */ + purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Write Error")); + return; + } + + purple_circ_buffer_mark_read(qd->tcp_txbuf, ret); +} + +static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len) +{ + gint ret; + + g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); + + /* + * purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); + */ + + if (qd->tx_handler == 0) { + ret = write(qd->fd, data, data_len); + } else { + ret = -1; + errno = EAGAIN; + } + + purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", + "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret); + if (ret < 0 && errno == EAGAIN) { + /* socket is busy, send later */ + /* + * purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n"); + */ + ret = 0; + } else if (ret <= 0) { + /* TODO: what to do here - do we really have to disconnect? */ + purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT", + "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno)); + purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); + return ret; + } + + if (ret < data_len) { + purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", + "Add %d bytes to buffer\n", data_len - ret); + if (qd->tx_handler == 0) { + qd->tx_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, qd); + } + purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret); + } + return ret; +} + +static gboolean trans_timeout(gpointer data) +{ + PurpleConnection *gc; + qq_data *qd; + guint8 *buf; + gint buf_len = 0; + guint16 cmd; + gint retries = 0; + int index; + + gc = (PurpleConnection *) data; + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE); + + qd = (qq_data *) gc->proto_data; + + index = 0; + buf = g_newa(guint8, MAX_PACKET_SIZE); + + while (1) { + if (index < 0) { + /* next record is NULL */ + break; + } + /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "scan begin %d\n", index); */ + memset(buf, 0, MAX_PACKET_SIZE); + buf_len = qq_send_trans_scan(qd, &index, buf, MAX_PACKET_SIZE, &cmd, &retries); + if (buf_len <= 0) { + /* curr record is empty, whole trans is NULL */ + break; + } + /* index = -1, when get last record of transactions */ + + /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "retries %d next index %d\n", retries, index); */ + if (retries > 0) { + if (qd->use_tcp) { + tcp_send_out(qd, buf, buf_len); + } else { + udp_send_out(qd, buf, buf_len); + } + continue; + } + + /* retries <= 0 */ + switch (cmd) { + case QQ_CMD_KEEP_ALIVE: + if (qd->logged_in) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n"); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); + qd->logged_in = FALSE; + } + break; + case QQ_CMD_LOGIN: + case QQ_CMD_REQUEST_LOGIN_TOKEN: + if (!qd->logged_in) { + /* cancel login progress */ + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply")); + } + break; + default: + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "%s packet lost.\n", qq_get_cmd_desc(cmd)); + } + } + + return TRUE; /* if return FALSE, timeout callback stops */ +} + +/* the callback function after socket is built + * we setup the qq protocol related configuration here */ +static void qq_connect_cb(gpointer data, gint source, const gchar *error_message) +{ + qq_data *qd; + PurpleConnection *gc; + gchar *conn_msg; + const gchar *passwd; + + gc = (PurpleConnection *) data; + + if (!PURPLE_CONNECTION_IS_VALID(gc)) { + purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n"); + close(source); + return; + } + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + /* Connect is now complete; clear the PurpleProxyConnectData */ + qd->connect_data = NULL; + + if (source < 0) { /* socket returns -1 */ + purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection, source is < 0\n"); + qq_disconnect(gc); + reconnect_later(gc); + return; + } + + /* _qq_show_socket("Got login socket", source); */ + + /* QQ use random seq, to minimize duplicated packets */ + srandom(time(NULL)); + qd->send_seq = random() & 0x0000ffff; + qd->fd = source; + qd->logged_in = FALSE; + qd->channel = 1; + qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); + + /* now generate md5 processed passwd */ + passwd = purple_account_get_password(purple_connection_get_account(gc)); + g_return_if_fail(qd->pwkey == NULL); + qd->pwkey = encrypt_account_password(passwd); + + g_return_if_fail(qd->resend_timeout == 0); + /* call trans_timeout every 5 seconds */ + qd->resend_timeout = purple_timeout_add(5000, trans_timeout, gc); + + if (qd->use_tcp) + gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc); + else + gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc); + + /* Update the login progress status display */ + conn_msg = g_strdup_printf("Login as %d", qd->uid); + purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS); + g_free(conn_msg); + + qq_send_packet_request_login_token(gc); +} + +static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleConnection *gc; + qq_data *qd; + socklen_t len; + int error=0, ret; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + + purple_debug_info("proxy", "Connected.\n"); + + /* + * getsockopt after a non-blocking connect returns -1 if something is + * really messed up (bad descriptor, usually). Otherwise, it returns 0 and + * error holds what connect would have returned if it blocked until now. + * Thus, error == 0 is success, error == EINPROGRESS means "try again", + * and anything else is a real error. + * + * (error == EINPROGRESS can happen after a select because the kernel can + * be overly optimistic sometimes. select is just a hint that you might be + * able to do something.) + */ + len = sizeof(error); + ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); + if (ret == 0 && error == EINPROGRESS) + return; /* we'll be called again later */ + + purple_input_remove(qd->tx_handler); + qd->tx_handler = 0; + if (ret < 0 || error != 0) { + if(ret != 0) + error = errno; + + close(source); + + purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error)); + + qq_connect_cb(gc, -1, _("Unable to connect")); + return; + } + + qq_connect_cb(gc, source, NULL); +} + +static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) { + PurpleConnection *gc; + qq_data *qd; + struct sockaddr server_addr; + int addr_size; + gint fd = -1; + int flags; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + /* udp_query_data must be set as NULL. + * Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error */ + qd->udp_query_data = NULL; + + if (!hosts || !hosts->data) { + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Couldn't resolve host")); + return; + } + + addr_size = GPOINTER_TO_INT(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); + memcpy(&server_addr, hosts->data, addr_size); + g_free(hosts->data); + + hosts = g_slist_remove(hosts, hosts->data); + while(hosts) { + hosts = g_slist_remove(hosts, hosts->data); + g_free(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); + } + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Unable to create socket: %s\n", g_strerror(errno)); + return; + } + + /* we use non-blocking mode to speed up connection */ + flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/ + * + * If a UDP socket is unconnected, which is the normal state after a + * bind() call, then send() or write() are not allowed, since no + * destination is available; only sendto() can be used to send data. + * + * Calling connect() on the socket simply records the specified address + * and port number as being the desired communications partner. That + * means that send() or write() are now allowed; they use the destination + * address and port given on the connect call as the destination of packets. + */ + if (connect(fd, &server_addr, addr_size) >= 0) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n"); + flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); + qq_connect_cb(gc, fd, NULL); + return; + } + + /* [EINPROGRESS] + * The socket is marked as non-blocking and the connection cannot be + * completed immediately. It is possible to select for completion by + * selecting the socket for writing. + * [EINTR] + * A signal interrupted the call. + * The connection is established asynchronously. + */ + if ((errno == EINPROGRESS) || (errno == EINTR)) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); + qd->tx_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc); + return; + } + + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %d\n", g_strerror(errno)); + close(fd); +} + +/* establish a generic QQ connection + * TCP/UDP, and direct/redirected */ +void qq_connect(PurpleAccount *account) +{ + PurpleConnection *gc; + qq_data *qd; + gchar *conn_msg; + + gc = purple_account_get_connection(account); + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + + /* test set_new_server + while (set_new_server(qd)) { + purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", + "New server %s:%d Real server %s:%d\n", + qd->server_name, qd->user_port, qd->real_hostname, qd->real_port); + } + purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", "qd->servers %lu\n", + qd->servers); + exit(1); + */ + if (qd->server_name == NULL) { + /* must be first call this function */ + if ( set_new_server(qd) != TRUE) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Failed to connect server")); + return; + } + } + + if (qd->real_hostname == NULL || qd->real_port == 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("hostname is NULL or port is 0")); + return; + } + + conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), + qd->real_hostname, qd->reconnect_times); + purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS); + g_free(conn_msg); + + if (qd->is_redirect) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n", + qd->real_hostname, qd->real_port); + } + qd->is_redirect = FALSE; + + qd->fd = -1; + qd->tx_handler = 0; + + /* QQ connection via UDP/TCP. + * Now use Purple proxy function to provide TCP proxy support, + * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ + if(qd->use_tcp) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n", + qd->real_hostname, qd->real_port); + + /* TODO: is there a good default grow size? */ + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create tcp_txbuf\n"); + qd->tcp_txbuf = purple_circ_buffer_new(0); + + qd->connect_data = purple_proxy_connect(NULL, account, + qd->real_hostname, qd->real_port, qq_connect_cb, gc); + if (qd->connect_data == NULL) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect.")); + } + return; + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Connect to %s:%d\n", + qd->real_hostname, qd->real_port); + + g_return_if_fail(qd->udp_query_data == NULL); + qd->udp_query_data = purple_dnsquery_a(qd->real_hostname, qd->real_port, + udp_host_resolved, gc); + if (qd->udp_query_data == NULL) { + purple_connection_error_reason(qd->gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Could not resolve hostname")); + } +} + +/* clean up qq_data structure and all its components + * always used before a redirectly connection */ +void qq_disconnect(PurpleConnection *gc) +{ + qq_data *qd; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n"); + /* finish all I/O */ + if (qd->fd >= 0 && qd->logged_in) { + qq_send_packet_logout(gc); + } + + if (qd->resend_timeout > 0) { + purple_timeout_remove(qd->resend_timeout); + qd->resend_timeout = 0; + } + + if (gc->inpa > 0) { + purple_input_remove(gc->inpa); + gc->inpa = 0; + } + + if (qd->fd >= 0) { + close(qd->fd); + qd->fd = -1; + } + + if (qd->reconnect_timeout > 0) { + purple_timeout_remove(qd->reconnect_timeout); + qd->reconnect_timeout = 0; + } + + if (qd->connect_data != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n"); + purple_proxy_connect_cancel(qd->connect_data); + } + + if(qd->tcp_txbuf != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n"); + purple_circ_buffer_destroy(qd->tcp_txbuf); + qd->tcp_txbuf = NULL; + } + + if (qd->tx_handler) { + purple_input_remove(qd->tx_handler); + qd->tx_handler = 0; + } + if (qd->tcp_rxqueue != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_rxqueue\n"); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = NULL; + qd->tcp_rxlen = 0; + } + + if (qd->udp_query_data != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n"); + purple_dnsquery_destroy(qd->udp_query_data); + qd->udp_query_data = NULL; + } + + memset(qd->rcv_window, 0, sizeof(qd->rcv_window)); + qq_rcv_trans_remove_all(qd); + qq_send_trans_remove_all(qd); + + if (qd->inikey) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free inikey\n"); + g_free(qd->inikey); + qd->inikey = NULL; + } + if (qd->pwkey) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free pwkey\n"); + g_free(qd->pwkey); + qd->pwkey = NULL; + } + if (qd->session_key) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_key\n"); + g_free(qd->session_key); + qd->session_key = NULL; + } + if (qd->session_md5) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_md5\n"); + g_free(qd->session_md5); + qd->session_md5 = NULL; + } + if (qd->my_ip) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n"); + g_free(qd->my_ip); + qd->my_ip = NULL; + } + + qq_group_packets_free(qd); + qq_group_free_all(qd); + qq_add_buddy_request_free(qd); + qq_info_query_free(qd); + qq_buddies_list_free(gc->account, qd); +} + +static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, + guint8 *data, gint data_len) +{ + gint bytes = 0; + g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1); + + if (data == NULL) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data is NULL\n"); + return -1; + } + if (data_len <= 0) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data len <= 0\n"); + return -1; + } + + /* QQ TCP packet has two bytes in the begining defines packet length + * so leave room here to store packet size */ + if (qd->use_tcp) { + bytes += qq_put16(buf + bytes, 0x0000); + } + /* now comes the normal QQ packet as UDP */ + bytes += qq_put8(buf + bytes, QQ_PACKET_TAG); + bytes += qq_put16(buf + bytes, QQ_CLIENT); + bytes += qq_put16(buf + bytes, cmd); + + bytes += qq_put16(buf + bytes, seq); + + bytes += qq_put32(buf + bytes, qd->uid); + bytes += qq_putdata(buf + bytes, data, data_len); + bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); + + /* set TCP packet length at begin of the packet */ + if (qd->use_tcp) { + qq_put16(buf, bytes); + } + + return bytes; +} + +gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint data_len) +{ + guint8 *buf; + gint buf_len; + gint bytes_sent; + gint seq; + + g_return_val_if_fail(qd != NULL, -1); + g_return_val_if_fail(data != NULL && data_len > 0, -1); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); + seq = ++(qd->send_seq); + buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); + if (buf_len <= 0) { + return -1; + } + + if (qd->use_tcp) { + bytes_sent = tcp_send_out(qd, buf, buf_len); + } else { + bytes_sent = udp_send_out(qd, buf, buf_len); + } + + /* always need ack */ + qq_send_trans_append(qd, buf, buf_len, cmd, seq); + + if (QQ_DEBUG) { + qq_show_packet("QQ_SEND_DATA", buf, buf_len); + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "<== [%05d], %s, total %d bytes is sent %d\n", + seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent); + } + return bytes_sent; +} + +/* send the packet generated with the given cmd and data + * return the number of bytes sent to socket if succeeds + * return -1 if there is any error */ +gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, + guint8 *data, gint data_len) +{ + guint8 *buf; + gint buf_len; + guint8 *encrypted_data; + gint encrypted_len; + gint bytes_sent; + + g_return_val_if_fail(qd != NULL && qd->session_key != NULL, -1); + g_return_val_if_fail(data != NULL && data_len > 0, -1); + + encrypted_len = data_len + 16; /* at most 16 bytes more */ + encrypted_data = g_newa(guint8, encrypted_len); + + qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); + buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, encrypted_data, encrypted_len); + if (buf_len <= 0) { + return -1; + } + + if (QQ_DEBUG) { + qq_show_packet("QQ_SEND_CMD", buf, buf_len); + } + if (qd->use_tcp) { + bytes_sent = tcp_send_out(qd, buf, buf_len); + } else { + bytes_sent = udp_send_out(qd, buf, buf_len); + } + + /* if it does not need ACK, we send ACK manually several times */ + if (need_ack) { + qq_send_trans_append(qd, buf, buf_len, cmd, seq); + } + + if (QQ_DEBUG) { + qq_show_packet("QQ_SEND_CMD", buf, buf_len); + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "<== [%05d], %s, total %d bytes is sent %d\n", + seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent); + } + return bytes_sent; +} + +gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len) +{ + g_return_val_if_fail(qd != NULL, -1); + g_return_val_if_fail(data != NULL && data_len > 0, -1); + + qd->send_seq++; + return qq_send_cmd_detail(qd, cmd, qd->send_seq, TRUE, data, data_len); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_network.h Wed Jul 09 00:32:18 2008 +0000 @@ -0,0 +1,44 @@ +/** + * @file qq_network.h + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _QQ_PROXY_H +#define _QQ_PROXY_H + +#include <glib.h> +#include "connection.h" + +#include "qq.h" + +#define QQ_CONNECT_STEPS 3 /* steps in connection */ + +void qq_connect(PurpleAccount *account); +void qq_disconnect(PurpleConnection *gc); +void qq_connect_later(PurpleConnection *gc); + +gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint datalen); +gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint datalen); +gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, + guint8 *data, gint data_len); + +#endif
--- a/libpurple/protocols/qq/qq_proxy.c Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,529 +0,0 @@ -/** - * @file qq_proxy.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "cipher.h" -#include "debug.h" -#include "internal.h" - -#ifdef _WIN32 -#define random rand -#define srandom srand -#endif - -#include "packet_parse.h" -#include "buddy_info.h" -#include "buddy_opt.h" -#include "char_conv.h" -#include "group_free.h" -#include "login_logout.h" -#include "qq_proxy.h" -#include "recv_core.h" -#include "send_core.h" -#include "sendqueue.h" -#include "udp_proxy_s5.h" -#include "utils.h" - -/* These functions are used only in development phase */ -/* -static void _qq_show_socket(gchar *desc, gint fd) { - struct sockaddr_in sin; - socklen_t len = sizeof(sin); - getsockname(fd, (struct sockaddr *)&sin, &len); - purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n", - inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); -} -*/ - -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len) -{ - char buf1[8*len+2], buf2[10]; - int i; - buf1[0] = 0; - for (i = 0; i < len; i++) { - sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff); - strcat(buf1, buf2); - } - strcat(buf1, "\n"); - purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1); -} - -/* QQ 2003iii uses double MD5 for the pwkey to get the session key */ -static guint8 *_gen_pwkey(const gchar *pwd) -{ - PurpleCipher *cipher; - PurpleCipherContext *context; - - guchar pwkey_tmp[QQ_KEY_LENGTH]; - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd)); - purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); - purple_cipher_context_destroy(context); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH); - purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); - purple_cipher_context_destroy(context); - - return g_memdup(pwkey_tmp, QQ_KEY_LENGTH); -} - -static gboolean _qq_fill_host(GSList *hosts, struct sockaddr_in *addr, gint *addr_size) -{ - if (!hosts || !hosts->data) - return FALSE; - - *addr_size = GPOINTER_TO_INT(hosts->data); - - hosts = g_slist_remove(hosts, hosts->data); - memcpy(addr, hosts->data, *addr_size); - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - while(hosts) { - hosts = g_slist_remove(hosts, hosts->data); - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - } - - return TRUE; -} - -/* set up any finalizing start-up stuff */ -static void _qq_start_services(PurpleConnection *gc) -{ - /* start watching for IMs about to be sent */ - /* - purple_signal_connect(purple_conversations_get_handle(), - "sending-im-msg", gc, - PURPLE_CALLBACK(qq_sending_im_msg_cb), NULL); - */ -} - -/* the callback function after socket is built - * we setup the qq protocol related configuration here */ -static void _qq_got_login(gpointer data, gint source, const gchar *error_message) -{ - qq_data *qd; - PurpleConnection *gc; - gchar *buf; - const gchar *passwd; - - gc = (PurpleConnection *) data; - - if (!PURPLE_CONNECTION_IS_VALID(gc)) { - close(source); - return; - } - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - - if (source < 0) { /* socket returns -1 */ - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); - return; - } - - qd = (qq_data *) gc->proto_data; - - /* - _qq_show_socket("Got login socket", source); - */ - - /* QQ use random seq, to minimize duplicated packets */ - srandom(time(NULL)); - qd->send_seq = random() & 0x0000ffff; - qd->fd = source; - qd->logged_in = FALSE; - qd->channel = 1; - qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); - - /* now generate md5 processed passwd */ - passwd = purple_account_get_password(purple_connection_get_account(gc)); - qd->pwkey = _gen_pwkey(passwd); - - qd->sendqueue_timeout = purple_timeout_add(QQ_SENDQUEUE_TIMEOUT, qq_sendqueue_timeout_callback, gc); - gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, qq_input_pending, gc); - - /* Update the login progress status display */ - buf = g_strdup_printf("Login as %d", qd->uid); - purple_connection_update_progress(gc, buf, 1, QQ_CONNECT_STEPS); - g_free(buf); - - _qq_start_services(gc); - - qq_send_packet_request_login_token(gc); -} - -/* clean up qq_data structure and all its components - * always used before a redirectly connection */ -static void _qq_common_clean(PurpleConnection *gc) -{ - qq_data *qd; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - /* finish all I/O */ - if (qd->fd >= 0 && qd->logged_in) - qq_send_packet_logout(gc); - close(qd->fd); - - if (qd->sendqueue_timeout > 0) { - purple_timeout_remove(qd->sendqueue_timeout); - qd->sendqueue_timeout = 0; - } - - if (gc->inpa > 0) { - purple_input_remove(gc->inpa); - gc->inpa = 0; - } - - qq_b4_packets_free(qd); - qq_sendqueue_free(qd); - qq_group_packets_free(qd); - qq_group_free_all(qd); - qq_add_buddy_request_free(qd); - qq_info_query_free(qd); - qq_buddies_list_free(gc->account, qd); -} - -static void no_one_calls(gpointer data, gint source, PurpleInputCondition cond) -{ - struct PHB *phb = data; - socklen_t len; - int error=0, ret; - - purple_debug_info("proxy", "Connected.\n"); - - len = sizeof(error); - - /* - * getsockopt after a non-blocking connect returns -1 if something is - * really messed up (bad descriptor, usually). Otherwise, it returns 0 and - * error holds what connect would have returned if it blocked until now. - * Thus, error == 0 is success, error == EINPROGRESS means "try again", - * and anything else is a real error. - * - * (error == EINPROGRESS can happen after a select because the kernel can - * be overly optimistic sometimes. select is just a hint that you might be - * able to do something.) - */ - ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); - if (ret == 0 && error == EINPROGRESS) - return; /* we'll be called again later */ - if (ret < 0 || error != 0) { - if(ret!=0) - error = errno; - close(source); - purple_input_remove(phb->inpa); - - purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error)); - - phb->func(phb->data, -1, _("Unable to connect")); - return; - } - - purple_input_remove(phb->inpa); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, source, NULL); - } - - g_free(phb->host); - g_free(phb); -} - -/* returns -1 if fails, otherwise returns the file handle */ -static gint _qq_proxy_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) -{ - gint fd = -1; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Using UDP without proxy\n"); - fd = socket(PF_INET, SOCK_DGRAM, 0); - - if (fd < 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ Redirect", - "Unable to create socket: %s\n", g_strerror(errno)); - return -1; - } - - /* we use non-blocking mode to speed up connection */ - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); - - /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/ - * - * If a UDP socket is unconnected, which is the normal state after a - * bind() call, then send() or write() are not allowed, since no - * destination is available; only sendto() can be used to send data. - * - * Calling connect() on the socket simply records the specified address - * and port number as being the desired communications partner. That - * means that send() or write() are now allowed; they use the destination - * address and port given on the connect call as the destination of packets. - */ - if (connect(fd, addr, addrlen) < 0) { - /* [EINPROGRESS] - * The socket is marked as non-blocking and the connection cannot be - * completed immediately. It is possible to select for completion by - * selecting the socket for writing. - * [EINTR] - * A signal interrupted the call. - * The connection is established asynchronously. - */ - if ((errno == EINPROGRESS) || (errno == EINTR)) { - purple_debug_warning("QQ", "Connect in asynchronous mode.\n"); - phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, no_one_calls, phb); - } else { - purple_debug_error("QQ", "Connection failed: %s\n", g_strerror(errno)); - close(fd); - return -1; - } /* if errno */ - } else { /* connect returns 0 */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n"); - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); - phb->func(phb->data, fd, NULL); - } - - return fd; -} - -static void _qq_proxy_resolved(GSList *hosts, gpointer data, const char *error_message) -{ - struct PHB *phb = (struct PHB *) data; - struct sockaddr_in addr; - gint addr_size, ret = -1; - - if(_qq_fill_host(hosts, &addr, &addr_size)) - ret = qq_proxy_socks5(phb, (struct sockaddr *) &addr, addr_size); - - if (ret < 0) { - phb->func(phb->data, -1, _("Unable to connect")); - g_free(phb->host); - g_free(phb); - } -} - -static void _qq_server_resolved(GSList *hosts, gpointer data, const char *error_message) -{ - struct PHB *phb = (struct PHB *) data; - PurpleConnection *gc = (PurpleConnection *) phb->data; - qq_data *qd = (qq_data *) gc->proto_data; - struct sockaddr_in addr; - gint addr_size, ret = -1; - - if(_qq_fill_host(hosts, &addr, &addr_size)) { - switch (purple_proxy_info_get_type(phb->gpi)) { - case PURPLE_PROXY_NONE: - ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size); - break; - case PURPLE_PROXY_SOCKS5: - ret = 0; - if (purple_proxy_info_get_host(phb->gpi) == NULL || - purple_proxy_info_get_port(phb->gpi) == 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Use of socks5 proxy selected but host or port info doesn't exist.\n"); - ret = -1; - } else { - /* as the destination is always QQ server during the session, - * we can set dest_sin here, instead of _qq_s5_canread_again */ - memcpy(&qd->dest_sin, &addr, addr_size); - if (purple_dnsquery_a(purple_proxy_info_get_host(phb->gpi), - purple_proxy_info_get_port(phb->gpi), - _qq_proxy_resolved, phb) == NULL) - ret = -1; - } - break; - default: - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Proxy type %i is unsupported, not using a proxy.\n", - purple_proxy_info_get_type(phb->gpi)); - ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size); - } - } - - if (ret < 0) { - phb->func(gc, -1, _("Unable to connect")); - g_free(phb->host); - g_free(phb); - } -} - -/* returns -1 if dns lookup fails, otherwise returns 0 */ -static gint _qq_udp_proxy_connect(PurpleAccount *account, - const gchar *server, guint16 port, - void callback(gpointer, gint, const gchar *error_message), - PurpleConnection *gc) -{ - PurpleProxyInfo *info; - struct PHB *phb; - qq_data *qd = (qq_data *) gc->proto_data; - - g_return_val_if_fail(gc != NULL && qd != NULL, -1); - - info = purple_proxy_get_setup(account); - - phb = g_new0(struct PHB, 1); - phb->host = g_strdup(server); - phb->port = port; - phb->account = account; - phb->gpi = info; - phb->func = callback; - phb->data = gc; - qd->proxy_type = purple_proxy_info_get_type(phb->gpi); - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Choosing proxy type %d\n", - purple_proxy_info_get_type(phb->gpi)); - - if (purple_dnsquery_a(server, port, _qq_server_resolved, phb) == NULL) { - phb->func(gc, -1, _("Unable to connect")); - g_free(phb->host); - g_free(phb); - return -1; - } else { - return 0; - } -} - -/* QQ connection via UDP/TCP. - * I use Purple proxy function to provide TCP proxy support, - * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ -static gint _proxy_connect_full (PurpleAccount *account, const gchar *host, guint16 port, - PurpleProxyConnectFunction func, gpointer data, gboolean use_tcp) -{ - PurpleConnection *gc; - qq_data *qd; - - gc = purple_account_get_connection(account); - qd = (qq_data *) gc->proto_data; - qd->server_ip = g_strdup(host); - qd->server_port = port; - - if(use_tcp) - return (purple_proxy_connect(NULL, account, host, port, func, data) == NULL); - else - return _qq_udp_proxy_connect(account, host, port, func, data); -} - -/* establish a generic QQ connection - * TCP/UDP, and direct/redirected */ -gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, - gboolean use_tcp, gboolean is_redirect) -{ - PurpleConnection *gc; - qq_data *qd; - - g_return_val_if_fail(host != NULL, -1); - g_return_val_if_fail(port > 0, -1); - - gc = purple_account_get_connection(account); - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - - if (is_redirect) - _qq_common_clean(gc); - - qd = (qq_data *) gc->proto_data; - qd->before_login_packets = g_queue_new(); - - return _proxy_connect_full(account, host, port, _qq_got_login, gc, use_tcp); -} - -/* clean up the given QQ connection and free all resources */ -void qq_disconnect(PurpleConnection *gc) -{ - qq_data *qd; - - g_return_if_fail(gc != NULL); - - _qq_common_clean(gc); - - qd = gc->proto_data; - g_free(qd->inikey); - g_free(qd->pwkey); - g_free(qd->session_key); - g_free(qd->session_md5); - g_free(qd->my_ip); - g_free(qd); - - gc->proto_data = NULL; -} - -/* send packet with proxy support */ -gint qq_proxy_write(qq_data *qd, guint8 *data, gint len) -{ - guint8 *buf; - gint ret; - - g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && len > 0, -1); - - /* TCP sock5 may be processed twice - * so we need to check qd->use_tcp as well */ - if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) { /* UDP sock5 */ - buf = g_newa(guint8, len + 10); - buf[0] = 0x00; - buf[1] = 0x00; /* reserved */ - buf[2] = 0x00; /* frag */ - buf[3] = 0x01; /* type */ - g_memmove(buf + 4, &(qd->dest_sin.sin_addr.s_addr), 4); - g_memmove(buf + 8, &(qd->dest_sin.sin_port), 2); - g_memmove(buf + 10, data, len); - errno = 0; - ret = send(qd->fd, buf, len + 10, 0); - } else { - errno = 0; - ret = send(qd->fd, data, len, 0); - } - if (ret == -1) - purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); - - return ret; -} - -/* read packet input with proxy support */ -gint qq_proxy_read(qq_data *qd, guint8 *data, gint len) -{ - guint8 *buf; - gint bytes; - buf = g_newa(guint8, MAX_PACKET_SIZE + 10); - - g_return_val_if_fail(qd != NULL && data != NULL && len > 0, -1); - g_return_val_if_fail(qd->fd > 0, -1); - - bytes = read(qd->fd, buf, len + 10); - if (bytes < 0) - return -1; - - if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) { /* UDP sock5 */ - if (bytes < 10) - return -1; - bytes -= 10; - g_memmove(data, buf + 10, bytes); /* cut off the header */ - } else { - g_memmove(data, buf, bytes); - } - - return bytes; -}
--- a/libpurple/protocols/qq/qq_proxy.h Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * @file qq_proxy.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _QQ_PROXY_H -#define _QQ_PROXY_H - -#include <glib.h> -#include "dnsquery.h" -#include "proxy.h" - -#include "qq.h" - -#define QQ_CONNECT_STEPS 2 /* steps in connection */ - -struct PHB { - PurpleProxyConnectFunction func; - gpointer data; - gchar *host; - gint port; - gint inpa; - PurpleProxyInfo *gpi; - PurpleAccount *account; - gint udpsock; - gpointer sockbuf; -}; - -gint qq_proxy_read(qq_data *qd, guint8 *data, gint len); -gint qq_proxy_write(qq_data *qd, guint8 *data, gint len); - -gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, gboolean use_tcp, gboolean is_redirect); -void qq_disconnect(PurpleConnection *gc); - -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len); - -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_trans.c Wed Jul 09 00:32:18 2008 +0000 @@ -0,0 +1,246 @@ +/** + * @file qq_trans.c + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "internal.h" + +#include "connection.h" +#include "debug.h" +#include "notify.h" +#include "prefs.h" +#include "request.h" + +#include "header_info.h" +#include "qq_network.h" +#include "qq_trans.h" + +#define QQ_RESEND_MAX 8 /* max resend per packet */ + +typedef struct _transaction { + guint16 seq; + guint16 cmd; + guint8 *buf; + gint buf_len; + + gint fd; + gint retries; + time_t create_time; +} transaction; + +void qq_send_trans_append(qq_data *qd, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq) +{ + transaction *trans = g_new0(transaction, 1); + + g_return_if_fail(trans != NULL); + + trans->fd = qd->fd; + trans->cmd = cmd; + trans->seq = seq; + trans->retries = QQ_RESEND_MAX; + trans->create_time = time(NULL); + trans->buf = g_memdup(buf, buf_len); /* don't use g_strdup, may have 0x00 */ + trans->buf_len = buf_len; + + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Add to transaction, seq = %d, buf = %p, len = %d\n", + trans->seq, trans->buf, trans->buf_len); + qd->send_trans = g_list_append(qd->send_trans, trans); +} + +/* Remove a packet with seq from send trans */ +void qq_send_trans_remove(qq_data *qd, gpointer data) +{ + transaction *trans = (transaction *)data; + + g_return_if_fail(qd != NULL && data != NULL); + + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "ack [%05d] %s, remove from send tranactions\n", + trans->seq, qq_get_cmd_desc(trans->cmd)); + + if (trans->buf) g_free(trans->buf); + qd->send_trans = g_list_remove(qd->send_trans, trans); + g_free(trans); +} + +gpointer qq_send_trans_find(qq_data *qd, guint16 seq) +{ + GList *curr; + GList *next; + transaction *trans; + + curr = qd->send_trans; + while(curr) { + next = curr->next; + trans = (transaction *) (curr->data); + if(trans->seq == seq) { + return trans; + } + curr = next; + } + + return NULL; +} + +/* clean up send trans and free all contents */ +void qq_send_trans_remove_all(qq_data *qd) +{ + GList *curr; + GList *next; + transaction *trans; + gint count = 0; + + curr = qd->send_trans; + while(curr) { + next = curr->next; + + trans = (transaction *) (curr->data); + /* + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Remove to transaction, seq = %d, buf = %p, len = %d\n", + trans->seq, trans->buf, trans->len); + */ + qq_send_trans_remove(qd, trans); + + count++; + curr = next; + } + g_list_free(qd->send_trans); + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in send tranactions are freed!\n", count); +} + +gint qq_send_trans_scan(qq_data *qd, gint *start, + guint8 *buf, gint maxlen, guint16 *cmd, gint *retries) +{ + GList *curr; + GList *next = NULL; + transaction *trans; + gint copylen; + + g_return_val_if_fail(qd != NULL && *start >= 0 && maxlen > 0, -1); + + /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Scan from %d\n", *start); */ + curr = g_list_nth(qd->send_trans, *start); + while(curr) { + next = curr->next; + *start = g_list_position(qd->send_trans, next); + + trans = (transaction *) (curr->data); + if (trans->buf == NULL || trans->buf_len <= 0) { + qq_send_trans_remove(qd, trans); + curr = next; + continue; + } + + if (trans->retries < 0) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Remove transaction, seq %d, buf %p, len %d, retries %d, next %d\n", + trans->seq, trans->buf, trans->buf_len, trans->retries, *start); + qq_send_trans_remove(qd, trans); + curr = next; + continue; + } + + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Resend transaction, seq %d, buf %p, len %d, retries %d, next %d\n", + trans->seq, trans->buf, trans->buf_len, trans->retries, *start); + copylen = MIN(trans->buf_len, maxlen); + g_memmove(buf, trans->buf, copylen); + + *cmd = trans->cmd; + *retries = trans->retries; + trans->retries--; + return copylen; + } + + /* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Scan finished\n"); */ + return -1; +} + +void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len) +{ + transaction *trans = g_new0(transaction, 1); + + g_return_if_fail(data != NULL && data_len > 0); + g_return_if_fail(trans != NULL); + + trans->cmd = cmd; + trans->seq = seq; + trans->buf = g_memdup(data, data_len); + trans->buf_len = data_len; + trans->create_time = time(NULL); + + if (qd->rcv_trans == NULL) + qd->rcv_trans = g_queue_new(); + + g_queue_push_head(qd->rcv_trans, trans); +} + +gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16 *seq, guint8 *data, gint max_len) +{ + transaction *trans = NULL; + gint copy_len; + + g_return_val_if_fail(data != NULL && max_len > 0, -1); + + if (g_queue_is_empty(qd->rcv_trans)) { + return -1; + } + trans = (transaction *) g_queue_pop_head(qd->rcv_trans); + if (trans == NULL) { + return 0; + } + if (trans->buf == NULL || trans->buf_len <= 0) { + return 0; + } + + copy_len = MIN(max_len, trans->buf_len); + g_memmove(data, trans->buf, copy_len); + *cmd = trans->cmd; + *seq = trans->seq; + + g_free(trans->buf); + g_free(trans); + return copy_len; +} + +/* clean up the packets before login */ +void qq_rcv_trans_remove_all(qq_data *qd) +{ + transaction *trans = NULL; + gint count = 0; + + g_return_if_fail(qd != NULL); + + /* now clean up my own data structures */ + if (qd->rcv_trans != NULL) { + while (NULL != (trans = g_queue_pop_tail(qd->rcv_trans))) { + g_free(trans->buf); + g_free(trans); + count++; + } + g_queue_free(qd->rcv_trans); + } + purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in receive tranactions are freed!\n", count); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_trans.h Wed Jul 09 00:32:18 2008 +0000 @@ -0,0 +1,42 @@ +/** + * @file qq_trans.h + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _QQ_SEND_QUEUE_H_ +#define _QQ_SEND_QUEUE_H_ + +#include <glib.h> +#include "qq.h" + +void qq_send_trans_append(qq_data *qd, guint8 *buf, gint bus_len, guint16 cmd, guint16 seq); +void qq_send_trans_remove(qq_data *qd, gpointer data); +gpointer qq_send_trans_find(qq_data *qd, guint16 seq); +void qq_send_trans_remove_all(qq_data *qd); + +gint qq_send_trans_scan(qq_data *qd, gint *start, guint8 *buf, gint maxlen, guint16 *cmd, gint *retries); + +void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len); +gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16* seq, guint8 *data, gint max_len); +void qq_rcv_trans_remove_all(qq_data *qd); + +#endif
--- a/libpurple/protocols/qq/recv_core.c Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,326 +0,0 @@ -/** - * @file recv_core.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "debug.h" -#include "internal.h" - -#include "buddy_info.h" -#include "buddy_list.h" -#include "buddy_opt.h" -#include "buddy_status.h" -#include "char_conv.h" -#include "crypt.h" -#include "group_network.h" -#include "header_info.h" -#include "keep_alive.h" -#include "im.h" -#include "login_logout.h" -#include "packet_parse.h" -#include "qq_proxy.h" -#include "recv_core.h" -#include "sendqueue.h" -#include "sys_msg.h" -#include "utils.h" - -typedef struct _packet_before_login packet_before_login; -typedef struct _qq_recv_msg_header qq_recv_msg_header; - -struct _packet_before_login { - guint8 *buf; - gint len; -}; - -struct _qq_recv_msg_header { - guint8 header_tag; - guint16 source_tag; - guint16 cmd; - guint16 seq; /* can be ack_seq or send_seq, depends on cmd */ -}; - -/* check whether one sequence number is duplicated or not - * return TRUE if it is duplicated, otherwise FALSE */ -static gboolean _qq_check_packet_set_window(guint16 seq, PurpleConnection *gc) -{ - qq_data *qd; - guint8 *byte, mask; - - qd = (qq_data *) gc->proto_data; - byte = &(qd->window[seq / 8]); - mask = (1 << (seq % 8)); - - if ((*byte) & mask) - return TRUE; /* check mask */ - (*byte) |= mask; - return FALSE; /* set mask */ -} - -/* default process, decrypt and dump */ -static void _qq_process_packet_default(guint8 *buf, gint buf_len, guint16 cmd, guint16 seq, PurpleConnection *gc) -{ - qq_data *qd; - guint8 *data; - gchar *msg_utf8; - gint len; - - g_return_if_fail(buf != NULL && buf_len != 0); - - qd = (qq_data *) gc->proto_data; - len = buf_len; - data = g_newa(guint8, len); - msg_utf8 = NULL; - - _qq_show_packet("Processing unknown packet", buf, len); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - gchar *hex_dump = hex_dump_to_str(data, len); - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - ">>> [%d] %s, %d bytes -> [default] decrypt and dump\n%s", - seq, qq_get_cmd_desc(cmd), buf_len, hex_dump); - g_free(hex_dump); - try_dump_as_gbk(data, len); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n"); - } -} - -/* process the incoming packet from qq_pending */ -static void _qq_packet_process(guint8 *buf, gint buf_len, PurpleConnection *gc) -{ - qq_data *qd; - gint len, bytes_expected, bytes_read; - guint16 buf_len_read; /* two bytes in the begining of TCP packet */ - guint8 *cursor; - qq_recv_msg_header header; - packet_before_login *b4_packet; - - g_return_if_fail(buf != NULL && buf_len > 0); - - qd = (qq_data *) gc->proto_data; - bytes_expected = qd->use_tcp ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH; - - if (buf_len < bytes_expected) { - gchar *hex_dump = hex_dump_to_str(buf, buf_len); - purple_debug(PURPLE_DEBUG_ERROR, - "QQ", "Received packet is too short, dump and drop\n%s", hex_dump); - g_free(hex_dump); - return; - } - /* initialize */ - cursor = buf; - bytes_read = 0; - - /* QQ TCP packet returns first 2 bytes the length of this packet */ - if (qd->use_tcp) { - bytes_read += read_packet_w(buf, &cursor, buf_len, &buf_len_read); - if (buf_len_read != buf_len) { /* wrong */ - purple_debug - (PURPLE_DEBUG_ERROR, - "QQ", - "TCP read %d bytes, header says %d bytes, use header anyway\n", buf_len, buf_len_read); - buf_len = buf_len_read; /* we believe header is more accurate */ - } - } - - /* now goes the normal QQ packet as UDP packet */ - bytes_read += read_packet_b(buf, &cursor, buf_len, &header.header_tag); - bytes_read += read_packet_w(buf, &cursor, buf_len, &header.source_tag); - bytes_read += read_packet_w(buf, &cursor, buf_len, &header.cmd); - bytes_read += read_packet_w(buf, &cursor, buf_len, &header.seq); - - if (bytes_read != bytes_expected) { /* read error */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail reading packet header, expect %d bytes, read %d bytes\n", - bytes_expected, bytes_read); - return; - } - - if ((buf[buf_len - 1] != QQ_PACKET_TAIL) || (header.header_tag != QQ_PACKET_TAG)) { - gchar *hex_dump = hex_dump_to_str(buf, buf_len); - purple_debug(PURPLE_DEBUG_ERROR, - "QQ", "Unknown QQ proctocol, dump and drop\n%s", hex_dump); - g_free(hex_dump); - return; - } - - if (QQ_DEBUG) - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "==> [%05d] %s, from (%s)\n", - header.seq, qq_get_cmd_desc(header.cmd), qq_get_source_str(header.source_tag)); - - if (header.cmd != QQ_CMD_LOGIN && header.cmd != QQ_CMD_REQUEST_LOGIN_TOKEN) { - if (!qd->logged_in) { /* packets before login */ - b4_packet = g_new0(packet_before_login, 1); - /* must duplicate, buffer will be freed after exiting this function */ - b4_packet->buf = g_memdup(buf, buf_len); - b4_packet->len = buf_len; - if (qd->before_login_packets == NULL) - qd->before_login_packets = g_queue_new(); - g_queue_push_head(qd->before_login_packets, b4_packet); - return; /* do not process it now */ - } else if (!g_queue_is_empty(qd->before_login_packets)) { - /* logged_in, but we have packets before login */ - b4_packet = (packet_before_login *) - g_queue_pop_head(qd->before_login_packets); - _qq_packet_process(b4_packet->buf, b4_packet->len, gc); - /* in fact this is a recursive call, - * all packets before login will be processed before goes on */ - g_free(b4_packet->buf); /* the buf is duplicated, need to be freed */ - g_free(b4_packet); - } - } - - /* this is the length of all the encrypted data (also remove tail tag */ - len = buf_len - (bytes_read) - 1; - - /* whether it is an ack */ - switch (header.cmd) { - case QQ_CMD_RECV_IM: - case QQ_CMD_RECV_MSG_SYS: - case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: - /* server intiated packet, we need to send ack and check duplicaion - * this must be put after processing b4_packet - * as these packets will be passed in twice */ - if (_qq_check_packet_set_window(header.seq, gc)) { - purple_debug(PURPLE_DEBUG_WARNING, - "QQ", "dup [%05d] %s, discard...\n", header.seq, qq_get_cmd_desc(header.cmd)); - return; - } - break; - default:{ /* ack packet, we need to update sendqueue */ - /* we do not check duplication for server ack */ - qq_sendqueue_remove(qd, header.seq); - if (QQ_DEBUG) - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "ack [%05d] %s, remove from sendqueue\n", - header.seq, qq_get_cmd_desc(header.cmd)); - } - } - - /* now process the packet */ - switch (header.cmd) { - case QQ_CMD_KEEP_ALIVE: - qq_process_keep_alive_reply(cursor, len, gc); - break; - case QQ_CMD_UPDATE_INFO: - qq_process_modify_info_reply(cursor, len, gc); - break; - case QQ_CMD_ADD_FRIEND_WO_AUTH: - qq_process_add_buddy_reply(cursor, len, header.seq, gc); - break; - case QQ_CMD_DEL_FRIEND: - qq_process_remove_buddy_reply(cursor, len, gc); - break; - case QQ_CMD_REMOVE_SELF: - qq_process_remove_self_reply(cursor, len, gc); - break; - case QQ_CMD_BUDDY_AUTH: - qq_process_add_buddy_auth_reply(cursor, len, gc); - break; - case QQ_CMD_GET_USER_INFO: - qq_process_get_info_reply(cursor, len, gc); - break; - case QQ_CMD_CHANGE_ONLINE_STATUS: - qq_process_change_status_reply(cursor, len, gc); - break; - case QQ_CMD_SEND_IM: - qq_process_send_im_reply(cursor, len, gc); - break; - case QQ_CMD_RECV_IM: - qq_process_recv_im(cursor, len, header.seq, gc); - break; - case QQ_CMD_LOGIN: - qq_process_login_reply(cursor, len, gc); - break; - case QQ_CMD_GET_FRIENDS_LIST: - qq_process_get_buddies_list_reply(cursor, len, gc); - break; - case QQ_CMD_GET_FRIENDS_ONLINE: - qq_process_get_buddies_online_reply(cursor, len, gc); - break; - case QQ_CMD_GROUP_CMD: - qq_process_group_cmd_reply(cursor, len, header.seq, gc); - break; - case QQ_CMD_GET_ALL_LIST_WITH_GROUP: - qq_process_get_all_list_with_group_reply(cursor, len, gc); - break; - case QQ_CMD_GET_LEVEL: - qq_process_get_level_reply(cursor, len, gc); - break; - case QQ_CMD_REQUEST_LOGIN_TOKEN: - qq_process_request_login_token_reply(cursor, len, gc); - break; - case QQ_CMD_RECV_MSG_SYS: - qq_process_msg_sys(cursor, len, header.seq, gc); - break; - case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: - qq_process_friend_change_status(cursor, len, gc); - break; - default: - _qq_process_packet_default(cursor, len, header.cmd, header.seq, gc); - break; - } -} - -/* clean up the packets before login */ -void qq_b4_packets_free(qq_data *qd) -{ - packet_before_login *b4_packet; - g_return_if_fail(qd != NULL); - /* now clean up my own data structures */ - if (qd->before_login_packets != NULL) { - while (NULL != (b4_packet = g_queue_pop_tail(qd->before_login_packets))) { - g_free(b4_packet->buf); - g_free(b4_packet); - } - g_queue_free(qd->before_login_packets); - } -} - -void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond) -{ - PurpleConnection *gc; - qq_data *qd; - guint8 *buf; - gint len; - - gc = (PurpleConnection *) data; - - if(cond != PURPLE_INPUT_READ) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Socket error")); - return; - } - - qd = (qq_data *) gc->proto_data; - buf = g_newa(guint8, MAX_PACKET_SIZE); - - /* here we have UDP proxy suppport */ - len = qq_proxy_read(qd, buf, MAX_PACKET_SIZE); - if (len <= 0) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to read from socket")); - return; - } else { - _qq_packet_process(buf, len, gc); - } -}
--- a/libpurple/protocols/qq/recv_core.h Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/** - * @file recv_core.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _QQ_RECV_CORE_H_ -#define _QQ_RECV_CORE_H_ - -#include <glib.h> -#include "connection.h" -#include "qq.h" - -void qq_b4_packets_free(qq_data *qd); - -void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond); - -#endif
--- a/libpurple/protocols/qq/send_core.c Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,163 +0,0 @@ -/** - * @file send_core.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "debug.h" -#include "internal.h" - -#include "crypt.h" -#include "header_info.h" -#include "packet_parse.h" -#include "qq.h" -#include "qq_proxy.h" -#include "send_core.h" -#include "sendqueue.h" - -/* create qq packet header with given sequence - * return the number of bytes in header if succeeds - * return -1 if there is any error */ -gint _create_packet_head_seq(guint8 *buf, guint8 **cursor, - PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq) -{ - qq_data *qd; - gint bytes_expected, bytes_written; - - g_return_val_if_fail(buf != NULL && cursor != NULL && *cursor != NULL, -1); - - qd = (qq_data *) gc->proto_data; - if (is_auto_seq) - *seq = ++(qd->send_seq); - - *cursor = buf; - bytes_written = 0; - bytes_expected = (qd->use_tcp) ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH; - - /* QQ TCP packet has two bytes in the begining defines packet length - * so I leave room here for size */ - if (qd->use_tcp) - bytes_written += create_packet_w(buf, cursor, 0x0000); - - /* now comes the normal QQ packet as UDP */ - bytes_written += create_packet_b(buf, cursor, QQ_PACKET_TAG); - bytes_written += create_packet_w(buf, cursor, QQ_CLIENT); - bytes_written += create_packet_w(buf, cursor, cmd); - bytes_written += create_packet_w(buf, cursor, *seq); - - if (bytes_written != bytes_expected) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create qq header, expect %d bytes, written %d bytes\n", bytes_expected, bytes_written); - bytes_written = -1; - } - return bytes_written; -} - -/* for those need ack and resend no ack feed back from server - * return number of bytes written to the socket, - * return -1 if there is any error */ -gint _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd) -{ - qq_data *qd; - qq_sendpacket *p; - gint bytes_sent; - guint8 *cursor; - - qd = (qq_data *) gc->proto_data; - - if (qd->use_tcp) { - if (len > MAX_PACKET_SIZE) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "xxx [%05d] %s, %d bytes is too large, do not send\n", - qd->send_seq, qq_get_cmd_desc(cmd), len); - return -1; - } else { /* I update the len for TCP packet */ - cursor = buf; - create_packet_w(buf, &cursor, len); - } - } - - bytes_sent = qq_proxy_write(qd, buf, len); - - if (bytes_sent >= 0) { /* put to queue, for matching server ACK usage */ - p = g_new0(qq_sendpacket, 1); - p->fd = qd->fd; - p->cmd = cmd; - p->send_seq = qd->send_seq; - p->resend_times = 0; - p->sendtime = time(NULL); - p->buf = g_memdup(buf, len); /* don't use g_strdup, may have 0x00 */ - p->len = len; - qd->sendqueue = g_list_append(qd->sendqueue, p); - } - - return bytes_sent; -} - -/* send the packet generated with the given cmd and data - * return the number of bytes sent to socket if succeeds - * return -1 if there is any error */ -gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, - gboolean is_auto_seq, guint16 seq, gboolean need_ack, guint8 *data, gint len) -{ - qq_data *qd; - guint8 *buf, *cursor, *encrypted_data; - guint16 seq_ret; - gint encrypted_len, bytes_written, bytes_expected, bytes_sent; - - qd = (qq_data *) gc->proto_data; - g_return_val_if_fail(qd->session_key != NULL, -1); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - encrypted_len = len + 16; /* at most 16 bytes more */ - encrypted_data = g_newa(guint8, encrypted_len); - cursor = buf; - bytes_written = 0; - - qq_encrypt(data, len, qd->session_key, encrypted_data, &encrypted_len); - - seq_ret = seq; - if (_create_packet_head_seq(buf, &cursor, gc, cmd, is_auto_seq, &seq_ret) >= 0) { - bytes_expected = 4 + encrypted_len + 1; - bytes_written += create_packet_dw(buf, &cursor, (guint32) qd->uid); - bytes_written += create_packet_data(buf, &cursor, encrypted_data, encrypted_len); - bytes_written += create_packet_b(buf, &cursor, QQ_PACKET_TAIL); - if (bytes_written == bytes_expected) { /* packet OK */ - /* if it does not need ACK, we send ACK manually several times */ - if (need_ack) /* my request, send it */ - bytes_sent = _qq_send_packet(gc, buf, cursor - buf, cmd); - else /* server's request, send ACK */ - bytes_sent = qq_proxy_write(qd, buf, cursor - buf); - - if (QQ_DEBUG) - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "<== [%05d] %s, %d bytes\n", seq_ret, qq_get_cmd_desc(cmd), bytes_sent); - return bytes_sent; - } else { /* bad packet */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail creating packet, expect %d bytes, written %d bytes\n", - bytes_expected, bytes_written); - return -1; - } - } - - return -1; -}
--- a/libpurple/protocols/qq/send_core.h Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/** - * @file send_core.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _QQ_SEND_CORE_H_ -#define _QQ_SEND_CORE_H_ - -#include <glib.h> -#include "connection.h" - -gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 seq, - gboolean need_ack, guint8 *data, gint len); -gint _qq_send_packet(PurpleConnection * gc, guint8 *buf, gint len, guint16 cmd); -gint _create_packet_head_seq(guint8 *buf, guint8 **cursor, - PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq); - -#endif
--- a/libpurple/protocols/qq/send_file.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/send_file.c Wed Jul 09 00:32:18 2008 +0000 @@ -36,7 +36,7 @@ #include "im.h" #include "keep_alive.h" #include "packet_parse.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" enum @@ -103,6 +103,7 @@ return send(info->sender_fd, buf, len, 0); } */ + static ssize_t _qq_xfer_udp_send(const guint8 *buf, size_t len, PurpleXfer *xfer) { struct sockaddr_in sin; @@ -243,42 +244,45 @@ g_free(internet_ip_str); } -void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info) +#define QQ_CONN_INFO_LEN 61 +gint qq_get_conn_info(ft_info *info, guint8 *data) { - read_packet_data(data, cursor, data_len, info->file_session_key, 16); - *cursor += 30; - read_packet_b(data, cursor, data_len, &info->conn_method); - read_packet_dw(data, cursor, data_len, &info->remote_internet_ip); - read_packet_w(data, cursor, data_len, &info->remote_internet_port); - read_packet_w(data, cursor, data_len, &info->remote_major_port); - read_packet_dw(data, cursor, data_len, &info->remote_real_ip); - read_packet_w(data, cursor, data_len, &info->remote_minor_port); + gint bytes = 0; + /* 16 + 30 + 1 + 4 + 2 + 2 + 4 + 2 = 61 */ + bytes += qq_getdata(info->file_session_key, 16, data + bytes); + bytes += 30; /* skip 30 bytes */ + bytes += qq_get8(&info->conn_method, data + bytes); + bytes += qq_get32(&info->remote_internet_ip, data + bytes); + bytes += qq_get16(&info->remote_internet_port, data + bytes); + bytes += qq_get16(&info->remote_major_port, data + bytes); + bytes += qq_get32(&info->remote_real_ip, data + bytes); + bytes += qq_get16(&info->remote_minor_port, data + bytes); qq_show_conn_info(info); + return bytes; } -gint qq_fill_conn_info(guint8 *raw_data, guint8 **cursor, ft_info *info) +gint qq_fill_conn_info(guint8 *raw_data, ft_info *info) { - gint bytes; - bytes = 0; + gint bytes = 0; /* 064: connection method, UDP 0x00, TCP 0x03 */ - bytes += create_packet_b (raw_data, cursor, info->conn_method); + bytes += qq_put8 (raw_data + bytes, info->conn_method); /* 065-068: outer ip address of sender (proxy address) */ - bytes += create_packet_dw (raw_data, cursor, info->local_internet_ip); + bytes += qq_put32 (raw_data + bytes, info->local_internet_ip); /* 069-070: sender port */ - bytes += create_packet_w (raw_data, cursor, info->local_internet_port); + bytes += qq_put16 (raw_data + bytes, info->local_internet_port); /* 071-072: the first listening port(TCP doesn't have this part) */ - bytes += create_packet_w (raw_data, cursor, info->local_major_port); + bytes += qq_put16 (raw_data + bytes, info->local_major_port); /* 073-076: real ip */ - bytes += create_packet_dw (raw_data, cursor, info->local_real_ip); + bytes += qq_put32 (raw_data + bytes, info->local_real_ip); /* 077-078: the second listening port */ - bytes += create_packet_w (raw_data, cursor, info->local_minor_port); + bytes += qq_put16 (raw_data + bytes, info->local_minor_port); return bytes; } /* fill in the common information of file transfer */ static gint _qq_create_packet_file_header -(guint8 *raw_data, guint8 **cursor, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack) +(guint8 *raw_data, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack) { gint bytes; time_t now; @@ -294,42 +298,42 @@ } /* 000-003: receiver uid */ - bytes += create_packet_dw (raw_data, cursor, qd->uid); + bytes += qq_put32 (raw_data + bytes, qd->uid); /* 004-007: sender uid */ - bytes += create_packet_dw (raw_data, cursor, to_uid); + bytes += qq_put32 (raw_data + bytes, to_uid); /* 008-009: sender client version */ - bytes += create_packet_w (raw_data, cursor, QQ_CLIENT); + bytes += qq_put16 (raw_data + bytes, QQ_CLIENT); /* 010-013: receiver uid */ - bytes += create_packet_dw (raw_data, cursor, qd->uid); + bytes += qq_put32 (raw_data + bytes, qd->uid); /* 014-017: sender uid */ - bytes += create_packet_dw (raw_data, cursor, to_uid); + bytes += qq_put32 (raw_data + bytes, to_uid); /* 018-033: md5 of (uid+session_key) */ - bytes += create_packet_data (raw_data, cursor, qd->session_md5, 16); + bytes += qq_putdata (raw_data + bytes, qd->session_md5, 16); /* 034-035: message type */ - bytes += create_packet_w (raw_data, cursor, message_type); + bytes += qq_put16 (raw_data + bytes, message_type); /* 036-037: sequence number */ - bytes += create_packet_w (raw_data, cursor, seq); + bytes += qq_put16 (raw_data + bytes, seq); /* 038-041: send time */ - bytes += create_packet_dw (raw_data, cursor, (guint32) now); + bytes += qq_put32 (raw_data + bytes, (guint32) now); /* 042-042: always 0x00 */ - bytes += create_packet_b (raw_data, cursor, 0x00); + bytes += qq_put8 (raw_data + bytes, 0x00); /* 043-043: sender icon */ - bytes += create_packet_b (raw_data, cursor, qd->my_icon); + bytes += qq_put8 (raw_data + bytes, qd->my_icon); /* 044-046: always 0x00 */ - bytes += create_packet_w (raw_data, cursor, 0x0000); - bytes += create_packet_b (raw_data, cursor, 0x00); + bytes += qq_put16 (raw_data + bytes, 0x0000); + bytes += qq_put8 (raw_data + bytes, 0x00); /* 047-047: we use font attr */ - bytes += create_packet_b (raw_data, cursor, 0x01); + bytes += qq_put8 (raw_data + bytes, 0x01); /* 048-051: always 0x00 */ - bytes += create_packet_dw (raw_data, cursor, 0x00000000); + bytes += qq_put32 (raw_data + bytes, 0x00000000); /* 052-062: always 0x00 */ - bytes += create_packet_dw (raw_data, cursor, 0x00000000); - bytes += create_packet_dw (raw_data, cursor, 0x00000000); - bytes += create_packet_w (raw_data, cursor, 0x0000); - bytes += create_packet_b (raw_data, cursor, 0x00); + bytes += qq_put32 (raw_data + bytes, 0x00000000); + bytes += qq_put32 (raw_data + bytes, 0x00000000); + bytes += qq_put16 (raw_data + bytes, 0x0000); + bytes += qq_put8 (raw_data + bytes, 0x00); /* 063: transfer_type, 0x65: FILE 0x6b: FACE */ - bytes += create_packet_b (raw_data, cursor, QQ_FILE_TRANSFER_FILE); /* FIXME */ + bytes += qq_put8 (raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME */ return bytes; } @@ -433,7 +437,7 @@ static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid, gchar *filename, gint filesize) { qq_data *qd; - guint8 *cursor, *raw_data; + guint8 *raw_data; gchar *filelen_str; gint filename_len, filelen_strlen, packet_len, bytes; ft_info *info; @@ -455,27 +459,24 @@ packet_len = 82 + filename_len + filelen_strlen; raw_data = g_newa(guint8, packet_len); - cursor = raw_data; + bytes = 0; - bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_REQ, qd, FALSE); - bytes += qq_fill_conn_info(raw_data, &cursor, info); + bytes += qq_fill_conn_info(raw_data + bytes, info); /* 079: 0x20 */ - bytes += create_packet_b (raw_data, &cursor, 0x20); + bytes += qq_put8 (raw_data + bytes, 0x20); /* 080: 0x1f */ - bytes += create_packet_b (raw_data, &cursor, 0x1f); + bytes += qq_put8 (raw_data + bytes, 0x1f); /* undetermined len: filename */ - bytes += create_packet_data (raw_data, &cursor, (guint8 *) filename, - filename_len); + bytes += qq_putdata (raw_data + bytes, (guint8 *) filename, filename_len); /* 0x1f */ - bytes += create_packet_b (raw_data, &cursor, 0x1f); + bytes += qq_put8 (raw_data + bytes, 0x1f); /* file length */ - bytes += create_packet_data (raw_data, &cursor, (guint8 *) filelen_str, - filelen_strlen); + bytes += qq_putdata (raw_data + bytes, (guint8 *) filelen_str, filelen_strlen); if (packet_len == bytes) - qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, - cursor - raw_data); + qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes); else purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_request", "%d bytes expected but got %d bytes\n", @@ -488,7 +489,7 @@ static void _qq_send_packet_file_accept(PurpleConnection *gc, guint32 to_uid) { qq_data *qd; - guint8 *cursor, *raw_data; + guint8 *raw_data; guint16 minor_port; guint32 real_ip; gint packet_len, bytes; @@ -502,22 +503,21 @@ packet_len = 79; raw_data = g_newa (guint8, packet_len); - cursor = raw_data; + bytes = 0; minor_port = info->local_minor_port; real_ip = info->local_real_ip; info->local_minor_port = 0; info->local_real_ip = 0; - bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE); - bytes += qq_fill_conn_info(raw_data, &cursor, info); + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE); + bytes += qq_fill_conn_info(raw_data + bytes, info); info->local_minor_port = minor_port; info->local_real_ip = real_ip; if (packet_len == bytes) - qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, - cursor - raw_data); + qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes); else purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_accept", "%d bytes expected but got %d bytes\n", @@ -529,7 +529,7 @@ PurpleXfer *xfer; ft_info *info; qq_data *qd; - guint8 *cursor, *raw_data; + guint8 *raw_data; gint packet_len, bytes; qd = (qq_data *) gc->proto_data; @@ -538,14 +538,13 @@ packet_len = 79; raw_data = g_newa (guint8, packet_len); - cursor = raw_data; + bytes = 0; purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== sending qq file notify ip packet\n"); - bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE); - bytes += qq_fill_conn_info(raw_data, &cursor, info); + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE); + bytes += qq_fill_conn_info(raw_data + bytes, info); if (packet_len == bytes) - qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, - cursor - raw_data); + qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes); else purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_notify", "%d bytes expected but got %d bytes\n", @@ -560,7 +559,7 @@ static void _qq_send_packet_file_reject (PurpleConnection *gc, guint32 to_uid) { qq_data *qd; - guint8 *cursor, *raw_data; + guint8 *raw_data; gint packet_len, bytes; purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_reject", "start"); @@ -568,14 +567,12 @@ packet_len = 64; raw_data = g_newa (guint8, packet_len); - cursor = raw_data; bytes = 0; - bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE); + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE); if (packet_len == bytes) - qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, - cursor - raw_data); + qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes); else purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file", "%d bytes expected but got %d bytes\n", @@ -586,7 +583,7 @@ static void _qq_send_packet_file_cancel (PurpleConnection *gc, guint32 to_uid) { qq_data *qd; - guint8 *cursor, *raw_data; + guint8 *raw_data; gint packet_len, bytes; purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "start\n"); @@ -594,17 +591,15 @@ packet_len = 64; raw_data = g_newa (guint8, packet_len); - cursor = raw_data; bytes = 0; purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before create header\n"); - bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE); + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE); purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "end create header\n"); if (packet_len == bytes) { purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before send cmd\n"); - qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, - cursor - raw_data); + qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes); } else purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file", @@ -688,7 +683,7 @@ } /* process reject im for file transfer request */ -void qq_process_recv_file_reject (guint8 *data, guint8 **cursor, gint data_len, +void qq_process_recv_file_reject (guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { gchar *msg, *filename; @@ -698,11 +693,13 @@ qd = (qq_data *) gc->proto_data; g_return_if_fail (qd->xfer != NULL); + /* border has been checked before if (*cursor >= (data + data_len - 1)) { purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received file reject message is empty\n"); return; } + */ filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1; msg = g_strdup_printf(_("%d has declined the file %s"), sender_uid, filename); @@ -715,7 +712,7 @@ } /* process cancel im for file transfer request */ -void qq_process_recv_file_cancel (guint8 *data, guint8 **cursor, gint data_len, +void qq_process_recv_file_cancel (guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { gchar *msg, *filename; @@ -726,11 +723,13 @@ g_return_if_fail (qd->xfer != NULL && purple_xfer_get_filename(qd->xfer) != NULL); + /* border has been checked before if (*cursor >= (data + data_len - 1)) { purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received file reject message is empty\n"); return; } + */ filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1; msg = g_strdup_printf (_("%d canceled the transfer of %s"), @@ -744,27 +743,26 @@ } /* process accept im for file transfer request */ -void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc) +void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { qq_data *qd; + gint bytes; ft_info *info; PurpleXfer *xfer; g_return_if_fail (data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; xfer = qd->xfer; + info = (ft_info *) qd->xfer->data; - if (*cursor >= (data + data_len - 1)) { + if (data_len <= 30 + QQ_CONN_INFO_LEN) { purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received file reject message is empty\n"); return; } - info = (ft_info *) qd->xfer->data; - - *cursor = data + 18 + 12; - qq_get_conn_info(data, cursor, data_len, info); + bytes = 18 + 12; /* skip 30 bytes */ + qq_get_conn_info(info, data + bytes); _qq_xfer_init_socket(qd->xfer); _qq_xfer_init_udp_channel(info); @@ -772,8 +770,7 @@ } /* process request from buddy's im for file transfer request */ -void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection * gc) +void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection * gc) { qq_data *qd; PurpleXfer *xfer; @@ -781,25 +778,27 @@ ft_info *info; PurpleBuddy *b; qq_buddy *q_bud; + gint bytes; g_return_if_fail (data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; - if (*cursor >= (data + data_len - 1)) { - purple_debug (PURPLE_DEBUG_WARNING, "QQ", - "Received file reject message is empty\n"); - return; - } - - info = g_new0(ft_info, 1); + info = g_newa(ft_info, 1); info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip)); info->local_internet_port = qd->my_port; info->local_real_ip = 0x00000000; info->to_uid = sender_uid; - read_packet_w(data, cursor, data_len, &(info->send_seq)); + + if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) { + purple_debug (PURPLE_DEBUG_WARNING, "QQ", + "Received file request message is empty\n"); + return; + } + bytes = 0; + bytes += qq_get16(&(info->send_seq), data + bytes); - *cursor = data + 18 + 12; - qq_get_conn_info(data, cursor, data_len, info); + bytes += 18 + 12; /* skip 30 bytes */ + bytes += qq_get_conn_info(info, data + bytes); fileinfo = g_strsplit((gchar *) (data + 81 + 12), "\x1f", 2); g_return_if_fail (fileinfo != NULL && fileinfo[0] != NULL && fileinfo[1] != NULL); @@ -880,9 +879,10 @@ */ } -void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len, +void qq_process_recv_file_notify(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { + gint bytes; qq_data *qd; ft_info *info; PurpleXfer *xfer; @@ -890,19 +890,19 @@ g_return_if_fail (data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; - if (*cursor >= (data + data_len - 1)) { + xfer = qd->xfer; + info = (ft_info *) qd->xfer->data; + if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) { purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received file notify message is empty\n"); return; } + + bytes = 0; + bytes += qq_get16(&(info->send_seq), data + bytes); - xfer = qd->xfer; - info = (ft_info *) qd->xfer->data; - /* FIXME */ - read_packet_w(data, cursor, data_len, &(info->send_seq)); - - *cursor = data + 18 + 12; - qq_get_conn_info(data, cursor, data_len, info); + bytes += 18 + 12; + bytes += qq_get_conn_info(info, data + bytes); _qq_xfer_init_udp_channel(info); @@ -938,7 +938,7 @@ /* static void qq_send_packet_request_key(PurpleConnection *gc, guint8 key) { - qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, TRUE, 0, TRUE, &key, 1); + qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, &key, 1); } static void qq_process_recv_request_key(PurpleConnection *gc)
--- a/libpurple/protocols/qq/send_file.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/send_file.h Wed Jul 09 00:32:18 2008 +0000 @@ -66,20 +66,15 @@ gboolean use_major; } ft_info; -void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc); -void qq_process_recv_file_reject(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc); -void qq_process_recv_file_cancel(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc); -void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc); -void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc); +void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc); +void qq_process_recv_file_reject(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc); +void qq_process_recv_file_cancel(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc); +void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc); +void qq_process_recv_file_notify(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc); gboolean qq_can_receive_file(PurpleConnection *gc, const char *who); void qq_send_file(PurpleConnection *gc, const char *who, const char *file); -void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info); -gint qq_fill_conn_info(guint8 *data, guint8 **cursor, ft_info *info); +gint qq_get_conn_info(ft_info *info, guint8 *data); +gint qq_fill_conn_info(guint8 *data, ft_info *info); gssize _qq_xfer_write(const guint8 *buf, size_t len, PurpleXfer *xfer); #endif
--- a/libpurple/protocols/qq/sendqueue.c Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +0,0 @@ -/** - * @file sendqueue.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "internal.h" - -#include "connection.h" -#include "debug.h" -#include "notify.h" -#include "prefs.h" -#include "request.h" - -#include "header_info.h" -#include "qq_proxy.h" -#include "sendqueue.h" - -#define QQ_RESEND_MAX 8 /* max resend per packet */ - -typedef struct _gc_and_packet gc_and_packet; - -struct _gc_and_packet { - PurpleConnection *gc; - qq_sendpacket *packet; -}; - -/* Remove a packet with send_seq from sendqueue */ -void qq_sendqueue_remove(qq_data *qd, guint16 send_seq) -{ - GList *list; - qq_sendpacket *p; - - list = qd->sendqueue; - while (list != NULL) { - p = (qq_sendpacket *) (list->data); - if (p->send_seq == send_seq) { - qd->sendqueue = g_list_remove(qd->sendqueue, p); - g_free(p->buf); - g_free(p); - break; - } - list = list->next; - } -} - -/* clean up sendqueue and free all contents */ -void qq_sendqueue_free(qq_data *qd) -{ - qq_sendpacket *p; - gint i; - - i = 0; - while (qd->sendqueue != NULL) { - p = (qq_sendpacket *) (qd->sendqueue->data); - qd->sendqueue = g_list_remove(qd->sendqueue, p); - g_free(p->buf); - g_free(p); - i++; - } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in sendqueue are freed!\n", i); -} - -/* FIXME We shouldn't be dropping packets, but for now we have to because - * somewhere we're generating invalid packets that the server won't ack. - * Given enough time, a buildup of those packets would crash the client. */ -gboolean qq_sendqueue_timeout_callback(gpointer data) -{ - PurpleConnection *gc; - qq_data *qd; - GList *list; - qq_sendpacket *p; - time_t now; - gint wait_time; - - gc = (PurpleConnection *) data; - qd = (qq_data *) gc->proto_data; - now = time(NULL); - list = qd->sendqueue; - - /* empty queue, return TRUE so that timeout continues functioning */ - if (qd->sendqueue == NULL) - return TRUE; - - while (list != NULL) { /* remove all packet whose resend_times == -1 */ - p = (qq_sendpacket *) list->data; - if (p->resend_times == -1) { /* to remove */ - qd->sendqueue = g_list_remove(qd->sendqueue, p); - g_free(p->buf); - g_free(p); - list = qd->sendqueue; - } else { - list = list->next; - } - } - - list = qd->sendqueue; - while (list != NULL) { - p = (qq_sendpacket *) list->data; - if (p->resend_times == QQ_RESEND_MAX) { /* reach max */ - switch (p->cmd) { - case QQ_CMD_KEEP_ALIVE: - if (qd->logged_in) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n"); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); - qd->logged_in = FALSE; - } - p->resend_times = -1; - break; - case QQ_CMD_LOGIN: - case QQ_CMD_REQUEST_LOGIN_TOKEN: - if (!qd->logged_in) /* cancel login progress */ - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply")); - p->resend_times = -1; - break; - default:{ - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "%s packet sent %d times but not acked. Not resending it.\n", - qq_get_cmd_desc(p->cmd), QQ_RESEND_MAX); - } - p->resend_times = -1; - } - } else { /* resend_times < QQ_RESEND_MAX, so sent it again */ - wait_time = (gint) (QQ_SENDQUEUE_TIMEOUT / 1000); - if (difftime(now, p->sendtime) > (wait_time * (p->resend_times + 1))) { - qq_proxy_write(qd, p->buf, p->len); - p->resend_times++; - purple_debug(PURPLE_DEBUG_INFO, - "QQ", "<<< [%05d] send again for %d times!\n", - p->send_seq, p->resend_times); - } - } - list = list->next; - } - return TRUE; /* if we return FALSE, the timeout callback stops functioning */ -}
--- a/libpurple/protocols/qq/sendqueue.h Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/** - * @file sendqueue.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _QQ_SEND_QUEUE_H_ -#define _QQ_SEND_QUEUE_H_ - -#include <glib.h> -#include "qq.h" - -#define QQ_SENDQUEUE_TIMEOUT 5000 /* in 1/1000 sec */ - -typedef struct _qq_sendpacket qq_sendpacket; - -struct _qq_sendpacket { - gint fd; - gint len; - guint8 *buf; - guint16 cmd; - guint16 send_seq; - gint resend_times; - time_t sendtime; -}; - -void qq_sendqueue_free(qq_data *qd); - -void qq_sendqueue_remove(qq_data *qd, guint16 send_seq); -gboolean qq_sendqueue_timeout_callback(gpointer data); - -#endif
--- a/libpurple/protocols/qq/sys_msg.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/sys_msg.c Wed Jul 09 00:32:18 2008 +0000 @@ -35,7 +35,7 @@ #include "header_info.h" #include "packet_parse.h" #include "qq.h" -#include "send_core.h" +#include "qq_network.h" #include "sys_msg.h" #include "utils.h" @@ -120,27 +120,29 @@ /* Send ACK if the sys message needs an ACK */ static void _qq_send_packet_ack_msg_sys(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq) { - guint8 bar, *ack, *cursor; + qq_data *qd; + guint8 bar, *ack; gchar *str; gint ack_len, bytes; + qd = (qq_data *) gc->proto_data; + str = g_strdup_printf("%d", from); bar = 0x1e; ack_len = 1 + 1 + strlen(str) + 1 + 2; ack = g_newa(guint8, ack_len); - cursor = ack; + bytes = 0; - - bytes += create_packet_b(ack, &cursor, code); - bytes += create_packet_b(ack, &cursor, bar); - bytes += create_packet_data(ack, &cursor, (guint8 *) str, strlen(str)); - bytes += create_packet_b(ack, &cursor, bar); - bytes += create_packet_w(ack, &cursor, seq); + bytes += qq_put8(ack + bytes, code); + bytes += qq_put8(ack + bytes, bar); + bytes += qq_putdata(ack + bytes, (guint8 *) str, strlen(str)); + bytes += qq_put8(ack + bytes, bar); + bytes += qq_put16(ack + bytes, seq); g_free(str); if (bytes == ack_len) /* creation OK */ - qq_send_cmd(gc, QQ_CMD_ACK_SYS_MSG, TRUE, 0, FALSE, ack, ack_len); + qq_send_cmd_detail(qd, QQ_CMD_ACK_SYS_MSG, 0, FALSE, ack, ack_len); else purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes);
--- a/libpurple/protocols/qq/udp_proxy_s5.c Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,381 +0,0 @@ -/** - * @file udp_proxy_s5.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "debug.h" - -#include "udp_proxy_s5.h" - -static void _qq_s5_canread_again(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - struct PHB *phb = data; - struct sockaddr_in sin; - int len, error; - socklen_t errlen; - int flags; - - purple_input_remove(phb->inpa); - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read again.\n"); - - len = read(source, buf, 10); - if (len < 10) { - purple_debug(PURPLE_DEBUG_WARNING, "socks5 proxy", "or not...\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, source, NULL); - } - - g_free(phb->host); - g_free(phb); - return; - } - if ((buf[0] != 0x05) || (buf[1] != 0x00)) { - if ((buf[0] == 0x05) && (buf[1] < 0x09)) - purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]); - else - purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Bad data.\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - sin.sin_family = AF_INET; - memcpy(&sin.sin_addr.s_addr, buf + 4, 4); - memcpy(&sin.sin_port, buf + 8, 2); - - if (connect(phb->udpsock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", g_strerror(errno)); - close(phb->udpsock); - close(source); - g_free(phb->host); - g_free(phb); - return; - } - - error = ETIMEDOUT; - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connect didn't block\n"); - errlen = sizeof(error); - if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &errlen) < 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "getsockopt failed.\n"); - close(phb->udpsock); - return; - } - flags = fcntl(phb->udpsock, F_GETFL); - fcntl(phb->udpsock, F_SETFL, flags & ~O_NONBLOCK); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, phb->udpsock, NULL); - } - - g_free(phb->host); - g_free(phb); -} - -static void _qq_s5_sendconnect(gpointer data, gint source) -{ - unsigned char buf[512]; - struct PHB *phb = data; - struct sockaddr_in sin, ctlsin; - int port; - socklen_t ctllen; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port); - - buf[0] = 0x05; - buf[1] = 0x03; /* udp relay */ - buf[2] = 0x00; /* reserved */ - buf[3] = 0x01; /* address type -- ipv4 */ - memset(buf + 4, 0, 0x04); - - ctllen = sizeof(ctlsin); - if (getsockname(source, (struct sockaddr *) &ctlsin, &ctllen) < 0) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "getsockname: %s\n", g_strerror(errno)); - close(source); - g_free(phb->host); - g_free(phb); - return; - } - - phb->udpsock = socket(PF_INET, SOCK_DGRAM, 0); - purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "UDP socket=%d\n", phb->udpsock); - if (phb->udpsock < 0) { - close(source); - g_free(phb->host); - g_free(phb); - return; - } - - flags = fcntl(phb->udpsock, F_GETFL); - fcntl(phb->udpsock, F_SETFL, flags | O_NONBLOCK); - - port = g_ntohs(ctlsin.sin_port) + 1; - while (1) { - inet_aton("0.0.0.0", &(sin.sin_addr)); - sin.sin_family = AF_INET; - sin.sin_port = g_htons(port); - if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - port++; - if (port > 65500) { - close(source); - g_free(phb->host); - g_free(phb); - return; - } - } else - break; - } - - memset(buf + 4, 0, 0x04); - memcpy(buf + 8, &(sin.sin_port), 0x02); - - if (write(source, buf, 10) < 10) { - close(source); - purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "packet too small\n"); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread_again, phb); -} - -static void _qq_s5_readauth(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - struct PHB *phb = data; - - purple_input_remove(phb->inpa); - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got auth response.\n"); - - if (read(source, buf, 2) < 2) { - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - if ((buf[0] != 0x01) || (buf[1] != 0x00)) { - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - _qq_s5_sendconnect(phb, source); -} - -static void _qq_s5_canread(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - struct PHB *phb; - int ret; - - phb = data; - - purple_input_remove(phb->inpa); - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read.\n"); - - ret = read(source, buf, 2); - if (ret < 2) { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - if ((buf[0] != 0x05) || (buf[1] == 0xff)) { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "unsupport\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - if (buf[1] == 0x02) { - unsigned int i, j; - - i = strlen(purple_proxy_info_get_username(phb->gpi)); - j = strlen(purple_proxy_info_get_password(phb->gpi)); - - buf[0] = 0x01; /* version 1 */ - buf[1] = i; - memcpy(buf + 2, purple_proxy_info_get_username(phb->gpi), i); - buf[2 + i] = j; - memcpy(buf + 2 + i + 1, purple_proxy_info_get_password(phb->gpi), j); - - if (write(source, buf, 3 + i + j) < 3 + i + j) { - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_readauth, phb); - } else { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n"); - _qq_s5_sendconnect(phb, source); - } -} - -static void _qq_s5_canwrite(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - int i; - struct PHB *phb = data; - socklen_t len; - int error = ETIMEDOUT; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Connected.\n"); - - if (phb->inpa > 0) - purple_input_remove(phb->inpa); - - len = sizeof(error); - if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { - purple_debug(PURPLE_DEBUG_INFO, "getsockopt", "%s\n", g_strerror(errno)); - close(source); - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - flags = fcntl(source, F_GETFL); - fcntl(source, F_SETFL, flags & ~O_NONBLOCK); - - i = 0; - buf[0] = 0x05; /* SOCKS version 5 */ - - if (purple_proxy_info_get_username(phb->gpi) != NULL) { - buf[1] = 0x02; /* two methods */ - buf[2] = 0x00; /* no authentication */ - buf[3] = 0x02; /* username/password authentication */ - i = 4; - } else { - buf[1] = 0x01; - buf[2] = 0x00; - i = 3; - } - - if (write(source, buf, i) < i) { - purple_debug(PURPLE_DEBUG_INFO, "write", "%s\n", g_strerror(errno)); - purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Unable to write\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread, phb); -} - -gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) -{ - gint fd; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Connecting to %s:%d via %s:%d using SOCKS5\n", - phb->host, phb->port, purple_proxy_info_get_host(phb->gpi), purple_proxy_info_get_port(phb->gpi)); - - if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) - return -1; - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd); - - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); - if (connect(fd, addr, addrlen) < 0) { - if ((errno == EINPROGRESS) || (errno == EINTR)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); - phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, _qq_s5_canwrite, phb); - } else { - close(fd); - return -1; - } - } else { - purple_debug(PURPLE_DEBUG_MISC, "QQ", "Connect in blocking mode.\n"); - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); - _qq_s5_canwrite(phb, fd, PURPLE_INPUT_WRITE); - } - - return fd; -}
--- a/libpurple/protocols/qq/udp_proxy_s5.h Wed Jul 09 00:27:44 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/** - * @file udp_proxy_s5.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _QQ_UDP_PROXY_S5_H_ -#define _QQ_UDP_PROXY_S5_H_ - -#include "internal.h" /* for socket stuff */ - -#include "qq_proxy.h" - -gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen); - -#endif
--- a/libpurple/protocols/qq/utils.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/utils.c Wed Jul 09 00:32:18 2008 +0000 @@ -22,7 +22,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include "cipher.h" #include "limits.h" #include "stdlib.h" #include "string.h" @@ -40,6 +39,17 @@ #define QQ_NAME_FORMAT "%d" +/* These functions are used only in development phase */ +/* + static void _qq_show_socket(gchar *desc, gint fd) { + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + getsockname(fd, (struct sockaddr *)&sin, &len); + purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n", + inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); + } + */ + gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount) { gint index; @@ -113,26 +123,6 @@ return segments; } -/* generate a md5 key using uid and session_key */ -guint8 *_gen_session_md5(gint uid, guint8 *session_key) -{ - guint8 *src, md5_str[QQ_KEY_LENGTH]; - PurpleCipher *cipher; - PurpleCipherContext *context; - - src = g_newa(guint8, 20); - memcpy(src, &uid, 4); - memcpy(src, session_key, QQ_KEY_LENGTH); - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, src, 20); - purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL); - purple_cipher_context_destroy(context); - - return g_memdup(md5_str, QQ_KEY_LENGTH); -} - /* given a four-byte ip data, convert it into a human readable ip string * the return needs to be freed */ gchar *gen_ip_str(guint8 *ip) @@ -194,7 +184,7 @@ } /* try to dump the data as GBK */ -void try_dump_as_gbk(const guint8 *const data, gint len) +gchar* try_dump_as_gbk(const guint8 *const data, gint len) { gint i; guint8 *incoming; @@ -215,8 +205,8 @@ if (msg_utf8 != NULL) { purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Try extract GB msg: %s\n", msg_utf8); - g_free(msg_utf8); } + return msg_utf8; } /* strips whitespace */ @@ -294,7 +284,7 @@ /* Dumps a chunk of raw data into an ASCII hex string. * The return should be freed later. */ -gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes) +static gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes) { GString *str; gchar *ret; @@ -331,6 +321,51 @@ return ret; } +void qq_hex_dump(PurpleDebugLevel level, const char *category, + const guint8 *pdata, gint bytes, + const char *format, ...) +{ + va_list args; + char *arg_s = NULL; + gchar *phex = NULL; + + g_return_if_fail(level != PURPLE_DEBUG_ALL); + g_return_if_fail(format != NULL); + + va_start(args, format); + arg_s = g_strdup_vprintf(format, args); + va_end(args); + + if (bytes <= 0) { + purple_debug(level, category, arg_s); + return; + } + + phex = hex_dump_to_str(pdata, bytes); + purple_debug(level, category, "%s - (len %d)\n%s", arg_s, bytes, phex); + g_free(phex); +} + +void qq_show_packet(const gchar *desc, const guint8 *buf, gint len) +{ + /* + char buf1[8*len+2], buf2[10]; + int i; + buf1[0] = 0; + for (i = 0; i < len; i++) { + sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff); + strcat(buf1, buf2); + } + strcat(buf1, "\n"); + purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1); + */ + + /* modified by s3e, 20080424 */ + qq_hex_dump(PURPLE_DEBUG_INFO, desc, + buf, len, + ""); +} + /* convert face num from packet (0-299) to local face (1-100) */ gchar *face_to_icon_str(gint face) {
--- a/libpurple/protocols/qq/utils.h Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/qq/utils.h Wed Jul 09 00:32:18 2008 +0000 @@ -28,12 +28,13 @@ #include <stdio.h> #include <glib.h> +#include "debug.h" + gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount); gchar *get_index_str_by_name(gchar **array, const gchar *name, gint amount); gint qq_string_to_dec_value(const gchar *str); gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_fields); -guint8 *_gen_session_md5(gint uid, guint8 *session_key); gchar *gen_ip_str(guint8 *ip); guint8 *str_ip_gen(gchar *str); @@ -44,10 +45,13 @@ gchar *face_to_icon_str(gint face); -void try_dump_as_gbk(const guint8 *const data, gint len); +gchar *try_dump_as_gbk(const guint8 *const data, gint len); +void qq_show_packet(const gchar *desc, const guint8 *buf, gint len); +void qq_hex_dump(PurpleDebugLevel level, const char *category, + const guint8 *pdata, gint bytes, + const char *format, ...); guint8 *hex_str_to_bytes(const gchar *buf, gint *out_len); -gchar *hex_dump_to_str(const guint8 *buf, gint buf_len); const gchar *qq_buddy_icon_dir(void); const gchar *qq_win32_buddy_icon_dir(void);
--- a/libpurple/protocols/silc/buddy.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/silc/buddy.c Wed Jul 09 00:32:18 2008 +0000 @@ -1434,13 +1434,25 @@ void silcpurple_idle_set(PurpleConnection *gc, int idle) { - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; + SilcPurple sg; + SilcClient client; + SilcClientConnection conn; SilcAttributeObjService service; const char *server; int port; + sg = gc->proto_data; + if (sg == NULL) + return; + + client = sg->client; + if (client == NULL) + return; + + conn = sg->conn; + if (conn == NULL) + return; + server = purple_account_get_string(sg->account, "server", "silc.silcnet.org"); port = purple_account_get_int(sg->account, "port", 706),
--- a/libpurple/protocols/silc10/buddy.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/protocols/silc10/buddy.c Wed Jul 09 00:32:18 2008 +0000 @@ -1434,13 +1434,25 @@ void silcpurple_idle_set(PurpleConnection *gc, int idle) { - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; + SilcPurple sg; + SilcClient client; + SilcClientConnection conn; SilcAttributeObjService service; const char *server; int port; + sg = gc->proto_data; + if (sg == NULL) + return; + + client = sg->client; + if (client == NULL) + return; + + conn = sg->conn; + if (conn == NULL) + return; + server = purple_account_get_string(sg->account, "server", "silc.silcnet.org"); port = purple_account_get_int(sg->account, "port", 706),
--- a/libpurple/util.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/util.c Wed Jul 09 00:32:18 2008 +0000 @@ -3599,7 +3599,7 @@ static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message); static gboolean -parse_redirect(const char *data, size_t data_len, gint sock, +parse_redirect(const char *data, size_t data_len, PurpleUtilFetchUrlData *gfud) { gchar *s; @@ -3773,7 +3773,7 @@ header_len, gfud->webdata); /* See if we can find a redirect. */ - if(parse_redirect(gfud->webdata, header_len, source, gfud)) + if(parse_redirect(gfud->webdata, header_len, gfud)) return; gfud->got_headers = TRUE;
--- a/libpurple/xmlnode.c Wed Jul 09 00:27:44 2008 +0000 +++ b/libpurple/xmlnode.c Wed Jul 09 00:32:18 2008 +0000 @@ -728,6 +728,13 @@ return ret; } +static void +xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data) +{ + GHashTable *ret = (GHashTable *)user_data; + g_hash_table_insert(ret, g_strdup(key), g_strdup(value)); +} + xmlnode * xmlnode_copy(const xmlnode *src) { @@ -739,17 +746,23 @@ ret = new_node(src->name, src->type); ret->xmlns = g_strdup(src->xmlns); - if(src->data) { - if(src->data_sz) { + if (src->data) { + if (src->data_sz) { ret->data = g_memdup(src->data, src->data_sz); ret->data_sz = src->data_sz; } else { ret->data = g_strdup(src->data); } } + ret->prefix = g_strdup(src->prefix); + if (src->namespace_map) { + ret->namespace_map = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + g_hash_table_foreach(src->namespace_map, xmlnode_copy_foreach_ns, ret->namespace_map); + } - for(child = src->child; child; child = child->next) { - if(sibling) { + for (child = src->child; child; child = child->next) { + if (sibling) { sibling->next = xmlnode_copy(child); sibling = sibling->next; } else {
--- a/pidgin/gtkblist.c Wed Jul 09 00:27:44 2008 +0000 +++ b/pidgin/gtkblist.c Wed Jul 09 00:32:18 2008 +0000 @@ -130,6 +130,7 @@ static guint visibility_manager_count = 0; static GdkVisibilityState gtk_blist_visibility = GDK_VISIBILITY_UNOBSCURED; +static gboolean gtk_blist_focused = FALSE; static gboolean editing_blist = FALSE; static GList *pidgin_blist_sort_methods = NULL; @@ -5166,9 +5167,14 @@ /******************************************/ static int -blist_focus_cb(GtkWidget *widget, gpointer data, PidginBuddyList *gtkblist) -{ - pidgin_set_urgent(GTK_WINDOW(gtkblist->window), FALSE); +blist_focus_cb(GtkWidget *widget, GdkEventFocus *event, PidginBuddyList *gtkblist) +{ + if(event->in) { + gtk_blist_focused = TRUE; + pidgin_set_urgent(GTK_WINDOW(gtkblist->window), FALSE); + } else { + gtk_blist_focused = FALSE; + } return 0; } @@ -5255,6 +5261,8 @@ gtkblist->window = pidgin_create_window(_("Buddy List"), 0, "buddy_list", TRUE); g_signal_connect(G_OBJECT(gtkblist->window), "focus-in-event", G_CALLBACK(blist_focus_cb), gtkblist); + g_signal_connect(G_OBJECT(gtkblist->window), "focus-out-event", + G_CALLBACK(blist_focus_cb), gtkblist); GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE; gtkblist->main_vbox = gtk_vbox_new(FALSE, 0); @@ -6988,8 +6996,15 @@ { if (gtkblist && gtkblist->window) { if (GTK_WIDGET_VISIBLE(gtkblist->window)) { + /* make the buddy list visible if it is iconified or if it is + * obscured and not currently focused (the focus part ensures + * that we do something reasonable if the buddy list is obscured + * by a window set to always be on top), otherwise hide the + * buddy list + */ purple_blist_set_visible(PIDGIN_WINDOW_ICONIFIED(gtkblist->window) || - gtk_blist_visibility != GDK_VISIBILITY_UNOBSCURED); + ((gtk_blist_visibility != GDK_VISIBILITY_UNOBSCURED) && + !gtk_blist_focused)); } else { purple_blist_set_visible(TRUE); }
--- a/pidgin/gtkimhtml.c Wed Jul 09 00:27:44 2008 +0000 +++ b/pidgin/gtkimhtml.c Wed Jul 09 00:32:18 2008 +0000 @@ -768,7 +768,7 @@ gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &end, buf_x + event->area.width, buf_y + event->area.height); - + gtk_text_iter_order(&start, &end); cur = start;
--- a/pidgin/gtkmenutray.c Wed Jul 09 00:27:44 2008 +0000 +++ b/pidgin/gtkmenutray.c Wed Jul 09 00:32:18 2008 +0000 @@ -98,8 +98,9 @@ gtk_widget_destroy(GTK_WIDGET(tray->tray)); #endif - if (tray->tooltips) - g_object_ref_sink(G_OBJECT(tray->tooltips)); + if (tray->tooltips) { + gtk_object_sink(GTK_OBJECT(tray->tooltips)); + } G_OBJECT_CLASS(parent_class)->finalize(obj); }
--- a/pidgin/win32/nsis/pidgin-installer.nsi Wed Jul 09 00:27:44 2008 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Wed Jul 09 00:32:18 2008 +0000 @@ -1388,67 +1388,6 @@ FunctionEnd -; This is a modified StartRadioButtons (from Sections.nsh) -; The only difference is that it allows for nothing in the group to be selected -; In that case, the default variable should be set to "" -!macro StartRadioButtonsUnselectable var - - !define StartRadioButtons_Var "${var}" - - Push $R0 - Push $R1 - - ;If we have no selection, don't try to unselect it - StrCmp "${StartRadioButtons_Var}" "" +4 - SectionGetFlags "${StartRadioButtons_Var}" $R0 - IntOp $R1 $R0 & ${SF_SELECTED} - IntOp $R0 $R0 & ${SECTION_OFF} - SectionSetFlags "${StartRadioButtons_Var}" $R0 - - ; If the previous value isn't currently selected, - ; we don't want to select it at the end - IntCmp $R1 ${SF_SELECTED} +2 - StrCpy "${StartRadioButtons_Var}" "" - - StrCpy $R1 "${StartRadioButtons_Var}" - -!macroend - -Function .onSelChange - Push $0 - Push $1 - Push $2 - - ; Check that at most one of the non-readonly spelling dictionaries are selected - ; We can't use $R0 or $R1 in this block since they're used in the macros - !insertmacro StartRadioButtonsUnselectable $SPELLCHECK_SEL - ; Start with the first language dictionary - IntOp $2 ${SecSpellCheck} + 1 - - start_spellcheck_radio: - SectionGetFlags $2 $0 - - IntOp $1 $0 & ${SF_SECGRPEND} - ; If it is the end of the section group, stop - IntCmp $1 ${SF_SECGRPEND} end_spellcheck_radio - - IntOp $0 $0 & ${SF_RO} - IntCmp $0 ${SF_RO} after_button_insert - ; If !readonly, then it is part of the radiobutton group - !insertmacro RadioButton $2 - after_button_insert: - - IntOp $2 $2 + 1 ;Advance to the next section - Goto start_spellcheck_radio - - end_spellcheck_radio: - !insertmacro EndRadioButtons - - Pop $2 - Pop $1 - Pop $0 -FunctionEnd - ; Page enter and exit functions.. Function preWelcomePage
--- a/po/de.po Wed Jul 09 00:27:44 2008 +0000 +++ b/po/de.po Wed Jul 09 00:32:18 2008 +0000 @@ -7,14 +7,13 @@ # # This file is distributed under the same license as the Pidgin package. # -# Bjoern Voigt <bjoern@cs.tu-berlin.de>, 2008. msgid "" msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-06-12 18:33+0200\n" -"PO-Revision-Date: 2008-06-12 18:32+0200\n" -"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n" +"POT-Creation-Date: 2008-06-25 23:30+0200\n" +"PO-Revision-Date: 2008-06-25 23:27+0200\n" +"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n" "Language-Team: Deutsch <de@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -622,7 +621,6 @@ msgid "Invite" msgstr "Einladen" -#, fuzzy msgid "" "Please enter the name of the user you wish to invite,\n" "along with an optional invite message." @@ -6282,9 +6280,8 @@ msgid "Camera" msgstr "Kamera" -#, fuzzy msgid "Screen Sharing" -msgstr "Screen Sharing" +msgstr "Gemeinsamer Bildschirm" msgid "Free For Chat" msgstr "Bereit zum Chatten" @@ -9766,7 +9763,6 @@ msgid "Stored Image" msgstr "Gespeichertes Bild" -#, fuzzy msgid "Stored Image. (that'll have to do for now)" msgstr "Gespeichertes Bild. (Das muss erstmal reichen)" @@ -11446,9 +11442,8 @@ msgid "Color to draw the name of an action message." msgstr "Farbe, mit der der Name in einer Aktions-Nachricht dargestellt wird." -#, fuzzy msgid "Action Message Name Color for Whispered Message" -msgstr "Farbe des Absendernamens für Aktions-Nachrichten" +msgstr "Farbe des Absendernamens für geflüsterte Aktions-Nachrichten" msgid "Whisper Message Name Color" msgstr "Farbe des Absendernamens für Flüster-Nachrichten" @@ -11523,7 +11518,6 @@ msgid "_Save Image..." msgstr "Bild _speichern..." -#, fuzzy msgid "_Add Custom Smiley..." msgstr "Benutzerdefinierten Smiley _hinzufügen..." @@ -11577,7 +11571,6 @@ msgid "Smile!" msgstr "Lächeln!" -#, fuzzy msgid "_Manage custom smileys" msgstr "Benutzerdefinierte Smileys _verwalten"
--- a/po/it.po Wed Jul 09 00:27:44 2008 +0000 +++ b/po/it.po Wed Jul 09 00:32:18 2008 +0000 @@ -3833,8 +3833,8 @@ msgstr "Impossibile trovare un'installazione di ActiveTCL. Se vuoi usare i plugin TCL, installa ActiveTCL da http://www.activestate.com\n" #: ../libpurple/protocols/bonjour/bonjour.c:107 -msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information." -msgstr "Il toolkit Apple Bonjour per Windows non è stato trovato. Leggi le FAQ su: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging per maggiori informazioni." +msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information." +msgstr "Il toolkit Apple Bonjour per Windows non è stato trovato. Leggi le FAQ su: http://d.pidgin.im/BonjourWindows per maggiori informazioni." #: ../libpurple/protocols/bonjour/bonjour.c:126 msgid "Unable to listen for incoming IM connections\n"
--- a/po/nb.po Wed Jul 09 00:27:44 2008 +0000 +++ b/po/nb.po Wed Jul 09 00:32:18 2008 +0000 @@ -3877,7 +3877,7 @@ msgstr "Kunne ikke finne en ActiveTCL installasjon. Om du ønsker å bruke TCL tillegg, installer ActiveTCL fra http://www.activestate.com\n" #: ../libpurple/protocols/bonjour/bonjour.c:101 -msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information." +msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information." msgstr "" #: ../libpurple/protocols/bonjour/bonjour.c:120
--- a/po/te.po Wed Jul 09 00:27:44 2008 +0000 +++ b/po/te.po Wed Jul 09 00:32:18 2008 +0000 @@ -4059,7 +4059,7 @@ msgstr "ActiveTCL ఇన్స్టాలేషన్ ను కనుగొనడంలో అశక్తత. మీరు TCL ప్లగ్ ఇన్లను ఉపయోగించాలనుకుంటే http://www.activestate.com\n" #: ../libpurple/protocols/bonjour/bonjour.c:108 -msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information." +msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information." msgstr "" #: ../libpurple/protocols/bonjour/bonjour.c:127
--- a/po/ur.po Wed Jul 09 00:27:44 2008 +0000 +++ b/po/ur.po Wed Jul 09 00:32:18 2008 +0000 @@ -3891,7 +3891,7 @@ msgstr "ایكٹیو TCL ا نسٹالیشن كی جانچ نا ممكن۔ اگر آپ TCL پلگ انس استعمال كرنا چاہتے ہیں ، ایكٹیوTCL انسٹال كروfrom http://www.activestate.com\n" #: ../libpurple/protocols/bonjour/bonjour.c:108 -msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information." +msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information." msgstr "" #: ../libpurple/protocols/bonjour/bonjour.c:127
--- a/po/vi.po Wed Jul 09 00:27:44 2008 +0000 +++ b/po/vi.po Wed Jul 09 00:32:18 2008 +0000 @@ -3916,7 +3916,7 @@ "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://" "developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-" "LocalMessaging for more information." -msgstr "Không tìm thấy bộ công cụ Apple Bonjour For Windows, xem FAQ (Hỏi Đáp) ở địa chỉ « http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging » để tìm chi tiết." +msgstr "Không tìm thấy bộ công cụ Apple Bonjour For Windows, xem FAQ (Hỏi Đáp) ở địa chỉ « http://d.pidgin.im/BonjourWindows » để tìm chi tiết." #: ../libpurple/protocols/bonjour/bonjour.c:120 msgid "Unable to listen for incoming IM connections\n"