# HG changeset patch # User John Bailey # Date 1304307744 0 # Node ID 3389c248e2b33aa6226e99fd0e1c8a8d9240a810 # Parent 000aac6e42fe7c5b0b02e1de597cf689c2803aa9# Parent 76595d4edf8630fd3a2c4cf0dcf60268ad2f445b propagate from branch 'im.pidgin.pidgin.mxit' (head 3d58b1f843fc2aebf9411756956d07ae96951623) to branch 'im.pidgin.pidgin' (head 68d280808dc7fafe82192c72f81929fa14b14b5c) diff -r 000aac6e42fe -r 3389c248e2b3 AUTHORS --- a/AUTHORS Mon Apr 18 21:20:33 2011 +0000 +++ b/AUTHORS Mon May 02 03:42:24 2011 +0000 @@ -138,4 +138,4 @@ Thanks to Jeroen van der Vegt for the initial smiley plugin and images. The OpenQ Team - Wrote the QQ plugin (see AUTHORS in the qq directory) + Wrote the QQ plugin dropped in 2.8.0 (see libpurple/qq/AUTHORS in 2.7.11) diff -r 000aac6e42fe -r 3389c248e2b3 COPYRIGHT --- a/COPYRIGHT Mon Apr 18 21:20:33 2011 +0000 +++ b/COPYRIGHT Mon May 02 03:42:24 2011 +0000 @@ -156,6 +156,7 @@ Ignacio J. Elia Brian Enigma Mattias Eriksson +Pat Erley Stefan Esser Steffen Eschenbacher Marc Etcheverry diff -r 000aac6e42fe -r 3389c248e2b3 ChangeLog --- a/ChangeLog Mon Apr 18 21:20:33 2011 +0000 +++ b/ChangeLog Mon May 02 03:42:24 2011 +0000 @@ -6,6 +6,8 @@ bandwidth for silent periods during a call. (Jakub Adam) (half of #13180) * Added the DigiCert High Assurance CA-3 intermediate CA, needed for validation of the Facebook XMPP interface's certificate. + * Removed the QQ protocol plugin. It hasn't worked in a long time and + isn't being maintained, therefore we no longer want it. Pidgin: * Duplicate code cleanup. (Gabriel Schulhof) (#10599) @@ -13,9 +15,12 @@ on the fly. (Jakub Adam) (half of #13535) * Don't cancel an ongoing call when rejecting the addition of a stream to the existing call. (Jakub Adam) (#13537) + * Pidgin plugins can now override tab completion and detect clicks on + usernames in the chat userlist. (kawaii.neko) (#12599) libpurple: - * media: Allow obtaining active local and remote candidates. (#11830) + * media: Allow obtaining active local and remote candidates. (Jakub + Adam) (#11830) * media: Allow getting/setting video capabilities. (Jakub Adam) (half of #13095) * Simple Silence Suppression is optional per-account. (Jakub Adam) (half @@ -27,6 +32,8 @@ (#13505) * When removing a buddy, delete the pounces associated with it. (Kartik Mohta) (#1131) + * media: Allow libpurple and plugins to set SDES properties for RTP + conferences. (Jakub Adam) (#12981) Gadu-Gadu: * Allow showing your status only to buddies. (Mateusz Piękos) (#13358) @@ -39,8 +46,18 @@ * Support typing notifications. (Jan Zachorowski, Tomasz Wasilczyk, Krzysztof Klinikowski) (#13362, #13590) * Require libgadu 1.10.1 to avoid using internal libgadu. - * SSL connection support for GNUTLS users (not on Windows yet!). - (Tomasz Wasilczyk) (#13613) + * Optional SSL connection support for GNUTLS users (not on Windows + yet!). (Tomasz Wasilczyk) (#13613, #13894) + * Don't count received messages or statuses when determining whether + to send a keepalive packet. (Jan Zachorowski) (#13699) + * Fix a crash when receiving images on Windows or an incorrect + timestamp in the log when receiving images on Linux. (Tomasz + Wasilczyk) (#10268) + * Support XML events, resulting in immediate update of other users' + buddy icons. (Tomasz Wasilczyk) (#13739) + * Accept poorly formatted URLs from other third-party clients in + the same manner as the official client. (Tomasz Wasilczyk) + (#13886) ICQ: * Fix unsetting your mood when "None" is selected. (Dustin Gathmann) @@ -73,6 +90,8 @@ Plugins: * The Voice/Video Settings plugin now includes the ability to test microphone settings. (Jakub Adam) (#13182) + * Fix a crash when handling some saved settings in the Voice/Video + Settings plugin. (Pat Erley) (13290, #13774) Windows-Specific Changes: * Fix building libpurple with Visual C++ .NET 2005. This was accidentally diff -r 000aac6e42fe -r 3389c248e2b3 ChangeLog.API --- a/ChangeLog.API Mon Apr 18 21:20:33 2011 +0000 +++ b/ChangeLog.API Mon May 02 03:42:24 2011 +0000 @@ -8,27 +8,38 @@ * cleared-message-history signal (conversation signals) * purple_account_add_buddy_with_invite * purple_account_add_buddies_with_invite + * purple_dnsquery_a_account * purple_notify_user_info_add_pair_plaintext * purple_media_get_active_local_candidates * purple_media_get_active_remote_candidates * purple_media_manager_get_video_caps (Jakub Adam) (#13095) * purple_media_manager_set_video_caps (Jakub Adam) (#13095) * purple_pounce_destroy_all_by_buddy (Kartik Mohta) (#1131) + * purple_proxy_connect_socks5_account + * purple_srv_resolve_account + * purple_txt_resolve_account * Added add_buddy_with_invite to PurplePluginProtocolInfo * Added add_buddies_with_invite to PurplePluginProtocolInfo * Added PurpleSrvTxtQueryUiOps which allow UIs to specify their own mechanisms to resolve SRV and/or TXT queries. It works similar to PurpleDnsQueryUiOps + * purple_marshal_BOOLEAN__POINTER_BOOLEAN (kawaii.neko) (#12599) Deprecated: * purple_account_add_buddy * purple_account_add_buddies_with_invite + * purple_dnsquery_a + * purple_proxy_connect_socks5 + * purple_srv_resolve + * purple_txt_resolve * add_buddy from PurplePluginProtocolInfo struct * add_buddies from PurplePluginProtocolInfo struct Pidgin: Added: * pidgin_make_scrollable (Gabriel Schulhof) (#10599) + * chat-nick-clicked signal (kawaii.neko) (#12599) + * chat-nick-autocomplete signal (kawaii.neko) (#12599) version 2.7.11 (03/10/2011): * libpurple: diff -r 000aac6e42fe -r 3389c248e2b3 configure.ac --- a/configure.ac Mon Apr 18 21:20:33 2011 +0000 +++ b/configure.ac Mon May 02 03:42:24 2011 +0000 @@ -1058,7 +1058,7 @@ ]])], [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ #if GG_DEFAULT_PROTOCOL_VERSION < 0x2e -#error "Your libgadu version is too old. libpurple requires 1.9.0-rc2 or higher." +#error "Your libgadu version is too old. libpurple requires 1.10.1 or higher." #endif ]])], [ AC_MSG_RESULT(yes) @@ -1069,7 +1069,7 @@ echo echo echo "Your supplied copy of libgadu is too old." - echo "Install version 1.9.0-rc2 or newer." + echo "Install version 1.10.1 or newer." echo "Then rerun this ./configure" echo echo "Falling back to using our own copy of libgadu" @@ -1114,7 +1114,7 @@ fi if test "x$STATIC_PRPLS" = "xall" ; then - STATIC_PRPLS="bonjour gg irc jabber msn myspace mxit novell oscar qq sametime silc simple yahoo zephyr" + STATIC_PRPLS="bonjour gg irc jabber msn myspace mxit novell oscar sametime silc simple yahoo zephyr" fi if test "x$have_meanwhile" != "xyes" ; then STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'` @@ -1170,7 +1170,6 @@ oscar) static_oscar=yes ;; aim) static_oscar=yes ;; icq) static_oscar=yes ;; - qq) static_qq=yes ;; sametime) static_sametime=yes ;; silc) static_silc=yes ;; silc10) static_silc=yes ;; @@ -1189,7 +1188,6 @@ AM_CONDITIONAL(STATIC_MXIT, test "x$static_mxit" = "xyes") AM_CONDITIONAL(STATIC_NOVELL, test "x$static_novell" = "xyes") AM_CONDITIONAL(STATIC_OSCAR, test "x$static_oscar" = "xyes") -AM_CONDITIONAL(STATIC_QQ, test "x$static_qq" = "xyes") AM_CONDITIONAL(STATIC_SAMETIME, test "x$static_sametime" = "xyes" -a "x$have_meanwhile" = "xyes") AM_CONDITIONAL(STATIC_SILC, test "x$static_silc" = "xyes" -a "x$have_silc" = "xyes") AM_CONDITIONAL(STATIC_SIMPLE, test "x$static_simple" = "xyes") @@ -1201,7 +1199,7 @@ AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`]) if test "x$DYNAMIC_PRPLS" = "xall" ; then - DYNAMIC_PRPLS="bonjour gg irc jabber msn myspace mxit novell oscar qq sametime silc simple yahoo zephyr" + DYNAMIC_PRPLS="bonjour gg irc jabber msn myspace mxit novell oscar sametime silc simple yahoo zephyr" fi if test "x$have_meanwhile" != "xyes"; then DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'` @@ -1230,7 +1228,6 @@ oscar) dynamic_oscar=yes ;; aim) dynamic_oscar=yes ;; icq) dynamic_oscar=yes ;; - qq) dynamic_qq=yes ;; sametime) dynamic_sametime=yes ;; silc) dynamic_silc=yes ;; silc10) dynamic_silc=yes ;; @@ -2580,7 +2577,6 @@ libpurple/protocols/novell/Makefile libpurple/protocols/null/Makefile libpurple/protocols/oscar/Makefile - libpurple/protocols/qq/Makefile libpurple/protocols/sametime/Makefile libpurple/protocols/silc/Makefile libpurple/protocols/silc10/Makefile diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/account.c --- a/libpurple/account.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/account.c Mon May 02 03:42:24 2011 +0000 @@ -294,6 +294,7 @@ proxy_type == PURPLE_PROXY_HTTP ? "http" : proxy_type == PURPLE_PROXY_SOCKS4 ? "socks4" : proxy_type == PURPLE_PROXY_SOCKS5 ? "socks5" : + proxy_type == PURPLE_PROXY_TOR ? "tor" : proxy_type == PURPLE_PROXY_USE_ENVVAR ? "envvar" : "unknown"), -1); if ((value = purple_proxy_info_get_host(proxy_info)) != NULL) @@ -746,6 +747,8 @@ purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_SOCKS4); else if (purple_strequal(data, "socks5")) purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_SOCKS5); + else if (purple_strequal(data, "tor")) + purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_TOR); else if (purple_strequal(data, "envvar")) purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_USE_ENVVAR); else diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/dnsquery.c --- a/libpurple/dnsquery.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/dnsquery.c Mon May 02 03:42:24 2011 +0000 @@ -54,6 +54,7 @@ PurpleDnsQueryConnectFunction callback; gpointer data; guint timeout; + PurpleAccount *account; #if defined(PURPLE_DNSQUERY_USE_FORK) PurpleDnsQueryResolverProcess *resolver; @@ -153,6 +154,7 @@ resolve_ip(PurpleDnsQueryData *query_data) { struct sockaddr_in sin; + /* TODO: Use inet_pton for IPv6 support */ if (inet_aton(query_data->hostname, &sin.sin_addr)) { /* @@ -341,20 +343,17 @@ } freeaddrinfo(tmp); #else - if (!inet_aton(hostname, &sin.sin_addr)) { - struct hostent *hp; - if (!(hp = gethostbyname(hostname))) { - write_to_parent(child_out, &h_errno, sizeof(int)); - close(child_out); - if (show_debug) - printf("DNS Error: %d\n", h_errno); - _exit(0); - } - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - } else - sin.sin_family = AF_INET; + struct hostent *hp; + if (!(hp = gethostbyname(hostname))) { + write_to_parent(child_out, &h_errno, sizeof(int)); + close(child_out); + if (show_debug) + printf("DNS Error: %d\n", h_errno); + _exit(0); + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; sin.sin_port = htons(dns_params.port); rc = 0; @@ -669,62 +668,12 @@ handle_next_queued_request(); } -static gboolean -resolve_host(gpointer data) +static void +resolve_host(PurpleDnsQueryData *query_data) { - PurpleDnsQueryData *query_data; - - query_data = data; - query_data->timeout = 0; - - if (resolve_ip(query_data)) - { - /* resolve_ip calls purple_dnsquery_resolved */ - return FALSE; - } - - if (purple_dnsquery_ui_resolve(query_data)) - { - /* The UI is handling the resolve; we're done */ - return FALSE; - } - queued_requests = g_slist_append(queued_requests, query_data); handle_next_queued_request(); - - return FALSE; -} - -PurpleDnsQueryData * -purple_dnsquery_a(const char *hostname, int port, - PurpleDnsQueryConnectFunction callback, gpointer data) -{ - PurpleDnsQueryData *query_data; - - g_return_val_if_fail(hostname != NULL, NULL); - g_return_val_if_fail(port != 0, NULL); - g_return_val_if_fail(callback != NULL, NULL); - - query_data = g_new(PurpleDnsQueryData, 1); - query_data->hostname = g_strdup(hostname); - g_strstrip(query_data->hostname); - query_data->port = port; - query_data->callback = callback; - query_data->data = data; - query_data->resolver = NULL; - - if (*query_data->hostname == '\0') - { - purple_dnsquery_destroy(query_data); - g_return_val_if_reached(NULL); - } - - purple_debug_info("dns", "DNS query for '%s' queued\n", query_data->hostname); - - query_data->timeout = purple_timeout_add(0, resolve_host, query_data); - - return query_data; } #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */ @@ -837,50 +786,116 @@ return 0; } +static void +resolve_host(PurpleDnsQueryData *query_data) +{ + GError *err = NULL; + + /* + * Spin off a separate thread to perform the DNS lookup so + * that we don't block the UI. + */ + query_data->resolver = g_thread_create(dns_thread, + query_data, FALSE, &err); + if (query_data->resolver == NULL) + { + char message[1024]; + g_snprintf(message, sizeof(message), _("Thread creation failure: %s"), + (err && err->message) ? err->message : _("Unknown reason")); + g_error_free(err); + purple_dnsquery_failed(query_data, message); + } +} + +#else /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */ + +/* + * We weren't able to do anything fancier above, so use the + * fail-safe name resolution code, which is blocking. + */ + +static void +resolve_host(PurpleDnsQueryData *query_data) +{ + struct sockaddr_in sin; + GSList *hosts = NULL; + struct hostent *hp; + gchar *hostname; +#ifdef USE_IDN + if (!dns_str_is_ascii(query_data->hostname)) { + int ret = purple_network_convert_idn_to_ascii(query_data->hostname, + &hostname); + if (ret != 0) { + char message[1024]; + g_snprintf(message, sizeof(message), _("Error resolving %s: %d"), + query_data->hostname, ret); + purple_dnsquery_failed(query_data, message); + return; + } + } else /* fallthrough is intentional to the g_strdup */ +#endif + hostname = g_strdup(query_data->hostname); + + if(!(hp = gethostbyname(hostname))) { + char message[1024]; + g_snprintf(message, sizeof(message), _("Error resolving %s: %d"), + query_data->hostname, h_errno); + purple_dnsquery_failed(query_data, message); + g_free(hostname); + return; + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + g_free(hostname); + sin.sin_port = htons(query_data->port); + + hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); + hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); + + purple_dnsquery_resolved(query_data, hosts); +} + +#endif /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */ + static gboolean -resolve_host(gpointer data) +initiate_resolving(gpointer data) { PurpleDnsQueryData *query_data; - GError *err = NULL; + PurpleProxyType proxy_type; query_data = data; query_data->timeout = 0; - if (purple_dnsquery_ui_resolve(query_data)) - { - /* The UI is handling the resolve; we're done */ + if (resolve_ip(query_data)) + /* resolve_ip calls purple_dnsquery_resolved */ + return FALSE; + + proxy_type = purple_proxy_info_get_type( + purple_proxy_get_setup(query_data->account)); + if (proxy_type == PURPLE_PROXY_TOR) { + purple_dnsquery_failed(query_data, + _("Aborting DNS lookup in Tor Proxy mode.")); return FALSE; } - if (!resolve_ip(query_data)) - { - /* - * Spin off a separate thread to perform the DNS lookup so - * that we don't block the UI. - */ - query_data->resolver = g_thread_create(dns_thread, - query_data, FALSE, &err); - if (query_data->resolver == NULL) - { - char message[1024]; - g_snprintf(message, sizeof(message), _("Thread creation failure: %s"), - (err && err->message) ? err->message : _("Unknown reason")); - g_error_free(err); - purple_dnsquery_failed(query_data, message); - } - } + if (purple_dnsquery_ui_resolve(query_data)) + /* The UI is handling the resolve; we're done */ + return FALSE; + + resolve_host(query_data); return FALSE; } PurpleDnsQueryData * -purple_dnsquery_a(const char *hostname, int port, +purple_dnsquery_a_account(PurpleAccount *account, const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data) { PurpleDnsQueryData *query_data; g_return_val_if_fail(hostname != NULL, NULL); - g_return_val_if_fail(port != 0, NULL); + g_return_val_if_fail(port != 0, NULL); g_return_val_if_fail(callback != NULL, NULL); purple_debug_info("dnsquery", "Performing DNS lookup for %s\n", hostname); @@ -891,115 +906,26 @@ query_data->port = port; query_data->callback = callback; query_data->data = data; + query_data->account = account; - if (strlen(query_data->hostname) == 0) + if (*query_data->hostname == '\0') { purple_dnsquery_destroy(query_data); g_return_val_if_reached(NULL); } - /* Don't call the callback before returning */ - query_data->timeout = purple_timeout_add(0, resolve_host, query_data); + query_data->timeout = purple_timeout_add(0, initiate_resolving, query_data); return query_data; } -#else /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */ - -/* - * We weren't able to do anything fancier above, so use the - * fail-safe name resolution code, which is blocking. - */ - -static gboolean -resolve_host(gpointer data) -{ - PurpleDnsQueryData *query_data; - struct sockaddr_in sin; - GSList *hosts = NULL; - - query_data = data; - query_data->timeout = 0; - - if (purple_dnsquery_ui_resolve(query_data)) - { - /* The UI is handling the resolve; we're done */ - return FALSE; - } - - if (!inet_aton(query_data->hostname, &sin.sin_addr)) { - struct hostent *hp; - gchar *hostname; -#ifdef USE_IDN - if (!dns_str_is_ascii(query_data->hostname)) { - int ret = purple_network_convert_idn_to_ascii(query_data->hostname, - &hostname); - if (ret != 0) { - char message[1024]; - g_snprintf(message, sizeof(message), _("Error resolving %s: %d"), - query_data->hostname, ret); - purple_dnsquery_failed(query_data, message); - return FALSE; - } - } else /* fallthrough is intentional to the g_strdup */ -#endif - hostname = g_strdup(query_data->hostname); - - if(!(hp = gethostbyname(hostname))) { - char message[1024]; - g_snprintf(message, sizeof(message), _("Error resolving %s: %d"), - query_data->hostname, h_errno); - purple_dnsquery_failed(query_data, message); - g_free(hostname); - return FALSE; - } - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - g_free(hostname); - } else - sin.sin_family = AF_INET; - sin.sin_port = htons(query_data->port); - - hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); - hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); - - purple_dnsquery_resolved(query_data, hosts); - - return FALSE; -} - PurpleDnsQueryData * purple_dnsquery_a(const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data) { - PurpleDnsQueryData *query_data; - - g_return_val_if_fail(hostname != NULL, NULL); - g_return_val_if_fail(port != 0, NULL); - g_return_val_if_fail(callback != NULL, NULL); - - query_data = g_new(PurpleDnsQueryData, 1); - query_data->hostname = g_strdup(hostname); - g_strstrip(query_data->hostname); - query_data->port = port; - query_data->callback = callback; - query_data->data = data; - - if (strlen(query_data->hostname) == 0) - { - purple_dnsquery_destroy(query_data); - g_return_val_if_reached(NULL); - } - - /* Don't call the callback before returning */ - query_data->timeout = purple_timeout_add(0, resolve_host, query_data); - - return query_data; + return purple_dnsquery_a_account(NULL, hostname, port, callback, data); } -#endif /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */ - void purple_dnsquery_destroy(PurpleDnsQueryData *query_data) { diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/dnsquery.h --- a/libpurple/dnsquery.h Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/dnsquery.h Mon May 02 03:42:24 2011 +0000 @@ -88,6 +88,7 @@ /** * Perform an asynchronous DNS query. * + * @param account the account that the query is being done for (or NULL) * @param hostname The hostname to resolve. * @param port A port number which is stored in the struct sockaddr. * @param callback The callback function to call after resolving. @@ -96,8 +97,28 @@ * @return NULL if there was an error, otherwise return a reference to * a data structure that can be used to cancel the pending * DNS query, if needed. + * + * @since 2.8.0 + */ +PurpleDnsQueryData *purple_dnsquery_a_account(PurpleAccount *account, const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data); + +#ifndef PURPLE_DISABLE_DEPRECATED +/** + * Perform an asynchronous DNS query. + * + * @param hostname The hostname to resolve. + * @param port A port number which is stored in the struct sockaddr. + * @param callback The callback function to call after resolving. + * @param data Extra data to pass to the callback function. + * + * @return NULL if there was an error, otherwise return a reference to + * a data structure that can be used to cancel the pending + * DNS query, if needed. + * + * @deprecated Use purple_dnsquery_a_account instead */ PurpleDnsQueryData *purple_dnsquery_a(const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data); +#endif /** * Cancel a DNS query and destroy the associated data structure. diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/dnssrv.c --- a/libpurple/dnssrv.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/dnssrv.c Mon May 02 03:42:24 2011 +0000 @@ -30,12 +30,6 @@ #ifdef HAVE_ARPA_NAMESER_COMPAT_H #include #endif -#ifndef T_SRV -#define T_SRV PurpleDnsTypeSrv -#endif -#ifndef T_TXT -#define T_TXT PurpleDnsTypeTxt -#endif #else /* WIN32 */ #include /* Missing from the mingw headers */ @@ -47,6 +41,13 @@ #endif #endif +#ifndef T_SRV +#define T_SRV PurpleDnsTypeSrv +#endif +#ifndef T_TXT +#define T_TXT PurpleDnsTypeTxt +#endif + #include "debug.h" #include "dnssrv.h" #include "eventloop.h" @@ -673,11 +674,22 @@ #endif PurpleSrvTxtQueryData * -purple_srv_resolve(const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata) +purple_srv_resolve(const char *protocol, const char *transport, + const char *domain, PurpleSrvCallback cb, gpointer extradata) +{ + return purple_srv_resolve_account(NULL, protocol, transport, domain, + cb, extradata); +} + +PurpleSrvTxtQueryData * +purple_srv_resolve_account(PurpleAccount *account, const char *protocol, + const char *transport, const char *domain, PurpleSrvCallback cb, + gpointer extradata) { char *query; char *hostname; PurpleSrvTxtQueryData *query_data; + PurpleProxyType proxy_type; #ifndef _WIN32 PurpleSrvInternalQuery internal_query; int in[2], out[2]; @@ -693,6 +705,14 @@ g_return_val_if_reached(NULL); } + proxy_type = purple_proxy_info_get_type( + purple_proxy_get_setup(account)); + if (proxy_type == PURPLE_PROXY_TOR) { + purple_debug_info("dnssrv", "Aborting SRV lookup in Tor Proxy mode."); + cb(NULL, 0, extradata); + return NULL; + } + #ifdef USE_IDN if (!dns_str_is_ascii(domain)) { int ret = purple_network_convert_idn_to_ascii(domain, &hostname); @@ -794,11 +814,20 @@ #endif } -PurpleSrvTxtQueryData *purple_txt_resolve(const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata) +PurpleSrvTxtQueryData *purple_txt_resolve(const char *owner, + const char *domain, PurpleTxtCallback cb, gpointer extradata) +{ + return purple_txt_resolve_account(NULL, owner, domain, cb, extradata); +} + +PurpleSrvTxtQueryData *purple_txt_resolve_account(PurpleAccount *account, + const char *owner, const char *domain, PurpleTxtCallback cb, + gpointer extradata) { char *query; char *hostname; PurpleSrvTxtQueryData *query_data; + PurpleProxyType proxy_type; #ifndef _WIN32 PurpleSrvInternalQuery internal_query; int in[2], out[2]; @@ -808,6 +837,14 @@ static gboolean initialized = FALSE; #endif + proxy_type = purple_proxy_info_get_type( + purple_proxy_get_setup(account)); + if (proxy_type == PURPLE_PROXY_TOR) { + purple_debug_info("dnssrv", "Aborting TXT lookup in Tor Proxy mode."); + cb(NULL, extradata); + return NULL; + } + #ifdef USE_IDN if (!dns_str_is_ascii(domain)) { int ret = purple_network_convert_idn_to_ascii(domain, &hostname); @@ -1042,4 +1079,4 @@ g_return_val_if_fail(query_data != NULL, 0); return query_data->type; -} \ No newline at end of file +} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/dnssrv.h --- a/libpurple/dnssrv.h Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/dnssrv.h Mon May 02 03:42:24 2011 +0000 @@ -103,13 +103,31 @@ /** * Queries an SRV record. * + * @param account the account that the query is being done for (or NULL) * @param protocol Name of the protocol (e.g. "sip") * @param transport Name of the transport ("tcp" or "udp") * @param domain Domain name to query (e.g. "blubb.com") * @param cb A callback which will be called with the results * @param extradata Extra data to be passed to the callback + * + * @since 2.8.0 + */ +PurpleSrvTxtQueryData *purple_srv_resolve_account(PurpleAccount *account, const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata); + +#ifndef PURPLE_DISABLE_DEPRECATED +/** + * Queries an SRV record. + * + * @param protocol Name of the protocol (e.g. "sip") + * @param transport Name of the transport ("tcp" or "udp") + * @param domain Domain name to query (e.g. "blubb.com") + * @param cb A callback which will be called with the results + * @param extradata Extra data to be passed to the callback + * + * @deprecated Use purple_srv_resolve_account instead */ PurpleSrvTxtQueryData *purple_srv_resolve(const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata); +#endif /** * Cancel an SRV or DNS query. @@ -121,14 +139,31 @@ /** * Queries an TXT record. * + * @param account the account that the query is being done for (or NULL) * @param owner Name of the protocol (e.g. "_xmppconnect") * @param domain Domain name to query (e.g. "blubb.com") * @param cb A callback which will be called with the results * @param extradata Extra data to be passed to the callback * + * @since 2.8.0 + */ +PurpleSrvTxtQueryData *purple_txt_resolve_account(PurpleAccount *account, const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata); + +#ifndef PURPLE_DISABLE_DEPRECATED +/** + * Queries an TXT record. + * + * @param owner Name of the protocol (e.g. "_xmppconnect") + * @param domain Domain name to query (e.g. "blubb.com") + * @param cb A callback which will be called with the results + * @param extradata Extra data to be passed to the callback + * + * @deprecated Use purple_txt_resolve_account instead + * * @since 2.6.0 */ PurpleSrvTxtQueryData *purple_txt_resolve(const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata); +#endif /** * Cancel an TXT DNS query. diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/media.c --- a/libpurple/media.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/media.c Mon May 02 03:42:24 2011 +0000 @@ -916,6 +916,47 @@ #endif } +void +purple_media_set_params(PurpleMedia *media, + guint num_params, GParameter *params) +{ +#ifdef USE_VV + g_return_if_fail(PURPLE_IS_MEDIA(media)); + + purple_media_backend_set_params(media->priv->backend, num_params, params); +#endif +} + +const gchar ** +purple_media_get_available_params(PurpleMedia *media) +{ + static const gchar *NULL_ARRAY[] = { NULL }; +#ifdef USE_VV + g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL_ARRAY); + + return purple_media_backend_get_available_params(media->priv->backend); +#else + return NULL_ARRAY; +#endif +} + +gboolean +purple_media_param_is_supported(PurpleMedia *media, const gchar *param) +{ +#ifdef USE_VV + const gchar **params; + + g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); + g_return_val_if_fail(param != NULL, FALSE); + + params = purple_media_backend_get_available_params(media->priv->backend); + for (; *params != NULL; ++params) + if (!strcmp(*params, param)) + return TRUE; +#endif + return FALSE; +} + #ifdef USE_VV static void purple_media_new_local_candidate_cb(PurpleMediaBackend *backend, diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/media.h --- a/libpurple/media.h Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/media.h Mon May 02 03:42:24 2011 +0000 @@ -144,6 +144,52 @@ gboolean local); /** + * Sets various optional parameters of the media call. + * + * Currently supported are: + * - "sdes-cname" : The CNAME for the RTP sessions + * - "sdes-name" : Real name used to describe the source in SDES messages + * - "sdes-tool" : The TOOL to put in SDES messages + * - "sdes-email" : Email address to put in SDES messages + * - "sdes-location" : The LOCATION to put in SDES messages + * - "sdes-note" : The NOTE to put in SDES messages + * - "sdes-phone" : The PHONE to put in SDES messages + * + * @param media The media object to set the parameters on. + * @param num_params The number of parameters to pass + * @param params Array of @c GParameter to pass + * + * @since 2.8.0 + */ +void purple_media_set_params(PurpleMedia *media, + guint num_params, GParameter *params); + +/** + * Gets the list of optional parameters supported by the media backend. + * + * The list is owned by the @c PurpleMedia internals and should NOT be freed. + * + * @param media The media object + * + * @return NULL-terminated array of names of supported parameters. + * + * @since 2.8.0 + */ +const gchar **purple_media_get_available_params(PurpleMedia *media); + +/** + * Checks if given optional parameter is supported by the media backend. + * + * @param media The media object + * @param param name of parameter + * + * @return @c TRUE if backend recognizes the parameter, @c FALSE otherwise. + * + * @since 2.8.0 + */ +gboolean purple_media_param_is_supported(PurpleMedia *media, const gchar *param); + +/** * Adds a stream to a session. * * It only adds a stream to one audio session or video session as diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/media/backend-fs2.c --- a/libpurple/media/backend-fs2.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/media/backend-fs2.c Mon May 02 03:42:24 2011 +0000 @@ -85,6 +85,9 @@ static gboolean purple_media_backend_fs2_set_send_codec( PurpleMediaBackend *self, const gchar *sess_id, PurpleMediaCodec *codec); +static void purple_media_backend_fs2_set_params(PurpleMediaBackend *self, + guint num_params, GParameter *params); +static const gchar **purple_media_backend_fs2_get_available_params(void); static void free_stream(PurpleMediaBackendFs2Stream *stream); static void free_session(PurpleMediaBackendFs2Session *session); @@ -413,6 +416,8 @@ purple_media_backend_fs2_get_local_candidates; iface->set_remote_codecs = purple_media_backend_fs2_set_remote_codecs; iface->set_send_codec = purple_media_backend_fs2_set_send_codec; + iface->set_params = purple_media_backend_fs2_set_params; + iface->get_available_params = purple_media_backend_fs2_get_available_params; } static FsMediaType @@ -2126,6 +2131,49 @@ return TRUE; } + +static void +purple_media_backend_fs2_set_params(PurpleMediaBackend *self, + guint num_params, GParameter *params) +{ + PurpleMediaBackendFs2Private *priv; + const gchar **supported = purple_media_backend_fs2_get_available_params(); + const gchar **p; + guint i; + + g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self)); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + + if (priv->conference == NULL && + !init_conference(PURPLE_MEDIA_BACKEND_FS2(self))) { + purple_debug_error("backend-fs2", + "Error initializing the conference.\n"); + return; + } + + for (i = 0; i != num_params; ++i) { + for (p = supported; *p != NULL; ++p) { + if (!strcmp(params[i].name, *p)) { + g_object_set(priv->conference, + params[i].name, g_value_get_string(¶ms[i].value), + NULL); + break; + } + } + } +} + +static const gchar ** +purple_media_backend_fs2_get_available_params(void) +{ + static const gchar *supported_params[] = { + "sdes-cname", "sdes-email", "sdes-location", "sdes-name", "sdes-note", + "sdes-phone", "sdes-tool", NULL + }; + + return supported_params; +} #else GType purple_media_backend_fs2_get_type(void) diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/media/backend-iface.c --- a/libpurple/media/backend-iface.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/media/backend-iface.c Mon May 02 03:42:24 2011 +0000 @@ -192,3 +192,20 @@ return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->set_send_codec(self, sess_id, codec); } + +void +purple_media_backend_set_params(PurpleMediaBackend *self, + guint num_params, GParameter *params) +{ + g_return_if_fail(PURPLE_IS_MEDIA_BACKEND(self)); + PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->set_params(self, num_params, params); +} + +const gchar ** +purple_media_backend_get_available_params(PurpleMediaBackend *self) +{ + static const gchar *NULL_ARRAY[] = { NULL }; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), NULL_ARRAY); + return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->get_available_params(); +} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/media/backend-iface.h --- a/libpurple/media/backend-iface.h Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/media/backend-iface.h Mon May 02 03:42:24 2011 +0000 @@ -68,6 +68,9 @@ GList *codecs); gboolean (*set_send_codec) (PurpleMediaBackend *self, const gchar *sess_id, PurpleMediaCodec *codec); + void (*set_params) (PurpleMediaBackend *self, + guint num_params, GParameter *params); + const gchar **(*get_available_params) (void); }; /** @@ -191,6 +194,31 @@ gboolean purple_media_backend_set_send_codec(PurpleMediaBackend *self, const gchar *sess_id, PurpleMediaCodec *codec); +/** + * Sets various optional parameters of the media backend. + * + * @param self The media backend to set the parameters on. + * @param num_params The number of parameters to pass to backend + * @param params Array of @c GParameter to pass to backend + * + * @since 2.8.0 + */ +void purple_media_backend_set_params(PurpleMediaBackend *self, + guint num_params, GParameter *params); + +/** + * Gets the list of optional parameters supported by the media backend. + * + * The list should NOT be freed. + * + * @param self The media backend + * + * @return NULL-terminated array of names of supported parameters. + * + * @since 2.8.0 + */ +const gchar **purple_media_backend_get_available_params(PurpleMediaBackend *self); + G_END_DECLS #endif /* _MEDIA_BACKEND_IFACE_H_ */ diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/network.c --- a/libpurple/network.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/network.c Mon May 02 03:42:24 2011 +0000 @@ -991,7 +991,7 @@ if (stun_server && stun_server[0] != '\0') { if (purple_network_is_available()) { purple_debug_info("network", "running DNS query for STUN server\n"); - purple_dnsquery_a(stun_server, 3478, purple_network_ip_lookup_cb, + purple_dnsquery_a_account(NULL, stun_server, 3478, purple_network_ip_lookup_cb, &stun_ip); } else { purple_debug_info("network", @@ -1009,7 +1009,7 @@ if (turn_server && turn_server[0] != '\0') { if (purple_network_is_available()) { purple_debug_info("network", "running DNS query for TURN server\n"); - purple_dnsquery_a(turn_server, + purple_dnsquery_a_account(NULL, turn_server, purple_prefs_get_int("/purple/network/turn_port"), purple_network_ip_lookup_cb, &turn_ip); } else { diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/Makefile.am --- a/libpurple/protocols/Makefile.am Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/Makefile.am Mon May 02 03:42:24 2011 +0000 @@ -1,5 +1,5 @@ EXTRA_DIST = Makefile.mingw -DIST_SUBDIRS = bonjour gg irc jabber msn myspace mxit novell null oscar qq sametime silc silc10 simple yahoo zephyr +DIST_SUBDIRS = bonjour gg irc jabber msn myspace mxit novell null oscar sametime silc silc10 simple yahoo zephyr SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS) diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/Makefile.mingw --- a/libpurple/protocols/Makefile.mingw Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/Makefile.mingw Mon May 02 03:42:24 2011 +0000 @@ -8,7 +8,7 @@ PIDGIN_TREE_TOP := ../.. include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak -SUBDIRS = gg irc jabber msn mxit novell null oscar qq sametime silc simple yahoo bonjour myspace +SUBDIRS = gg irc jabber msn mxit novell null oscar sametime silc simple yahoo bonjour myspace .PHONY: all install clean diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/bonjour/bonjour_ft.c --- a/libpurple/protocols/bonjour/bonjour_ft.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Mon May 02 03:42:24 2011 +0000 @@ -883,8 +883,9 @@ purple_proxy_info_set_type(xf->proxy_info, PURPLE_PROXY_SOCKS5); purple_proxy_info_set_host(xf->proxy_info, xf->proxy_host); purple_proxy_info_set_port(xf->proxy_info, xf->proxy_port); - xf->proxy_connection = purple_proxy_connect_socks5( + xf->proxy_connection = purple_proxy_connect_socks5_account( purple_account_get_connection(account), + account, xf->proxy_info, dstaddr, 0, bonjour_bytestreams_connect_cb, xfer); diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/gg/gg.c --- a/libpurple/protocols/gg/gg.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/gg/gg.c Mon May 02 03:42:24 2011 +0000 @@ -1044,6 +1044,29 @@ } /** + * Try to update avatar of the buddy. + * + * @param gc PurpleConnection + * @param uin UIN of the buddy. + */ +static void ggp_update_buddy_avatar(PurpleConnection *gc, uin_t uin) +{ + gchar *avatarurl; + PurpleUtilFetchUrlData *url_data; + + purple_debug_info("gg", "ggp_update_buddy_avatar(gc, %u)\n", uin); + + avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%u/0.xml", uin); + + url_data = purple_util_fetch_url_request_len_with_account( + purple_connection_get_account(gc), avatarurl, TRUE, + "Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1, + gg_get_avatar_url_cb, gc); + + g_free(avatarurl); +} + +/** * Handle change of the status of the buddy. * * @param gc PurpleConnection @@ -1056,18 +1079,10 @@ { gchar *from; const char *st; - gchar *avatarurl; - PurpleUtilFetchUrlData *url_data; + + ggp_update_buddy_avatar(gc, uin); from = g_strdup_printf("%u", uin); - avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%s/0.xml", from); - - url_data = purple_util_fetch_url_request_len_with_account( - purple_connection_get_account(gc), avatarurl, TRUE, - "Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1, - gg_get_avatar_url_cb, gc); - - g_free(avatarurl); switch (status) { case GG_STATUS_NOT_AVAIL: @@ -1383,8 +1398,8 @@ info->pending_richtext_messages = g_list_remove(info->pending_richtext_messages, entry->data); /* We don't have any more images to download */ if (strstr(text, "event.msg.sender); - serv_got_im(gc, buf, text, PURPLE_MESSAGE_IMAGES, ev->event.msg.time); + gchar *buf = g_strdup_printf("%lu", (unsigned long int)ev->event.image_reply.sender); + serv_got_im(gc, buf, text, PURPLE_MESSAGE_IMAGES, time(NULL)); g_free(buf); purple_debug_info("gg", "ggp_recv_image_handler: richtext message: %s\n", text); g_free(text); @@ -1614,6 +1629,75 @@ g_free(from); } +/** + * Handling of XML events. + * + * @param gc PurpleConnection. + * @param data Raw XML contents. + * + * @see http://toxygen.net/libgadu/protocol/#ch1.13 + */ +static void ggp_xml_event_handler(PurpleConnection *gc, char *data) +{ + xmlnode *xml = NULL; + xmlnode *xmlnode_next_event; + + xml = xmlnode_from_str(data, -1); + if (xml == NULL) + goto out; + + xmlnode_next_event = xmlnode_get_child(xml, "event"); + while (xmlnode_next_event != NULL) + { + xmlnode *xmlnode_current_event = xmlnode_next_event; + + xmlnode *xmlnode_type; + char *event_type_raw; + int event_type = 0; + + xmlnode *xmlnode_sender; + char *event_sender_raw; + uin_t event_sender = 0; + + xmlnode_next_event = xmlnode_get_next_twin(xmlnode_next_event); + + xmlnode_type = xmlnode_get_child(xmlnode_current_event, "type"); + if (xmlnode_type == NULL) + continue; + event_type_raw = xmlnode_get_data(xmlnode_type); + if (event_type_raw != NULL) + event_type = atoi(event_type_raw); + g_free(event_type_raw); + + xmlnode_sender = xmlnode_get_child(xmlnode_current_event, "sender"); + if (xmlnode_sender != NULL) + { + event_sender_raw = xmlnode_get_data(xmlnode_sender); + if (event_sender_raw != NULL) + event_sender = ggp_str_to_uin(event_sender_raw); + g_free(event_sender_raw); + } + + switch (event_type) + { + case 28: /* avatar update */ + purple_debug_info("gg", + "ggp_xml_event_handler: avatar updated (uid: %u)\n", + event_sender); + ggp_update_buddy_avatar(gc, event_sender); + break; + default: + purple_debug_error("gg", + "ggp_xml_event_handler: unsupported event type=%d from=%u\n", + event_type, event_sender); + } + } + + out: + if (xml) + xmlnode_free(xml); +} + static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond) { PurpleConnection *gc = _gc; @@ -1629,7 +1713,7 @@ _("Unable to read from socket")); return; } - gc->last_received = time(NULL); + switch (ev->type) { case GG_EVENT_NONE: /* Nothing happened. */ @@ -1731,6 +1815,10 @@ ggp_typing_notification_handler(gc, ev->event.typing_notification.uin, ev->event.typing_notification.length); break; + case GG_EVENT_XML_EVENT: + purple_debug_info("gg", "GG_EVENT_XML_EVENT\n"); + ggp_xml_event_handler(gc, ev->event.xml_event.data); + break; default: purple_debug_error("gg", "unsupported event type=%d\n", ev->type); @@ -1757,6 +1845,9 @@ case GG_STATE_RESOLVING: purple_debug_info("gg", "GG_STATE_RESOLVING\n"); break; + case GG_STATE_RESOLVING_GG: + purple_debug_info("gg", "GG_STATE_RESOLVING_GG\n"); + break; case GG_STATE_CONNECTING_HUB: purple_debug_info("gg", "GG_STATE_CONNECTING_HUB\n"); break; @@ -1772,6 +1863,9 @@ case GG_STATE_READING_REPLY: purple_debug_info("gg", "GG_STATE_READING_REPLY\n"); break; + case GG_STATE_TLS_NEGOTIATION: + purple_debug_info("gg", "GG_STATE_TLS_NEGOTIATION\n"); + break; default: purple_debug_error("gg", "unknown state = %d\n", info->session->state); @@ -1792,10 +1886,11 @@ purple_input_remove(gc->inpa); /** XXX I think that this shouldn't be done if ev->type is GG_EVENT_CONN_FAILED or GG_EVENT_CONN_SUCCESS -datallah */ - gc->inpa = purple_input_add(info->session->fd, - (info->session->check == 1) ? PURPLE_INPUT_WRITE - : PURPLE_INPUT_READ, - ggp_async_login_handler, gc); + if (info->session->fd >= 0) + gc->inpa = purple_input_add(info->session->fd, + (info->session->check == 1) ? PURPLE_INPUT_WRITE : + PURPLE_INPUT_READ, + ggp_async_login_handler, gc); switch (ev->type) { case GG_EVENT_NONE: @@ -2008,6 +2103,7 @@ struct gg_login_params *glp; GGPInfo *info; const char *address; + const gchar *encryption_type; if (ggp_setup_proxy(account) == -1) return; @@ -2041,11 +2137,13 @@ glp->async = 1; glp->status = ggp_to_gg_status(status, &glp->status_descr); -#if defined(USE_GNUTLS) || !defined(USE_INTERNAL_LIBGADU) - glp->tls = 1; -#else - glp->tls = 0; -#endif + + encryption_type = purple_account_get_string(account, "encryption", "none"); + purple_debug_info("gg", "Requested encryption type: %s\n", encryption_type); + if (strcmp(encryption_type, "opportunistic_tls") == 0) + glp->tls = 1; + else + glp->tls = 0; purple_debug_info("gg", "TLS enabled: %d\n", glp->tls); if (!info->status_broadcasting) @@ -2692,6 +2790,7 @@ static void init_plugin(PurplePlugin *plugin) { PurpleAccountOption *option; + GList *encryption_options = NULL; option = purple_account_option_string_new(_("Nickname"), "nick", _("Gadu-Gadu User")); @@ -2703,6 +2802,26 @@ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); +#define ADD_VALUE(list, desc, v) { \ + PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \ + kvp->key = g_strdup((desc)); \ + kvp->value = g_strdup((v)); \ + list = g_list_append(list, kvp); \ +} + + ADD_VALUE(encryption_options, _("Don't use encryption"), "none"); + ADD_VALUE(encryption_options, _("Use encryption if available"), + "opportunistic_tls"); +#if 0 + /* TODO */ + ADD_VALUE(encryption_options, _("Require encryption"), "require_tls"); +#endif + + option = purple_account_option_list_new(_("Connection security"), + "encryption", encryption_options); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + option); + my_protocol = plugin; gg_debug_handler = purple_gg_debug_handler; diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/gg/lib/message.c --- a/libpurple/protocols/gg/lib/message.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/gg/lib/message.c Mon May 02 03:42:24 2011 +0000 @@ -361,7 +361,7 @@ * \param src Dodawany tekst * \param len Długość dodawanego tekstu */ -static void gg_append(char *dst, int *pos, const void *src, int len) +static void gg_append(char *dst, size_t *pos, const void *src, int len) { if (dst != NULL) memcpy(&dst[*pos], src, len); @@ -394,7 +394,8 @@ int format_idx = 0; unsigned char old_attr = 0; const unsigned char *color = (const unsigned char*) "\x00\x00\x00"; - int len, i; + int i; + size_t len; const unsigned char *format_ = (const unsigned char*) format; len = 0; @@ -584,6 +585,11 @@ entity = NULL; for (src = html; *src != 0; src++) { + if (in_entity && !(isalnum(*src) || *src == '#' || *src == ';')) { + in_entity = 0; + gg_append(dst, &len, entity, src - entity); + } + if (*src == '<') { tag = src; in_tag = 1; diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/jabber/disco.c --- a/libpurple/protocols/jabber/disco.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/jabber/disco.c Mon May 02 03:42:24 2011 +0000 @@ -480,10 +480,12 @@ js->srv_query_data = NULL; if (results > 0) { + PurpleAccount *account; purple_debug_info("jabber", "looking up IP for %s:%d\n", resp[0].hostname, resp[0].port); + account = purple_connection_get_account(js->gc); js->stun_query = - purple_dnsquery_a(resp[0].hostname, resp[0].port, + purple_dnsquery_a_account(account, resp[0].hostname, resp[0].port, jabber_disco_stun_lookup_cb, js); } } @@ -548,7 +550,9 @@ } else if (purple_network_get_stun_ip() == NULL || purple_strequal(purple_network_get_stun_ip(), "")) { js->srv_query_data = - purple_srv_resolve("stun", "udp", js->user->domain, + purple_srv_resolve_account( + purple_connection_get_account(js->gc), "stun", "udp", + js->user->domain, jabber_disco_stun_srv_resolve_cb, js); /* TODO: add TURN support later... */ } diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/jabber/google/jingleinfo.c --- a/libpurple/protocols/jabber/google/jingleinfo.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/jabber/google/jingleinfo.c Mon May 02 03:42:24 2011 +0000 @@ -109,13 +109,15 @@ const gchar *udp = xmlnode_get_attrib(server, "udp"); if (host && udp) { + PurpleAccount *account; int port = atoi(udp); /* if there, would already be an ongoing query, cancel it */ if (js->stun_query) purple_dnsquery_destroy(js->stun_query); - js->stun_query = purple_dnsquery_a(host, port, + account = purple_connection_get_account(js->gc); + js->stun_query = purple_dnsquery_a_account(account, host, port, jabber_google_stun_lookup_cb, js); } } diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/jabber/jabber.c Mon May 02 03:42:24 2011 +0000 @@ -813,7 +813,8 @@ try_srv_connect(js); } else { purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); - js->srv_query_data = purple_txt_resolve("_xmppconnect", + js->srv_query_data = purple_txt_resolve_account( + purple_connection_get_account(gc), "_xmppconnect", js->user->domain, txt_resolved_cb, js); } return; @@ -1072,7 +1073,7 @@ jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE); } else { - js->srv_query_data = purple_srv_resolve("xmpp-client", + js->srv_query_data = purple_srv_resolve_account(account, "xmpp-client", "tcp", js->user->domain, srv_resolved_cb, js); } } diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/jabber/si.c --- a/libpurple/protocols/jabber/si.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/jabber/si.c Mon May 02 03:42:24 2011 +0000 @@ -277,6 +277,7 @@ if(dstjid != NULL && streamhost->host && streamhost->port > 0) { char *dstaddr, *hash; + PurpleAccount *account; jsx->gpi = purple_proxy_info_new(); purple_proxy_info_set_type(jsx->gpi, PURPLE_PROXY_SOCKS5); purple_proxy_info_set_host(jsx->gpi, streamhost->host); @@ -293,8 +294,9 @@ /* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */ hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1"); - jsx->connect_data = purple_proxy_connect_socks5(NULL, jsx->gpi, - hash, 0, + account = purple_connection_get_account(jsx->js->gc); + jsx->connect_data = purple_proxy_connect_socks5_account(NULL, account, + jsx->gpi, hash, 0, jabber_si_bytestreams_connect_cb, xfer); g_free(hash); g_free(dstaddr); diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/AUTHORS --- a/libpurple/protocols/qq/AUTHORS Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -Code Contributors -========= -puzzlebird : original author -gfhuang(poppyer): patches for libpurple 2.0.0beta2, maintainer -Yuan Qingyun : patches for libpurple 1.5.0, maintainer -henryouly : file transfer, udp sock5 proxy and qq_show, maintainer -hzhr : maintainer -joymarquis : maintainer -arfankai : fixed bugs in char_conv.c -rakescar : provided filter for HTML tag -yyw : improved performance on PPC linux -lvxiang : provided ip to location original code -markhuetsch : OpenQ merge into libpurple, maintainer 2006-2007 -ccpaging : maintainer since 2007 -icesky : maintainer since 2007 -csyfek : faces, maintainer since 2007 - -Lovely Patch Writers -========= -gnap : message displaying, documentation -manphiz : qun processing -moo : qun processing -Coly Li : qun processing -Emil Alexiev : captcha verification on login based on LumaQQ for MAC (2007), - login, add buddy, remove buddy, message exchange and logout -Chengming Wang : buddy memo -lonicerae : chat room window bugfix, server list bugfix, buddy memo - -Acknowledgement -========= -Shufeng Tan : http://sf.net/projects/perl-oicq -Jeff Ye : http://www.sinomac.com -Hu Zheng : http://forlinux.yeah.net -yunfan : http://www.myswear.net -OpenQ Team : http://openq.linuxsir.org -LumaQQ Team : http://lumaqq.linuxsir.org -Pidgin Team : http://www.pidgin.im -Huang Guan : http://home.xxsyzx.com -OpenQ Google Group : http://groups.google.com/group/openq - -Scrupulous Testers -========= -yegle -cnzhangbx -casparant -wd -x6719620 -netelk -and more, please let me know... thank you! diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/ChangeLog --- a/libpurple/protocols/qq/ChangeLog Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,382 +0,0 @@ -2010.01.23 - flos - * added an option to force incoming message in chat room to use a default font instead of the font in message itself - -2010.01.18 - flos - * added type 'UPDCLS' and 'UID' for implementing business logic layer - -2010.01.13 - ccpaging - * qq2009-1 patch from ccpaging - -2009.04.23 - flos - * Fixed a bug of updating buddy who is not in user's buddy list - -2009.02.25 - flos - * Changed text 'ZipCode' to 'Postal Code' - -2009.02.24 - flos - * NEVER forget our COPYRIGHT, ;) - -2009.02.24 - flos - * Changed update_class type from gint to guint32 - * Fixed a bug of displaying of memo when memo is nothing - -2009.02.21 - flos - * Added 'qq_strlen' and 'qq_strcmp' funtions in qq.h - * Fixed compiling problems with lower version of GTK+ - -2009.02.21 - flos - * Rewrite whole buddy memo part - * Remove 'qq_to_utf8_len' and 'utf8_to_qq_len' functions in char_conv.c - * Update ChangeLog, AUTHORS - -2009.02.09 - Chengming Wang - * Rewrite buddy_memo using qq_put/qq_get series functions - -2009.02.09 - Chengming Wang - * Added buddy 'get memo', 'remove memo', 'upload memo' functions - -2009.02.08 - flos - * Fixed showing message of chat room when message comes in - -2008.12.28 - flos - * Fixes #7908 - -2008.12.28 - flos - * References #7908 - -2008.12.25 - flos - * References #7821 - * Updated authors - -2008.12.17 - flos - * Minor fixed in buddy_info.c - -2008.12.06 - flos - * Removed version checking script in Makefiles since our developers all migrated to monotone - * Use our development revision as OPENQ_VERSION in qq.c - -2008.12.05 - flos - * Fixed a bug after propagating - -2008.11.18 - ccpaging - * Fixed: IM format suuport in IM and QUN IM - * Divide long IM message into segment and sending - * Divide long QUN IM message in to segment and sending - * Add some new function in im.c to put format when sending - * Add some new function in im.c to get format when receiving - * Need improvement: - Merge long IM message when receiving. Need a buffer to store segments of long IM message. - Send segment of long IM message one by one. Need a buffer to store segments of long IM message. - -2008.11.11 - ccpaging - * Change QQ number to unsigned long - * Change Qun ID and Qun extend ID to unsigned long - * Rewrite smiley convert function, use qsort and bsearch - * Update smiley map according EVA and pidgin theme file - * Support long IM message in private and Qun - -2008.10.27 - ccpaging - * Fixed a bug in group_join.c - -2008.10.30 - flos - * Fixed a bug which made xgettext failed in buddy_info.c - * Fixed a bug in Makefile.am and Makefile.mingw - * Updated acknowledgement in qq.c - -2008.10.28 - flos - * Updated AUTHORS - -2008.10.27 - ccpaging - * Fixed a bug in buddy_info.c - -2008.10.27 - ccpaging - * Update 'buddy_adding' protocol - -2008.10.22 - ccpaging - * 20081022 - -2008.10.20 - ccpaging - * Support incoming authorization of 'buddy_adding' protocol of QQ2007/2008 - -2008.10.14 - ccpaging - * 2007 remove buddy ok - * Removed group_search.c/h - -2008.10.10 - ccpaging - * Support part of 'buddy' protocol of QQ2007/2008 - -2008.10.10 - ccpaging - * Keep group_search.c/h for later use - * Update 'group' - -2008.10.09 - ccpaging - * 20081009-1 - -2008.10.09 - ccpaging - * Update 'group' protocol - * Functions of group_find, group_free, group_search merged into group_join and group_internal - * Removed group_find.c/h, group_free.c/h, group_search.c/h - -2008.10.08 - ccpaging - * Update 'group' protocol - -2008.10.08 - ccpaging - * 20081008-1 - -2008.10.08 - ccpaging - * Update group part - * Delete some meaningless functions and data - * Added 'change my icon' - -2008.10.08 - lonicerae - * Update Makefile.mingw - -2008.10.08 - ccpaging - * Fixed QQ_BUDDY_ICON_DIR problem - -2008.10.07 - lonicerae - * Update 'version display' - -2008.10.07 - lonicerae - * Added some defensive code for 'action' series functions of qq.c - -2008.10.07 - ccpaging - * Update buddy icon - -2008.10.07 - ccpaging - * Update qq_buddy - -2008.10.07 - ccpaging - * Update qun conversation - -2008.10.05 - lonicerae - * Bug fix in 'About OpenQ' dialog - -2008.10.05 - lonicerae - * Added 'About OpenQ' dialog - -2008.10.05 - ccpaging - * Add my uid into buddy list - * Fixed a minor bug in qq_create_buddy. Not get new buddy's info. - * There are 38 fields in protocol 2008, one more than 2005/2007. - * The packet of Modifing buddy info is changed. Need sample to fix it. - -2008.10.04 - ccpaging - * Update protocol for 2007 - * Code cleanup - -2008.10.04 - lonicerae - * fixed a bug in qq_base.c - -2008.10.03 - ccpaging - * 2007 protocol: - 1. fixed 'get room info' - 2. fixed 'get buddy level' - -2008.10.02 - ccpaging - * Added 'Captcha Display' function - * QQ2007 for openq, programed by Emil Alexiev: - 1. Most functions from patch written by Emil Alexiev merged into trunk, except 'buddy operations' - 2. 'online buddy status' and 'qun buddies' still have problems - * QQ2008 console client, programed by Shrimp: - 1. 'before login' and 'keep alive' parts merged into trunk - -2008.09.30 - ccpaging - * Successfully login using 2007/2008 protocols - -2008.09.29 - ccpaging - * 'Check Password' function for protocol 2007/2008 - -2008.09.28 - ccpaging - * The source is only for debug, not for user: - 1. Implement new QQ protocol 2007/2008, include login and change status - 2. Check 2005's login reply packet, get last 3 login time. - 3. Server's notice and news is displayed in self buddy (The new buddy created in buddy list). - 4. The notice messages when adding/removing QQ Qun's buddy displayed in char conversation. They are displayed as purple notify windows in the past. - 5. The notice messages when adding/removing buddy displayed in self buddy's conversation. They are displayed as purple notify windows in the past. - 6. Client version can be selected in account option. Now only qq2005 is working, other new version is only for debug. - -2008.09.26 - ccpaging - * Added 'Request/Add/Remove Buddy' functions - -2008.09.19 - ccpaging - * Rewrite buddy modify info, there is a ticket for this problem - * Use ship32 to trans action code between request packet and reply packet process - -2008.09.15 - csyfek - * im.pidgin.pidgin.openq branch - -2008.09.05 - ccpaging - * Filter chars 0x01-0x20 in nickname - -2008.09.05 - ccpaging - * Fixed compilation even pidgin-udp-patch not applied - * Place and analysis 'before login packet' after login. packages will be updated slowly and server may send lots of 'server command packet', while 'before login packet' is placed after 'finished update' - -2008.09.02 - ccpaging - * Bugfix: can not send message to the QUN blocked adding - * Tickets: - Fixes #6957 - -2008.09.02 - ccpaging - * Use new tactics of information update: - 1. send next package till the previous package received - 2. fix duplicated get_room_info and get_room_buddies commands - -2008.08.16 - ccpaging - * Rename group to room. If you used pidginqq before, this may create a new room with same title, you may delete old one - * Replace purple_debug with purple_debug_info, purple_debug_warning, purple_debug_error - * Add server notice and server new, and two options to turn on/off - * Minor modify for reducing transaction's debug infor - * Minor modifies for system notice and QQ news. - * Add 4 new strings need translate compare with p10. - * Tickets: - Fixes #6990 - -2008.08.10 - csyfek - * Commit to Pidgin - -2008.08.07 - ccpaging - * Support managing multi-connections according to simple.c - -2008.08.06 - ccpaging - * Rename names of variables, Group, to Room - * Functions of group_network merged into qq_network and qq_process - * Cancelled managing glist of group packet, add sub_cmdd and room_id to transaction - * Fixed error of demo group: - If 'room list' and 'room infor' are not setup, response received from server will emits 'room_id = 0' packet. - -2008.08.04 - ccpaging - * Use new crypt/decrypt functions - * Rename crypt.c/h to qq_crypt.c/h - * Clean code of decrypt functions - * Fixed decryption failure - -2008.08.04 - csyfek - * Update AUTHORS - -2008.08.03 - csyfek - * Commit lost files to Pidgin - -2008.08.02 - csyfek - * Commit to Pidgin - * Tickets: - Fixes #1861 - Fixes #1902 - References #5112 - -2008.08.02 - ccpaging - * Store all keys and md5 values of qq_data in char[QQ_KEY_LENGTH] - * Use random value in inikey - * TEA header padding in crypt.c - * Rewrite login part of qq_process - -2008.07.31 - ccpaging - * Fixed: send reply when get duplicate server command. The server may not get our reply before. - * Tag custom picture as text "(Broken)" - -2008.07.30 - ccpaging , csyfek - * Change some debug message - * Modify buddy status flag according to eva for QQ2006 - * Modify buddy status parse and correspond to eva2 - * Add getIP/putIP functions to packet_parse.c, and replace some gen_ip_str - * Replace guint32 *ip with struct in_addr, and reduce g_new/g_free operation - * Source file changed: - Merge buddy_status into buddy_list - Change login_logout to qq_base - Merge keep_alive into qq_base - New qq_process extract from qq_network - * Fixed: Byte alignment bug in crypt.c, tested in ARM PDA - * Fixed: group chat message may get in before getting group info, and so group info is empty - * Add qq_send_cmd_group_get_group_info when joined a group chat in group_im.c - * Add some new group command identify according eva but further program - * Add some new QQ client version identify - * Fixed: Identify buddy's client version by IM packet, and not by status - * Add some new info in buddy's tooltip text - * Add video falg to buddy's emblem. But those flag in buddy status may not prasing correctly - * Use new timeout function to handle send keep_alive, resend packet, update buddy status - * Add new advanced options: - The end user may change interval of keep_alive, resend packet, update buddy status to feed their need. - For example, saving network flow when use mobile phone. - Keep alive packet must be sent in 60-120 seconds whatever client rcved data of not. - The intervals of keep alive and update status should be multiple of resend's interval, - Since we use counter not time() in a single timeout function for efficiency. - * Rewrite qq_trans.c, and use one g_list to manage: - Store server packet before login, and prase all of them when get login - Store client send packet for resend scanning, confirm server reply, filter duplicate server reply - Store server packet for filter out duplicate - * Add QQ_MSG_SYS_NOTICE = 0x06 in sys_msg.c - * Rewrite qq_proc_cmd_reply and qq_proc_cmd_server: - In QQ protocol, one packet reply may need a new packet send later. - We may call it packet trigger. The triggers always is hided in every qq_process_reply. - Now we try to extract those triggers and put into a single function, - and then every trigger should be obviously and easy to manage. - -2008.07.12 - ccpaging - * Fixed: Always lost connection. Now send keep alive packet in every 30 seconds - * Minor fix for debug information - * Filter \r\n and replace with SPCAE in group notive - * Fixed a memory leak - * Tickets: - * Fixes #4024. - -2008.06.29 - csyfek - * Minor bug fix - * Add some doxygen syntax for preparing development documentation - * References #6199 - -2008.06.28 - ccpaging , moo - * Patches from moo and ccpaging. - * Tickets: - * Fixes #4956. - * Fixes #2998. - -2008.06.07 - ccpaging , csyfek - * Clean code and apply patches from QuLogic - -2008.05.19 - ccpaging , csyfek - * Reconnect server 5 time in 5000 ms, when connect failed - * Rename sendqueue.c/sendqueue.h to qq_trans.c/qq_trans.h - * Rewrite packet_process - * Rewrite qq_send_cmd - * Create server list, try to connect every server when failed - -2008.05.14 - ccpaging - * 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 - * 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 - * Clean code of packet_parse.c, enable PARSER_DEBUG - * Rewrite send_queue - -2008.05.08 - ccpaging - * Rewrite qq_network - * Add srv resolve function when qq_login - * Merge function _qq_common_clean in qq_proxy.c to qq_disconnect - * 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 - * 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 - * Add qq_hex_dump function - -2008.04.25 - ccpaging , csyfek - * Rewrite read_packet and create_packet functions, use qq_put and qq_get functions instead - * New logic in accord with protocol models to handle packets, some related functions rewritten - -2008.03.24 - ccpaging - * Remove qq_crypt function in crypt.c, use qq_crypt and qq_decrypt directly - -** since pidgin-2.4.0 *** diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/Makefile.am --- a/libpurple/protocols/qq/Makefile.am Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -EXTRA_DIST = \ - Makefile.mingw - -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) - -QQSOURCES = \ - buddy_info.c \ - buddy_info.h \ - buddy_memo.c \ - buddy_memo.h \ - buddy_list.c \ - buddy_list.h \ - buddy_opt.c \ - buddy_opt.h \ - char_conv.c \ - char_conv.h \ - qq_crypt.c \ - qq_crypt.h \ - file_trans.c \ - file_trans.h \ - group.c \ - group.h \ - group_internal.c \ - group_internal.h \ - group_im.c \ - group_im.h \ - group_info.c \ - group_info.h \ - group_join.c \ - group_join.h \ - group_opt.c \ - group_opt.h \ - qq_define.c \ - qq_define.h \ - im.c \ - im.h \ - qq_process.c \ - qq_process.h \ - qq_base.c \ - qq_base.h \ - packet_parse.c \ - packet_parse.h \ - qq.c \ - qq.h \ - qq_network.c \ - qq_network.h \ - send_file.c \ - send_file.h \ - qq_trans.c \ - qq_trans.h \ - utils.c \ - utils.h - -AM_CFLAGS = $(st) - -libqq_la_LDFLAGS = -module -avoid-version - -if STATIC_QQ - -st = -DPURPLE_STATIC_PRPL -noinst_LTLIBRARIES = libqq.la -libqq_la_SOURCES = $(QQSOURCES) -libqq_la_CFLAGS = $(AM_CFLAGS) - -else - -st = -noinst_LTLIBRARIES = -pkg_LTLIBRARIES = libqq.la -libqq_la_SOURCES = $(QQSOURCES) -libqq_la_LIBADD = $(GLIB_LIBS) - -endif - -# For 'make check' -noinst_LTLIBRARIES += libqq_tmp.la -libqq_tmp_la_SOURCES = $(libqq_la_SOURCES) -libqq_tmp_la_LIBADD = $(libqq_la_LIBADD) - - -# QQ_BUDDY_ICON_DIR is the path where a bunch of stock icons can be -# places for users to choose from when setting their buddy icon. We -# don't distribute those icons ourselves because of possibly copyright -# concerns, but distributions might want to put icons here. -AM_CPPFLAGS = \ - -I$(top_srcdir)/libpurple \ - -I$(top_builddir)/libpurple \ - -DQQ_BUDDY_ICON_DIR=\"$(datadir)/pixmaps/purple/buddy_icons/qq\" \ - $(DEBUG_CFLAGS) \ - $(GLIB_CFLAGS) diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/Makefile.mingw --- a/libpurple/protocols/qq/Makefile.mingw Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,101 +0,0 @@ -# -# Makefile.mingw -# -# Description: Makefile for win32 (mingw) version of the QQ prpl -# - -PIDGIN_TREE_TOP := ../../.. -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak - -TARGET = libqq -TYPE = PLUGIN - -# Static or Plugin... -ifeq ($(TYPE),STATIC) - DEFINES += -DSTATIC - DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR) -else -ifeq ($(TYPE),PLUGIN) - DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR) -endif -endif - -## -## INCLUDE PATHS -## -INCLUDE_PATHS += \ - -I$(GTK_TOP)/include \ - -I$(GTK_TOP)/include/glib-2.0 \ - -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(PURPLE_TOP) \ - -I$(PURPLE_TOP)/win32 \ - -I$(PIDGIN_TREE_TOP) - -LIB_PATHS += \ - -L$(GTK_TOP)/lib \ - -L$(PURPLE_TOP) - -## -## SOURCES, OBJECTS -## -C_SRC = \ - buddy_info.c \ - buddy_list.c \ - buddy_memo.c \ - buddy_opt.c \ - char_conv.c \ - qq_crypt.c \ - file_trans.c \ - group.c \ - group_internal.c \ - group_im.c \ - group_info.c \ - group_join.c \ - group_opt.c \ - qq_define.c \ - im.c \ - packet_parse.c \ - qq.c \ - qq_base.c \ - qq_network.c \ - qq_process.c \ - qq_trans.c \ - send_file.c \ - utils.c - -OBJECTS = $(C_SRC:%.c=%.o) - -## -## LIBRARIES -## -LIBS = \ - -lglib-2.0 \ - -lws2_32 \ - -lintl \ - -lpurple - -include $(PIDGIN_COMMON_RULES) - -## -## TARGET DEFINITIONS -## -.PHONY: all install clean - -all: $(TARGET).dll - -install: all $(DLL_INSTALL_DIR) - cp $(TARGET).dll $(DLL_INSTALL_DIR) - -$(OBJECTS): $(PURPLE_CONFIG_H) - -$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll - -## -## CLEAN RULES -## -clean: - rm -f $(OBJECTS) - rm -f $(TARGET).dll - -include $(PIDGIN_COMMON_TARGETS) diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/buddy_info.c --- a/libpurple/protocols/qq/buddy_info.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,898 +0,0 @@ -/** - * @file buddy_info.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 "debug.h" -#include "notify.h" -#include "request.h" -#include "connection.h" - -#include "utils.h" -#include "packet_parse.h" -#include "buddy_list.h" -#include "buddy_info.h" -#include "char_conv.h" -#include "im.h" -#include "qq_define.h" -#include "qq_base.h" -#include "qq_network.h" - -#define QQ_HOROSCOPE_SIZE 13 -static const gchar *horoscope_names[] = { - "-", N_("Aquarius"), N_("Pisces"), N_("Aries"), N_("Taurus"), - N_("Gemini"), N_("Cancer"), N_("Leo"), N_("Virgo"), N_("Libra"), - N_("Scorpio"), N_("Sagittarius"), N_("Capricorn") -}; - -#define QQ_ZODIAC_SIZE 13 -static const gchar *zodiac_names[] = { - "-", N_("Rat"), N_("Ox"), N_("Tiger"), N_("Rabbit"), - N_("Dragon"), N_("Snake"), N_("Horse"), N_("Goat"), N_("Monkey"), - N_("Rooster"), N_("Dog"), N_("Pig") -}; - -#define QQ_BLOOD_SIZE 6 -static const gchar *blood_types[] = { - "-", "A", "B", "O", "AB", N_("Other") -}; - -#define QQ_PUBLISH_SIZE 3 -static const gchar *publish_types[] = { - N_("Visible"), N_("Friend Only"), N_("Private") -}; - -#define QQ_GENDER_SIZE 3 -static const gchar *genders[] = { - N_("Private"), - N_("Male"), - N_("Female"), -}; - -static const gchar *genders_zh[] = { - "-", - "\xc4\xd0", - "\xc5\xae", -}; - -#define QQ_FACES 134 -#define QQ_ICON_PREFIX "qq_" -#define QQ_ICON_SUFFIX ".png" - -enum { - QQ_INFO_UID = 0, QQ_INFO_NICK, QQ_INFO_COUNTRY, QQ_INFO_PROVINCE, QQ_INFO_ZIPCODE, - QQ_INFO_ADDR, QQ_INFO_TEL, QQ_INFO_AGE, QQ_INFO_GENDER, QQ_INFO_NAME, QQ_INFO_EMAIL, - QQ_INFO_PG_SN, QQ_INFO_PG_NUM, QQ_INFO_PG_SP, QQ_INFO_PG_BASE_NUM, QQ_INFO_PG_TYPE, - QQ_INFO_OCCU, QQ_INFO_HOME_PAGE, QQ_INFO_AUTH_TYPE, QQ_INFO_UNKNOW1, QQ_INFO_UNKNOW2, - QQ_INFO_FACE, QQ_INFO_MOBILE, QQ_INFO_MOBILE_TYPE, QQ_INFO_INTRO, QQ_INFO_CITY, - QQ_INFO_UNKNOW3, QQ_INFO_UNKNOW4, QQ_INFO_UNKNOW5, - QQ_INFO_IS_PUB_MOBILE, QQ_INFO_IS_PUB_CONTACT, QQ_INFO_COLLEGE, QQ_INFO_HOROSCOPE, - QQ_INFO_ZODIAC, QQ_INFO_BLOOD, QQ_INFO_SHOW, QQ_INFO_UNKNOW6, - QQ_INFO_LAST_2007, QQ_INFO_LAST -}; - -enum { - QQ_FIELD_UNUSED = 0, QQ_FIELD_BASE, QQ_FIELD_EXT, QQ_FIELD_CONTACT, QQ_FIELD_ADDR -}; - -enum { - QQ_FIELD_LABEL = 0, QQ_FIELD_STRING, QQ_FIELD_MULTI, QQ_FIELD_BOOL, QQ_FIELD_CHOICE -}; - -typedef struct { - int iclass; - int type; - char *id; - char *text; - const gchar **choice; - int choice_size; -} QQ_FIELD_INFO; - -static const QQ_FIELD_INFO field_infos[] = { - { QQ_FIELD_BASE, QQ_FIELD_STRING, "uid", N_("QQ Number"), NULL, 0 }, - { QQ_FIELD_BASE, QQ_FIELD_STRING, "nick", N_("Nickname"), NULL, 0 }, - { QQ_FIELD_ADDR, QQ_FIELD_STRING, "country", N_("Country/Region"), NULL, 0 }, - { QQ_FIELD_ADDR, QQ_FIELD_STRING, "province", N_("Province/State"), NULL, 0 }, - { QQ_FIELD_ADDR, QQ_FIELD_STRING, "zipcode", N_("Zipcode"), NULL, 0 }, - { QQ_FIELD_ADDR, QQ_FIELD_STRING, "address", N_("Address"), NULL, 0 }, - { QQ_FIELD_CONTACT, QQ_FIELD_STRING, "tel", N_("Phone Number"), NULL, 0 }, - { QQ_FIELD_BASE, QQ_FIELD_STRING, "age", N_("Age"), NULL, 0 }, - { QQ_FIELD_BASE, QQ_FIELD_CHOICE, "gender", N_("Gender"), genders, QQ_GENDER_SIZE }, - { QQ_FIELD_BASE, QQ_FIELD_STRING, "name", N_("Name"), NULL, 0 }, - { QQ_FIELD_CONTACT, QQ_FIELD_STRING, "email", N_("Email"), NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "pg_sn", "Pager Serial Num", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "pg_num", "Pager Num", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "pg_sp", "Pager Serivce Provider", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "pg_sta", "Pager Station Num", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "pg_type", "Pager Type", NULL, 0 }, - { QQ_FIELD_BASE, QQ_FIELD_STRING, "occupation", N_("Occupation"), NULL, 0 }, - { QQ_FIELD_CONTACT, QQ_FIELD_STRING, "homepage", N_("Homepage"), NULL, 0 }, - { QQ_FIELD_BASE, QQ_FIELD_BOOL, "auth", N_("Authorize adding"), NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow1", "Unknow1", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow2", "Unknow2", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "face", "Face", NULL, 0 }, - { QQ_FIELD_CONTACT, QQ_FIELD_STRING, "mobile", N_("Cellphone Number"), NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "mobile_type", "Cellphone Type", NULL, 0 }, - { QQ_FIELD_BASE, QQ_FIELD_MULTI, "intro", N_("Personal Introduction"), NULL, 0 }, - { QQ_FIELD_ADDR, QQ_FIELD_STRING, "city", N_("City/Area"), NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow3", "Unknow3", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow4", "Unknow4", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow5", "Unknow5", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_CHOICE, "pub_mobile", N_("Publish Mobile"), publish_types, QQ_PUBLISH_SIZE }, - { QQ_FIELD_CONTACT, QQ_FIELD_CHOICE, "pub_contact", N_("Publish Contact"), publish_types, QQ_PUBLISH_SIZE }, - { QQ_FIELD_EXT, QQ_FIELD_STRING, "college", N_("College"), NULL, 0 }, - { QQ_FIELD_EXT, QQ_FIELD_CHOICE, "horoscope", N_("Horoscope"), horoscope_names, QQ_HOROSCOPE_SIZE }, - { QQ_FIELD_EXT, QQ_FIELD_CHOICE, "zodiac", N_("Zodiac"), zodiac_names, QQ_ZODIAC_SIZE }, - { QQ_FIELD_EXT, QQ_FIELD_CHOICE, "blood", N_("Blood"), blood_types, QQ_BLOOD_SIZE }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "qq_show", "QQ Show", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow6", "Unknow6", NULL, 0 }, - { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "LAST_2005", "LAST_2005", NULL, 0 } -}; - -typedef struct _modify_info_request { - PurpleConnection *gc; - int iclass; - gchar **segments; -} modify_info_request; - -#ifdef DEBUG -static void info_debug(gchar **segments) -{ -#if 0 - int index; - gchar *utf8_str; - for (index = 0; segments[index] != NULL && index < QQ_INFO_LAST; index++) { - if (field_infos[index].type == QQ_FIELD_STRING - || field_infos[index].type == QQ_FIELD_LABEL - || field_infos[index].type == QQ_FIELD_MULTI - || index == QQ_INFO_GENDER) { - utf8_str = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT); - purple_debug_info("QQ_BUDDY_INFO", "%s: %s\n", field_infos[index].text, utf8_str); - g_free(utf8_str); - continue; - } - purple_debug_info("QQ_BUDDY_INFO", "%s: %s\n", field_infos[index].text, segments[index]); - } -#endif -} -#endif - -static void info_display_only(PurpleConnection *gc, gchar **segments) -{ - PurpleNotifyUserInfo *user_info; - gchar *utf8_value; - int index; - int choice_num; - - user_info = purple_notify_user_info_new(); - - for (index = 1; segments[index] != NULL && index < QQ_INFO_LAST; index++) { - if (field_infos[index].iclass == QQ_FIELD_UNUSED) { - continue; - } - switch (field_infos[index].type) { - case QQ_FIELD_BOOL: - purple_notify_user_info_add_pair(user_info, _(field_infos[index].text), - strtol(segments[index], NULL, 10) ? _("True") : _("False")); - break; - case QQ_FIELD_CHOICE: - choice_num = strtol(segments[index], NULL, 10); - if (choice_num < 0 || choice_num >= field_infos[index].choice_size) { - choice_num = 0; - } - - purple_notify_user_info_add_pair(user_info, _(field_infos[index].text), field_infos[index].choice[choice_num]); - break; - case QQ_FIELD_LABEL: - case QQ_FIELD_STRING: - case QQ_FIELD_MULTI: - default: - if (strlen(segments[index]) != 0) { - utf8_value = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT); - purple_notify_user_info_add_pair(user_info, _(field_infos[index].text), utf8_value); - g_free(utf8_value); - } - break; - } - } - - purple_notify_userinfo(gc, segments[0], user_info, NULL, NULL); - - purple_notify_user_info_destroy(user_info); - g_strfreev(segments); -} - -void qq_request_buddy_info(PurpleConnection *gc, UID uid, - UPDCLS update_class, int action) -{ - gchar raw_data[16] = {0}; - - g_return_if_fail(uid != 0); - - g_snprintf(raw_data, sizeof(raw_data), "%u", uid); - qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDY_INFO, (guint8 *) raw_data, strlen(raw_data), - update_class, action); -} - -/* send packet to modify personal information */ -static void request_change_info(PurpleConnection *gc, gchar **segments) -{ - gint bytes = 0; - guint8 raw_data[MAX_PACKET_SIZE - 128] = {0}; - guint8 bar; - gchar *join; - - g_return_if_fail(segments != NULL); - - bar = 0x1f; - - bytes += qq_put8(raw_data + bytes, bar); - bytes += qq_put8(raw_data + bytes, bar); - - /* important! skip the first uid entry */ - join = g_strjoinv("\x1f", segments + 1); - bytes += qq_putdata(raw_data + bytes, (guint8 *)join, strlen(join)); - g_free(join); - - bytes += qq_put8(raw_data + bytes, bar); - - /* qq_show_packet("request_modify_info", raw_data, bytes); */ - qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, raw_data, bytes); -} - -static void info_modify_cancel_cb(modify_info_request *info_request) -{ - g_strfreev(info_request->segments); - g_free(info_request); -} - -/* parse fields and send info packet */ -static void info_modify_ok_cb(modify_info_request *info_request, PurpleRequestFields *fields) -{ - PurpleConnection *gc; - gchar **segments; - int index; - const char *utf8_str; - gchar *value; - int choice_num; - - gc = info_request->gc; - g_return_if_fail(gc != NULL); - segments = info_request->segments; - g_return_if_fail(segments != NULL); - - for (index = 1; segments[index] != NULL && index < QQ_INFO_LAST; index++) { - if (field_infos[index].iclass == QQ_FIELD_UNUSED) { - continue; - } - if (!purple_request_fields_exists(fields, field_infos[index].id)) { - continue; - } - switch (field_infos[index].type) { - case QQ_FIELD_BOOL: - value = purple_request_fields_get_bool(fields, field_infos[index].id) - ? g_strdup("1") : g_strdup("0"); - g_free(segments[index]); - segments[index] = value; - break; - case QQ_FIELD_CHOICE: - choice_num = purple_request_fields_get_choice(fields, field_infos[index].id); - if (choice_num < 0 || choice_num >= field_infos[index].choice_size) choice_num = 0; - - if (index == QQ_INFO_GENDER) { - /* QQ Server only recept gender in Chinese */ - value = g_strdup(genders_zh[choice_num]); - } else { - value = g_strdup_printf("%d", choice_num); - } - g_free(segments[index]); - segments[index] = value; - break; - case QQ_FIELD_LABEL: - case QQ_FIELD_STRING: - case QQ_FIELD_MULTI: - default: - utf8_str = purple_request_fields_get_string(fields, field_infos[index].id); - if (utf8_str == NULL) { - value = g_strdup("-"); - } else { - value = utf8_to_qq(utf8_str, QQ_CHARSET_DEFAULT); - if (value == NULL) value = g_strdup("-"); - } - g_free(segments[index]); - segments[index] = value; - break; - } - } - request_change_info(gc, segments); - - g_strfreev(segments); - g_free(info_request); -} - -static void field_request_new(PurpleRequestFieldGroup *group, gint index, gchar **segments) -{ - PurpleRequestField *field; - gchar *utf8_value; - int choice_num; - int i; - - g_return_if_fail(index >=0 && segments[index] != NULL && index < QQ_INFO_LAST); - - switch (field_infos[index].type) { - case QQ_FIELD_STRING: - case QQ_FIELD_MULTI: - utf8_value = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT); - if (field_infos[index].type == QQ_FIELD_STRING) { - field = purple_request_field_string_new( - field_infos[index].id, _(field_infos[index].text), utf8_value, FALSE); - } else { - field = purple_request_field_string_new( - field_infos[index].id, _(field_infos[index].text), utf8_value, TRUE); - } - purple_request_field_group_add_field(group, field); - g_free(utf8_value); - break; - case QQ_FIELD_BOOL: - field = purple_request_field_bool_new( - field_infos[index].id, _(field_infos[index].text), - strtol(segments[index], NULL, 10) ? TRUE : FALSE); - purple_request_field_group_add_field(group, field); - break; - case QQ_FIELD_CHOICE: - choice_num = strtol(segments[index], NULL, 10); - if (choice_num < 0 || choice_num >= field_infos[index].choice_size) choice_num = 0; - - if (index == QQ_INFO_GENDER && strlen(segments[index]) != 0) { - for (i = 0; i < QQ_GENDER_SIZE; i++) { - if (strcmp(segments[index], genders_zh[i]) == 0) { - choice_num = i; - } - } - } - field = purple_request_field_choice_new( - field_infos[index].id, _(field_infos[index].text), choice_num); - for (i = 0; i < field_infos[index].choice_size; i++) { - purple_request_field_choice_add(field, field_infos[index].choice[i]); - } - purple_request_field_group_add_field(group, field); - break; - case QQ_FIELD_LABEL: - default: - field = purple_request_field_label_new(field_infos[index].id, segments[index]); - purple_request_field_group_add_field(group, field); - break; - } -} - -static void info_modify_dialogue(PurpleConnection *gc, gchar **segments, int iclass) -{ - PurpleRequestFieldGroup *group; - PurpleRequestFields *fields; - modify_info_request *info_request; - gchar *utf8_title, *utf8_prim; - int index; - - /* Keep one dialog once a time */ - purple_request_close_with_handle(gc); - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - for (index = 1; segments[index] != NULL && index < QQ_INFO_LAST; index++) { - if (field_infos[index].iclass != iclass) { - continue; - } - field_request_new(group, index, segments); - } - - switch (iclass) { - case QQ_FIELD_CONTACT: - utf8_title = g_strdup(_("Modify Contact")); - utf8_prim = g_strdup_printf("%s for %s", _("Modify Contact"), segments[0]); - break; - case QQ_FIELD_ADDR: - utf8_title = g_strdup(_("Modify Address")); - utf8_prim = g_strdup_printf("%s for %s", _("Modify Address"), segments[0]); - break; - case QQ_FIELD_EXT: - utf8_title = g_strdup(_("Modify Extended Information")); - utf8_prim = g_strdup_printf("%s for %s", _("Modify Extended Information"), segments[0]); - break; - case QQ_FIELD_BASE: - default: - utf8_title = g_strdup(_("Modify Information")); - utf8_prim = g_strdup_printf("%s for %s", _("Modify Information"), segments[0]); - break; - } - - info_request = g_new0(modify_info_request, 1); - info_request->gc = gc; - info_request->iclass = iclass; - info_request->segments = segments; - - purple_request_fields(gc, - utf8_title, - utf8_prim, - NULL, - fields, - _("Update"), G_CALLBACK(info_modify_ok_cb), - _("Cancel"), G_CALLBACK(info_modify_cancel_cb), - purple_connection_get_account(gc), NULL, NULL, - info_request); - - g_free(utf8_title); - g_free(utf8_prim); -} - -/* process the reply of modify_info packet */ -void qq_process_change_info(PurpleConnection *gc, guint8 *data, gint data_len) -{ - qq_data *qd; - g_return_if_fail(data != NULL && data_len != 0); - - qd = (qq_data *) gc->proto_data; - - data[data_len] = '\0'; - if (qd->uid != atoi((gchar *) data)) { /* return should be my uid */ - purple_debug_info("QQ", "Failed Updating info\n"); - qq_got_message(gc, _("Could not change buddy information.")); - } -} - -static void request_set_buddy_icon(PurpleConnection *gc, gint face_num) -{ - PurpleAccount *account = purple_connection_get_account(gc); - PurplePresence *presence = purple_account_get_presence(account); - qq_data *qd = (qq_data *) gc->proto_data; - gint offset; - - if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) { - offset = 2; - } else if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY) - || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)) { - offset = 1; - } else { - offset = 0; - } - - qd->my_icon = 3 * (face_num - 1) + offset; - qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_SET_ICON); -} - -void qq_change_icon_cb(PurpleConnection *gc, const char *filepath) -{ - gchar *basename; - size_t index; - gint face; - - g_return_if_fail(filepath != NULL); - - purple_debug_info("QQ", "Change my icon to %s\n", filepath); - - basename = g_path_get_basename(filepath); - index = strcspn(basename, "0123456789"); - face = strtol(basename + index, NULL, 10); - g_free(basename); - purple_debug_info("QQ", "Set face to %d\n", face); - - request_set_buddy_icon(gc, face); -} - -void qq_set_custom_icon(PurpleConnection *gc, PurpleStoredImage *img) -{ - PurpleAccount *account = purple_connection_get_account(gc); - const gchar *icon_path = purple_account_get_buddy_icon_path(account); - - g_return_if_fail(icon_path != NULL); - - /* Fixme: - * icon_path is always null - * purple_imgstore_get_filename is always new file - * QQ buddy may set custom icon if level is over 16 */ - purple_debug_info("QQ", "Change my icon to %s\n", icon_path); -} - -gchar *qq_get_icon_name(gint face) -{ - gint icon; - gchar *icon_name; - - icon = face / 3 + 1; - if (icon < 1 || icon > QQ_FACES) { - icon = 1; - } - - icon_name = g_strdup_printf("%s%d%s", QQ_ICON_PREFIX, icon, QQ_ICON_SUFFIX); - return icon_name; -} - -/* - * This function seems to let people set their buddy icon, but it restricts - * them to using a small list of stock icons. Wouldn't it make more sense - * to use libpurple's normal icon setting stuff? - * - * Also it would be nice to unify the icon_dir code for Windows and Linux. - */ -gchar *qq_get_icon_path(gchar *icon_name) -{ - gchar *icon_path; - const gchar *icon_dir; -#ifdef _WIN32 - static char *dir = NULL; - if (dir == NULL) { - dir = g_build_filename(wpurple_install_dir(), "pixmaps", - "purple", "buddy_icons", "qq", NULL); - } -#endif - - /* - * TODO: The QQ protocol plugin should probably call - * purple_prefs_add_string() at startup to initialize this - * preference. It is used to allow users or distributions - * to specify this directory. We don't include these icons - * with libpurple because of possible copyright concerns. - */ - icon_dir = purple_prefs_get_string("/plugins/prpl/qq/icon_dir"); - if ( icon_dir == NULL || strlen(icon_dir) == 0) { -#ifdef _WIN32 - icon_dir = dir; -#else - icon_dir = QQ_BUDDY_ICON_DIR; -#endif - } - icon_path = g_strdup_printf("%s%c%s", icon_dir, G_DIR_SEPARATOR, icon_name); - - return icon_path; -} - -void qq_update_buddy_icon(PurpleAccount *account, const gchar *who, gint face) -{ - PurpleBuddy *buddy; - const gchar *icon_name_prev = NULL; - gchar *icon_name; - gchar *icon_path; - gchar *icon_file_content; - gsize icon_file_size; - - g_return_if_fail(account != NULL && who != NULL); - - /* purple_debug_info("QQ", "Update %s icon to %d\n", who, face); */ - - icon_name = qq_get_icon_name(face); - g_return_if_fail(icon_name != NULL); - /* purple_debug_info("QQ", "icon file name is %s\n", icon_name); */ - - if ((buddy = purple_find_buddy(account, who))) { - icon_name_prev = purple_buddy_icons_get_checksum_for_user(buddy); - /* - purple_debug_info("QQ", "Previous icon is %s\n", - icon_name_prev != NULL ? icon_name_prev : "(NULL)"); - */ - } - if (icon_name_prev != NULL && !strcmp(icon_name, icon_name_prev)) { - /* purple_debug_info("QQ", "Icon is not changed\n"); */ - g_free(icon_name); - return; - } - - icon_path = qq_get_icon_path(icon_name); - if (icon_path == NULL) { - g_free(icon_name); - return; - } - - if (!g_file_get_contents(icon_path, &icon_file_content, &icon_file_size, NULL)) { - purple_debug_error("QQ", "Failed reading icon file %s\n", icon_path); - } else { - purple_debug_info("QQ", "Update %s icon to %d (%s)\n", - who, face, icon_path); - purple_buddy_icons_set_for_user(account, who, - icon_file_content, icon_file_size, icon_name); - } - g_free(icon_name); - g_free(icon_path); -} - -/* after getting info or modify myself, refresh the buddy list accordingly */ -static void update_buddy_info(PurpleConnection *gc, gchar **segments) -{ - PurpleBuddy *buddy = NULL; - qq_data *qd = NULL; - qq_buddy_data *bd = NULL; - UID uid; - gchar *who; - gchar *alias_utf8; - - PurpleAccount *account = purple_connection_get_account(gc); - qd = (qq_data *)purple_connection_get_protocol_data(gc); - - uid = strtoul(segments[QQ_INFO_UID], NULL, 10); - who = uid_to_purple_name(uid); - qq_filter_str(segments[QQ_INFO_NICK]); - alias_utf8 = qq_to_utf8(segments[QQ_INFO_NICK], QQ_CHARSET_DEFAULT); - - if (uid == qd->uid) { /* it is me */ - purple_debug_info("QQ", "Got my info\n"); - qd->my_icon = strtol(segments[QQ_INFO_FACE], NULL, 10); - if (alias_utf8 != NULL) { - purple_account_set_alias(account, alias_utf8); - } - /* add me to buddy list */ - buddy = qq_buddy_find_or_new(gc, uid); - } else { - buddy = purple_find_buddy(gc->account, who); - /* purple_debug_info("QQ", "buddy=%p\n", (void*)buddy); */ - } - - /* if the buddy is null, the api will catch it and return null here */ - bd = purple_buddy_get_protocol_data(buddy); - /* purple_debug_info("QQ", "bd=%p\n", (void*)bd); */ - - if (bd == NULL || buddy == NULL) { - g_free(who); - g_free(alias_utf8); - return; - } - - /* update buddy list (including myself, if myself is the buddy) */ - bd->age = strtol(segments[QQ_INFO_AGE], NULL, 10); - bd->gender = strtol(segments[QQ_INFO_GENDER], NULL, 10); - bd->face = strtol(segments[QQ_INFO_FACE], NULL, 10); - - if (alias_utf8 != NULL) { - if (bd->nickname) g_free(bd->nickname); - bd->nickname = g_strdup(alias_utf8); - } - bd->last_update = time(NULL); - - purple_blist_server_alias_buddy(buddy, bd->nickname); - - /* convert face num from packet (0-299) to local face (1-100) */ - qq_update_buddy_icon(gc->account, who, bd->face); - - g_free(who); - g_free(alias_utf8); -} - -/* process reply to get_info packet */ -void qq_process_get_buddy_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc) -{ - qq_data *qd; - gchar **segments; - gint field_count; - gchar *icon_name; - - g_return_if_fail(data != NULL && data_len != 0); - - qd = (qq_data *) gc->proto_data; - - if (qd->client_version >= 2008) { - field_count = QQ_INFO_LAST; - } else { - field_count = QQ_INFO_LAST_2007; - } - if (NULL == (segments = split_data(data, data_len, "\x1e", field_count))) - return; - -#ifdef DEBUG - info_debug(segments); -#endif - - if (action == QQ_BUDDY_INFO_SET_ICON) { - if (strtol(segments[QQ_INFO_FACE], NULL, 10) != qd->my_icon) { - icon_name = g_strdup_printf("%d", qd->my_icon); - g_free(segments[QQ_INFO_FACE]); - segments[QQ_INFO_FACE] = icon_name; - - /* Update me in buddy list */ - update_buddy_info(gc, segments); - /* send new face to server */ - request_change_info(gc, segments); - } - g_strfreev(segments); - return; - } - - update_buddy_info(gc, segments); - switch (action) { - case QQ_BUDDY_INFO_DISPLAY: - info_display_only(gc, segments); - break; - case QQ_BUDDY_INFO_SET_ICON: - g_return_if_reached(); - break; - case QQ_BUDDY_INFO_MODIFY_BASE: - info_modify_dialogue(gc, segments, QQ_FIELD_BASE); - break; - case QQ_BUDDY_INFO_MODIFY_EXT: - info_modify_dialogue(gc, segments, QQ_FIELD_EXT); - break; - case QQ_BUDDY_INFO_MODIFY_ADDR: - info_modify_dialogue(gc, segments, QQ_FIELD_ADDR); - break; - case QQ_BUDDY_INFO_MODIFY_CONTACT: - info_modify_dialogue(gc, segments, QQ_FIELD_CONTACT); - break; - default: - g_strfreev(segments); - break; - } - return; -} - -void qq_request_get_level(PurpleConnection *gc, UID uid) -{ - qq_data *qd = (qq_data *) gc->proto_data; - guint8 buf[16] = {0}; - gint bytes = 0; - - if (qd->client_version >= 2007) { - bytes += qq_put8(buf + bytes, 0x02); - } else { - bytes += qq_put8(buf + bytes, 0x00); - } - bytes += qq_put32(buf + bytes, uid); - qq_send_cmd(gc, QQ_CMD_GET_LEVEL, buf, bytes); -} - -void qq_request_get_level_2007(PurpleConnection *gc, UID uid) -{ - guint8 buf[16] = {0}; - gint bytes = 0; - - bytes += qq_put8(buf + bytes, 0x08); - bytes += qq_put32(buf + bytes, uid); - bytes += qq_put8(buf + bytes, 0x00); - qq_send_cmd(gc, QQ_CMD_GET_LEVEL, buf, bytes); -} - -void qq_request_get_buddies_level(PurpleConnection *gc, UPDCLS update_class) -{ - qq_data *qd = (qq_data *) gc->proto_data; - PurpleBuddy *buddy; - qq_buddy_data *bd; - guint8 *buf; - GSList *buddies, *it; - gint bytes; - - /* server only reply levels for online buddies */ - buf = g_newa(guint8, MAX_PACKET_SIZE); - - bytes = 0; - bytes += qq_put8(buf + bytes, 0x00); - buddies = purple_find_buddies(purple_connection_get_account(gc), NULL); - for (it = buddies; it; it = it->next) { - buddy = it->data; - if (buddy == NULL) continue; - if ((bd = purple_buddy_get_protocol_data(buddy)) == NULL) continue; - if (bd->uid == 0) continue; /* keep me as end of packet*/ - if (bd->uid == qd->uid) continue; - bytes += qq_put32(buf + bytes, bd->uid); - } - bytes += qq_put32(buf + bytes, qd->uid); - qq_send_cmd_mess(gc, QQ_CMD_GET_LEVEL, buf, bytes, update_class, 0); -} - -static void process_level(PurpleConnection *gc, guint8 *data, gint data_len) -{ - gint bytes = 0; - UID uid; - guint32 onlineTime; - guint16 level, timeRemainder; - qq_buddy_data *bd; - - while (data_len - bytes >= 12) { - bytes += qq_get32(&uid, data + bytes); - bytes += qq_get32(&onlineTime, data + bytes); - bytes += qq_get16(&level, data + bytes); - bytes += qq_get16(&timeRemainder, data + bytes); - purple_debug_info("QQ", "level: %d, uid %u, tmOnline: %d, tmRemainder: %d\n", - level, uid, onlineTime, timeRemainder); - - bd = qq_buddy_data_find(gc, uid); - if (bd == NULL) { - purple_debug_error("QQ", "Got levels of %u not in my buddy list\n", uid); - continue; - } - - bd->onlineTime = onlineTime; - bd->level = level; - bd->timeRemainder = timeRemainder; - } - - if (bytes != data_len) { - purple_debug_error("QQ", - "Wrong format of Get levels. Truncate %d bytes.\n", data_len - bytes); - } -} - -static void process_level_2007(PurpleConnection *gc, guint8 *data, gint data_len) -{ - gint bytes; - UID uid; - guint32 onlineTime; - guint16 level, timeRemainder; - qq_buddy_data *bd; - guint16 str_len; - gchar *str; - gchar *str_utf8; - - bytes = 0; - bytes += qq_get32(&uid, data + bytes); - bytes += qq_get32(&onlineTime, data + bytes); - bytes += qq_get16(&level, data + bytes); - bytes += qq_get16(&timeRemainder, data + bytes); - purple_debug_info("QQ", "level: %d, uid %u, tmOnline: %d, tmRemainder: %d\n", - level, uid, onlineTime, timeRemainder); - - bd = qq_buddy_data_find(gc, uid); - if (bd == NULL) { - purple_debug_error("QQ", "Got levels of %u not in my buddy list\n", uid); - return; - } - - bd->onlineTime = onlineTime; - bd->level = level; - bd->timeRemainder = timeRemainder; - - /* extend bytes in qq2007*/ - bytes += 4; /* skip 8 bytes */ - /* qq_show_packet("Buddies level", data + bytes, data_len - bytes); */ - - do { - bytes += qq_get16(&str_len, data + bytes); - if (str_len <= 0 || bytes + str_len > data_len) { - purple_debug_error("QQ", - "Wrong format of Get levels. Truncate %d bytes.\n", data_len - bytes); - break; - } - str = g_strndup((gchar *)data + bytes, str_len); - bytes += str_len; - str_utf8 = qq_to_utf8(str, QQ_CHARSET_DEFAULT); - purple_debug_info("QQ", "%s\n", str_utf8); - g_free(str_utf8); - g_free(str); - } while (bytes < data_len); -} - -void qq_process_get_level_reply(guint8 *data, gint data_len, PurpleConnection *gc) -{ - gint bytes; - guint8 sub_cmd; - - bytes = 0; - bytes += qq_get8(&sub_cmd, data + bytes); - switch (sub_cmd) { - case 0x08: - process_level_2007(gc, data + bytes, data_len - bytes); - break; - case 0x00: - case 0x02: - default: - process_level(gc, data + bytes, data_len - bytes); - break; - } -} - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/buddy_info.h --- a/libpurple/protocols/qq/buddy_info.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/** - * @file buddy_info.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_BUDDY_INFO_H_ -#define _QQ_BUDDY_INFO_H_ - -#include -#include "connection.h" - -#include "buddy_opt.h" -#include "qq.h" - -/* use in qq2005 - * ext_flag: (0-7) - * bit1 => qq space - * comm_flag: (0-7) - * bit1 => member - * bit4 => TCP mode - * bit5 => open mobile QQ - * bit6 => bind to mobile - * bit7 => whether having a video -#define QQ_COMM_FLAG_QQ_MEMBER 0x02 -#define QQ_COMM_FLAG_TCP_MODE 0x10 -#define QQ_COMM_FLAG_MOBILE 0x20 -#define QQ_COMM_FLAG_BIND_MOBILE 0x40 -#define QQ_COMM_FLAG_VIDEO 0x80 - */ -/* status in eva for qq2006 -#define QQ_FRIEND_FLAG_QQ_MEMBER 0x01 -#define QQ_FRIEND_FLAG_MOBILE 0x10 -#define QQ_FRIEND_FLAG_BIND_MOBILE 0x20 -*/ -#define QQ_COMM_FLAG_QQ_VIP 0x02 -#define QQ_COMM_FLAG_QQ_MEMBER 0x04 -#define QQ_COMM_FLAG_TCP_MODE 0x10 -#define QQ_COMM_FLAG_MOBILE 0x20 -#define QQ_COMM_FLAG_BIND_MOBILE 0x40 -#define QQ_COMM_FLAG_VIDEO 0x80 - -#define QQ_EXT_FLAG_ZONE 0x02 - -#define QQ_BUDDY_GENDER_GG 0x00 -#define QQ_BUDDY_GENDER_MM 0x01 -#define QQ_BUDDY_GENDER_UNKNOWN 0xff - -enum { - QQ_BUDDY_INFO_UPDATE_ONLY = 0, - QQ_BUDDY_INFO_DISPLAY, - QQ_BUDDY_INFO_SET_ICON, - QQ_BUDDY_INFO_MODIFY_BASE, - QQ_BUDDY_INFO_MODIFY_EXT, - QQ_BUDDY_INFO_MODIFY_ADDR, - QQ_BUDDY_INFO_MODIFY_CONTACT -}; - -gchar *qq_get_icon_name(gint face); -gchar *qq_get_icon_path(gchar *icon_name); -void qq_change_icon_cb(PurpleConnection *gc, const char *filepath); - -void qq_request_buddy_info(PurpleConnection *gc, UID uid, UPDCLS update_class, int action); -void qq_set_custom_icon(PurpleConnection *gc, PurpleStoredImage *img); -void qq_process_change_info(PurpleConnection *gc, guint8 *data, gint data_len); -void qq_process_get_buddy_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc); - -void qq_request_get_level(PurpleConnection *gc, UID uid); -void qq_request_get_level_2007(PurpleConnection *gc, UID uid); -void qq_request_get_buddies_level(PurpleConnection *gc, UPDCLS update_class); -void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); - -void qq_update_buddy_icon(PurpleAccount *account, const gchar *who, gint face); -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/buddy_list.c --- a/libpurple/protocols/qq/buddy_list.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,693 +0,0 @@ -/** - * @file buddy_list.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "qq.h" - -#include "debug.h" -#include "notify.h" -#include "utils.h" -#include "packet_parse.h" -#include "buddy_info.h" -#include "buddy_memo.h" -#include "buddy_list.h" -#include "buddy_opt.h" -#include "char_conv.h" -#include "qq_define.h" -#include "qq_base.h" -#include "group.h" -#include "group_internal.h" -#include "group_info.h" - -#include "qq_network.h" - -#define QQ_GET_ONLINE_BUDDY_02 0x02 -#define QQ_GET_ONLINE_BUDDY_03 0x03 /* unknown function */ - -typedef struct _qq_buddy_online { - guint16 unknown1; - guint8 ext_flag; - guint8 comm_flag; - guint16 unknown2; - guint8 ending; /* 0x00 */ -} qq_buddy_online; - -/* get a list of online_buddies */ -void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, UPDCLS update_class) -{ - guint8 *raw_data; - gint bytes = 0; - - raw_data = g_newa(guint8, 5); - - /* 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 */ - bytes += qq_put8(raw_data + bytes, QQ_GET_ONLINE_BUDDY_02); - /* 001-001 seems it supports 255 online buddies at most */ - bytes += qq_put8(raw_data + bytes, position); - /* 002-002 */ - bytes += qq_put8(raw_data + bytes, 0x00); - /* 003-004 */ - bytes += qq_put16(raw_data + bytes, 0x0000); - - qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5, update_class, 0); -} - -/* position starts with 0x0000, - * server may return a position tag if list is too long for one packet */ -void qq_request_get_buddies(PurpleConnection *gc, guint16 position, UPDCLS update_class) -{ - qq_data *qd; - guint8 raw_data[16] = {0}; - gint bytes = 0; - - qd = (qq_data *) gc->proto_data; - - /* 000-001 starting position, can manually specify */ - 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 */ - bytes += qq_put8(raw_data + bytes, 0x00); - if (qd->client_version >= 2007) { - bytes += qq_put16(raw_data + bytes, 0x0000); - } - - qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_LIST, raw_data, bytes, update_class, 0); -} - -/* get all list, buddies & Quns with groupsid support */ -void qq_request_get_buddies_and_rooms(PurpleConnection *gc, guint32 position, UPDCLS update_class) -{ - guint8 raw_data[16] = {0}; - gint bytes = 0; - - /* 0x01 download, 0x02, upload */ - bytes += qq_put8(raw_data + bytes, 0x01); - /* unknown 0x02 */ - bytes += qq_put8(raw_data + bytes, 0x02); - /* unknown 00 00 00 00 */ - bytes += qq_put32(raw_data + bytes, 0x00000000); - bytes += qq_put32(raw_data + bytes, position); - - qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_AND_ROOMS, raw_data, bytes, update_class, 0); -} - -/* parse the data into qq_buddy_status */ -static gint get_buddy_status(qq_buddy_status *bs, guint8 *data) -{ - gint bytes = 0; - - g_return_val_if_fail(data != NULL && bs != NULL, -1); - - /* 000-003: uid */ - bytes += qq_get32(&bs->uid, data + bytes); - /* 004-004: 0x01 */ - bytes += qq_get8(&bs->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 */ - bytes += qq_getIP(&bs->ip, data + bytes); - /* port info is no longer here either */ - /* 009-010: port */ - bytes += qq_get16(&bs->port, data + bytes); - /* 011-011: 0x00 */ - bytes += qq_get8(&bs->unknown2, data + bytes); - /* 012-012: status */ - bytes += qq_get8(&bs->status, data + bytes); - /* 013-014: client tag */ - bytes += qq_get16(&bs->unknown3, data + bytes); - /* 015-030: unknown key */ - bytes += qq_getdata(&(bs->unknown_key[0]), QQ_KEY_LENGTH, data + bytes); - - purple_debug_info("QQ", "Status:%d, uid: %u, ip: %s:%d, U: %d - %d - %04X\n", - bs->status, bs->uid, inet_ntoa(bs->ip), bs->port, - bs->unknown1, bs->unknown2, bs->unknown3); - - return bytes; -} - -/* process the reply packet for get_buddies_online packet */ -guint8 qq_process_get_buddies_online(guint8 *data, gint data_len, PurpleConnection *gc) -{ - qq_data *qd; - gint bytes, bytes_start; - gint count; - guint8 position; - gchar *who; - PurpleBuddy *buddy; - qq_buddy_data *bd; - int entry_len = 38; - - qq_buddy_status bs; - struct { - guint16 unknown1; - guint8 ext_flag; - guint8 comm_flag; - guint16 unknown2; - guint8 ending; /* 0x00 */ - } packet; - - g_return_val_if_fail(data != NULL && data_len != 0, -1); - - qd = (qq_data *) gc->proto_data; - - /* qq_show_packet("Get buddies online reply packet", data, len); */ - if (qd->client_version >= 2007) entry_len += 4; - - bytes = 0; - bytes += qq_get8(&position, data + bytes); - - count = 0; - while (bytes < data_len) { - if (data_len - bytes < entry_len) { - purple_debug_error("QQ", "[buddies online] only %d, need %d\n", - (data_len - bytes), entry_len); - break; - } - memset(&bs, 0 ,sizeof(bs)); - memset(&packet, 0 ,sizeof(packet)); - - /* set flag */ - bytes_start = bytes; - /* based on one online buddy entry */ - /* 000-030 qq_buddy_status */ - bytes += get_buddy_status(&bs, data + bytes); - /* 031-032: */ - bytes += qq_get16(&packet.unknown1, data + bytes); - /* 033-033: ext_flag */ - bytes += qq_get8(&packet.ext_flag, data + bytes); - /* 034-034: comm_flag */ - bytes += qq_get8(&packet.comm_flag, data + bytes); - /* 035-036: */ - bytes += qq_get16(&packet.unknown2, data + bytes); - /* 037-037: */ - bytes += qq_get8(&packet.ending, data + bytes); /* 0x00 */ - /* skip 4 bytes in qq2007 */ - if (qd->client_version >= 2007) bytes += 4; - - if (bs.uid == 0 || (bytes - bytes_start) != entry_len) { - purple_debug_error("QQ", "uid=0 or entry complete len(%d) != %d\n", - (bytes - bytes_start), entry_len); - continue; - } /* check if it is a valid entry */ - - if (bs.uid == qd->uid) { - purple_debug_warning("QQ", "I am in online list %u\n", bs.uid); - } - - /* update buddy information */ - who = uid_to_purple_name(bs.uid); - buddy = purple_find_buddy(gc->account, who); - g_free(who); - if (buddy == NULL) { - /* create no-auth buddy */ - buddy = qq_buddy_new(gc, bs.uid); - } - bd = (buddy == NULL) ? NULL : (qq_buddy_data *)purple_buddy_get_protocol_data(buddy); - if (bd == NULL) { - purple_debug_error("QQ", - "Got an online buddy %u, but not in my buddy list\n", bs.uid); - continue; - } - /* - if(0 != fe->s->client_tag) - q_bud->client_tag = fe->s->client_tag; - */ - if (bd->status != bs.status || bd->comm_flag != packet.comm_flag) { - bd->status = bs.status; - bd->comm_flag = packet.comm_flag; - qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag); - } - bd->ip.s_addr = bs.ip.s_addr; - bd->port = bs.port; - bd->ext_flag = packet.ext_flag; - bd->last_update = time(NULL); - count++; - } - - if(bytes > data_len) { - purple_debug_error("QQ", - "qq_process_get_buddies_online: Dangerous error! maybe protocol changed, notify developers!\n"); - } - - purple_debug_info("QQ", "Received %d online buddies, nextposition=%u\n", - count, (guint) position); - return position; -} - - -/* process reply for get_buddies_list */ -guint16 qq_process_get_buddies(guint8 *data, gint data_len, PurpleConnection *gc) -{ - qq_data *qd; - qq_buddy_data bd; - gint bytes_expected, count; - gint bytes, buddy_bytes; - gint nickname_len; - guint16 position, unknown; - PurpleBuddy *buddy; - - g_return_val_if_fail(data != NULL && data_len != 0, -1); - - qd = (qq_data *) gc->proto_data; - - if (data_len <= 2) { - purple_debug_error("QQ", "empty buddies list\n"); - return -1; - } - /* qq_show_packet("QQ get buddies list", data, data_len); */ - bytes = 0; - bytes += qq_get16(&position, data + bytes); - /* the following data is buddy list in this packet */ - count = 0; - while (bytes < data_len) { - memset(&bd, 0, sizeof(bd)); - /* set flag */ - buddy_bytes = bytes; - /* 000-003: uid */ - bytes += qq_get32(&bd.uid, data + bytes); - /* 004-005: icon index (1-255) */ - bytes += qq_get16(&bd.face, data + bytes); - /* 006-006: age */ - bytes += qq_get8(&bd.age, data + bytes); - /* 007-007: gender */ - bytes += qq_get8(&bd.gender, data + bytes); - - nickname_len = qq_get_vstr(&bd.nickname, QQ_CHARSET_DEFAULT, data + bytes); - bytes += nickname_len; - qq_filter_str(bd.nickname); - - /* Fixme: merge following as 32bit flag */ - bytes += qq_get16(&unknown, data + bytes); - bytes += qq_get8(&bd.ext_flag, data + bytes); - bytes += qq_get8(&bd.comm_flag, data + bytes); - - if (qd->client_version >= 2007) { - bytes += 4; /* skip 4 bytes */ - bytes_expected = 16 + nickname_len; - } else { - bytes_expected = 12 + nickname_len; - } - - if (bd.uid == 0 || (bytes - buddy_bytes) != bytes_expected) { - purple_debug_info("QQ", - "Buddy entry, expect %d bytes, read %d bytes\n", - bytes_expected, bytes - buddy_bytes); - g_free(bd.nickname); - continue; - } else { - count++; - } - -#if 1 - purple_debug_info("QQ", "buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n", - bd.uid, bd.ext_flag, bd.comm_flag, bd.nickname); -#endif - - buddy = qq_buddy_find_or_new(gc, bd.uid); - if (buddy == NULL || purple_buddy_get_protocol_data(buddy) == NULL) { - g_free(bd.nickname); - continue; - } - purple_blist_server_alias_buddy(buddy, bd.nickname); - bd.last_update = time(NULL); - qq_update_buddy_status(gc, bd.uid, bd.status, bd.comm_flag); - - g_memmove(purple_buddy_get_protocol_data(buddy), &bd, sizeof(qq_buddy_data)); - /* nickname has been copy to buddy_data do not free - g_free(bd.nickname); - */ - /*qq_request_buddy_memo(gc, ((qq_buddy_data*)buddy->proto_data)->uid, 0, QQ_BUDDY_MEMO_GET);*/ - qq_request_buddy_memo(gc, bd.uid, bd.uid, QQ_BUDDY_MEMO_GET); - } - - if(bytes > data_len) { - purple_debug_error("QQ", - "qq_process_get_buddies: Dangerous error! maybe protocol changed, notify developers!\n"); - } - - purple_debug_info("QQ", "Received %d buddies, nextposition=%u\n", - count, (guint) position); - return position; -} - -guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConnection *gc) -{ - gint i, j; - gint bytes; - guint8 sub_cmd, reply_code; - guint32 unknown, position; - UID uid; - guint8 type; - qq_room_data *rmd; - - g_return_val_if_fail(data != NULL && data_len != 0, -1); - - bytes = 0; - bytes += qq_get8(&sub_cmd, data + bytes); - g_return_val_if_fail(sub_cmd == 0x01, -1); - - bytes += qq_get8(&reply_code, data + bytes); - if(0 != reply_code) { - purple_debug_warning("QQ", "qq_process_get_buddies_and_rooms, %d\n", 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 < data_len) { - /* 00-03: uid */ - bytes += qq_get32(&uid, data + bytes); - /* 04: type 0x1:buddy 0x4:Qun */ - bytes += qq_get8(&type, data + bytes); - /* 05: skip unknow 0x00 */ - bytes += 1; - if (uid == 0 || (type != 0x1 && type != 0x4)) { - purple_debug_info("QQ", "Buddy entry, uid=%u, type=%d\n", uid, type); - continue; - } - if(0x1 == type) { /* a buddy */ - /* don't do anything but count - buddies are handled by - * qq_request_get_buddies */ - ++i; - } else { /* a group */ - rmd = qq_room_data_find(gc, uid); - if(rmd == NULL) { - purple_debug_info("QQ", "Unknown room id %u\n", uid); - qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, uid); - } else { - rmd->my_role = QQ_ROOM_ROLE_YES; - } - ++j; - } - } - - if(bytes > data_len) { - purple_debug_error("QQ", - "qq_process_get_buddies_and_rooms: Dangerous error! maybe protocol changed, notify developers!\n"); - } - - purple_debug_info("QQ", "Received %d buddies and %d groups, nextposition=%u\n", i, j, (guint) position); - return position; -} - -#define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001 -#define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 0x30 /* ASCII value of "0" */ - -/* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses, - * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy, - * using different accounts to get info. */ -static guint8 get_status_from_purple(PurpleConnection *gc) -{ - qq_data *qd; - PurpleAccount *account; - PurplePresence *presence; - guint8 ret; - - qd = (qq_data *) gc->proto_data; - account = purple_connection_get_account(gc); - presence = purple_account_get_presence(account); - - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) { - ret = QQ_BUDDY_ONLINE_INVISIBLE; - } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) - { - if (qd->client_version >= 2007) { - ret = QQ_BUDDY_ONLINE_BUSY; - } else { - ret = QQ_BUDDY_ONLINE_INVISIBLE; - } - } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY) - || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY) - || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) { - ret = QQ_BUDDY_ONLINE_AWAY; - } else { - ret = QQ_BUDDY_ONLINE_NORMAL; - } - return ret; -} - -/* send a packet to change my online status */ -void qq_request_change_status(PurpleConnection *gc, UPDCLS update_class) -{ - qq_data *qd; - guint8 raw_data[16] = {0}; - gint bytes = 0; - guint8 away_cmd; - guint32 misc_status; - gboolean fake_video; - - qd = (qq_data *) gc->proto_data; - if (!qd->is_login) - return; - - away_cmd = get_status_from_purple(gc); - - 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; - - if (qd->client_version >= 2007) { - bytes = 0; - bytes += qq_put8(raw_data + bytes, away_cmd); - /* status version */ - bytes += qq_put16(raw_data + bytes, 0); - bytes += qq_put16(raw_data + bytes, 0); - bytes += qq_put32(raw_data + bytes, misc_status); - /* Fixme: custom status message, now is empty */ - bytes += qq_put16(raw_data + bytes, 0); - } else { - bytes = 0; - bytes += qq_put8(raw_data + bytes, away_cmd); - bytes += qq_put32(raw_data + bytes, misc_status); - } - qq_send_cmd_mess(gc, QQ_CMD_CHANGE_STATUS, raw_data, bytes, update_class, 0); -} - -/* parse the reply packet for change_status */ -void qq_process_change_status(guint8 *data, gint data_len, PurpleConnection *gc) -{ - qq_data *qd; - gint bytes; - guint8 reply; - qq_buddy_data *bd; - - g_return_if_fail(data != NULL && data_len != 0); - - qd = (qq_data *) gc->proto_data; - - bytes = 0; - bytes = qq_get8(&reply, data + bytes); - if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) { - purple_debug_warning("QQ", "Change status fail 0x%02X\n", reply); - return; - } - - /* purple_debug_info("QQ", "Change status OK\n"); */ - bd = qq_buddy_data_find(gc, qd->uid); - if (bd != NULL) { - bd->status = get_status_from_purple(gc); - bd->last_update = time(NULL); - qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag); - } -} - -/* it is a server message indicating that one of my buddies has changed its status */ -void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnection *gc) -{ - qq_data *qd; - gint bytes; - UID my_uid; - gchar *who; - PurpleBuddy *buddy; - qq_buddy_data *bd; - qq_buddy_status bs; - - g_return_if_fail(data != NULL && data_len != 0); - - qd = (qq_data *) gc->proto_data; - - if (data_len < 35) { - purple_debug_error("QQ", "[buddy status change] only %d, need 35 bytes\n", data_len); - return; - } - - memset(&bs, 0, sizeof(bs)); - bytes = 0; - /* 000-030: qq_buddy_status */ - bytes += get_buddy_status(&bs, data + bytes); - /* 031-034: Unknow, maybe 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); - - /* update buddy information */ - who = uid_to_purple_name(bs.uid); - buddy = purple_find_buddy(gc->account, who); - g_free(who); - if (buddy == NULL) { - /* create no-auth buddy */ - buddy = qq_buddy_new(gc, bs.uid); - } - bd = (buddy == NULL) ? NULL : (qq_buddy_data *)purple_buddy_get_protocol_data(buddy); - if (bd == NULL) { - purple_debug_warning("QQ", "Got status of no-auth buddy %u\n", bs.uid); - return; - } - - if(bs.ip.s_addr != 0) { - bd->ip.s_addr = bs.ip.s_addr; - bd->port = bs.port; - } - if (bd->status != bs.status) { - bd->status = bs.status; - qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag); - } - bd->last_update = time(NULL); - - if (bd->status == QQ_BUDDY_ONLINE_NORMAL && bd->level <= 0) { - if (qd->client_version >= 2007) { - qq_request_get_level_2007(gc, bd->uid); - } else { - qq_request_get_level(gc, bd->uid); - } - } -} - -/*TODO: maybe this should be qq_update_buddy_status() ?*/ -void qq_update_buddy_status(PurpleConnection *gc, UID uid, guint8 status, guint8 flag) -{ - gchar *who; - const gchar *status_id; - - g_return_if_fail(uid != 0); - - /* purple supports signon and idle time - * but it is not much use for QQ, I do not use them */ - /* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */ - switch(status) { - case QQ_BUDDY_OFFLINE: - status_id = "offline"; - break; - case QQ_BUDDY_ONLINE_NORMAL: - status_id = "available"; - break; - case QQ_BUDDY_CHANGE_TO_OFFLINE: - status_id = "offline"; - break; - case QQ_BUDDY_ONLINE_AWAY: - status_id = "away"; - break; - case QQ_BUDDY_ONLINE_INVISIBLE: - status_id = "invisible"; - break; - case QQ_BUDDY_ONLINE_BUSY: - status_id = "busy"; - break; - default: - status_id = "invisible"; - purple_debug_error("QQ", "unknown status: 0x%X\n", status); - break; - } - - purple_debug_info("QQ", "buddy %u status = %s\n", uid, status_id); - who = uid_to_purple_name(uid); - purple_prpl_got_user_status(gc->account, who, status_id, NULL); - - if (flag & QQ_COMM_FLAG_MOBILE && status != QQ_BUDDY_OFFLINE) - purple_prpl_got_user_status(gc->account, who, "mobile", NULL); - else - purple_prpl_got_user_status_deactive(gc->account, who, "mobile"); - - g_free(who); -} - -/* refresh all buddies online/offline, - * after receiving reply for get_buddies_online packet */ -void qq_update_buddyies_status(PurpleConnection *gc) -{ - qq_data *qd; - PurpleBuddy *buddy; - qq_buddy_data *bd; - GSList *buddies, *it; - time_t tm_limit = time(NULL); - - qd = (qq_data *) (gc->proto_data); - - tm_limit -= QQ_UPDATE_ONLINE_INTERVAL; - - buddies = purple_find_buddies(purple_connection_get_account(gc), NULL); - for (it = buddies; it; it = it->next) { - buddy = it->data; - if (buddy == NULL) continue; - - bd = purple_buddy_get_protocol_data(buddy); - if (bd == NULL) continue; - - if (bd->uid == 0) continue; - if (bd->uid == qd->uid) continue; /* my status is always online in my buddy list */ - if (tm_limit < bd->last_update) continue; - if (bd->status == QQ_BUDDY_ONLINE_INVISIBLE) continue; - if (bd->status == QQ_BUDDY_CHANGE_TO_OFFLINE) continue; - - bd->status = QQ_BUDDY_CHANGE_TO_OFFLINE; - bd->last_update = time(NULL); - qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag); - } -} - -void qq_buddy_data_free_all(PurpleConnection *gc) -{ - PurpleBuddy *buddy; - GSList *buddies, *it; - gint count = 0; - - buddies = purple_find_buddies(purple_connection_get_account(gc), NULL); - for (it = buddies; it; it = it->next) { - qq_buddy_data *qbd = NULL; - - buddy = it->data; - if (buddy == NULL) continue; - - qbd = purple_buddy_get_protocol_data(buddy); - if (qbd == NULL) continue; - - qq_buddy_data_free(qbd); - purple_buddy_set_protocol_data(buddy, NULL); - - count++; - } - - if (count > 0) { - purple_debug_info("QQ", "%d buddies' data are freed\n", count); - } -} - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/buddy_list.h --- a/libpurple/protocols/qq/buddy_list.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/** - * @file buddy_list.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_BUDDY_LIST_H_ -#define _QQ_BUDDY_LIST_H_ - -#include -#include "connection.h" - -#include "qq.h" -typedef struct _qq_buddy_status { - UID uid; - guint8 unknown1; - struct in_addr ip; - guint16 port; - guint8 unknown2; - guint8 status; - guint16 unknown3; - guint8 unknown_key[QQ_KEY_LENGTH]; -} qq_buddy_status; - -void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, UPDCLS update_class); -guint8 qq_process_get_buddies_online(guint8 *data, gint data_len, PurpleConnection *gc); - -void qq_request_get_buddies(PurpleConnection *gc, guint16 position, UPDCLS update_class); -guint16 qq_process_get_buddies(guint8 *data, gint data_len, PurpleConnection *gc); - -void qq_request_get_buddies_and_rooms(PurpleConnection *gc, guint32 position, UPDCLS update_class); -guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConnection *gc); - -void qq_request_change_status(PurpleConnection *gc, UPDCLS update_class); -void qq_process_change_status(guint8 *data, gint data_len, PurpleConnection *gc); -void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnection *gc); - -void qq_update_buddyies_status(PurpleConnection *gc); -void qq_update_buddy_status(PurpleConnection *gc, UID uid, guint8 status, guint8 flag); -void qq_buddy_data_free_all(PurpleConnection *gc); -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/buddy_memo.c --- a/libpurple/protocols/qq/buddy_memo.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,398 +0,0 @@ -/** - * @file buddy_memo.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 "debug.h" -#include "notify.h" -#include "request.h" - -#include "buddy_memo.h" -#include "utils.h" -#include "packet_parse.h" -#include "buddy_list.h" -#include "buddy_info.h" -#include "char_conv.h" -#include "im.h" -#include "qq_define.h" -#include "qq_base.h" -#include "qq_network.h" -#include "qq.h" - - -#include -#include -#include - -#include -#include - -/* memo index */ -enum { - QQ_MEMO_ALIAS = 0, - QQ_MEMO_MOBILD, - QQ_MEMO_TELEPHONE, - QQ_MEMO_ADDRESS, - QQ_MEMO_EMAIL, - QQ_MEMO_ZIPCODE, - QQ_MEMO_NOTE, - QQ_MEMO_SIZE -}; - -/* memo id */ -static const gchar *memo_id[] = { - "mm_alias", - "mm_mobile", - "mm_telephone", - "mm_address", - "mm_email", - "mm_zipcode", - "mm_note" -}; - -/* memo text */ -static const gchar *memo_txt[] = { - N_("Alias"), - N_("Mobile"), - N_("Telephone"), - N_("Address"), - N_("Email"), - N_("Postal Code"), - N_("Note") -}; - -typedef struct _modify_memo_request { - PurpleConnection *gc; - UID bd_uid; - gchar **segments; -} modify_memo_request; - - -static void memo_debug(gchar **segments) -{ - gint index; - g_return_if_fail(NULL != segments); - for (index = 0; index < QQ_MEMO_SIZE; index++) { - purple_debug_info("QQ","memo[%i]=%s\n", index, segments[index]); - } -} - -static void memo_free(gchar **segments) -{ - gint index; - g_return_if_fail(NULL != segments); - for (index = 0; index < QQ_MEMO_SIZE; index++) { - g_free(segments[index]); - } - purple_debug_info("QQ", "memo freed\n"); -} - -static void update_buddy_memo(PurpleConnection *gc, UID bd_uid, gchar *alias) -{ - PurpleAccount *account; - PurpleBuddy *buddy; - gchar *who; - g_return_if_fail(NULL != gc && NULL != alias); - - account = (PurpleAccount *)gc->account; - g_return_if_fail(NULL != account); - - who = uid_to_purple_name(bd_uid); - buddy = purple_find_buddy(account, who); - if (buddy == NULL || purple_buddy_get_protocol_data(buddy) == NULL) { - g_free(who); - purple_debug_info("QQ", "Error...Can NOT find %d!\n", bd_uid); - return; - } - purple_blist_alias_buddy(buddy, (const char*)alias); -} - -static void request_change_memo(PurpleConnection *gc, UID bd_uid, gchar **segments) -{ - gint bytes; - /* Attention, length of each segment must be guint8(0~255), - * so length of memo string is limited. - * convert it to guint8 first before putting data */ - guint seg_len; - gint index; - guint8 raw_data[MAX_PACKET_SIZE - 16] = {0}; - - purple_debug_info( "QQ", "request_change_memo\n" ); - g_return_if_fail(NULL != gc && NULL != segments); - - bytes = 0; - bytes += qq_put8(raw_data+bytes, QQ_BUDDY_MEMO_MODIFY); - bytes += qq_put8(raw_data+bytes, 0x00); - bytes += qq_put32(raw_data+bytes, (guint32)bd_uid); - bytes += qq_put8(raw_data+bytes, 0x00); - for (index = 0; index < QQ_MEMO_SIZE; index++) { - seg_len = strlen(segments[index]); - seg_len = seg_len & 0xff; - bytes += qq_put8(raw_data+bytes, (guint8)seg_len); - bytes += qq_putdata(raw_data+bytes, (const guint8 *)segments[index], (guint8)seg_len); - } - - /* debug */ - /* - qq_show_packet("MEMO MODIFY", raw_data, bytes); - */ - - qq_send_cmd(gc, QQ_CMD_BUDDY_MEMO, raw_data, bytes); -} - -static void memo_modify_cancle_cb(modify_memo_request *memo_request, PurpleRequestFields *fields) -{ - memo_free(memo_request->segments); - g_free(memo_request); -} - -/* prepare segments to be sent, string all convert to qq charset */ -static void memo_modify_ok_cb(modify_memo_request *memo_request, PurpleRequestFields *fields) -{ - PurpleConnection *gc; - UID bd_uid; - gchar **segments; - const gchar *utf8_str; - gchar *value = NULL; - gint index; - - g_return_if_fail(NULL != memo_request); - gc = (PurpleConnection *)memo_request->gc; - segments = (gchar **)memo_request->segments; - g_return_if_fail(NULL != gc && NULL != segments); - bd_uid = (guint32)memo_request->bd_uid; - - - for (index = 0; index < QQ_MEMO_SIZE; index++) { - utf8_str = purple_request_fields_get_string(fields, memo_id[index]); - /* update alias */ - if (QQ_MEMO_ALIAS == index) { - update_buddy_memo(gc, (UID)bd_uid, segments[QQ_MEMO_ALIAS]); - } - if (NULL == utf8_str) { - value = g_strdup(""); - } - else { - value = utf8_to_qq(utf8_str, QQ_CHARSET_DEFAULT); - /* Warnning: value will be string "(NULL)" instead of NULL */ - if (!qq_strcmp("(NULL)", value)) { - value = g_strdup(""); - } - } - g_free(segments[index]); - segments[index] = value; - } - - memo_debug(segments); - /* send segments */ - request_change_memo(gc, bd_uid, segments); - - /* free segments */ - memo_free(segments); - g_free(memo_request); -} - -/* memo modify dialogue */ -static void memo_modify_dialogue(PurpleConnection *gc, UID bd_uid, gchar **segments, guint32 action) -{ - modify_memo_request *memo_request; - PurpleRequestField *field; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - int index; - gchar *utf8_title; - gchar *utf8_primary; - - g_return_if_fail(NULL != gc && NULL != segments); - - switch (action) { - case QQ_BUDDY_MEMO_GET: - memo_free(segments); - break; - case QQ_BUDDY_MEMO_MODIFY: - /* keep one dialog once a time */ - purple_request_close_with_handle(gc); - /* show dialog */ - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - for(index = 0; index < QQ_MEMO_SIZE; index++) { - /* - purple_debug_info("QQ", "id:%s txt:%s segment:%s\n", - memo_id[index], memo_txt[index], segments[index]); - */ - field = purple_request_field_string_new(memo_id[index], memo_txt[index], - segments[index], FALSE); - purple_request_field_group_add_field(group, field); - } - - /* for upload cb */ - memo_request = g_new0(modify_memo_request, 1); - memo_request->gc = gc; - memo_request->bd_uid = bd_uid; - memo_request->segments = segments; - /* callback */ - utf8_title = g_strdup(_("Buddy Memo")); - utf8_primary = g_strdup(_("Change his/her memo as you like")); - - purple_request_fields(gc, utf8_title, utf8_primary, NULL, - fields, - _("_Modify"), G_CALLBACK(memo_modify_ok_cb), - _("_Cancel"), G_CALLBACK(memo_modify_cancle_cb), - purple_connection_get_account(gc), NULL, NULL, - memo_request); - - g_free(utf8_title); - g_free(utf8_primary); - break; - default: - purple_debug_info("QQ", "Error...unknown memo action, please tell us\n"); - break; - } -} - -static void qq_create_buddy_memo(PurpleConnection *gc, UID bd_uid, guint32 action) -{ - gchar **segments; - gint index; - g_return_if_fail(NULL != gc); - - segments = g_new0(gchar*, QQ_MEMO_SIZE); - for (index = 0; index < QQ_MEMO_SIZE; index++) { - segments[index] = g_strdup("");; - } - memo_modify_dialogue(gc, bd_uid, segments, action); -} - -/* process reply to get_memo packet - * here, update_class will be regarded as buddy's uid. because some - * memo packages returned without uid, which will make us confused */ -void qq_process_get_buddy_memo(PurpleConnection *gc, guint8* data, gint data_len, - UPDCLS update_class, guint32 action) -{ - gchar **segments; - gint bytes; - gint index; - guint8 rcv_cmd; - guint32 rcv_uid; - guint8 unk1_8; - guint8 is_success; - - g_return_if_fail(NULL != gc && NULL != data && 0 != data_len); - /* - qq_show_packet("MEMO REACH", data, data_len); - */ - purple_debug_info("QQ", "action=0x%02X\n", action); - - bytes = 0; - - /* TX looks a bit clever than before... :) */ - bytes += qq_get8(&rcv_cmd, data+bytes); - purple_debug_info("QQ", "rcv_cmd=0x%02X\n", rcv_cmd); - - /* it's possible that packet contains no buddy uid and no memo!!! - * go to next step according to previous action sent */ - if (1 == data_len) { /* only one byte */ - purple_debug_info("QQ", "memo packet contains no buddy uid and memo...\n"); - if (QQ_BUDDY_MEMO_MODIFY == action) { - UID mod_uid; - mod_uid = (UID)update_class; - qq_create_buddy_memo(gc, mod_uid, QQ_BUDDY_MEMO_MODIFY); - return; - } - return; - } - - switch (rcv_cmd) { - case QQ_BUDDY_MEMO_MODIFY: - case QQ_BUDDY_MEMO_REMOVE: - bytes += qq_get8(&is_success, data+bytes); - if (QQ_BUDDY_MEMO_REQUEST_SUCCESS == is_success) { - purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO, - _("Memo Modify"), _("Server says:"), - _("Your request was accepted."), - NULL, NULL); - purple_debug_info("QQ", "memo change succeessfully!\n"); - } - else { - purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO, - _("Memo Modify"), _("Server says:"), - _("Your request was rejected."), - NULL, NULL); - purple_debug_info("QQ", "memo change failed\n"); - } - break; - case QQ_BUDDY_MEMO_GET: - bytes += qq_get32(&rcv_uid, data+bytes); - purple_debug_info("QQ", "rcv_uid=%u\n", rcv_uid); - bytes += qq_get8(&unk1_8, data+bytes); - purple_debug_info("QQ", "unk1_8=0x%02X\n", unk1_8); - segments = g_new0(gchar*, QQ_MEMO_SIZE); - for (index = 0; index < QQ_MEMO_SIZE; index++) { - /* get utf8 string */ - bytes += qq_get_vstr(&segments[index], QQ_CHARSET_DEFAULT, data+bytes); - /* - purple_debug_info("QQ", "bytes:%d, seg:%s\n", bytes, segments[index]); - */ - } - - /* common action, update buddy memo */ - update_buddy_memo(gc, (UID)rcv_uid, segments[QQ_MEMO_ALIAS]); - - /* memo is a thing that we regard our buddy as, so we need one more buddy_uid */ - memo_modify_dialogue(gc, rcv_uid, segments, action); - break; - default: - purple_debug_info("QQ", "received an UNKNOWN memo cmd!!!\n"); - break; - } -} - -/* request buddy memo - * - * param: gc, uid, update_class, action - * here, update_class will be set to buddy's uid. because some memo - * packages returned without uid, which will make us confused */ -void qq_request_buddy_memo(PurpleConnection *gc, UID bd_uid, UPDCLS update_class, guint32 action) -{ - guint8 raw_data[16] = {0}; - gint bytes; - - purple_debug_info("QQ", "qq_request_buddy_memo, buddy uid=%u, update_class=%u\n", - bd_uid, update_class); - g_return_if_fail(NULL != gc); - /* '0' is ok - g_return_if_fail(uid != 0); - */ - bytes = 0; - bytes += qq_put8(raw_data+bytes, QQ_BUDDY_MEMO_GET); - bytes += qq_put32(raw_data+bytes, bd_uid); - /* - qq_show_packet("MEMO REQUEST", raw_data, bytes); - */ - - qq_send_cmd_mess(gc, QQ_CMD_BUDDY_MEMO, (guint8*)raw_data, bytes, update_class, action); -} - - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/buddy_memo.h --- a/libpurple/protocols/qq/buddy_memo.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/** - * @file buddy_memo.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_BUDDY_MEMO_H_ -#define _QQ_BUDDY_MEMO_H_ - -#include -#include "connection.h" -#include "blist.h" -#include "qq.h" - -#define QQ_BUDDY_MEMO_REQUEST_SUCCESS 0x00 - -/* clan command for memo */ -enum -{ - QQ_BUDDY_MEMO_MODIFY = 0x01, /* upload memo */ - QQ_BUDDY_MEMO_REMOVE, /* remove memo */ - QQ_BUDDY_MEMO_GET /* get memo */ -}; - - -void qq_process_get_buddy_memo(PurpleConnection *gc, guint8* data, gint data_len, UPDCLS update_class, guint32 action); - -void qq_request_buddy_memo(PurpleConnection *gc, UID bd_uid, UPDCLS update_class, guint32 action); - -#endif - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/buddy_opt.c --- a/libpurple/protocols/qq/buddy_opt.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1290 +0,0 @@ -/** - * @file buddy_opt.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 "debug.h" -#include "notify.h" -#include "request.h" -#include "privacy.h" - -#include "buddy_info.h" -#include "buddy_list.h" -#include "buddy_opt.h" -#include "char_conv.h" -#include "qq_define.h" -#include "im.h" -#include "qq_base.h" -#include "packet_parse.h" -#include "qq_network.h" -#include "utils.h" - -#define PURPLE_GROUP_QQ_FORMAT "QQ (%s)" - -#define QQ_REMOVE_SELF_REPLY_OK 0x00 - -enum { - QQ_MY_AUTH_APPROVE = 0x30, /* ASCII value of "0" */ - QQ_MY_AUTH_REJECT = 0x31, /* ASCII value of "1" */ - QQ_MY_AUTH_REQUEST = 0x32 /* ASCII value of "2" */ -}; - -typedef struct _qq_buddy_req { - PurpleConnection *gc; - UID uid; - guint8 *auth; - guint8 auth_len; -} qq_buddy_req; - -void add_buddy_authorize_input(PurpleConnection *gc, UID uid, - guint8 *auth, guint8 auth_len); - -static void buddy_req_free(qq_buddy_req *add_req) -{ - g_return_if_fail(add_req != NULL); - if (add_req->auth) g_free(add_req->auth); - g_free(add_req); -} - -static void buddy_req_cancel_cb(qq_buddy_req *add_req, const gchar *msg) -{ - g_return_if_fail(add_req != NULL); - buddy_req_free(add_req); -} - -PurpleGroup *qq_group_find_or_new(const gchar *group_name) -{ - PurpleGroup *g; - - g_return_val_if_fail(group_name != NULL, NULL); - - g = purple_find_group(group_name); - if (g == NULL) { - g = purple_group_new(group_name); - purple_blist_add_group(g, NULL); - purple_debug_warning("QQ", "Add new group: %s\n", group_name); - } - - return g; -} - -static qq_buddy_data *qq_buddy_data_new(UID uid) -{ - qq_buddy_data *bd = g_new0(qq_buddy_data, 1); - memset(bd, 0, sizeof(qq_buddy_data)); - bd->uid = uid; - bd->status = QQ_BUDDY_OFFLINE; - return bd; -} - -qq_buddy_data *qq_buddy_data_find(PurpleConnection *gc, UID uid) -{ - gchar *who; - PurpleBuddy *buddy; - qq_buddy_data *bd; - - g_return_val_if_fail(gc != NULL, NULL); - - who = uid_to_purple_name(uid); - if (who == NULL) return NULL; - buddy = purple_find_buddy(purple_connection_get_account(gc), who); - g_free(who); - - if (buddy == NULL) { - purple_debug_error("QQ", "Can not find purple buddy of %u\n", uid); - return NULL; - } - - if ((bd = purple_buddy_get_protocol_data(buddy)) == NULL) { - purple_debug_error("QQ", "Can not find buddy data of %u\n", uid); - return NULL; - } - return bd; -} - -void qq_buddy_data_free(qq_buddy_data *bd) -{ - g_return_if_fail(bd != NULL); - - if (bd->nickname) g_free(bd->nickname); - g_free(bd); -} - -/* create purple buddy without data and display with no-auth icon */ -PurpleBuddy *qq_buddy_new(PurpleConnection *gc, UID uid) -{ - PurpleBuddy *buddy; - PurpleGroup *group; - gchar *who; - gchar *group_name; - - g_return_val_if_fail(gc->account != NULL && uid != 0, NULL); - - group_name = g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, - purple_account_get_username(gc->account)); - group = qq_group_find_or_new(group_name); - if (group == NULL) { - purple_debug_error("QQ", "Failed creating group %s\n", group_name); - return NULL; - } - - purple_debug_info("QQ", "Add new purple buddy: [%u]\n", uid); - who = uid_to_purple_name(uid); - buddy = purple_buddy_new(gc->account, who, NULL); /* alias is NULL */ - purple_buddy_set_protocol_data(buddy, NULL); - - g_free(who); - - purple_blist_add_buddy(buddy, NULL, group, NULL); - - g_free(group_name); - - return buddy; -} - -static void qq_buddy_free(PurpleBuddy *buddy) -{ - qq_buddy_data *bd; - - g_return_if_fail(buddy); - - if ((bd = purple_buddy_get_protocol_data(buddy)) != NULL) { - qq_buddy_data_free(bd); - } - purple_buddy_set_protocol_data(buddy, NULL); - purple_blist_remove_buddy(buddy); -} - -PurpleBuddy *qq_buddy_find(PurpleConnection *gc, UID uid) -{ - PurpleBuddy *buddy; - gchar *who; - - g_return_val_if_fail(gc->account != NULL && uid != 0, NULL); - - who = uid_to_purple_name(uid); - buddy = purple_find_buddy(gc->account, who); - g_free(who); - return buddy; -} - -PurpleBuddy *qq_buddy_find_or_new(PurpleConnection *gc, UID uid) -{ - PurpleBuddy *buddy; - qq_buddy_data *bd; - - g_return_val_if_fail(gc->account != NULL && uid != 0, NULL); - - buddy = qq_buddy_find(gc, uid); - if (buddy == NULL) { - buddy = qq_buddy_new(gc, uid); - if (buddy == NULL) { - return NULL; - } - } - - if (purple_buddy_get_protocol_data(buddy) != NULL) { - return buddy; - } - - bd = qq_buddy_data_new(uid); - purple_buddy_set_protocol_data(buddy, bd); - return buddy; -} - -/* send packet to remove a buddy from my buddy list */ -static void request_remove_buddy(PurpleConnection *gc, UID uid) -{ - gchar uid_str[11]; - gint bytes; - - g_return_if_fail(uid > 0); - - g_snprintf(uid_str, sizeof(uid_str), "%u", uid); - bytes = strlen(uid_str); - qq_send_cmd_mess(gc, QQ_CMD_REMOVE_BUDDY, (guint8 *) uid_str, bytes, 0, uid); -} - -static void request_remove_buddy_ex(PurpleConnection *gc, - UID uid, guint8 *auth, guint8 auth_len) -{ - gint bytes; - guint8 *raw_data; - gchar uid_str[16]; - - g_return_if_fail(uid != 0); - g_return_if_fail(auth != NULL && auth_len > 0); - - raw_data = g_newa(guint8, auth_len + sizeof(uid_str) ); - bytes = 0; - bytes += qq_put8(raw_data + bytes, auth_len); - bytes += qq_putdata(raw_data + bytes, auth, auth_len); - - g_snprintf(uid_str, sizeof(uid_str), "%u", uid); - bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str)); - - qq_send_cmd_mess(gc, QQ_CMD_REMOVE_BUDDY, raw_data, bytes, 0, uid); -} - -void qq_request_auth_code(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, UID uid) -{ - guint8 raw_data[16]; - gint bytes; - - g_return_if_fail(uid > 0); - bytes = 0; - bytes += qq_put8(raw_data + bytes, cmd); - bytes += qq_put16(raw_data + bytes, sub_cmd); - bytes += qq_put32(raw_data + bytes, uid); - - qq_send_cmd_mess(gc, QQ_CMD_AUTH_CODE, raw_data, bytes, 0, uid); -} - -void qq_process_auth_code(PurpleConnection *gc, guint8 *data, gint data_len, UID uid) -{ - gint bytes; - guint8 cmd, reply; - guint16 sub_cmd; - guint8 *code = NULL; - guint16 code_len = 0; - - g_return_if_fail(data != NULL && data_len != 0); - g_return_if_fail(uid != 0); - - qq_show_packet("qq_process_auth_code", data, data_len); - bytes = 0; - bytes += qq_get8(&cmd, data + bytes); - bytes += qq_get16(&sub_cmd, data + bytes); - bytes += qq_get8(&reply, data + bytes); - g_return_if_fail(bytes + 2 <= data_len); - - bytes += qq_get16(&code_len, data + bytes); - g_return_if_fail(code_len > 0); - g_return_if_fail(bytes + code_len <= data_len); - code = g_newa(guint8, code_len); - bytes += qq_getdata(code, code_len, data + bytes); - - if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_REMOVE_BUDDY) { - request_remove_buddy_ex(gc, uid, code, code_len); - return; - } - if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_ADD_BUDDY) { - add_buddy_authorize_input(gc, uid, code, code_len); - return; - } - purple_debug_info("QQ", "Got auth info cmd 0x%x, sub 0x%x, reply 0x%x\n", - cmd, sub_cmd, reply); -} - -static void add_buddy_question_cb(qq_buddy_req *add_req, const gchar *text) -{ - g_return_if_fail(add_req != NULL); - if (add_req->gc == NULL || add_req->uid == 0) { - buddy_req_free(add_req); - return; - } - - qq_request_question(add_req->gc, QQ_QUESTION_ANSWER, add_req->uid, NULL, text); - buddy_req_free(add_req); -} - -static void add_buddy_question_input(PurpleConnection *gc, UID uid, gchar *question) -{ - gchar *who, *msg; - qq_buddy_req *add_req; - g_return_if_fail(uid != 0); - - add_req = g_new0(qq_buddy_req, 1); - add_req->gc = gc; - add_req->uid = uid; - add_req->auth = NULL; - add_req->auth_len = 0; - - who = uid_to_purple_name(uid); - msg = g_strdup_printf(_("%u requires verification: %s"), uid, question); - purple_request_input(gc, _("Add buddy question"), msg, - _("Enter answer here"), - NULL, - TRUE, FALSE, NULL, - _("Send"), G_CALLBACK(add_buddy_question_cb), - _("Cancel"), G_CALLBACK(buddy_req_cancel_cb), - purple_connection_get_account(gc), who, NULL, - add_req); - - g_free(msg); - g_free(who); -} - -void qq_request_question(PurpleConnection *gc, - guint8 cmd, UID uid, const gchar *question_utf8, const gchar *answer_utf8) -{ - guint8 raw_data[MAX_PACKET_SIZE - 16]; - gint bytes; - - g_return_if_fail(uid > 0); - bytes = 0; - bytes += qq_put8(raw_data + bytes, cmd); - if (cmd == QQ_QUESTION_GET) { - bytes += qq_put8(raw_data + bytes, 0); - qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid); - return; - } - if (cmd == QQ_QUESTION_SET) { - bytes += qq_put_vstr(raw_data + bytes, question_utf8, QQ_CHARSET_DEFAULT); - bytes += qq_put_vstr(raw_data + bytes, answer_utf8, QQ_CHARSET_DEFAULT); - bytes += qq_put8(raw_data + bytes, 0); - qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid); - return; - } - /* Unknow 2 bytes, 0x(00 01) */ - bytes += qq_put8(raw_data + bytes, 0x00); - bytes += qq_put8(raw_data + bytes, 0x01); - g_return_if_fail(uid != 0); - bytes += qq_put32(raw_data + bytes, uid); - if (cmd == QQ_QUESTION_REQUEST) { - qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid); - return; - } - bytes += qq_put_vstr(raw_data + bytes, answer_utf8, QQ_CHARSET_DEFAULT); - bytes += qq_put8(raw_data + bytes, 0); - qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid); - return; -} - -static void request_add_buddy_by_question(PurpleConnection *gc, UID uid, - guint8 *code, guint16 code_len) -{ - guint8 raw_data[MAX_PACKET_SIZE - 16]; - gint bytes = 0; - - g_return_if_fail(uid != 0 && code_len > 0); - - bytes = 0; - bytes += qq_put8(raw_data + bytes, 0x10); - bytes += qq_put32(raw_data + bytes, uid); - bytes += qq_put16(raw_data + bytes, 0); - - bytes += qq_put8(raw_data + bytes, 0); - bytes += qq_put8(raw_data + bytes, 0); /* no auth code */ - - bytes += qq_put16(raw_data + bytes, code_len); - bytes += qq_putdata(raw_data + bytes, code, code_len); - - bytes += qq_put8(raw_data + bytes, 1); /* ALLOW ADD ME FLAG */ - bytes += qq_put8(raw_data + bytes, 0); /* group number? */ - qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH_EX, raw_data, bytes); -} - -void qq_process_question(PurpleConnection *gc, guint8 *data, gint data_len, UID uid) -{ - gint bytes; - guint8 cmd, reply; - gchar *question, *answer; - guint16 code_len; - guint8 *code; - - g_return_if_fail(data != NULL && data_len != 0); - - qq_show_packet("qq_process_question", data, data_len); - bytes = 0; - bytes += qq_get8(&cmd, data + bytes); - if (cmd == QQ_QUESTION_GET) { - bytes += qq_get_vstr(&question, QQ_CHARSET_DEFAULT, data + bytes); - bytes += qq_get_vstr(&answer, QQ_CHARSET_DEFAULT, data + bytes); - purple_debug_info("QQ", "Get buddy adding Q&A:\n%s\n%s\n", question, answer); - g_free(question); - g_free(answer); - return; - } - if (cmd == QQ_QUESTION_SET) { - bytes += qq_get8(&reply, data + bytes); - if (reply == 0) { - purple_debug_info("QQ", "Successed setting Q&A\n"); - } else { - purple_debug_warning("QQ", "Failed setting Q&A, reply %d\n", reply); - } - return; - } - - g_return_if_fail(uid != 0); - bytes += 2; /* skip 2 bytes, 0x(00 01)*/ - if (cmd == QQ_QUESTION_REQUEST) { - bytes += qq_get8(&reply, data + bytes); - if (reply == 0x01) { - purple_debug_warning("QQ", "Failed getting question, reply %d\n", reply); - return; - } - bytes += qq_get_vstr(&question, QQ_CHARSET_DEFAULT, data + bytes); - purple_debug_info("QQ", "Get buddy question:\n%s\n", question); - add_buddy_question_input(gc, uid, question); - g_free(question); - return; - } - - if (cmd == QQ_QUESTION_ANSWER) { - bytes += qq_get8(&reply, data + bytes); - if (reply == 0x01) { - purple_notify_error(gc, _("Add Buddy"), _("Invalid answer."), NULL); - return; - } - bytes += qq_get16(&code_len, data + bytes); - g_return_if_fail(code_len > 0); - g_return_if_fail(bytes + code_len <= data_len); - - code = g_newa(guint8, code_len); - bytes += qq_getdata(code, code_len, data + bytes); - request_add_buddy_by_question(gc, uid, code, code_len); - return; - } - - g_return_if_reached(); -} - -/* try to remove myself from someone's buddy list */ -static void request_buddy_remove_me(PurpleConnection *gc, UID uid) -{ - guint8 raw_data[16] = {0}; - gint bytes = 0; - - g_return_if_fail(uid > 0); - - bytes += qq_put32(raw_data + bytes, uid); - - qq_send_cmd_mess(gc, QQ_CMD_REMOVE_ME, raw_data, bytes, 0, uid); -} - -/* try to add a buddy without authentication */ -static void request_add_buddy_no_auth(PurpleConnection *gc, UID uid) -{ - gchar uid_str[11]; - - g_return_if_fail(uid > 0); - - /* we need to send the ascii code of this uid to qq server */ - g_snprintf(uid_str, sizeof(uid_str), "%u", uid); - qq_send_cmd_mess(gc, QQ_CMD_ADD_BUDDY_NO_AUTH, - (guint8 *) uid_str, strlen(uid_str), 0, uid); -} - -static void request_add_buddy_no_auth_ex(PurpleConnection *gc, UID uid) -{ - guint bytes; - guint8 raw_data[16]; - - g_return_if_fail(uid != 0); - - bytes = 0; - bytes += qq_put32(raw_data + bytes, uid); - qq_send_cmd_mess(gc, QQ_CMD_ADD_BUDDY_NO_AUTH_EX, raw_data, bytes, 0, uid); -} - -/* this buddy needs authentication, text conversion is done at lowest level */ -static void request_add_buddy_auth(PurpleConnection *gc, UID uid, const gchar response, const gchar *text) -{ - guint8 raw_data[MAX_PACKET_SIZE - 16]; - gint bytes; - gchar *msg, uid_str[11]; - guint8 bar; - - g_return_if_fail(uid != 0); - - g_snprintf(uid_str, sizeof(uid_str), "%u", uid); - bar = 0x1f; - - bytes = 0; - bytes += qq_putdata(raw_data + bytes, (guint8 *) uid_str, strlen(uid_str)); - bytes += qq_put8(raw_data + bytes, bar); - bytes += qq_put8(raw_data + bytes, response); - - if (text != NULL) { - msg = utf8_to_qq(text, QQ_CHARSET_DEFAULT); - bytes += qq_put8(raw_data + bytes, bar); - bytes += qq_putdata(raw_data + bytes, (guint8 *) msg, strlen(msg)); - g_free(msg); - } - - qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH, raw_data, bytes); -} - -static void request_add_buddy_auth_ex(PurpleConnection *gc, UID uid, - const gchar *text, guint8 *auth, guint8 auth_len) -{ - guint8 raw_data[MAX_PACKET_SIZE - 16]; - gint bytes = 0; - - g_return_if_fail(uid != 0); - - bytes = 0; - bytes += qq_put8(raw_data + bytes, 0x02); - bytes += qq_put32(raw_data + bytes, uid); - bytes += qq_put16(raw_data + bytes, 0); - - bytes += qq_put8(raw_data + bytes, 0); - if (auth == NULL || auth_len <= 0) { - bytes += qq_put8(raw_data + bytes, 0); - } else { - bytes += qq_put8(raw_data + bytes, auth_len); - bytes += qq_putdata(raw_data + bytes, auth, auth_len); - } - bytes += qq_put8(raw_data + bytes, 1); /* ALLOW ADD ME FLAG */ - bytes += qq_put8(raw_data + bytes, 0); /* group number? */ - bytes += qq_put_vstr(raw_data + bytes, text, QQ_CHARSET_DEFAULT); - qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH_EX, raw_data, bytes); -} - -void qq_process_add_buddy_auth_ex(PurpleConnection *gc, guint8 *data, gint data_len, guint32 ship32) -{ - g_return_if_fail(data != NULL && data_len != 0); - - qq_show_packet("qq_process_question", data, data_len); -} - -static void add_buddy_auth_cb(qq_buddy_req *add_req, const gchar *text) -{ - qq_data *qd; - g_return_if_fail(add_req != NULL); - if (add_req->gc == NULL || add_req->uid == 0) { - buddy_req_free(add_req); - return; - } - - qd = (qq_data *)add_req->gc->proto_data; - if (qd->client_version > 2005) { - request_add_buddy_auth_ex(add_req->gc, add_req->uid, - text, add_req->auth, add_req->auth_len); - } else { - request_add_buddy_auth(add_req->gc, add_req->uid, QQ_MY_AUTH_REQUEST, text); - } - buddy_req_free(add_req); -} - -/* the real packet to reject and request is sent from here */ -static void buddy_add_deny_reason_cb(qq_buddy_req *add_req, const gchar *reason) -{ - g_return_if_fail(add_req != NULL); - if (add_req->gc == NULL || add_req->uid == 0) { - buddy_req_free(add_req); - return; - } - - request_add_buddy_auth(add_req->gc, add_req->uid, QQ_MY_AUTH_REJECT, reason); - buddy_req_free(add_req); -} - -static void buddy_add_deny_noreason_cb(qq_buddy_req *add_req) -{ - buddy_add_deny_reason_cb(add_req, NULL); -} - -/* we approve other's request of adding me as friend */ -static void buddy_add_authorize_cb(gpointer data) -{ - qq_buddy_req *add_req = (qq_buddy_req *)data; - - g_return_if_fail(add_req != NULL); - if (add_req->gc == NULL || add_req->uid == 0) { - buddy_req_free(add_req); - return; - } - - request_add_buddy_auth(add_req->gc, add_req->uid, QQ_MY_AUTH_APPROVE, NULL); - buddy_req_free(add_req); -} - -/* we reject other's request of adding me as friend */ -static void buddy_add_deny_cb(gpointer data) -{ - qq_buddy_req *add_req = (qq_buddy_req *)data; - gchar *who = uid_to_purple_name(add_req->uid); - purple_request_input(add_req->gc, NULL, _("Authorization denied message:"), - NULL, _("Sorry, you're not my style."), TRUE, FALSE, NULL, - _("OK"), G_CALLBACK(buddy_add_deny_reason_cb), - _("Cancel"), G_CALLBACK(buddy_add_deny_noreason_cb), - purple_connection_get_account(add_req->gc), who, NULL, - add_req); - g_free(who); -} - -static void add_buddy_no_auth_cb(qq_buddy_req *add_req) -{ - qq_data *qd; - g_return_if_fail(add_req != NULL); - if (add_req->gc == NULL || add_req->uid == 0) { - buddy_req_free(add_req); - return; - } - - qd = (qq_data *) add_req->gc->proto_data; - if (qd->client_version > 2005) { - request_add_buddy_no_auth_ex(add_req->gc, add_req->uid); - } else { - request_add_buddy_no_auth(add_req->gc, add_req->uid); - } - buddy_req_free(add_req); -} - -void add_buddy_authorize_input(PurpleConnection *gc, UID uid, - guint8 *auth, guint8 auth_len) -{ - gchar *who, *msg; - qq_buddy_req *add_req; - g_return_if_fail(uid != 0); - - add_req = g_new0(qq_buddy_req, 1); - add_req->gc = gc; - add_req->uid = uid; - add_req->auth = NULL; - add_req->auth_len = 0; - if (auth != NULL && auth_len > 0) { - add_req->auth = g_new0(guint8, auth_len); - g_memmove(add_req->auth, auth, auth_len); - add_req->auth_len = auth_len; - } - - who = uid_to_purple_name(uid); - msg = g_strdup_printf(_("%u needs authorization"), uid); - purple_request_input(gc, _("Add buddy authorize"), msg, - _("Enter request here"), - _("Would you be my friend?"), - TRUE, FALSE, NULL, - _("Send"), G_CALLBACK(add_buddy_auth_cb), - _("Cancel"), G_CALLBACK(buddy_req_cancel_cb), - purple_connection_get_account(gc), who, NULL, - add_req); - - g_free(msg); - g_free(who); -} - -/* add a buddy and send packet to QQ server - * note that when purple load local cached buddy list into its blist - * it also calls this funtion, so we have to - * define qd->is_login=TRUE AFTER LOGIN */ -void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - qq_data *qd; - UID uid; - - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - g_return_if_fail(buddy != NULL); - - qd = (qq_data *) gc->proto_data; - if (!qd->is_login) - return; /* IMPORTANT ! */ - - uid = purple_name_to_uid(purple_buddy_get_name(buddy)); - if (uid > 0) { - if (qd->client_version > 2005) { - request_add_buddy_no_auth_ex(gc, uid); - } else { - request_add_buddy_no_auth(gc, uid); - } - return; - } - - purple_notify_error(gc, _("QQ Buddy"), _("Add buddy"), _("Invalid QQ Number")); - if (buddy == NULL) { - return; - } - - purple_debug_info("QQ", "Remove buddy with invalid QQ number %u\n", uid); - qq_buddy_free(buddy); -} - -/* process reply to add_buddy_auth request */ -void qq_process_add_buddy_auth(guint8 *data, gint data_len, PurpleConnection *gc) -{ - gchar **segments, *msg_utf8; - - g_return_if_fail(data != NULL && data_len != 0); - - if (data[0] == '0') { - purple_debug_info("QQ", "Reply OK for sending authorize\n"); - return; - } - - if (NULL == (segments = split_data(data, data_len, "\x1f", 2))) { - purple_notify_error(gc, _("QQ Buddy"), _("Failed sending authorize"), NULL); - return; - } - msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); - purple_notify_error(gc, _("QQ Buddy"), _("Failed sending authorize"), msg_utf8); - g_free(msg_utf8); -} - -/* process the server reply for my request to remove a buddy */ -void qq_process_remove_buddy(PurpleConnection *gc, guint8 *data, gint data_len, UID uid) -{ - PurpleBuddy *buddy = NULL; - gchar *msg; - - g_return_if_fail(data != NULL && data_len != 0); - g_return_if_fail(uid != 0); - - buddy = qq_buddy_find(gc, uid); - if (data[0] != 0) { - msg = g_strdup_printf(_("Failed removing buddy %u"), uid); - purple_notify_info(gc, _("QQ Buddy"), msg, NULL); - g_free(msg); - } - - purple_debug_info("QQ", "Reply OK for removing buddy\n"); - /* remove buddy again */ - if (buddy != NULL) { - qq_buddy_free(buddy); - } -} - -/* process the server reply for my request to remove myself from a buddy */ -void qq_process_buddy_remove_me(PurpleConnection *gc, guint8 *data, gint data_len, UID uid) -{ - gchar *msg; - - g_return_if_fail(data != NULL && data_len != 0); - - if (data[0] == 0) { - purple_debug_info("QQ", "Reply OK for removing me from %u's buddy list\n", uid); - return; - } - msg = g_strdup_printf(_("Failed removing me from %d's buddy list"), uid); - purple_notify_info(gc, _("QQ Buddy"), msg, NULL); - g_free(msg); -} - -void qq_process_add_buddy_no_auth(PurpleConnection *gc, - guint8 *data, gint data_len, UID uid) -{ - qq_data *qd; - gchar **segments; - gchar *dest_uid, *reply; - PurpleBuddy *buddy; - qq_buddy_data *bd; - - g_return_if_fail(data != NULL && data_len != 0); - g_return_if_fail(uid != 0); - - qd = (qq_data *) gc->proto_data; - - purple_debug_info("QQ", "Process buddy add for id [%u]\n", uid); - qq_show_packet("buddy_add_no_auth", data, data_len); - - if (NULL == (segments = split_data(data, data_len, "\x1f", 2))) - return; - - dest_uid = segments[0]; - reply = segments[1]; - if (strtoul(dest_uid, NULL, 10) != qd->uid) { /* should not happen */ - purple_debug_error("QQ", "Add buddy reply is to [%s], not me!\n", dest_uid); - g_strfreev(segments); - return; - } - - if (strtol(reply, NULL, 10) == 0) { - /* add OK */ - qq_buddy_find_or_new(gc, uid); - - qq_request_buddy_info(gc, uid, 0, 0); - if (qd->client_version >= 2007) { - qq_request_get_level_2007(gc, uid); - } else { - qq_request_get_level(gc, uid); - } - qq_request_get_buddies_online(gc, 0, 0); - - purple_debug_info("QQ", "Successed adding into %u's buddy list\n", uid); - g_strfreev(segments); - return; - } - - /* need auth */ - purple_debug_warning("QQ", "Failed adding buddy, need authorize\n"); - - buddy = qq_buddy_find(gc, uid); - if (buddy == NULL) { - buddy = qq_buddy_new(gc, uid); - } - if (buddy != NULL && (bd = purple_buddy_get_protocol_data(buddy)) != NULL) { - /* Not authorized now, free buddy data */ - qq_buddy_data_free(bd); - purple_buddy_set_protocol_data(buddy, NULL); - } - - add_buddy_authorize_input(gc, uid, NULL, 0); - g_strfreev(segments); -} - -void qq_process_add_buddy_no_auth_ex(PurpleConnection *gc, - guint8 *data, gint data_len, UID uid) -{ - qq_data *qd; - gint bytes; - UID dest_uid; - guint8 reply; - guint8 auth_type; - - g_return_if_fail(data != NULL && data_len >= 5); - g_return_if_fail(uid != 0); - - qd = (qq_data *) gc->proto_data; - - purple_debug_info("QQ", "Process buddy add no auth for id [%u]\n", uid); - qq_show_packet("buddy_add_no_auth_ex", data, data_len); - - bytes = 0; - bytes += qq_get32(&dest_uid, data + bytes); - bytes += qq_get8(&reply, data + bytes); - - g_return_if_fail(dest_uid == uid); - - if (reply == 0x99) { - purple_debug_info("QQ", "Successfully added buddy %u\n", uid); - qq_buddy_find_or_new(gc, uid); - - qq_request_buddy_info(gc, uid, 0, 0); - if (qd->client_version >= 2007) { - qq_request_get_level_2007(gc, uid); - } else { - qq_request_get_level(gc, uid); - } - qq_request_get_buddies_online(gc, 0, 0); - return; - } - - if (reply != 0) { - purple_debug_info("QQ", "Failed adding buddy %u, Unknown reply 0x%02X\n", - uid, reply); - } - - /* need auth */ - g_return_if_fail(data_len > bytes); - bytes += qq_get8(&auth_type, data + bytes); - purple_debug_warning("QQ", "Adding buddy needs authorize 0x%02X\n", auth_type); - - switch (auth_type) { - case 0x00: /* no authorize */ - break; - case 0x01: /* authorize */ - qq_request_auth_code(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_ADD_BUDDY, uid); - break; - case 0x02: /* disable */ - break; - case 0x03: /* answer question */ - qq_request_question(gc, QQ_QUESTION_REQUEST, uid, NULL, NULL); - break; - default: - g_return_if_reached(); - break; - } - return; -} - -/* remove a buddy and send packet to QQ server accordingly */ -void qq_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - qq_data *qd; - qq_buddy_data *bd; - UID uid; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - g_return_if_fail(buddy != NULL); - - qd = (qq_data *) gc->proto_data; - if (!qd->is_login) - return; - - uid = purple_name_to_uid(purple_buddy_get_name(buddy)); - if (uid > 0 && uid != qd->uid) { - if (qd->client_version > 2005) { - qq_request_auth_code(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_REMOVE_BUDDY, uid); - } else { - request_remove_buddy(gc, uid); - request_buddy_remove_me(gc, uid); - } - } - - if ((bd = purple_buddy_get_protocol_data(buddy)) != NULL) { - qq_buddy_data_free(bd); - purple_buddy_set_protocol_data(buddy, NULL); - } else { - purple_debug_warning("QQ", "Empty buddy data of %s\n", purple_buddy_get_name(buddy)); - } - - /* Do not call purple_blist_remove_buddy, - * otherwise purple segmentation fault */ -} - -static void buddy_add_input(PurpleConnection *gc, UID uid, gchar *reason) -{ - PurpleAccount *account = purple_connection_get_account(gc); - qq_buddy_req *add_req; - gchar *who; - - g_return_if_fail(uid != 0 && reason != NULL); - - purple_debug_info("QQ", "Buddy %u request adding, msg: %s\n", uid, reason); - - add_req = g_new0(qq_buddy_req, 1); - add_req->gc = gc; - add_req->uid = uid; - - if (purple_prefs_get_bool("/plugins/prpl/qq/auto_get_authorize_info")) { - qq_request_buddy_info(gc, add_req->uid, 0, QQ_BUDDY_INFO_DISPLAY); - } - who = uid_to_purple_name(add_req->uid); - - purple_account_request_authorization(account, - who, NULL, - NULL, reason, - purple_find_buddy(account, who) != NULL, - buddy_add_authorize_cb, - buddy_add_deny_cb, - add_req); - - g_free(who); -} - -/* someone wants to add you to his buddy list */ -static void server_buddy_add_request(PurpleConnection *gc, gchar *from, gchar *to, - guint8 *data, gint data_len) -{ - UID uid; - gchar *msg, *reason; - - g_return_if_fail(from != NULL && to != NULL); - uid = strtoul(from, NULL, 10); - g_return_if_fail(uid != 0); - - if (purple_prefs_get_bool("/plugins/prpl/qq/auto_get_authorize_info")) { - qq_request_buddy_info(gc, uid, 0, QQ_BUDDY_INFO_DISPLAY); - } - - if (data_len <= 0) { - reason = g_strdup( _("No reason given") ); - } else { - msg = g_strndup((gchar *)data, data_len); - reason = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); - if (reason == NULL) reason = g_strdup( _("Unknown reason") ); - g_free(msg); - } - - buddy_add_input(gc, uid, reason); - g_free(reason); -} - -void qq_process_buddy_check_code(PurpleConnection *gc, guint8 *data, gint data_len) -{ - gint bytes; - guint8 cmd; - guint8 reply; - UID uid; - guint16 flag1, flag2; - - g_return_if_fail(data != NULL && data_len >= 5); - - qq_show_packet("buddy_check_code", data, data_len); - - bytes = 0; - bytes += qq_get8(&cmd, data + bytes); /* 0x03 */ - bytes += qq_get8(&reply, data + bytes); - - if (reply == 0) { - purple_debug_info("QQ", "Failed checking code\n"); - return; - } - - bytes += qq_get32(&uid, data + bytes); - g_return_if_fail(uid != 0); - bytes += qq_get16(&flag1, data + bytes); - bytes += qq_get16(&flag2, data + bytes); - purple_debug_info("QQ", "Check code reply Ok, uid %u, flag 0x%04X-0x%04X\n", - uid, flag1, flag2); - return; -} - -static void request_buddy_check_code(PurpleConnection *gc, - gchar *from, guint8 *code, gint code_len) -{ - guint8 *raw_data; - gint bytes; - UID uid; - - g_return_if_fail(code != NULL && code_len > 0 && from != NULL); - - uid = strtoul(from, NULL, 10); - raw_data = g_newa(guint8, code_len + 16); - bytes = 0; - bytes += qq_put8(raw_data + bytes, 0x03); - bytes += qq_put8(raw_data + bytes, 0x01); - bytes += qq_put32(raw_data + bytes, uid); - bytes += qq_put16(raw_data + bytes, code_len); - bytes += qq_putdata(raw_data + bytes, code, code_len); - - qq_send_cmd(gc, QQ_CMD_BUDDY_CHECK_CODE, raw_data, bytes); -} - -static gint server_buddy_check_code(PurpleConnection *gc, - gchar *from, guint8 *data, gint data_len) -{ - gint bytes; - guint16 code_len; - guint8 *code; - - g_return_val_if_fail(data != NULL && data_len > 0, 0); - - bytes = 0; - bytes += qq_get16(&code_len, data + bytes); - if (code_len <= 0) { - purple_debug_info("QQ", "Server msg for buddy has no code\n"); - return bytes; - } - if (bytes + code_len < data_len) { - purple_debug_error("QQ", "Code len error in server msg for buddy\n"); - qq_show_packet("server_buddy_check_code", data, data_len); - code_len = data_len - bytes; - } - code = g_newa(guint8, code_len); - bytes += qq_getdata(code, code_len, data + bytes); - - request_buddy_check_code(gc, from, code, code_len); - return bytes; -} - -static void server_buddy_add_request_ex(PurpleConnection *gc, gchar *from, gchar *to, - guint8 *data, gint data_len) -{ - gint bytes; - UID uid; - gchar *msg; - guint8 allow_reverse; - - g_return_if_fail(from != NULL && to != NULL); - g_return_if_fail(data != NULL && data_len >= 3); - uid = strtoul(from, NULL, 10); - g_return_if_fail(uid != 0); - - /* qq_show_packet("server_buddy_add_request_ex", data, data_len); */ - - bytes = 0; - bytes += qq_get_vstr(&msg, QQ_CHARSET_DEFAULT, data+bytes); - bytes += qq_get8(&allow_reverse, data + bytes); /* allow_reverse = 0x01, allowed */ - server_buddy_check_code(gc, from, data + bytes, data_len - bytes); - - if (strlen(msg) <= 0) { - g_free(msg); - msg = g_strdup( _("No reason given") ); - } - buddy_add_input(gc, uid, msg); - g_free(msg); -} - -/* when you are added by a person, QQ server will send sys message */ -static void server_buddy_added(PurpleConnection *gc, gchar *from, gchar *to, - guint8 *data, gint data_len) -{ - PurpleAccount *account = purple_connection_get_account(gc); - PurpleBuddy *buddy; - UID uid; - qq_buddy_req *add_req; - gchar *who; - gchar *primary; - - g_return_if_fail(from != NULL && to != NULL); - - uid = strtoul(from, NULL, 10); - who = uid_to_purple_name(uid); - - buddy = purple_find_buddy(account, who); - if (buddy != NULL) { - purple_account_notify_added(account, from, to, NULL, NULL); - } - - add_req = g_new0(qq_buddy_req, 1); - add_req->gc = gc; - add_req->uid = uid; /* only need to get value */ - primary = g_strdup_printf(_("You have been added by %s"), from); - purple_request_action(gc, NULL, primary, - _("Would you like to add him?"), - PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(gc), who, NULL, - add_req, 2, - _("Add"), G_CALLBACK(add_buddy_no_auth_cb), - _("Cancel"), G_CALLBACK(buddy_req_cancel_cb)); - - g_free(who); - g_free(primary); -} - -static void server_buddy_added_ex(PurpleConnection *gc, gchar *from, gchar *to, - guint8 *data, gint data_len) -{ - gint bytes; - guint8 allow_reverse; - gchar *msg; - - g_return_if_fail(from != NULL && to != NULL); - g_return_if_fail(data != NULL && data_len >= 3); - - qq_show_packet("server_buddy_added_ex", data, data_len); - - bytes = 0; - bytes += qq_get_vstr(&msg, QQ_CHARSET_DEFAULT, data+bytes); /* always empty msg */ - purple_debug_info("QQ", "Buddy added msg: %s\n", msg); - bytes += qq_get8(&allow_reverse, data + bytes); /* allow_reverse = 0x01, allowed */ - server_buddy_check_code(gc, from, data + bytes, data_len - bytes); - - g_free(msg); -} - -static void server_buddy_adding_ex(PurpleConnection *gc, gchar *from, gchar *to, - guint8 *data, gint data_len) -{ - gint bytes; - guint8 allow_reverse; - - g_return_if_fail(from != NULL && to != NULL); - g_return_if_fail(data != NULL && data_len >= 3); - - qq_show_packet("server_buddy_adding_ex", data, data_len); - - bytes = 0; - bytes += qq_get8(&allow_reverse, data + bytes); /* allow_reverse = 0x01, allowed */ - server_buddy_check_code(gc, from, data + bytes, data_len - bytes); -} - -/* the buddy approves your request of adding him/her as your friend */ -static void server_buddy_added_me(PurpleConnection *gc, gchar *from, gchar *to, - guint8 *data, gint data_len) -{ - PurpleAccount *account = purple_connection_get_account(gc); - qq_data *qd; - UID uid; - - g_return_if_fail(from != NULL && to != NULL); - - qd = (qq_data *) gc->proto_data; - - uid = strtoul(from, NULL, 10); - g_return_if_fail(uid > 0); - - server_buddy_check_code(gc, from, data, data_len); - - qq_buddy_find_or_new(gc, uid); - qq_request_buddy_info(gc, uid, 0, 0); - qq_request_get_buddies_online(gc, 0, 0); - if (qd->client_version >= 2007) { - qq_request_get_level_2007(gc, uid); - } else { - qq_request_get_level(gc, uid); - } - - purple_account_notify_added(account, to, from, NULL, NULL); -} - -/* you are rejected by the person */ -static void server_buddy_rejected_me(PurpleConnection *gc, gchar *from, gchar *to, - guint8 *data, gint data_len) -{ - UID uid; - PurpleBuddy *buddy; - gchar *msg, *msg_utf8; - gint bytes; - gchar **segments; - gchar *primary, *secondary; - qq_buddy_data *bd; - - g_return_if_fail(from != NULL && to != NULL); - - qq_show_packet("server_buddy_rejected_me", data, data_len); - - if (data_len <= 0) { - msg = g_strdup( _("No reason given") ); - } else { - segments = g_strsplit((gchar *)data, "\x1f", 1); - if (segments != NULL && segments[0] != NULL) { - msg = g_strdup(segments[0]); - g_strfreev(segments); - bytes = strlen(msg) + 1; - if (bytes < data_len) { - server_buddy_check_code(gc, from, data + bytes, data_len - bytes); - } - } else { - msg = g_strdup( _("No reason given") ); - } - } - msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); - if (msg_utf8 == NULL) { - msg_utf8 = g_strdup( _("Unknown reason") ); - } - g_free(msg); - - primary = g_strdup_printf(_("Rejected by %s"), from); - secondary = g_strdup_printf(_("Message: %s"), msg_utf8); - - purple_notify_info(gc, _("QQ Buddy"), primary, secondary); - - g_free(msg_utf8); - g_free(primary); - g_free(secondary); - - uid = strtoul(from, NULL, 10); - g_return_if_fail(uid != 0); - - buddy = qq_buddy_find(gc, uid); - if (buddy != NULL && (bd = purple_buddy_get_protocol_data(buddy)) != NULL) { - /* Not authorized now, free buddy data */ - qq_buddy_data_free(bd); - purple_buddy_set_protocol_data(buddy, NULL); - } -} - -void qq_process_buddy_from_server(PurpleConnection *gc, int funct, - gchar *from, gchar *to, guint8 *data, gint data_len) -{ - switch (funct) { - case QQ_SERVER_BUDDY_ADDED: - server_buddy_added(gc, from, to, data, data_len); - break; - case QQ_SERVER_BUDDY_ADD_REQUEST: - server_buddy_add_request(gc, from, to, data, data_len); - break; - case QQ_SERVER_BUDDY_ADD_REQUEST_EX: - server_buddy_add_request_ex(gc, from, to, data, data_len); - break; - case QQ_SERVER_BUDDY_ADDED_ME: - server_buddy_added_me(gc, from, to, data, data_len); - break; - case QQ_SERVER_BUDDY_REJECTED_ME: - server_buddy_rejected_me(gc, from, to, data, data_len); - break; - case QQ_SERVER_BUDDY_ADDED_EX: - server_buddy_added_ex(gc, from, to, data, data_len); - break; - case QQ_SERVER_BUDDY_ADDING_EX: - case QQ_SERVER_BUDDY_ADDED_ANSWER: - server_buddy_adding_ex(gc, from, to, data, data_len); - break; - default: - purple_debug_warning("QQ", "Unknow buddy operate (%d) from server\n", funct); - break; - } -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/buddy_opt.h --- a/libpurple/protocols/qq/buddy_opt.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/** - * @file buddy_opt.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_BUDDY_OPT_H_ -#define _QQ_BUDDY_OPT_H_ - -#include -#include "connection.h" - -#include "qq.h" - -enum { - QQ_AUTH_INFO_BUDDY = 0x01, - QQ_AUTH_INFO_ROOM = 0x02, - - QQ_AUTH_INFO_ADD_BUDDY = 0x0001, - QQ_AUTH_INFO_TEMP_SESSION = 0x0003, - QQ_AUTH_INFO_CLUSTER = 0x0002, - QQ_AUTH_INFO_REMOVE_BUDDY = 0x0006, - QQ_AUTH_INFO_UPDATE_BUDDY_INFO = 0x0007 -}; - -enum { - QQ_QUESTION_GET = 0x01, - QQ_QUESTION_SET = 0x02, - QQ_QUESTION_REQUEST = 0x03, /* get question only*/ - QQ_QUESTION_ANSWER = 0x04 -}; - -void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); -void qq_change_buddys_group(PurpleConnection *gc, const char *who, - const char *old_group, const char *new_group); -void qq_remove_buddy_and_me(PurpleBlistNode * node); -void qq_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); - -void qq_process_remove_buddy(PurpleConnection *gc, guint8 *data, gint data_len, UID uid); -void qq_process_buddy_remove_me(PurpleConnection *gc, guint8 *data, gint data_len, UID uid); -void qq_process_add_buddy_no_auth(PurpleConnection *gc, - guint8 *data, gint data_len, UID uid); -void qq_process_add_buddy_no_auth_ex(PurpleConnection *gc, - guint8 *data, gint data_len, UID uid); -void qq_process_add_buddy_auth(guint8 *data, gint data_len, PurpleConnection *gc); -void qq_process_buddy_from_server(PurpleConnection *gc, int funct, - gchar *from, gchar *to, guint8 *data, gint data_len); - -void qq_process_buddy_check_code(PurpleConnection *gc, guint8 *data, gint data_len); - -void qq_request_auth_code(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, UID uid); -void qq_process_auth_code(PurpleConnection *gc, guint8 *data, gint data_len, UID uid); -void qq_request_question(PurpleConnection *gc, - guint8 cmd, UID uid, const gchar *question_utf8, const gchar *answer_utf8); -void qq_process_question(PurpleConnection *gc, guint8 *data, gint data_len, UID uid); - -void qq_process_add_buddy_auth_ex(PurpleConnection *gc, guint8 *data, gint data_len, guint32 ship32); - -qq_buddy_data *qq_buddy_data_find(PurpleConnection *gc, UID uid); -void qq_buddy_data_free(qq_buddy_data *bd); - -PurpleBuddy *qq_buddy_new(PurpleConnection *gc, UID uid); -PurpleBuddy *qq_buddy_find_or_new(PurpleConnection *gc, UID uid); -PurpleBuddy *qq_buddy_find(PurpleConnection *gc, UID uid); -PurpleGroup *qq_group_find_or_new(const gchar *group_name); -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/char_conv.c --- a/libpurple/protocols/qq/char_conv.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/** - * @file char_conv.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 "debug.h" - -#include "char_conv.h" -#include "packet_parse.h" -#include "utils.h" - -#define UTF8 "UTF-8" -#define QQ_CHARSET_ZH_CN "GB18030" -#define QQ_CHARSET_ENG "ISO-8859-1" - -#define QQ_NULL_MSG "(NULL)" /* return this if conversion fails */ - -/* convert a string from from_charset to to_charset, using g_convert */ -/* Warning: do not return NULL */ -static gchar *do_convert(const gchar *str, gssize len, guint8 *out_len, const gchar *to_charset, const gchar *from_charset) -{ - GError *error = NULL; - gchar *ret; - gsize byte_read, byte_write; - - g_return_val_if_fail(str != NULL && to_charset != NULL && from_charset != NULL, g_strdup(QQ_NULL_MSG)); - - ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error); - - if (error == NULL) { - if (out_len) - *out_len = byte_write; - return ret; /* convert is OK */ - } - - /* convert error */ - purple_debug_error("QQ_CONVERT", "%s\n", error->message); - qq_show_packet("Dump failed text", (guint8 *) str, (len == -1) ? strlen(str) : len); - - 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 - * returns the number of bytes read, return -1 if fatal error - * the converted UTF-8 will be saved in ret - * Return: *ret != NULL - */ -gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data) -{ - gssize len; - guint8 out_len; - - g_return_val_if_fail(data != NULL && from_charset != NULL, -1); - - len = data[0]; - if (len == 0) { - *ret = g_strdup(""); - return 1; - } - *ret = do_convert((gchar *) (data + 1), len, &out_len, UTF8, from_charset); - - return out_len + 1; -} - -gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset) -{ - gchar *str; - guint8 len; - - if (str_utf8 == NULL || str_utf8[0] == '\0') { - buf[0] = 0; - return 1; - } - str = do_convert(str_utf8, -1, &len, to_charset, UTF8); - buf[0] = len; - if (len > 0) { - memcpy(buf + 1, str, len); - } - return 1 + len; -} - -/* Warning: do not return NULL */ -gchar *utf8_to_qq(const gchar *str, const gchar *to_charset) -{ - return do_convert(str, -1, NULL, to_charset, UTF8); -} - -/* Warning: do not return NULL */ -gchar *qq_to_utf8(const gchar *str, const gchar *from_charset) -{ - return do_convert(str, -1, NULL, UTF8, from_charset); -} - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/char_conv.h --- a/libpurple/protocols/qq/char_conv.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/** - * @file char_conv.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_CHAR_CONV_H_ -#define _QQ_CHAR_CONV_H_ - -#include - -#define QQ_CHARSET_DEFAULT "GB18030" - -gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data); -gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset); - -gchar *utf8_to_qq(const gchar *str, const gchar *to_charset); -gchar *qq_to_utf8(const gchar *str, const gchar *from_charset); - -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/file_trans.c --- a/libpurple/protocols/qq/file_trans.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,817 +0,0 @@ -/** - * @file file_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 "debug.h" -#include "ft.h" -#include "cipher.h" - -#include "qq_crypt.h" -#include "file_trans.h" -#include "qq_define.h" -#include "im.h" -#include "packet_parse.h" -#include "proxy.h" -#include "qq_network.h" -#include "send_file.h" -#include "utils.h" - -typedef struct _qq_file_header { - guint16 client_ver; - guint8 file_key; - UID sender_uid; - UID receiver_uid; -} qq_file_header; - -static guint32 _get_file_key(guint8 seed) -{ - guint32 key; - key = seed | (seed << 8) | (seed << 16) | (seed << 24); - return key; -} - -static guint32 _gen_file_key(void) -{ - guint8 seed; - - seed = rand() & 0xFF; - return _get_file_key(seed); -} - -static guint32 _decrypt_qq_uid(UID uid, guint32 key) -{ - return ~(uid ^ key); -} - -static guint32 _encrypt_qq_uid(UID uid, guint32 key) -{ - return (~uid) ^ key; -} - -static void _fill_file_md5(const gchar *filename, gint filelen, guint8 *md5) -{ - FILE *fp; - guint8 *buffer; - size_t wc; - - const gint QQ_MAX_FILE_MD5_LENGTH = 10002432; - - g_return_if_fail(filename != NULL && md5 != NULL); - if (filelen > QQ_MAX_FILE_MD5_LENGTH) - filelen = QQ_MAX_FILE_MD5_LENGTH; - - fp = g_fopen(filename, "rb"); - g_return_if_fail(fp != NULL); - - buffer = g_newa(guint8, filelen); - g_return_if_fail(buffer != NULL); - wc = fread(buffer, filelen, 1, fp); - fclose(fp); - if (wc != 1) { - purple_debug_error("qq", "Unable to read file: %s\n", filename); - - /* TODO: XXX: Really, the caller should be modified to deal with this properly. */ - return; - } - - qq_get_md5(md5, QQ_KEY_LENGTH, buffer, filelen); -} - -static gint _qq_get_file_header(qq_file_header *fh, guint8 *buf) -{ - 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) -{ - switch (type) { - case QQ_FILE_CMD_SENDER_SAY_HELLO: - return "QQ_FILE_CMD_SENDER_SAY_HELLO"; - case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: - return "QQ_FILE_CMD_SENDER_SAY_HELLO_ACK"; - case QQ_FILE_CMD_RECEIVER_SAY_HELLO: - return "QQ_FILE_CMD_RECEIVER_SAY_HELLO"; - case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: - return "QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK"; - case QQ_FILE_CMD_NOTIFY_IP_ACK: - return "QQ_FILE_CMD_NOTIFY_IP_ACK"; - case QQ_FILE_CMD_PING: - return "QQ_FILE_CMD_PING"; - case QQ_FILE_CMD_PONG: - return "QQ_FILE_CMD_PONG"; - case QQ_FILE_CMD_INITATIVE_CONNECT: - return "QQ_FILE_CMD_INITATIVE_CONNECT"; - case QQ_FILE_CMD_FILE_OP: - return "QQ_FILE_CMD_FILE_OP"; - case QQ_FILE_CMD_FILE_OP_ACK: - return "QQ_FILE_CMD_FILE_OP_ACK"; - case QQ_FILE_BASIC_INFO: - return "QQ_FILE_BASIC_INFO"; - case QQ_FILE_DATA_INFO: - return "QQ_FILE_DATA_INFO"; - case QQ_FILE_EOF: - return "QQ_FILE_EOF"; - default: - return "UNKNOWN_TYPE"; - } -} - -/* The memmap version has better performance for big files transfering - * but it will spend plenty of memory, so do not use it in a low-memory host */ -#ifdef USE_MMAP -#include - -static int _qq_xfer_open_file(const gchar *filename, const gchar *method, PurpleXfer *xfer) -{ - ft_info *info = xfer->data; - int fd; - if (method[0] == 'r') { - fd = open(purple_xfer_get_local_filename(xfer), O_RDONLY); - info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ, MAP_PRIVATE, fd, 0); - } - else - { - 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; - } - return 0; -} - -static gint _qq_xfer_read_file(guint8 *buffer, guint index, guint len, PurpleXfer *xfer) -{ - ft_info *info = xfer->data; - gint readbytes; - - buffer = info->buffer + len * index; - readbytes = purple_xfer_get_size(xfer) - (buffer - info->buffer); - if (readbytes > info->fragment_len) readbytes = info->fragment_len; - return readbytes; -} - -static gint _qq_xfer_write_file(guint8 *buffer, guint index, guint len, PurpleXfer *xfer) -{ - ft_info *info = xfer->data; - - memcpy(info->buffer + index * len, buffer, len); - return 0; -} - -void qq_xfer_close_file(PurpleXfer *xfer) -{ - ft_info *info = xfer->data; - - if (info->buffer) munmap(info->buffer, purple_xfer_get_size(xfer)); -} -#else -static int _qq_xfer_open_file(const gchar *filename, const gchar *method, PurpleXfer *xfer) -{ - ft_info *info = xfer->data; - info->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer), method); - if (info->dest_fp == NULL) { - return -1; - } - return 0; -} - -static gint _qq_xfer_read_file(guint8 *buffer, guint index, guint len, PurpleXfer *xfer) -{ - ft_info *info = xfer->data; - - fseek(info->dest_fp, index * len, SEEK_SET); - return fread(buffer, 1, len, info->dest_fp); -} - -static gint _qq_xfer_write_file(guint8 *buffer, guint index, guint len, PurpleXfer *xfer) -{ - ft_info *info = xfer->data; - fseek(info->dest_fp, index * len, SEEK_SET); - return fwrite(buffer, 1, len, info->dest_fp); -} - -void qq_xfer_close_file(PurpleXfer *xfer) -{ - ft_info *info = xfer->data; - - if (info->dest_fp) fclose(info->dest_fp); -} -#endif - -static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16 packet_type, UID to_uid) -{ - guint8 *raw_data; - gint bytes = 0; - guint32 file_key; - qq_data *qd; - - qd = (qq_data *) gc->proto_data; - - raw_data = g_newa(guint8, MAX_PACKET_SIZE); - file_key = _gen_file_key(); - - bytes += qq_put8(raw_data + bytes, packet_type); - bytes += qq_put16(raw_data + bytes, qd->client_tag); - 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(raw_data, bytes, qd->xfer); - } else - purple_debug_info("QQ", "send_file: want %d but got %d\n", len + 12, bytes); - return bytes; -} - -/* send a file to udp channel with QQ_FILE_CONTROL_PACKET_TAG */ -void qq_send_file_ctl_packet(PurpleConnection *gc, guint16 packet_type, UID to_uid, guint8 hellobyte) -{ - qq_data *qd; - gint bytes, bytes_expected, encrypted_len; - guint8 *raw_data, *encrypted; - time_t now; - ft_info *info; - - qd = (qq_data *) gc->proto_data; - info = (ft_info *) qd->xfer->data; - - raw_data = g_newa (guint8, 61); - bytes = 0; - - now = time(NULL); - - 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 += qq_put16(raw_data + bytes, info->send_seq); - break; - default: - bytes += qq_put16(raw_data + bytes, ++qd->send_seq); - } - 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 += 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 += 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, info); - bytes_expected = 61; - break; - default: - purple_debug_info("QQ", "qq_send_file_ctl_packet: Unknown packet type[%d]\n", - packet_type); - bytes_expected = 0; - } - - if (bytes != bytes_expected) { - purple_debug_error("QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d\n", - 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 = g_newa(guint8, bytes + 17); - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, info->file_session_key); - /*debug: try to decrypt it */ - -#if 0 - guint8 *buf; - int buflen; - hex_dump = hex_dump_to_str(encrypted, encrypted_len); - purple_debug_info("QQ", "encrypted packet: \n%s\n", hex_dump); - g_free(hex_dump); - buf = g_newa(guint8, MAX_PACKET_SIZE); - buflen = encrypted_len; - if (qq_crypt(DECRYPT, encrypted, encrypted_len, info->file_session_key, buf, &buflen)) { - purple_debug_info("QQ", "decrypt success\n"); - if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0) - purple_debug_info("QQ", "checksum ok\n"); - - hex_dump = hex_dump_to_str(buf, buflen); - purple_debug_info("QQ", "decrypted packet: \n%s\n", hex_dump); - g_free(hex_dump); - } else { - purple_debug_info("QQ", "decrypt fail\n"); - } -#endif - - purple_debug_info("QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type)); - _qq_send_file(gc, encrypted, 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; - guint32 fragment_size = 1000; - const char *filename; - gint filename_len, filesize; - qq_data *qd; - ft_info *info; - - qd = (qq_data *) gc->proto_data; - info = (ft_info *) qd->xfer->data; - - filename = purple_xfer_get_filename(qd->xfer); - filesize = purple_xfer_get_size(qd->xfer); - - raw_data = g_newa(guint8, MAX_PACKET_SIZE); - bytes = 0; - - 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 += qq_put16(raw_data + bytes, 0x0000); - bytes += qq_put8(raw_data + bytes, 0x00); - break; - case QQ_FILE_CMD_FILE_OP: - switch(sub_type) - { - case QQ_FILE_BASIC_INFO: - filename_len = strlen(filename); - qq_get_md5(filename_md5, sizeof(filename_md5), (guint8 *)filename, filename_len); - _fill_file_md5(purple_xfer_get_local_filename(qd->xfer), - purple_xfer_get_size(qd->xfer), - file_md5); - - info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1; - info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN; - - purple_debug_info("QQ", - "start transfering data, %d fragments with %d length each\n", - info->fragment_num, info->fragment_len); - /* Unknown */ - bytes += qq_put16(raw_data + bytes, 0x0000); - /* Sub-operation type */ - bytes += qq_put8(raw_data + bytes, sub_type); - /* Length of file */ - bytes += qq_put32(raw_data + bytes, filesize); - /* Number of fragments */ - bytes += qq_put32(raw_data + bytes, info->fragment_num); - /* Length of a single fragment */ - 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 += qq_put16(raw_data + bytes, filename_len); - /* 8 unknown bytes */ - bytes += qq_put32(raw_data + bytes, 0x00000000); - bytes += qq_put32(raw_data + bytes, 0x00000000); - /* filename */ - bytes += qq_putdata(raw_data + bytes, (guint8 *) filename, - filename_len); - break; - case QQ_FILE_DATA_INFO: - purple_debug_info("QQ", - "sending %dth fragment with length %d, offset %d\n", - fragment_index, len, (fragment_index-1)*fragment_size); - /* 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_info("QQ", "end of sending data\n"); - /* 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; - case QQ_FILE_CMD_FILE_OP_ACK: - switch (sub_type) - { - case QQ_FILE_BASIC_INFO: - 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 += 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 += qq_put16(raw_data + bytes, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2); - bytes += qq_put8(raw_data + bytes, sub_type); - break; - } - } - purple_debug_info("QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type)); - _qq_send_file(gc, raw_data, bytes, QQ_FILE_DATA_PACKET_TAG, info->to_uid); -} - -/* A conversation starts like this: - * Sender ==> Receiver [QQ_FILE_CMD_PING] - * Sender <== Receiver [QQ_FILE_CMD_PONG] - * Sender ==> Receiver [QQ_FILE_CMD_SENDER_SAY_HELLO] - * Sender <== Receiver [QQ_FILE_CMD_SENDER_SAY_HELLO_ACK] - * Sender <== Receiver [QQ_FILE_CMD_RECEIVER_SAY_HELLO] - * Sender ==> Receiver [QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK] - * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO] - * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_BASIC_INFO] - * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO] - * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_DATA_INFO] - * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO] - * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_DATA_INFO] - * ...... - * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF] - * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_EOF] - */ - - -static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint data_len) -{ - gint bytes ; - gint decryped_bytes; - qq_file_header fh; - guint8 *decrypted_data; - gint decrypted_len; - qq_data *qd = (qq_data *) gc->proto_data; - guint16 packet_type; - guint16 seq; - 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, data_len); - decrypted_len = qq_decrypt(decrypted_data, data, data_len, qd->session_md5); - if ( decrypted_len <= 0 ) { - 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_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:"); - - 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_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) -{ - qq_data *qd = (qq_data *) gc->proto_data; - PurpleXfer *xfer = qd->xfer; - ft_info *info = (ft_info *) xfer->data; - guint32 mask; - - purple_debug_info("QQ", - "receiving %dth fragment with length %d, slide window status %o, max_fragment_index %d\n", - index, len, info->window, info->max_fragment_index); - if (info->window == 0 && info->max_fragment_index == 0) { - if (_qq_xfer_open_file(purple_xfer_get_local_filename(xfer), "wb", xfer) == -1) { - purple_xfer_cancel_local(xfer); - return; - } - purple_debug_info("QQ", "object file opened for writing\n"); - } - mask = 0x1 << (index % sizeof(info->window)); - if (index < info->max_fragment_index || (info->window & mask)) { - 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) - { - info->window &= ~mask; - info->max_fragment_index ++; - if (mask & 0x8000) mask = 0x0001; - else mask = mask << 1; - } - purple_debug_info("QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n", - index, info->window, info->max_fragment_index); -} - -static void _qq_send_file_progess(PurpleConnection *gc) -{ - qq_data *qd = (qq_data *) gc->proto_data; - PurpleXfer *xfer = qd->xfer; - ft_info *info = (ft_info *) xfer->data; - guint32 mask; - guint8 *buffer; - guint i; - gint readbytes; - - if (purple_xfer_get_bytes_remaining(xfer) <= 0) return; - if (info->window == 0 && info->max_fragment_index == 0) - { - if (_qq_xfer_open_file(purple_xfer_get_local_filename(xfer), "rb", xfer) == -1) { - purple_xfer_cancel_local(xfer); - return; - } - } - buffer = g_newa(guint8, info->fragment_len); - mask = 0x1 << (info->max_fragment_index % sizeof(info->window)); - for (i = 0; i < sizeof(info->window); i++) { - if ((info->window & mask) == 0) { - 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); - } - if (mask & 0x8000) mask = 0x0001; - else mask = mask << 1; - } -} - -static void _qq_update_send_progess(PurpleConnection *gc, guint32 fragment_index) -{ - guint32 mask; - guint8 *buffer; - gint readbytes; - qq_data *qd = (qq_data *) gc->proto_data; - PurpleXfer *xfer = qd->xfer; - ft_info *info = (ft_info *) xfer->data; - - purple_debug_info("QQ", - "receiving %dth fragment ack, slide window status %o, max_fragment_index %d\n", - fragment_index, info->window, info->max_fragment_index); - if (fragment_index < info->max_fragment_index || - fragment_index >= info->max_fragment_index + sizeof(info->window)) { - purple_debug_info("QQ", "duplicate %dth fragment, drop it!\n", fragment_index+1); - return; - } - mask = 0x1 << (fragment_index % sizeof(info->window)); - if ((info->window & mask) == 0) - { - info->window |= mask; - if (fragment_index + 1 != info->fragment_num) { - xfer->bytes_sent += info->fragment_len; - } else { - xfer->bytes_sent += purple_xfer_get_size(xfer) % info->fragment_len; - } - xfer->bytes_remaining = purple_xfer_get_size(xfer) - purple_xfer_get_bytes_sent(xfer); - purple_xfer_update_progress(xfer); - if (purple_xfer_get_bytes_remaining(xfer) <= 0) { - /* We have finished sending the file */ - purple_xfer_set_completed(xfer, TRUE); - return; - } - mask = 0x1 << (info->max_fragment_index % sizeof(info->window)); - while (info->window & mask) - { - /* move the slide window */ - info->window &= ~mask; - - buffer = g_newa(guint8, info->fragment_len); - readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + sizeof(info->window), - 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 ++; - if (mask & 0x8000) mask = 0x0001; - else mask = mask << 1; - } - } - purple_debug_info("QQ", - "procceed %dth fragment ack, slide window status %o, max_fragment_index %d\n", - fragment_index, info->window, info->max_fragment_index); -} - -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; - guint32 fragment_index; - guint16 fragment_len; - guint32 fragment_offset; - qq_data *qd = (qq_data *) gc->proto_data; - ft_info *info = (ft_info *) qd->xfer->data; - - 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: - bytes += qq_get16(&packet_seq, data + bytes); - bytes += qq_get8(&sub_type, data + bytes); - switch (sub_type) - { - case QQ_FILE_BASIC_INFO: - 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 */ - - info->max_fragment_index = 0; - info->window = 0; - purple_debug_info("QQ", - "start receiving data, %d fragments with %d length each\n", - info->fragment_num, info->fragment_len); - _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type, - 0, 0, NULL, 0); - break; - case QQ_FILE_DATA_INFO: - bytes += qq_get32(&fragment_index, data + bytes); - bytes += qq_get32(&fragment_offset, data + bytes); - bytes += qq_get16(&fragment_len, data + bytes); - 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, data + bytes, fragment_len, fragment_index, fragment_offset); - break; - case QQ_FILE_EOF: - 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); - break; - } - break; - case QQ_FILE_CMD_FILE_OP_ACK: - bytes += qq_get16(&packet_seq, data + bytes); - bytes += qq_get8(&sub_type, data + bytes); - switch (sub_type) - { - case QQ_FILE_BASIC_INFO: - info->max_fragment_index = 0; - info->window = 0; - /* It is ready to send file data */ - _qq_send_file_progess(gc); - break; - case QQ_FILE_DATA_INFO: - 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 - _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; - } - break; - case QQ_FILE_EOF: - _qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0); - purple_xfer_set_completed(qd->xfer, TRUE); - purple_xfer_end(qd->xfer); - break; - case QQ_FILE_BASIC_INFO: - purple_debug_info("QQ", "here\n"); - _qq_send_file_data_packet(gc, QQ_FILE_DATA_INFO, 0, 0, 0, NULL, 0); - break; - default: - purple_debug_info("QQ", "_qq_process_recv_file_data: unknown packet type [%d]\n", - packet_type); - break; - } -} - -void qq_process_recv_file(PurpleConnection *gc, guint8 *data, gint len) -{ - gint bytes; - guint8 tag; - - bytes = 0; - bytes += qq_get8(&tag, data + bytes); - - switch (tag) { - case QQ_FILE_CONTROL_PACKET_TAG: - _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 + bytes, len - bytes); - break; - default: - purple_debug_info("QQ", "unknown packet tag\n"); - } -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/file_trans.h --- a/libpurple/protocols/qq/file_trans.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/** - * @file file_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_QQ_FILE_TRANS_H_ -#define _QQ_QQ_FILE_TRANS_H_ - -#include "server.h" -#include "qq.h" - -enum { - QQ_FILE_CMD_SENDER_SAY_HELLO = 0x31, - QQ_FILE_CMD_SENDER_SAY_HELLO_ACK = 0x32, - QQ_FILE_CMD_RECEIVER_SAY_HELLO = 0x33, - QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK = 0x34, - QQ_FILE_CMD_NOTIFY_IP_ACK = 0x3c, - QQ_FILE_CMD_PING = 0x3d, - QQ_FILE_CMD_PONG = 0x3e, - QQ_FILE_CMD_INITATIVE_CONNECT = 0x40 -}; - -enum { - QQ_FILE_TRANSFER_FILE = 0x65, - QQ_FILE_TRANSFER_FACE = 0x6b -}; - -enum { - QQ_FILE_BASIC_INFO = 0x01, - QQ_FILE_DATA_INFO = 0x02, - QQ_FILE_EOF = 0x03, - QQ_FILE_CMD_FILE_OP = 0x07, - QQ_FILE_CMD_FILE_OP_ACK = 0x08 -}; - -#define QQ_FILE_FRAGMENT_MAXLEN 1000 - -#define QQ_FILE_CONTROL_PACKET_TAG 0x00 -/* #define QQ_PACKET_TAG 0x02 */ /* all QQ text packets starts with it */ -#define QQ_FILE_DATA_PACKET_TAG 0x03 -#define QQ_FILE_AGENT_PACKET_TAG 0x04 -/* #define QQ_PACKET_TAIL 0x03 */ /* all QQ text packets end with it */ - -void qq_send_file_ctl_packet(PurpleConnection *gc, guint16 packet_type, UID to_uid, guint8 hellobyte); -void qq_process_recv_file(PurpleConnection *gc, guint8 *data, gint len); -/* void qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type, guint32 fragment_index, guint16 seq, guint8 *data, gint len); */ -void qq_xfer_close_file(PurpleXfer *xfer); -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group.c --- a/libpurple/protocols/qq/group.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,129 +0,0 @@ -/** - * @file group.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 "debug.h" -#include "prpl.h" -#include "request.h" - -#include "group_internal.h" -#include "group_info.h" -#include "group_join.h" -#include "utils.h" -#include "qq_network.h" -#include "qq_define.h" - -static void _qq_group_search_callback(PurpleConnection *gc, const gchar *input) -{ - guint32 ext_id; - - g_return_if_fail(input != NULL); - ext_id = strtoul(input, NULL, 10); - /* 0x00000000 means search for demo group */ - qq_request_room_search(gc, ext_id, QQ_ROOM_SEARCH_ONLY); -} - -static void _qq_group_search_cancel_callback(PurpleConnection *gc, const gchar *input) -{ - qq_data *qd; - - qd = (qq_data *) gc->proto_data; - purple_roomlist_set_in_progress(qd->roomlist, FALSE); -} - -/* This is needed for PurpleChat node to be valid */ -GList *qq_chat_info(PurpleConnection *gc) -{ - GList *m; - struct proto_chat_entry *pce; - - m = NULL; - - pce = g_new0(struct proto_chat_entry, 1); - pce->label = _("ID: "); - pce->identifier = QQ_ROOM_KEY_EXTERNAL_ID; - m = g_list_append(m, pce); - - return m; -} - -GHashTable *qq_chat_info_defaults(PurpleConnection *gc, const gchar *chat_name) -{ - GHashTable *defaults; - - defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); - - if (chat_name != NULL) - g_hash_table_insert(defaults, QQ_ROOM_KEY_EXTERNAL_ID, g_strdup(chat_name)); - - return defaults; -} - -/* get a list of qq groups */ -PurpleRoomlist *qq_roomlist_get_list(PurpleConnection *gc) -{ - GList *fields; - qq_data *qd; - PurpleRoomlist *rl; - PurpleRoomlistField *f; - - qd = (qq_data *) gc->proto_data; - - fields = NULL; - rl = purple_roomlist_new(purple_connection_get_account(gc)); - qd->roomlist = rl; - - f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Group ID"), QQ_ROOM_KEY_EXTERNAL_ID, FALSE); - fields = g_list_append(fields, f); - f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_ROOM_KEY_INTERNAL_ID, TRUE); - fields = g_list_append(fields, f); - - fields = g_list_append(fields, f); - purple_roomlist_set_fields(rl, fields); - purple_roomlist_set_in_progress(qd->roomlist, TRUE); - - purple_request_input(gc, _("QQ Qun"), - _("Please enter Qun number"), - _("You can only search for permanent Qun\n"), - NULL, FALSE, FALSE, NULL, - _("Search"), G_CALLBACK(_qq_group_search_callback), - _("Cancel"), G_CALLBACK(_qq_group_search_cancel_callback), - purple_connection_get_account(gc), NULL, NULL, - gc); - - return qd->roomlist; -} - -/* free roomlist space, I have no idea when this one is called... */ -void qq_roomlist_cancel(PurpleRoomlist *list) -{ - PurpleConnection *gc; - - g_return_if_fail(list != NULL); - gc = purple_account_get_connection(list->account); - - purple_roomlist_set_in_progress(list, FALSE); - purple_roomlist_unref(list); -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group.h --- a/libpurple/protocols/qq/group.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/** - * @file group.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_GROUP_H_ -#define _QQ_GROUP_H_ - -#include -#include "account.h" -#include "connection.h" -#include "roomlist.h" -#include "qq.h" - -#define PURPLE_GROUP_QQ_QUN "QQ 群" - -typedef enum { - QQ_ROOM_ROLE_NO = 0x00, /* default 0x00 means not member */ - QQ_ROOM_ROLE_YES, - QQ_ROOM_ROLE_REQUESTING, - QQ_ROOM_ROLE_ADMIN -} qq_room_role; - -typedef struct _qq_room_data { - /* all these will be saved when we exit Purple */ - qq_room_role my_role; /* my role for this room */ - guint32 id; - guint32 ext_id; - guint8 type8; /* permanent or temporory */ - UID creator_uid; - guint32 category; - guint8 auth_type; - gchar *title_utf8; - gchar *desc_utf8; - /* all these will be loaded from the network */ - gchar *notice_utf8; /* group notice by admin */ - - gboolean is_got_buddies; - GList *members; -} qq_room_data; - -GList *qq_chat_info(PurpleConnection *gc); -GHashTable *qq_chat_info_defaults(PurpleConnection *gc, const gchar *chat_name); - -PurpleRoomlist *qq_roomlist_get_list(PurpleConnection *gc); - -void qq_roomlist_cancel(PurpleRoomlist *list); - -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group_im.c --- a/libpurple/protocols/qq/group_im.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,429 +0,0 @@ -/** - * @file group_im.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 "conversation.h" -#include "debug.h" -#include "notify.h" -#include "prefs.h" -#include "request.h" -#include "util.h" - -#include "char_conv.h" -#include "group_internal.h" -#include "group_info.h" -#include "group_join.h" -#include "group_im.h" -#include "group_opt.h" -#include "im.h" -#include "qq_define.h" -#include "packet_parse.h" -#include "qq_network.h" -#include "qq_process.h" -#include "utils.h" - -/* show group conversation window */ -PurpleConversation *qq_room_conv_open(PurpleConnection *gc, qq_room_data *rmd) -{ - PurpleConversation *conv; - gchar *topic_utf8; - - g_return_val_if_fail(rmd != NULL, NULL); - g_return_val_if_fail(rmd->title_utf8, NULL); - - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - rmd->title_utf8, purple_connection_get_account(gc)); - if (conv != NULL) { - /* show only one conversation per room */ - return conv; - } - - serv_got_joined_chat(gc, rmd->id, rmd->title_utf8); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, rmd->title_utf8, purple_connection_get_account(gc)); - if (conv != NULL) { - if (rmd->notice_utf8 != NULL) { - topic_utf8 = g_strdup_printf("%u %s", rmd->ext_id, rmd->notice_utf8); - } else { - topic_utf8 = g_strdup_printf("%u", rmd->ext_id); - } - purple_debug_info("QQ", "Chat topic = %s\n", topic_utf8); - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8); - g_free(topic_utf8); - - if (rmd->is_got_buddies) - qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_ONLINES, rmd->id); - else - qq_update_room(gc, 0, rmd->id); - return conv; - } - return NULL; -} - -/* refresh online member in group conversation window */ -void qq_room_conv_set_onlines(PurpleConnection *gc, qq_room_data *rmd) -{ - GList *names, *list, *flags; - qq_buddy_data *bd; - gchar *member_name, *member_uid; - PurpleConversation *conv; - gint flag; - gboolean is_find; - - g_return_if_fail(rmd != NULL); - - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - rmd->title_utf8, purple_connection_get_account(gc)); - if (conv == NULL) { - purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", rmd->title_utf8); - return; - } - g_return_if_fail(rmd->members != NULL); - - names = NULL; - flags = NULL; - - list = rmd->members; - while (list != NULL) { - bd = (qq_buddy_data *) list->data; - - /* we need unique identifiers for everyone in the chat or else we'll - * run into problems with functions like get_cb_real_name from qq.c */ - member_name = (bd->nickname != NULL && *(bd->nickname) != '\0') ? - g_strdup_printf("%s (%u)", bd->nickname, bd->uid) : - g_strdup_printf("(%u)", bd->uid); - member_uid = g_strdup_printf("(%u)", bd->uid); - - flag = 0; - /* TYPING to put online above OP and FOUNDER */ - if (is_online(bd->status)) flag |= (PURPLE_CBFLAGS_TYPING | PURPLE_CBFLAGS_VOICE); - if(1 == (bd->role & 1)) flag |= PURPLE_CBFLAGS_OP; - if(bd->uid == rmd->creator_uid) flag |= PURPLE_CBFLAGS_FOUNDER; - - is_find = TRUE; - if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_name)) - { - purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(conv), - member_name, - flag); - } else if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_uid)) - { - purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(conv), - member_uid, - flag); - purple_conv_chat_rename_user(PURPLE_CONV_CHAT(conv), member_uid, member_name); - } else { - is_find = FALSE; - } - if (!is_find) { - /* always put it even offline */ - names = g_list_append(names, member_name); - flags = g_list_append(flags, GINT_TO_POINTER(flag)); - } else { - g_free(member_name); - } - g_free(member_uid); - list = list->next; - } - - if (names != NULL && flags != NULL) { - purple_conv_chat_add_users(PURPLE_CONV_CHAT(conv), names, NULL, flags, FALSE); - } - - /* clean up names */ - while (names != NULL) { - member_name = (gchar *) names->data; - names = g_list_remove(names, member_name); - g_free(member_name); - } - g_list_free(flags); -} - -void qq_room_got_chat_in(PurpleConnection *gc, - guint32 room_id, UID uid_from, const gchar *msg, time_t in_time) -{ - PurpleConversation *conv; - qq_data *qd; - qq_buddy_data *bd; - qq_room_data *rmd; - gchar *from; - - g_return_if_fail(gc != NULL && room_id != 0); - g_return_if_fail(msg != NULL); - - qd = (qq_data *)gc->proto_data; - conv = purple_find_chat(gc, room_id); - rmd = qq_room_data_find(gc, room_id); - g_return_if_fail(rmd != NULL); - - purple_debug_info("QQ", "is_show_chat:%d\n", qd->is_show_chat); - if (NULL == conv && qd->is_show_chat) { - conv = qq_room_conv_open(gc, rmd); - } - - if (NULL == conv) { - purple_debug_info("QQ", "Conversion of %u is not open, missing from %d:/n%s/v\n", - room_id, uid_from, msg); - return; - } - - if (uid_from != 0) { - - bd = qq_room_buddy_find(rmd, uid_from); - if (bd == NULL || bd->nickname == NULL) - from = g_strdup_printf("%u", uid_from); - else - from = g_strdup(bd->nickname); - } else { - from = g_strdup(""); - } - serv_got_chat_in(gc, room_id, from, 0, msg, in_time); - g_free(from); -} - -/* recv an IM from a group chat */ -void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type) -{ - gchar *msg_smiley, *msg_fmt, *msg_utf8; - gint bytes, tail_len; - qq_data *qd; - struct { - guint32 ext_id; - guint8 type8; - UID member_uid; - guint16 unknown; - guint16 msg_seq; - time_t send_time; - guint32 version; - guint16 msg_len; - gchar *msg; - } im_text; - guint32 temp_id; - guint16 content_type; - guint8 frag_count, frag_index; - guint16 msg_id; - guint32 use_default_font; - qq_im_format *fmt = NULL; - qd = (qq_data *) gc->proto_data; - - /* at least include im_text.msg_len */ - g_return_if_fail(data != NULL && data_len > 23); - - use_default_font = (qd->custom) & QQ_CUSTOM_USE_DEFAULT_FONT; - - /* qq_show_packet("ROOM_IM", data, data_len); */ - memset(&im_text, 0, sizeof(im_text)); - bytes = 0; - bytes += qq_get32(&(im_text.ext_id), data + bytes); - bytes += qq_get8(&(im_text.type8), data + bytes); - - if(QQ_MSG_TEMP_QUN_IM == msg_type) { - bytes += qq_get32(&temp_id, data + bytes); - } - - bytes += qq_get32(&(im_text.member_uid), bytes + data); - bytes += qq_get16(&im_text.unknown, data + bytes); /* 0x0001? */ - bytes += qq_get16(&(im_text.msg_seq), data + bytes); - bytes += qq_getime(&im_text.send_time, data + bytes); - bytes += qq_get32(&im_text.version, data + bytes); - bytes += qq_get16(&(im_text.msg_len), data + bytes); - purple_debug_info("QQ", "Room IM, ext id %u, seq %u, version 0x%04X, len %u\n", - im_text.ext_id, im_text.msg_seq, im_text.version, im_text.msg_len); - - if (im_text.msg_len != data_len - bytes) { - purple_debug_warning("QQ", "Room IM length %d should be %d\n", - im_text.msg_len, data_len - bytes); - im_text.msg_len = data_len - bytes; - } - - g_return_if_fail(im_text.msg_len > 0 && bytes + im_text.msg_len <= data_len); - if(msg_type != QQ_MSG_QUN_IM_UNKNOWN) { - g_return_if_fail(im_text.msg_len >= 10); - - bytes += qq_get16(&content_type, data + bytes); - bytes += qq_get8(&frag_count, data + bytes); - bytes += qq_get8(&frag_index, data + bytes); - bytes += qq_get16(&msg_id, data + bytes); - bytes += 4; /* skip 0x(00 00 00 00) */ - purple_debug_info("QQ", "Room IM, content %d, frag %d-%d, msg id %u\n", - content_type, frag_count, frag_index, msg_id); - im_text.msg_len -= 10; - } - g_return_if_fail(im_text.msg_len > 0); - - /* qq_show_packet("Message", data + bytes, data_len - bytes); */ - if (frag_count <= 1 || frag_count == frag_index + 1) { - fmt = qq_im_fmt_new(); - tail_len = qq_get_im_tail(fmt, data + bytes, data_len - bytes); - im_text.msg = g_strndup((gchar *)(data + bytes), data_len - tail_len); - } else { - im_text.msg = g_strndup((gchar *)(data + bytes), data_len - bytes); - } - - /* group im_group has no flag to indicate whether it has font_attr or not */ - msg_smiley = qq_emoticon_to_purple(im_text.msg); - if (fmt != NULL) { - purple_debug_info("QQ", "going to use_default_font\n"); - if (QQ_CUSTOM_USE_DEFAULT_FONT == use_default_font) { - qq_im_fmt_reset_font(fmt); - purple_debug_info("QQ", "use_default_font set\n"); - } - msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley); - msg_utf8 = qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT); - purple_debug_info("QQ", "passed!\n"); - g_free(msg_fmt); - qq_im_fmt_free(fmt); - } else { - msg_utf8 = qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT); - } - g_free(msg_smiley); - - purple_debug_info("QQ", "Room (%u) IM from %u: %s\n", - im_text.ext_id, im_text.member_uid, msg_utf8); - qq_room_got_chat_in(gc, id, im_text.member_uid, msg_utf8, im_text.send_time); - - g_free(msg_utf8); - g_free(im_text.msg); -} - -/* send IM to a group */ -static void request_room_send_im(PurpleConnection *gc, guint32 room_id, qq_im_format *fmt, const gchar *msg) -{ - guint8 raw_data[MAX_PACKET_SIZE - 16]; - gint bytes; - - g_return_if_fail(room_id != 0 && msg != NULL); - - bytes = 0; - bytes += qq_put16(raw_data + bytes, 0); - bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg)); - bytes += qq_put_im_tail(raw_data + bytes, fmt); - /* reset first two bytes */ - qq_put16(raw_data, bytes - 2); - - qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_IM, room_id, raw_data, bytes); -} - -/* this is the ACK */ -void qq_process_room_send_im(PurpleConnection *gc, guint8 *data, gint len) -{ - /* return should be the internal group id - * but we have nothing to do with it */ - return; -} - -void qq_process_room_send_im_ex(PurpleConnection *gc, guint8 *data, gint len) -{ - /* return should be the internal group id - * but we have nothing to do with it */ - return; -} - -#if 0 -static void request_room_send_im_ex(PurpleConnection *gc, guint32 room_id, - qq_im_format *fmt, gchar *msg, guint16 msg_id, guint8 frag_count, guint8 frag_index) -{ - guint8 raw_data[MAX_PACKET_SIZE - 16]; - gint bytes; - - - g_return_if_fail(room_id != 0 && msg != NULL); - - bytes = 0; - bytes += qq_put16(raw_data + bytes, 0); /* packet len */ - /* type 0x0001, text only; 0x0002, with custom emoticon */ - bytes += qq_put16(raw_data + bytes, 0x0001); - bytes += qq_put8(raw_data + bytes, frag_count); - bytes += qq_put8(raw_data + bytes, frag_index); - bytes += qq_put16(raw_data + bytes, msg_id); - bytes += qq_put32(raw_data + bytes, 0); /* unknow 4 bytes */ - bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg)); - if (frag_count == frag_index + 1) { - bytes += qq_put8(raw_data + bytes, 0x20); /* add extra SPACE */ - bytes += qq_put_im_tail(raw_data + bytes, fmt); - } - - /* reset first two bytes as length */ - qq_put16(raw_data, bytes - 2); - - /*qq_show_packet("QQ_ROOM_CMD_SEND_IM_EX", raw_data, bytes); */ - qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_IM_EX, room_id, raw_data, bytes); -} -#endif - -/* send a chat msg to a QQ Qun - * called by purple */ -int qq_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFlags flags) -{ - qq_data *qd; - qq_im_format *fmt; - gchar *msg_stripped, *tmp; - GSList *segments, *it; - gint msg_len; - const gchar *start_invalid; - gboolean is_smiley_none; - - g_return_val_if_fail(NULL != gc && NULL != gc->proto_data, -1); - g_return_val_if_fail(id != 0 && what != NULL, -1); - - qd = (qq_data *) gc->proto_data; - purple_debug_info("QQ", "Send chat IM to %u, len %" G_GSIZE_FORMAT ":\n%s\n", id, strlen(what), what); - - /* qq_show_packet("chat IM UTF8", (guint8 *)what, strlen(what)); */ - - msg_stripped = purple_markup_strip_html(what); - g_return_val_if_fail(msg_stripped != NULL, -1); - /* qq_show_packet("IM Stripped", (guint8 *)what, strlen(what)); */ - - /* Check and valid utf8 string */ - msg_len = strlen(msg_stripped); - if (!g_utf8_validate(msg_stripped, msg_len, &start_invalid)) { - if (start_invalid > msg_stripped) { - tmp = g_strndup(msg_stripped, start_invalid - msg_stripped); - g_free(msg_stripped); - msg_stripped = g_strconcat(tmp, _("(Invalid UTF-8 string)"), NULL); - g_free(tmp); - } else { - g_free(msg_stripped); - msg_stripped = g_strdup(_("(Invalid UTF-8 string)")); - } - } - - is_smiley_none = qq_im_smiley_none(what); - segments = qq_im_get_segments(msg_stripped, is_smiley_none); - g_free(msg_stripped); - - if (segments == NULL) { - return -1; - } - - qd->send_im_id++; - fmt = qq_im_fmt_new_by_purple(what); - for (it = segments; it; it = g_slist_delete_link(it, it)) { - request_room_send_im(gc, id, fmt, (gchar *)it->data); - g_free(it->data); - } - qq_im_fmt_free(fmt); - return 1; -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group_im.h --- a/libpurple/protocols/qq/group_im.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/** - * @file group_im.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_GROUP_IM_H_ -#define _QQ_GROUP_IM_H_ - -#include -#include "connection.h" -#include "conversation.h" -#include "group.h" - -enum { - QQ_CUSTOM_USE_DEFAULT_FONT = 0x00000001, - QQ_CUSTOM_NONE = 0x00000000 -}; - -PurpleConversation *qq_room_conv_open(PurpleConnection *gc, qq_room_data *rmd); -void qq_room_conv_set_onlines(PurpleConnection *gc, qq_room_data *rmd); - -void qq_room_got_chat_in(PurpleConnection *gc, - guint32 room_id, UID uid_from, const gchar *msg, time_t in_time); - -int qq_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags); -void qq_process_room_send_im(PurpleConnection *gc, guint8 *data, gint len); -void qq_process_room_send_im_ex(PurpleConnection *gc, guint8 *data, gint len); - -void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type); - -#endif - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group_info.c --- a/libpurple/protocols/qq/group_info.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,396 +0,0 @@ -/** - * @file group_info.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 "conversation.h" -#include "debug.h" - -#include "char_conv.h" -#include "group_im.h" -#include "group_internal.h" -#include "group_info.h" -#include "buddy_list.h" -#include "qq_define.h" -#include "packet_parse.h" -#include "qq_network.h" -#include "utils.h" - -/* we check who needs to update member info every minutes - * this interval determines if their member info is outdated */ -#define QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL 180 - -static gboolean check_update_interval(qq_buddy_data *member) -{ - g_return_val_if_fail(member != NULL, FALSE); - return (member->nickname == NULL) || - (time(NULL) - member->last_update) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL; -} - -/* this is done when we receive the reply to get_online_members sub_cmd - * all member are set offline, and then only those in reply packets are online */ -static void set_all_offline(qq_room_data *rmd) -{ - GList *list; - qq_buddy_data *bd; - g_return_if_fail(rmd != NULL); - - list = rmd->members; - while (list != NULL) { - bd = (qq_buddy_data *) list->data; - bd->status = QQ_BUDDY_CHANGE_TO_OFFLINE; - list = list->next; - } -} - -/* send packet to get info for each group member */ -gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, UPDCLS update_class) -{ - guint8 *raw_data; - gint bytes, num; - GList *list; - qq_room_data *rmd; - qq_buddy_data *bd; - - g_return_val_if_fail(room_id > 0, 0); - - rmd = qq_room_data_find(gc, room_id); - g_return_val_if_fail(rmd != NULL, 0); - - for (num = 0, list = rmd->members; list != NULL; list = list->next) { - bd = (qq_buddy_data *) list->data; - if (check_update_interval(bd)) - num++; - } - - if (num <= 0) { - purple_debug_info("QQ", "No group member info needs to be updated now.\n"); - return 0; - } - - raw_data = g_newa(guint8, 4 * num); - - bytes = 0; - - list = rmd->members; - while (list != NULL) { - bd = (qq_buddy_data *) list->data; - if (check_update_interval(bd)) - bytes += qq_put32(raw_data + bytes, bd->uid); - list = list->next; - } - - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_BUDDIES, rmd->id, raw_data, bytes, - update_class, 0); - return num; -} - -static gchar *get_role_desc(qq_room_role role) -{ - const char *role_desc; - switch (role) { - case QQ_ROOM_ROLE_NO: - role_desc = _("Not member"); - break; - case QQ_ROOM_ROLE_YES: - role_desc = _("Member"); - break; - case QQ_ROOM_ROLE_REQUESTING: - role_desc = _("Requesting"); - break; - case QQ_ROOM_ROLE_ADMIN: - role_desc = _("Admin"); - break; - default: - role_desc = _("Unknown"); - } - - return g_strdup(role_desc); -} - -static void room_info_display(PurpleConnection *gc, qq_room_data *rmd) -{ - PurpleNotifyUserInfo *room_info; - gchar *utf8_value; - - g_return_if_fail(rmd != NULL && rmd->id > 0); - - room_info = purple_notify_user_info_new(); - - /* XXX: Should this be "Topic"? */ - purple_notify_user_info_add_pair(room_info, _("Room Title"), rmd->title_utf8); - purple_notify_user_info_add_pair(room_info, _("Notice"), rmd->notice_utf8); - purple_notify_user_info_add_pair(room_info, _("Detail"), rmd->desc_utf8); - - purple_notify_user_info_add_section_break(room_info); - - utf8_value = g_strdup_printf(("%u"), rmd->creator_uid); - purple_notify_user_info_add_pair(room_info, _("Creator"), utf8_value); - g_free(utf8_value); - - utf8_value = get_role_desc(rmd->my_role); - purple_notify_user_info_add_pair(room_info, _("About me"), utf8_value); - g_free(utf8_value); - - utf8_value = g_strdup_printf(("%d"), rmd->category); - purple_notify_user_info_add_pair(room_info, _("Category"), utf8_value); - g_free(utf8_value); - - utf8_value = g_strdup_printf(("%d"), rmd->auth_type); - purple_notify_user_info_add_pair(room_info, _("Authorize"), utf8_value); - g_free(utf8_value); - - utf8_value = g_strdup_printf(("%u"), rmd->ext_id); - purple_notify_userinfo(gc, utf8_value, room_info, NULL, NULL); - g_free(utf8_value); - - purple_notify_user_info_destroy(room_info); -} - -void qq_process_room_cmd_get_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc) -{ - qq_data *qd; - qq_room_data *rmd; - qq_buddy_data *bd; - PurpleChat *chat; - PurpleConversation *conv; - guint8 organization, role; - guint16 unknown, max_members; - UID member_uid; - guint32 id, ext_id; - guint32 unknown4; - guint8 unknown1; - gint bytes, num; - gchar *notice; - gchar *topic_utf8; - - g_return_if_fail(data != NULL && data_len > 0); - qd = (qq_data *) gc->proto_data; - - /* qq_show_packet("Room Info", data, data_len); */ - - bytes = 0; - bytes += qq_get32(&id, data + bytes); - g_return_if_fail(id > 0); - - bytes += qq_get32(&ext_id, data + bytes); - g_return_if_fail(ext_id > 0); - - chat = qq_room_find_or_new(gc, id, ext_id); - g_return_if_fail(chat != NULL); - rmd = qq_room_data_find(gc, id); - g_return_if_fail(rmd != NULL); - - bytes += qq_get8(&(rmd->type8), data + bytes); - bytes += qq_get32(&unknown4, data + bytes); /* unknown 4 bytes */ - bytes += qq_get32(&(rmd->creator_uid), data + bytes); - bytes += qq_get8(&(rmd->auth_type), data + bytes); - bytes += qq_get32(&unknown4, data + bytes); /* oldCategory */ - bytes += qq_get16(&unknown, data + bytes); - bytes += qq_get32(&(rmd->category), data + bytes); - bytes += qq_get16(&max_members, data + bytes); - bytes += qq_get8(&unknown1, data + bytes); - /* the following, while Eva: - * 4(unk), 4(verID), 1(nameLen), nameLen(qunNameContent), 1(0x00), - * 2(qunNoticeLen), qunNoticeLen(qunNoticeContent, 1(qunDescLen), - * qunDestLen(qunDestcontent)) */ - bytes += qq_get8(&unknown1, data + bytes); - purple_debug_info("QQ", "type: %u creator: %u category: %u maxmembers: %u\n", - rmd->type8, rmd->creator_uid, rmd->category, max_members); - - if (qd->client_version >= 2007) { - /* skip 7 bytes unknow in qq2007 0x(00 00 01 00 00 00 fc)*/ - bytes += 7; - } - /* qq_show_packet("Room Info", data + bytes, data_len - bytes); */ - /* strlen + */ - bytes += qq_get_vstr(&(rmd->title_utf8), QQ_CHARSET_DEFAULT, data + bytes); - bytes += qq_get16(&unknown, data + bytes); /* 0x0000 */ - bytes += qq_get_vstr(¬ice, QQ_CHARSET_DEFAULT, data + bytes); - bytes += qq_get_vstr(&(rmd->desc_utf8), QQ_CHARSET_DEFAULT, data + bytes); - - purple_debug_info("QQ", "room [%s] notice [%s] desc [%s] unknow 0x%04X\n", - rmd->title_utf8, notice, rmd->desc_utf8, unknown); - - num = 0; - /* now comes the member list separated by 0x00 */ - while (bytes < data_len) { - bytes += qq_get32(&member_uid, data + bytes); - num++; - bytes += qq_get8(&organization, data + bytes); - bytes += qq_get8(&role, data + bytes); - -#if 0 - if(organization != 0 || role != 0) { - purple_debug_info("QQ", "%u, organization=%d, role=%d\n", member_uid, organization, role); - } -#endif - - bd = qq_room_buddy_find_or_new(gc, rmd, member_uid); - if (bd != NULL) - bd->role = role; - } - if(bytes > data_len) { - purple_debug_error("QQ", - "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!"); - } - - purple_debug_info("QQ", "group \"%s\" has %d members\n", rmd->title_utf8, num); - - if (rmd->creator_uid == qd->uid) - rmd->my_role = QQ_ROOM_ROLE_ADMIN; - - /* filter \r\n in notice */ - qq_filter_str(notice); - rmd->notice_utf8 = strdup(notice); - g_free(notice); - - qq_room_update_chat_info(chat, rmd); - - if (action == QQ_ROOM_INFO_DISPLAY) { - room_info_display(gc, rmd); - } - - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - rmd->title_utf8, purple_connection_get_account(gc)); - if(NULL == conv) { - purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", rmd->title_utf8); - return; - } - - topic_utf8 = g_strdup_printf("%u %s", rmd->ext_id, rmd->notice_utf8); - purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8); - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8); - g_free(topic_utf8); -} - -void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc) -{ - guint32 room_id; - UID member_uid; - guint8 unknown; - gint bytes, num; - qq_room_data *rmd; - qq_buddy_data *bd; - - g_return_if_fail(data != NULL && len > 0); - - if (len <= 3) { - purple_debug_error("QQ", "Invalid group online member reply, discard it!\n"); - return; - } - - bytes = 0; - bytes += qq_get32(&room_id, data + bytes); - bytes += qq_get8(&unknown, data + bytes); /* 0x3c ?? */ - g_return_if_fail(room_id > 0); - - rmd = qq_room_data_find(gc, room_id); - if (rmd == NULL) { - purple_debug_error("QQ", "Can not info of room id [%u]\n", room_id); - return; - } - - /* set all offline first, then update those online */ - set_all_offline(rmd); - num = 0; - while (bytes < len) { - bytes += qq_get32(&member_uid, data + bytes); - num++; - bd = qq_room_buddy_find_or_new(gc, rmd, member_uid); - if (bd != NULL) - bd->status = QQ_BUDDY_ONLINE_NORMAL; - } - if(bytes > len) { - purple_debug_error("QQ", - "group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!"); - } - - purple_debug_info("QQ", "Group \"%s\" has %d online members\n", rmd->title_utf8, num); - qq_room_conv_set_onlines(gc, rmd); -} - -/* process the reply to get_members_info packet */ -void qq_process_room_cmd_get_buddies(guint8 *data, gint len, PurpleConnection *gc) -{ - gint bytes; - gint num; - guint32 id; - UID member_uid; - guint16 unknown; - qq_room_data *rmd; - qq_buddy_data *bd; - gchar *nick; - - g_return_if_fail(data != NULL && len > 0); - - /* qq_show_packet("qq_process_room_cmd_get_buddies", data, len); */ - - bytes = 0; - bytes += qq_get32(&id, data + bytes); - g_return_if_fail(id > 0); - - rmd = qq_room_data_find(gc, id); - g_return_if_fail(rmd != NULL); - - num = 0; - /* now starts the member info, as get buddy list reply */ - while (bytes < len) { - bytes += qq_get32(&member_uid, data + bytes); - g_return_if_fail(member_uid > 0); - bd = qq_room_buddy_find_or_new(gc, rmd, member_uid); - g_return_if_fail(bd != NULL); - - num++; - bytes += qq_get16(&(bd->face), data + bytes); - bytes += qq_get8(&(bd->age), data + bytes); - bytes += qq_get8(&(bd->gender), data + bytes); - bytes += qq_get_vstr(&nick, QQ_CHARSET_DEFAULT, data + bytes); - bytes += qq_get16(&unknown, data + bytes); - bytes += qq_get8(&(bd->ext_flag), data + bytes); - bytes += qq_get8(&(bd->comm_flag), data + bytes); - - /* filter \r\n in nick */ - qq_filter_str(nick); - bd->nickname = g_strdup(nick); - g_free(nick); - -#if 0 - purple_debug_info("QQ", - "member [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n", - member_uid, bd->ext_flag, bd->comm_flag, bd->nickname); -#endif - - bd->last_update = time(NULL); - } - if (bytes > len) { - purple_debug_error("QQ", - "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!"); - } - purple_debug_info("QQ", "Group \"%s\" got %d member info\n", rmd->title_utf8, num); - - rmd->is_got_buddies = TRUE; - qq_room_conv_set_onlines(gc, rmd); -} - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group_info.h --- a/libpurple/protocols/qq/group_info.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/** - * @file group_info.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_GROUP_INFO_H_ -#define _QQ_GROUP_INFO_H_ - -#include -#include "connection.h" -#include "group.h" - -enum { - QQ_ROOM_INFO_UPDATE_ONLY = 0, - QQ_ROOM_INFO_DISPLAY -}; - -gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, UPDCLS update_class); - -void qq_process_room_cmd_get_info(guint8 *data, gint len, guint32 action, PurpleConnection *gc); -void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc); -void qq_process_room_cmd_get_buddies(guint8 *data, gint len, PurpleConnection *gc); -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group_internal.c --- a/libpurple/protocols/qq/group_internal.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,434 +0,0 @@ -/** - * @file group_internal.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 "blist.h" -#include "debug.h" - -#include "buddy_opt.h" -#include "group_internal.h" -#include "utils.h" - -static qq_room_data *room_data_new(guint32 id, guint32 ext_id, const gchar *title) -{ - qq_room_data *rmd; - - purple_debug_info("QQ", "Created room data: %s, ext id %u, id %u\n", - title == NULL ? "(NULL)" : title, - ext_id, id); - rmd = g_new0(qq_room_data, 1); - rmd->my_role = QQ_ROOM_ROLE_NO; - rmd->id = id; - rmd->ext_id = ext_id; - rmd->type8 = 0x01; /* assume permanent Qun */ - rmd->creator_uid = 10000; /* assume by QQ admin */ - rmd->category = 0x01; - rmd->auth_type = 0x02; /* assume need auth */ - rmd->title_utf8 = g_strdup(title == NULL ? "" : title); - rmd->desc_utf8 = g_strdup(""); - rmd->notice_utf8 = g_strdup(""); - rmd->members = NULL; - rmd->is_got_buddies = FALSE; - return rmd; -} - -/* create a qq_room_data from hashtable */ -static qq_room_data *room_data_new_by_hashtable(PurpleConnection *gc, GHashTable *data) -{ - qq_room_data *rmd; - guint32 id, ext_id; - gchar *value; - - value = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID); - id = value ? strtoul(value, NULL, 10) : 0; - value = g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID); - ext_id = value ? strtoul(value, NULL, 10) : 0; - value = g_hash_table_lookup(data, QQ_ROOM_KEY_TITLE_UTF8); - - rmd = room_data_new(id, ext_id, value); - rmd->my_role = QQ_ROOM_ROLE_YES; - return rmd; -} - -/* gracefully free all members in a room */ -static void room_buddies_free(qq_room_data *rmd) -{ - gint i; - GList *list; - qq_buddy_data *bd; - - g_return_if_fail(rmd != NULL); - i = 0; - while (NULL != (list = rmd->members)) { - bd = (qq_buddy_data *) list->data; - i++; - rmd->members = g_list_remove(rmd->members, bd); - qq_buddy_data_free(bd); - } - - rmd->members = NULL; -} - -/* gracefully free the memory for one qq_room_data */ -static void room_data_free(qq_room_data *rmd) -{ - g_return_if_fail(rmd != NULL); - room_buddies_free(rmd); - g_free(rmd->title_utf8); - g_free(rmd->desc_utf8); - g_free(rmd->notice_utf8); - g_free(rmd); -} - -void qq_room_update_chat_info(PurpleChat *chat, qq_room_data *rmd) -{ - GHashTable *components; - - if (rmd->title_utf8 != NULL && strlen(rmd->title_utf8) > 0) { - purple_blist_alias_chat(chat, rmd->title_utf8); - } - - components = purple_chat_get_components(chat); - - g_hash_table_replace(components, - g_strdup(QQ_ROOM_KEY_INTERNAL_ID), - g_strdup_printf("%u", rmd->id)); - g_hash_table_replace(components, - g_strdup(QQ_ROOM_KEY_EXTERNAL_ID), - g_strdup_printf("%u", rmd->ext_id)); - g_hash_table_replace(components, - g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8)); -} - -static PurpleChat *chat_new(PurpleConnection *gc, qq_room_data *rmd) -{ - GHashTable *components; - PurpleGroup *g; - PurpleChat *chat; - - purple_debug_info("QQ", "Add new chat: id %u, ext id %u, title %s\n", - rmd->id, rmd->ext_id, - rmd->title_utf8 == NULL ? "(NULL)" : rmd->title_utf8); - - components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - g_hash_table_insert(components, - g_strdup(QQ_ROOM_KEY_INTERNAL_ID), g_strdup_printf("%u", rmd->id)); - g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_EXTERNAL_ID), - g_strdup_printf("%u", rmd->ext_id)); - g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8)); - - chat = purple_chat_new(purple_connection_get_account(gc), rmd->title_utf8, components); - g = qq_group_find_or_new(PURPLE_GROUP_QQ_QUN); - purple_blist_add_chat(chat, g, NULL); - - return chat; -} - -PurpleChat *qq_room_find_or_new(PurpleConnection *gc, guint32 id, guint32 ext_id) -{ - qq_data *qd; - qq_room_data *rmd; - PurpleChat *chat; - gchar *num_str; - - g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, NULL); - qd = (qq_data *) gc->proto_data; - - g_return_val_if_fail(id != 0 && ext_id != 0, NULL); - - purple_debug_info("QQ", "Find or add new room: id %u, ext id %u\n", id, ext_id); - - rmd = qq_room_data_find(gc, id); - if (rmd == NULL) { - rmd = room_data_new(id, ext_id, NULL); - g_return_val_if_fail(rmd != NULL, NULL); - rmd->my_role = QQ_ROOM_ROLE_YES; - qd->groups = g_list_append(qd->groups, rmd); - } - - num_str = g_strdup_printf("%u", ext_id); - chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str); - g_free(num_str); - if (chat) { - return chat; - } - - return chat_new(gc, rmd); -} - -void qq_room_remove(PurpleConnection *gc, guint32 id) -{ - qq_data *qd; - PurpleChat *chat; - qq_room_data *rmd; - gchar *num_str; - guint32 ext_id; - - g_return_if_fail (gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - purple_debug_info("QQ", "Find and remove room data, id %u\n", id); - rmd = qq_room_data_find(gc, id); - g_return_if_fail (rmd != NULL); - - ext_id = rmd->ext_id; - qd->groups = g_list_remove(qd->groups, rmd); - room_data_free(rmd); - - purple_debug_info("QQ", "Find and remove chat, ext_id %u\n", ext_id); - num_str = g_strdup_printf("%u", ext_id); - chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str); - g_free(num_str); - - g_return_if_fail (chat != NULL); - - purple_blist_remove_chat(chat); -} - -/* find a qq_buddy_data by uid, called by im.c */ -qq_buddy_data *qq_room_buddy_find(qq_room_data *rmd, UID uid) -{ - GList *list; - qq_buddy_data *bd; - g_return_val_if_fail(rmd != NULL && uid > 0, NULL); - - list = rmd->members; - while (list != NULL) { - bd = (qq_buddy_data *) list->data; - if (bd->uid == uid) - return bd; - else - list = list->next; - } - - return NULL; -} - -/* remove a qq_buddy_data by uid, called by qq_group_opt.c */ -void qq_room_buddy_remove(qq_room_data *rmd, UID uid) -{ - GList *list; - qq_buddy_data *bd; - g_return_if_fail(rmd != NULL && uid > 0); - - list = rmd->members; - while (list != NULL) { - bd = (qq_buddy_data *) list->data; - if (bd->uid == uid) { - rmd->members = g_list_remove(rmd->members, bd); - return; - } else { - list = list->next; - } - } -} - -qq_buddy_data *qq_room_buddy_find_or_new(PurpleConnection *gc, qq_room_data *rmd, UID member_uid) -{ - qq_buddy_data *member, *bd; - PurpleBuddy *buddy; - g_return_val_if_fail(rmd != NULL && member_uid > 0, NULL); - - member = qq_room_buddy_find(rmd, member_uid); - if (member == NULL) { /* first appear during my session */ - member = g_new0(qq_buddy_data, 1); - member->uid = member_uid; - buddy = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(member_uid)); - if (buddy != NULL) { - const gchar *alias = NULL; - - bd = purple_buddy_get_protocol_data(buddy); - if (bd != NULL && bd->nickname != NULL) - member->nickname = g_strdup(bd->nickname); - else if ((alias = purple_buddy_get_alias(buddy)) != NULL) - member->nickname = g_strdup(alias); - } - rmd->members = g_list_append(rmd->members, member); - } - - return member; -} - -qq_room_data *qq_room_data_find(PurpleConnection *gc, guint32 room_id) -{ - GList *list; - qq_room_data *rmd; - qq_data *qd; - - qd = (qq_data *) gc->proto_data; - - if (qd->groups == NULL || room_id <= 0) - return 0; - - list = qd->groups; - while (list != NULL) { - rmd = (qq_room_data *) list->data; - if (rmd->id == room_id) { - return rmd; - } - list = list->next; - } - - return NULL; -} - -guint32 qq_room_get_next(PurpleConnection *gc, guint32 room_id) -{ - GList *list; - qq_room_data *rmd; - qq_data *qd; - gboolean is_find = FALSE; - - qd = (qq_data *) gc->proto_data; - - if (qd->groups == NULL) { - return 0; - } - - if (room_id <= 0) { - rmd = (qq_room_data *) qd->groups->data; - return rmd->id; - } - - list = qd->groups; - while (list != NULL) { - rmd = (qq_room_data *) list->data; - list = list->next; - if (rmd->id == room_id) { - is_find = TRUE; - break; - } - } - - g_return_val_if_fail(is_find, 0); - if (list == NULL) return 0; /* be the end */ - rmd = (qq_room_data *) list->data; - g_return_val_if_fail(rmd != NULL, 0); - return rmd->id; -} - -guint32 qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id) -{ - GList *list; - qq_room_data *rmd; - qq_data *qd; - gboolean is_find; - - qd = (qq_data *) gc->proto_data; - - list = qd->groups; - if (room_id > 0) { - /* search next room */ - is_find = FALSE; - while (list != NULL) { - rmd = (qq_room_data *) list->data; - list = list->next; - if (rmd->id == room_id) { - is_find = TRUE; - break; - } - } - g_return_val_if_fail(is_find, 0); - } - - while (list != NULL) { - rmd = (qq_room_data *) list->data; - g_return_val_if_fail(rmd != NULL, 0); - - if (rmd->my_role == QQ_ROOM_ROLE_YES || rmd->my_role == QQ_ROOM_ROLE_ADMIN) { - if (NULL != purple_find_conversation_with_account( - PURPLE_CONV_TYPE_CHAT,rmd->title_utf8, purple_connection_get_account(gc))) { - /* In convseration*/ - return rmd->id; - } - } - list = list->next; - } - - return 0; -} - -/* this should be called upon signin, even when we did not open group chat window */ -void qq_room_data_initial(PurpleConnection *gc) -{ - PurpleAccount *account; - PurpleChat *chat; - PurpleGroup *purple_group; - PurpleBlistNode *node; - qq_data *qd; - qq_room_data *rmd; - gint count; - - account = purple_connection_get_account(gc); - qd = (qq_data *) gc->proto_data; - - purple_debug_info("QQ", "Initial QQ Qun configurations\n"); - purple_group = purple_find_group(PURPLE_GROUP_QQ_QUN); - if (purple_group == NULL) { - purple_debug_info("QQ", "We have no QQ Qun\n"); - return; - } - - count = 0; - for (node = purple_blist_node_get_first_child((PurpleBlistNode *)purple_group); - node != NULL; - node = purple_blist_node_get_sibling_next(node)) - { - if ( !PURPLE_BLIST_NODE_IS_CHAT(node)) { - continue; - } - /* got one */ - chat = (PurpleChat *) node; - if (account != purple_chat_get_account(chat)) /* not qq account*/ - continue; - - rmd = room_data_new_by_hashtable(gc, purple_chat_get_components(chat)); - qd->groups = g_list_append(qd->groups, rmd); - count++; - } - - purple_debug_info("QQ", "Load %d QQ Qun configurations\n", count); -} - -void qq_room_data_free_all(PurpleConnection *gc) -{ - qq_data *qd; - qq_room_data *rmd; - gint count; - - g_return_if_fail (gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - count = 0; - while (qd->groups != NULL) { - rmd = (qq_room_data *) qd->groups->data; - qd->groups = g_list_remove(qd->groups, rmd); - room_data_free(rmd); - count++; - } - - if (count > 0) { - purple_debug_info("QQ", "%d rooms are freed\n", count); - } -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group_internal.h --- a/libpurple/protocols/qq/group_internal.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/** - * @file group_internal.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_GROUP_HASH_H_ -#define _QQ_GROUP_HASH_H_ - -#include -#include "group.h" - -#define QQ_ROOM_KEY_INTERNAL_ID "id" -#define QQ_ROOM_KEY_EXTERNAL_ID "ext_id" -#define QQ_ROOM_KEY_TITLE_UTF8 "title_utf8" - -PurpleChat *qq_room_find_or_new(PurpleConnection *gc, guint32 id, guint32 ext_id); -void qq_room_remove(PurpleConnection *gc, guint32 id); -void qq_room_update_chat_info(PurpleChat *chat, qq_room_data *rmd); - -qq_buddy_data *qq_room_buddy_find(qq_room_data *rmd, UID uid); -void qq_room_buddy_remove(qq_room_data *rmd, UID uid); -qq_buddy_data *qq_room_buddy_find_or_new(PurpleConnection *gc, qq_room_data *rmd, UID member_uid); - -void qq_room_data_initial(PurpleConnection *gc); -void qq_room_data_free_all(PurpleConnection *gc); -qq_room_data *qq_room_data_find(PurpleConnection *gc, guint32 room_id); - -guint32 qq_room_get_next(PurpleConnection *gc, guint32 room_id); -guint32 qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id); - -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group_join.c --- a/libpurple/protocols/qq/group_join.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,423 +0,0 @@ -/** - * @file group_join.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 "debug.h" -#include "notify.h" -#include "request.h" -#include "server.h" - -#include "char_conv.h" -#include "im.h" -#include "group_internal.h" -#include "group_info.h" -#include "group_join.h" -#include "group_opt.h" -#include "group_im.h" -#include "qq_define.h" -#include "packet_parse.h" -#include "qq_network.h" -#include "qq_process.h" - -enum { - QQ_ROOM_JOIN_OK = 0x01, - QQ_ROOM_JOIN_NEED_AUTH = 0x02, - QQ_ROOM_JOIN_DENIED = 0x03 -}; - -enum { - QQ_ROOM_SEARCH_TYPE_BY_ID = 0x01, - QQ_ROOM_SEARCH_TYPE_DEMO = 0x02 -}; - -static void group_quit_cb(qq_room_req *add_req) -{ - PurpleConnection *gc; - guint32 id; - qq_room_data *rmd; - - if (add_req->gc == NULL || add_req->id == 0) { - g_free(add_req); - return; - } - - gc = add_req->gc; - id = add_req->id; - - rmd = qq_room_data_find(gc, id); - if (rmd == NULL) { - g_free(add_req); - return; - } - - qq_send_room_cmd_only(gc, QQ_ROOM_CMD_QUIT, rmd->id); - g_free(add_req); -} - -/* send packet to join a group without auth */ -void qq_request_room_join(PurpleConnection *gc, qq_room_data *rmd) -{ - g_return_if_fail(rmd != NULL); - - if (rmd->my_role == QQ_ROOM_ROLE_NO) { - rmd->my_role = QQ_ROOM_ROLE_REQUESTING; - } - - switch (rmd->auth_type) { - case QQ_ROOM_AUTH_TYPE_NO_AUTH: - case QQ_ROOM_AUTH_TYPE_NEED_AUTH: - break; - case QQ_ROOM_AUTH_TYPE_NO_ADD: - if (rmd->my_role == QQ_ROOM_ROLE_NO - && rmd->my_role == QQ_ROOM_ROLE_REQUESTING) { - purple_notify_warning(gc, NULL, _("The Qun does not allow others to join"), NULL); - return; - } - break; - default: - purple_debug_error("QQ", "Unknown room auth type: %d\n", rmd->auth_type); - break; - } - - qq_send_room_cmd_only(gc, QQ_ROOM_CMD_JOIN, rmd->id); -} - -static void group_join_cb(qq_room_req *add_req, const gchar *reason_utf8) -{ - qq_room_data *rmd; - - g_return_if_fail(add_req != NULL); - if (add_req->gc == NULL || add_req->id == 0) { - g_free(add_req); - return; - } - - rmd = qq_room_data_find(add_req->gc, add_req->id); - if (rmd == NULL) { - purple_debug_error("QQ", "Can not find room data of %u\n", add_req->id); - g_free(add_req); - return; - } - - qq_send_cmd_group_auth(add_req->gc, rmd, QQ_ROOM_AUTH_REQUEST_APPLY, 0, reason_utf8); - g_free(add_req); -} - -static void room_join_cancel_cb(qq_room_req *add_req, const gchar *msg) -{ - g_return_if_fail(add_req != NULL); - g_free(add_req); -} - -static void do_room_join_request(PurpleConnection *gc, qq_room_data *rmd) -{ - gchar *msg; - qq_room_req *add_req; - g_return_if_fail(rmd != NULL); - - purple_debug_info("QQ", "Room id %u needs authentication\n", rmd->id); - - msg = g_strdup_printf("QQ Qun %u needs authentication\n", rmd->ext_id); - add_req = g_new0(qq_room_req, 1); - add_req->gc = gc; - add_req->id = rmd->id; - purple_request_input(gc, _("Join QQ Qun"), msg, - _("Input request here"), - _("Would you be my friend?"), TRUE, FALSE, NULL, - _("Send"), - G_CALLBACK(group_join_cb), - _("Cancel"), G_CALLBACK(room_join_cancel_cb), - purple_connection_get_account(gc), rmd->title_utf8, NULL, - add_req); - g_free(msg); -} - -void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd, - guint8 opt, UID uid, const gchar *reason_utf8) -{ - guint8 raw_data[MAX_PACKET_SIZE - 16]; - gint bytes; - - g_return_if_fail(rmd != NULL); - - if (opt == QQ_ROOM_AUTH_REQUEST_APPLY) { - rmd->my_role = QQ_ROOM_ROLE_REQUESTING; - uid = 0; - } - - bytes = 0; - bytes += qq_put8(raw_data + bytes, opt); - bytes += qq_put32(raw_data + bytes, uid); - bytes += qq_put_vstr(raw_data + bytes, reason_utf8, QQ_CHARSET_DEFAULT); - - qq_send_room_cmd(gc, QQ_ROOM_CMD_AUTH, rmd->id, raw_data, bytes); -} - -/* If comes here, cmd is OK already */ -void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc) -{ - gint bytes; - guint32 id; - - g_return_if_fail(data != NULL && len > 0); - - if (len < 4) { - purple_debug_error("QQ", "Invalid exit group reply, expect %d bytes, read %d bytes\n", 4, len); - return; - } - - bytes = 0; - bytes += qq_get32(&id, data + bytes); - - qq_room_remove(gc, id); -} - -/* Process the reply to group_auth subcmd */ -void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc) -{ - gint bytes; - guint32 id; - qq_room_data *rmd; - gchar *msg; - - g_return_if_fail(data != NULL && len > 0); - - if (len < 4) { - purple_debug_error("QQ", - "Invalid join room reply, expect %d bytes, read %d bytes\n", 4, len); - return; - } - bytes = 0; - bytes += qq_get32(&id, data + bytes); - g_return_if_fail(id > 0); - - rmd = qq_room_data_find(gc, id); - if (rmd != NULL) { - msg = g_strdup_printf(_("Successfully joined Qun %s (%u)"), rmd->title_utf8, rmd->ext_id); - qq_got_message(gc, msg); - g_free(msg); - } else { - qq_got_message(gc, _("Successfully joined Qun")); - } -} - -/* process group cmd reply "join group" */ -void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc) -{ - gint bytes; - guint32 id; - guint8 reply; - qq_room_data *rmd; - gchar *msg; - - g_return_if_fail(data != NULL && len > 0); - - if (len < 5) { - purple_debug_error("QQ", - "Invalid join room reply, expect %d bytes, read %d bytes\n", 5, len); - return; - } - - bytes = 0; - bytes += qq_get32(&id, data + bytes); - bytes += qq_get8(&reply, data + bytes); - - /* join group OK */ - rmd = qq_room_data_find(gc, id); - /* need to check if group is NULL or not. */ - g_return_if_fail(rmd != NULL); - switch (reply) { - case QQ_ROOM_JOIN_OK: - purple_debug_info("QQ", "Succeeded in joining group \"%s\"\n", rmd->title_utf8); - rmd->my_role = QQ_ROOM_ROLE_YES; - /* this must be shown before getting online members */ - qq_room_conv_open(gc, rmd); - break; - case QQ_ROOM_JOIN_NEED_AUTH: - purple_debug_info("QQ", - "Failed to join room ext id %u %s, needs authentication\n", - rmd->ext_id, rmd->title_utf8); - rmd->my_role = QQ_ROOM_ROLE_NO; - do_room_join_request(gc, rmd); - break; - case QQ_ROOM_JOIN_DENIED: - msg = g_strdup_printf(_("Qun %u denied from joining"), rmd->ext_id); - purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), msg); - g_free(msg); - break; - default: - purple_debug_info("QQ", - "Failed to join room ext id %u %s, unknown reply: 0x%02x\n", - rmd->ext_id, rmd->title_utf8, reply); - - purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), _("Join Qun, Unknown Reply")); - } -} - -/* Attempt to join a group without auth */ -void qq_group_join(PurpleConnection *gc, GHashTable *data) -{ - gchar *ext_id_str; - gchar *id_str; - guint32 ext_id; - guint32 id; - qq_room_data *rmd; - - g_return_if_fail(data != NULL); - - ext_id_str = g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID); - id_str = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID); - purple_debug_info("QQ", "Join room %s, extend id %s\n", id_str, ext_id_str); - - if (id_str != NULL) { - id = strtoul(id_str, NULL, 10); - if (id != 0) { - rmd = qq_room_data_find(gc, id); - if (rmd) { - qq_request_room_join(gc, rmd); - return; - } - } - } - - purple_debug_info("QQ", "Search and join extend id %s\n", ext_id_str); - if (ext_id_str == NULL) { - return; - } - ext_id = strtoul(ext_id_str, NULL, 10); - if (ext_id == 0) { - return; - } - - qq_request_room_search(gc, ext_id, QQ_ROOM_SEARCH_FOR_JOIN); -} - -void qq_room_quit(PurpleConnection *gc, guint32 room_id) -{ - qq_room_req *add_req; - - add_req = g_new0(qq_room_req, 1); - add_req->gc = gc; - add_req->id = room_id; - - purple_request_action(gc, _("QQ Qun Operation"), - _("Quit Qun"), - _("Note, if you are the creator, \nthis operation will eventually remove this Qun."), - 1, - purple_connection_get_account(gc), NULL, NULL, - add_req, 2, _("Cancel"), - G_CALLBACK(room_join_cancel_cb), - _("Continue"), G_CALLBACK(group_quit_cb)); -} - -/* send packet to search for qq_group */ -void qq_request_room_search(PurpleConnection *gc, guint32 ext_id, int action) -{ - guint8 raw_data[16] = {0}; - gint bytes = 0; - guint8 type; - - purple_debug_info("QQ", "Search QQ Qun %u\n", ext_id); - type = (ext_id == 0x00000000) ? QQ_ROOM_SEARCH_TYPE_DEMO : QQ_ROOM_SEARCH_TYPE_BY_ID; - - bytes = 0; - bytes += qq_put8(raw_data + bytes, type); - bytes += qq_put32(raw_data + bytes, ext_id); - - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_SEARCH, 0, raw_data, bytes, 0, action); -} - -static void add_to_roomlist(qq_data *qd, qq_room_data *rmd) -{ - PurpleRoomlistRoom *room; - gchar field[11]; - - room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, rmd->title_utf8, NULL); - g_snprintf(field, sizeof(field), "%u", rmd->ext_id); - purple_roomlist_room_add_field(qd->roomlist, room, field); - g_snprintf(field, sizeof(field), "%u", rmd->creator_uid); - purple_roomlist_room_add_field(qd->roomlist, room, field); - purple_roomlist_room_add_field(qd->roomlist, room, rmd->desc_utf8); - g_snprintf(field, sizeof(field), "%u", rmd->id); - purple_roomlist_room_add_field(qd->roomlist, room, field); - g_snprintf(field, sizeof(field), "%d", rmd->type8); - purple_roomlist_room_add_field(qd->roomlist, room, field); - g_snprintf(field, sizeof(field), "%d", rmd->auth_type); - purple_roomlist_room_add_field(qd->roomlist, room, field); - g_snprintf(field, sizeof(field), "%d", rmd->category); - purple_roomlist_room_add_field(qd->roomlist, room, field); - purple_roomlist_room_add_field(qd->roomlist, room, rmd->title_utf8); - purple_roomlist_room_add(qd->roomlist, room); - - purple_roomlist_set_in_progress(qd->roomlist, FALSE); -} - -/* process group cmd reply "search group" */ -void qq_process_room_search(PurpleConnection *gc, guint8 *data, gint len, guint32 ship32) -{ - qq_data *qd; - qq_room_data rmd; - PurpleChat *chat; - gint bytes; - guint8 search_type; - guint16 unknown; - - g_return_if_fail(data != NULL && len > 0); - qd = (qq_data *) gc->proto_data; - - bytes = 0; - bytes += qq_get8(&search_type, data + bytes); - - /* now it starts with group_info_entry */ - bytes += qq_get32(&(rmd.id), data + bytes); - bytes += qq_get32(&(rmd.ext_id), data + bytes); - bytes += qq_get8(&(rmd.type8), data + bytes); - bytes += qq_get16(&(unknown), data + bytes); - bytes += qq_get16(&(unknown), data + bytes); - bytes += qq_get32(&(rmd.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(&(rmd.category), data + bytes); - bytes += qq_get_vstr(&(rmd.title_utf8), QQ_CHARSET_DEFAULT, data + bytes); - bytes += qq_get16(&(unknown), data + bytes); - bytes += qq_get8(&(rmd.auth_type), data + bytes); - bytes += qq_get_vstr(&(rmd.desc_utf8), QQ_CHARSET_DEFAULT, data + bytes); - /* end of one qq_group */ - if(bytes != len) { - purple_debug_error("QQ", - "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!"); - } - - if (ship32 == QQ_ROOM_SEARCH_FOR_JOIN) { - chat = qq_room_find_or_new(gc, rmd.id, rmd.ext_id); - g_return_if_fail(chat != NULL); - - qq_room_update_chat_info(chat, &rmd); - qq_request_room_join(gc, &rmd); - } else { - add_to_roomlist(qd, &rmd); - } -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group_join.h --- a/libpurple/protocols/qq/group_join.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/** - * @file group_join.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_GROUP_JOIN_H_ -#define _QQ_GROUP_JOIN_H_ - -#include -#include "connection.h" -#include "group.h" - -enum { - QQ_ROOM_AUTH_TYPE_NO_AUTH = 0x01, - QQ_ROOM_AUTH_TYPE_NEED_AUTH = 0x02, - QQ_ROOM_AUTH_TYPE_NO_ADD = 0x03 -}; - -enum { - QQ_ROOM_AUTH_REQUEST_APPLY = 0x01, - QQ_ROOM_AUTH_REQUEST_APPROVE = 0x02, - QQ_ROOM_AUTH_REQUEST_REJECT = 0x03 -}; - -enum { - QQ_ROOM_SEARCH_ONLY = 0, - QQ_ROOM_SEARCH_FOR_JOIN -}; - -void qq_request_room_search(PurpleConnection *gc, guint32 ext_id, int action); -void qq_process_room_search(PurpleConnection *gc, guint8 *data, gint len, guint32 ship32); - -void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd, guint8 opt, UID uid, const gchar *reason_utf8); -void qq_group_join(PurpleConnection *gc, GHashTable *data); -void qq_request_room_join(PurpleConnection *gc, qq_room_data *rmd); -void qq_room_quit(PurpleConnection *gc, guint32 room_id); -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 - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group_opt.c --- a/libpurple/protocols/qq/group_opt.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,592 +0,0 @@ -/** - * @file group_opt.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 "qq.h" - -#include "debug.h" -#include "notify.h" -#include "request.h" - -#include "buddy_info.h" -#include "char_conv.h" -#include "group_internal.h" -#include "group_info.h" -#include "group_join.h" -#include "group_im.h" -#include "group_opt.h" -#include "qq_define.h" -#include "packet_parse.h" -#include "qq_network.h" -#include "qq_process.h" -#include "utils.h" - -static int _compare_guint32(const void *a, - const void *b) -{ - const guint32 *x = a; - const guint32 *y = b; - return (*x - *y); -} - -static void _sort(guint32 *list) -{ - gint i; - for (i = 0; list[i] < 0xffffffff; i++) {; - } - qsort (list, i, sizeof (guint32), _compare_guint32); -} - -static void _qq_group_member_opt(PurpleConnection *gc, qq_room_data *rmd, gint operation, guint32 *members) -{ - guint8 *data; - gint i, count, data_len; - gint bytes; - g_return_if_fail(members != NULL); - - for (count = 0; members[count] != 0xffffffff; count++) {; - } - data_len = 6 + count * 4; - data = g_newa(guint8, data_len); - - bytes = 0; - bytes += qq_put8(data + bytes, operation); - for (i = 0; i < count; i++) - bytes += qq_put32(data + bytes, members[i]); - - qq_send_room_cmd(gc, QQ_ROOM_CMD_MEMBER_OPT, rmd->id, data, bytes); -} - -static void room_req_cancel_cb(qq_room_req *add_req) -{ - if (add_req != NULL) - g_free(add_req); -} - -static void member_join_authorize_cb(gpointer data) -{ - qq_room_req *add_req = (qq_room_req *)data; - qq_room_data *rmd; - g_return_if_fail(add_req != NULL && add_req->gc != NULL); - g_return_if_fail(add_req->id > 0 && add_req->member > 0); - rmd = qq_room_data_find(add_req->gc, add_req->id); - g_return_if_fail(rmd != NULL); - - qq_send_cmd_group_auth(add_req->gc, rmd, QQ_ROOM_AUTH_REQUEST_APPROVE, add_req->member, ""); - qq_room_buddy_find_or_new(add_req->gc, rmd, add_req->member); - g_free(add_req); -} - -static void member_join_deny_reason_cb(qq_room_req *add_req, gchar *msg_utf8) -{ - qq_room_data *rmd; - g_return_if_fail(add_req != NULL && add_req->gc != NULL); - g_return_if_fail(add_req->id > 0 && add_req->member > 0); - rmd = qq_room_data_find(add_req->gc, add_req->id); - g_return_if_fail(rmd != NULL); - qq_send_cmd_group_auth(add_req->gc, rmd, QQ_ROOM_AUTH_REQUEST_REJECT, add_req->member, msg_utf8); - g_free(add_req); -} - -static void member_join_deny_noreason_cb(qq_room_req *add_req, gchar *msg_utf8) -{ - member_join_deny_reason_cb(add_req, NULL); -} - -static void member_join_deny_cb(gpointer data) -{ - qq_room_req *add_req = (qq_room_req *)data; - gchar *who; - g_return_if_fail(add_req != NULL && add_req->gc != NULL); - g_return_if_fail(add_req->id > 0 && add_req->member > 0); - - who = uid_to_purple_name(add_req->member); - purple_request_input(add_req->gc, NULL, _("Authorization denied message:"), - NULL, _("Sorry, you are not our style"), TRUE, FALSE, NULL, - _("OK"), G_CALLBACK(member_join_deny_reason_cb), - _("Cancel"), G_CALLBACK(member_join_deny_noreason_cb), - purple_connection_get_account(add_req->gc), who, NULL, - add_req); - g_free(who); -} - -void qq_group_modify_members(PurpleConnection *gc, qq_room_data *rmd, guint32 *new_members) -{ - guint32 *old_members, *del_members, *add_members; - qq_buddy_data *bd; - gint i = 0, old = 0, new = 0, del = 0, add = 0; - GList *list; - - g_return_if_fail(rmd != NULL); - if (new_members[0] == 0xffffffff) - return; - - old_members = g_newa(guint32, QQ_QUN_MEMBER_MAX); - del_members = g_newa(guint32, QQ_QUN_MEMBER_MAX); - add_members = g_newa(guint32, QQ_QUN_MEMBER_MAX); - - /* construct the old member list */ - list = rmd->members; - while (list != NULL) { - bd = (qq_buddy_data *) list->data; - if (bd != NULL) - old_members[i++] = bd->uid; - list = list->next; - } - old_members[i] = 0xffffffff; /* this is the end */ - - /* sort to speed up making del_members and add_members list */ - _sort(old_members); - _sort(new_members); - - for (old = 0, new = 0; old_members[old] < 0xffffffff || new_members[new] < 0xffffffff;) { - if (old_members[old] > new_members[new]) { - add_members[add++] = new_members[new++]; - } else if (old_members[old] < new_members[new]) { - del_members[del++] = old_members[old++]; - } else { - if (old_members[old] < 0xffffffff) - old++; - if (new_members[new] < 0xffffffff) - new++; - } - } - del_members[del] = add_members[add] = 0xffffffff; - - for (i = 0; i < del; i++) - qq_room_buddy_remove(rmd, del_members[i]); - for (i = 0; i < add; i++) - qq_room_buddy_find_or_new(gc, rmd, add_members[i]); - - if (del > 0) - _qq_group_member_opt(gc, rmd, QQ_ROOM_MEMBER_DEL, del_members); - if (add > 0) - _qq_group_member_opt(gc, rmd, QQ_ROOM_MEMBER_ADD, add_members); -} - -void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc) -{ - gint bytes; - guint32 id; - time_t now = time(NULL); - qq_room_data *rmd; - g_return_if_fail(data != NULL); - - bytes = 0; - bytes += qq_get32(&id, data + bytes); - g_return_if_fail(id > 0); - - /* we should have its info locally */ - rmd = qq_room_data_find(gc, id); - g_return_if_fail(rmd != NULL); - - purple_debug_info("QQ", "Succeed in modify members for room %u\n", rmd->ext_id); - - qq_room_got_chat_in(gc, id, 0, _("Successfully changed Qun members"), now); -} - -void qq_room_change_info(PurpleConnection *gc, qq_room_data *rmd) -{ - guint8 data[MAX_PACKET_SIZE - 16]; - gint bytes; - - g_return_if_fail(rmd != NULL); - - bytes = 0; - /* 005-005 */ - bytes += qq_put8(data + bytes, 0x01); - /* 006-006 */ - bytes += qq_put8(data + bytes, rmd->auth_type); - /* 007-008 */ - bytes += qq_put16(data + bytes, 0x0000); - /* 009-010 */ - bytes += qq_put16(data + bytes, rmd->category); - - bytes += qq_put_vstr(data + bytes, rmd->title_utf8, QQ_CHARSET_DEFAULT); - - bytes += qq_put16(data + bytes, 0x0000); - - bytes += qq_put_vstr(data + bytes, rmd->notice_utf8, QQ_CHARSET_DEFAULT); - bytes += qq_put_vstr(data + bytes, rmd->desc_utf8, QQ_CHARSET_DEFAULT); - - qq_send_room_cmd(gc, QQ_ROOM_CMD_CHANGE_INFO, rmd->id, data, bytes); -} - -void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc) -{ - gint bytes; - guint32 id; - time_t now = time(NULL); - - g_return_if_fail(data != NULL); - - bytes = 0; - bytes += qq_get32(&id, data + bytes); - g_return_if_fail(id > 0); - - purple_debug_info("QQ", "Successfully modified room info of %u\n", id); - - qq_room_got_chat_in(gc, id, 0, _("Successfully changed Qun information"), now); -} - -/* we create a very simple room first, and then let the user to modify */ -void qq_create_room(PurpleConnection *gc, const gchar *name) -{ - guint8 *data; - gint data_len; - gint bytes; - qq_data *qd; - g_return_if_fail(name != NULL); - - qd = (qq_data *) gc->proto_data; - - data_len = 64 + strlen(name); - data = g_newa(guint8, data_len); - - bytes = 0; - /* we create the simpleset group, only group name is given */ - /* 001 */ - bytes += qq_put8(data + bytes, QQ_ROOM_TYPE_PERMANENT); - /* 002 */ - bytes += qq_put8(data + bytes, QQ_ROOM_AUTH_TYPE_NEED_AUTH); - /* 003-004 */ - bytes += qq_put16(data + bytes, 0x0000); - /* 005-006 */ - bytes += qq_put16(data + bytes, 0x0003); - /* 007 */ - 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 (bytes > data_len) { - purple_debug_error("QQ", - "Overflow in qq_room_create, max %d bytes, now %d bytes\n", - data_len, bytes); - return; - } - qq_send_room_cmd_noid(gc, QQ_ROOM_CMD_CREATE, data, bytes); -} - -static void room_create_cb(qq_room_req *add_req) -{ - qq_room_data *rmd; - g_return_if_fail(add_req != NULL); - if (add_req->gc == NULL || add_req->id == 0) { - g_free(add_req); - return; - } - - rmd = qq_room_data_find(add_req->gc, add_req->id); - if (rmd == NULL) { - g_free(add_req); - return; - } - - /* TODO insert UI code here */ - /* qq_group_detail_window_show(g->gc, rmd); */ - g_free(add_req); -} - -void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc) -{ - gint bytes; - guint32 id, ext_id; - qq_room_data *rmd; - qq_room_req *add_req; - qq_data *qd; - - g_return_if_fail(data != NULL); - g_return_if_fail(gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - bytes = 0; - bytes += qq_get32(&id, data + bytes); - bytes += qq_get32(&ext_id, data + bytes); - g_return_if_fail(id > 0 && ext_id); - - qq_room_find_or_new(gc, id, ext_id); - rmd = qq_room_data_find(gc, id); - g_return_if_fail(rmd != NULL); - - rmd->my_role = QQ_ROOM_ROLE_ADMIN; - rmd->creator_uid = qd->uid; - - qq_send_room_cmd_only(gc, QQ_ROOM_CMD_ACTIVATE, id); - qq_update_room(gc, 0, rmd->id); - - purple_debug_info("QQ", "Succeed in create Qun, ext id %u\n", rmd->ext_id); - - add_req = g_new0(qq_room_req, 1); - add_req->gc = gc; - add_req->id = id; - - purple_request_action(gc, _("QQ Qun Operation"), - _("You have successfully created a Qun"), - _("Would you like to set up detailed information now?"), - 1, - purple_connection_get_account(gc), NULL, NULL, - add_req, 2, - _("Setup"), G_CALLBACK(room_create_cb), - _("Cancel"), G_CALLBACK(room_req_cancel_cb)); -} - -void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc) -{ - gint bytes; - guint32 id; - qq_room_data *rmd; - g_return_if_fail(data != NULL); - - bytes = 0; - bytes += qq_get32(&id, data + bytes); - g_return_if_fail(id > 0); - - /* we should have its info locally */ - rmd = qq_room_data_find(gc, id); - g_return_if_fail(rmd != NULL); - - purple_debug_info("QQ", "Succeed in activate Qun %u\n", rmd->ext_id); -} - -void qq_group_manage_group(PurpleConnection *gc, GHashTable *data) -{ - gchar *id_ptr; - guint32 id; - qq_room_data *rmd; - - g_return_if_fail(data != NULL); - - id_ptr = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID); - id = strtoul(id_ptr, NULL, 10); - g_return_if_fail(id > 0); - - rmd = qq_room_data_find(gc, id); - g_return_if_fail(rmd != NULL); - - /* XXX insert UI code here */ - /* qq_group_detail_window_show(gc, rmd); */ -} - -/* receive an application to join the group */ -void qq_process_room_buddy_request_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc) -{ - guint32 ext_id, member_id; - guint8 type8; - gchar *msg, *reason; - qq_room_req *add_req; - gchar *who; - gint bytes = 0; - qq_room_data *rmd; - time_t now = time(NULL); - - g_return_if_fail(id > 0 && data != NULL && len > 0); - - /* FIXME: check length here */ - - bytes += qq_get32(&ext_id, data + bytes); - bytes += qq_get8(&type8, data + bytes); - bytes += qq_get32(&member_id, data + bytes); - - g_return_if_fail(ext_id > 0 && member_id > 0); - - bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes); - - purple_debug_info("QQ", "%u requested to join room, ext id %u\n", member_id, ext_id); - - rmd = qq_room_data_find(gc, id); - g_return_if_fail(rmd != NULL); - if (qq_room_buddy_find(rmd, member_id)) { - purple_debug_info("QQ", "Approve join, buddy joined before\n"); - msg = g_strdup_printf(_("%u requested to join Qun %u for %s"), - member_id, ext_id, reason); - qq_room_got_chat_in(gc, id, 0, msg, now); - qq_send_cmd_group_auth(gc, rmd, QQ_ROOM_AUTH_REQUEST_APPROVE, member_id, ""); - g_free(msg); - g_free(reason); - return; - } - - if (purple_prefs_get_bool("/plugins/prpl/qq/auto_get_authorize_info")) { - qq_request_buddy_info(gc, member_id, 0, QQ_BUDDY_INFO_DISPLAY); - } - who = uid_to_purple_name(member_id); - msg = g_strdup_printf(_("%u request to join Qun %u"), member_id, ext_id); - - add_req = g_new0(qq_room_req, 1); - add_req->gc = gc; - add_req->id = id; - add_req->member = member_id; - - purple_request_action(gc, _("QQ Qun Operation"), - msg, reason, - PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(gc), who, NULL, - add_req, 2, - _("Deny"), G_CALLBACK(member_join_deny_cb), - _("Authorize"), G_CALLBACK(member_join_authorize_cb)); - - g_free(who); - g_free(msg); - g_free(reason); -} - -/* the request to join a group is rejected */ -void qq_process_room_buddy_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc) -{ - guint32 ext_id, admin_uid; - guint8 type8; - gchar *msg, *reason; - qq_room_data *rmd; - gint bytes; - - g_return_if_fail(data != NULL && len > 0); - - /* FIXME: check length here */ - bytes = 0; - bytes += qq_get32(&ext_id, data + bytes); - bytes += qq_get8(&type8, data + bytes); - bytes += qq_get32(&admin_uid, data + bytes); - - g_return_if_fail(ext_id > 0 && admin_uid > 0); - - bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes); - - msg = g_strdup_printf - (_("Failed to join Qun %u, operated by admin %u"), ext_id, admin_uid); - - purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason); - - qq_room_find_or_new(gc, id, ext_id); - rmd = qq_room_data_find(gc, id); - if (rmd != NULL) { - rmd->my_role = QQ_ROOM_ROLE_NO; - } - - g_free(msg); - g_free(reason); -} - -/* the request to join a group is approved */ -void qq_process_room_buddy_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc) -{ - guint32 ext_id, admin_uid; - guint8 type8; - gchar *msg, *reason; - qq_room_data *rmd; - gint bytes; - time_t now; - - g_return_if_fail(data != NULL && len > 0); - - /* FIXME: check length here */ - bytes = 0; - bytes += qq_get32(&ext_id, data + bytes); - bytes += qq_get8(&type8, data + bytes); - bytes += qq_get32(&admin_uid, data + bytes); - - g_return_if_fail(ext_id > 0 && admin_uid > 0); - /* it is also a "无" here, so do not display */ - bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes); - - qq_room_find_or_new(gc, id, ext_id); - rmd = qq_room_data_find(gc, id); - if (rmd != NULL) { - rmd->my_role = QQ_ROOM_ROLE_YES; - } - - msg = g_strdup_printf(_("Joining Qun %u is approved by admin %u for %s"), - ext_id, admin_uid, reason); - now = time(NULL); - qq_room_got_chat_in(gc, id, 0, msg, now); - - g_free(msg); - g_free(reason); -} - -/* process the packet when removed from a group */ -void qq_process_room_buddy_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc) -{ - guint32 ext_id, uid; - guint8 type8; - gchar *msg; - qq_room_data *rmd; - gint bytes = 0; - time_t now = time(NULL); - - g_return_if_fail(data != NULL && len > 0); - - /* FIXME: check length here */ - bytes = 0; - bytes += qq_get32(&ext_id, data + bytes); - bytes += qq_get8(&type8, data + bytes); - bytes += qq_get32(&uid, data + bytes); - - g_return_if_fail(ext_id > 0 && uid > 0); - - qq_room_find_or_new(gc, id, ext_id); - rmd = qq_room_data_find(gc, id); - if (rmd != NULL) { - rmd->my_role = QQ_ROOM_ROLE_NO; - } - - msg = g_strdup_printf(_("Removed buddy %u."), uid); - qq_room_got_chat_in(gc, id, 0, msg, now); - g_free(msg); -} - -/* process the packet when added to a group */ -void qq_process_room_buddy_joined(guint8 *data, gint len, guint32 id, PurpleConnection *gc) -{ - guint32 ext_id, uid; - guint8 type8; - qq_room_data *rmd; - gint bytes; - gchar *msg; - time_t now = time(NULL); - - g_return_if_fail(data != NULL && len > 0); - - /* FIXME: check length here */ - bytes = 0; - bytes += qq_get32(&ext_id, data + bytes); - bytes += qq_get8(&type8, data + bytes); - bytes += qq_get32(&uid, data + bytes); - - g_return_if_fail(ext_id > 0 && id > 0); - - qq_room_find_or_new(gc, id, ext_id); - rmd = qq_room_data_find(gc, id); - g_return_if_fail(rmd != NULL); - - rmd->my_role = QQ_ROOM_ROLE_YES; - - qq_update_room(gc, 0, rmd->id); - - msg = g_strdup_printf(_("New buddy %u joined."), uid); - qq_room_got_chat_in(gc, id, 0, msg, now); - g_free(msg); -} - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/group_opt.h --- a/libpurple/protocols/qq/group_opt.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/** - * @file group_opt.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_GROUP_OPT_H_ -#define _QQ_GROUP_OPT_H_ - -#include -#include "connection.h" -#include "group.h" - -#define QQ_QUN_MEMBER_MAX 80 /* max number of the group */ - -typedef struct _qq_room_req { - PurpleConnection *gc; - guint32 id; - guint32 member; -} qq_room_req; - -enum { - QQ_ROOM_TYPE_PERMANENT = 0x01, - QQ_ROOM_TYPE_TEMPORARY -}; - -enum { - QQ_ROOM_MEMBER_ADD = 0x01, - QQ_ROOM_MEMBER_DEL -}; - -void qq_group_modify_members(PurpleConnection *gc, qq_room_data *rmd, guint32 *new_members); -void qq_room_change_info(PurpleConnection *gc, qq_room_data *rmd); - -void qq_create_room(PurpleConnection *gc, const gchar *name); -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_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc); -void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc); - -void qq_process_room_buddy_request_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc); -void qq_process_room_buddy_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc); -void qq_process_room_buddy_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc); -void qq_process_room_buddy_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc); -void qq_process_room_buddy_joined(guint8 *data, gint len, guint32 id, PurpleConnection *gc); -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/im.c --- a/libpurple/protocols/qq/im.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1325 +0,0 @@ -/** - * @file im.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 "conversation.h" -#include "debug.h" -#include "internal.h" -#include "notify.h" -#include "server.h" -#include "util.h" - -#include "buddy_info.h" -#include "buddy_list.h" -#include "buddy_opt.h" -#include "char_conv.h" -#include "qq_define.h" -#include "im.h" -#include "packet_parse.h" -#include "qq_network.h" -#include "send_file.h" -#include "utils.h" - -#define QQ_MSG_IM_MAX 700 /* max length of IM */ - -enum { - QQ_IM_TEXT = 0x01, - QQ_IM_AUTO_REPLY = 0x02 -}; - -enum -{ - 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, - QQ_NORMAL_IM_FILE_REQUEST_UDP = 0x0035, - QQ_NORMAL_IM_FILE_APPROVE_UDP = 0x0037, - QQ_NORMAL_IM_FILE_REJECT_UDP = 0x0039, - QQ_NORMAL_IM_FILE_NOTIFY = 0x003b, - QQ_NORMAL_IM_FILE_PASV = 0x003f, /* are you behind a firewall? */ - QQ_NORMAL_IM_FILE_CANCEL = 0x0049, - QQ_NORMAL_IM_FILE_EX_REQUEST_UDP = 0x81, - QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT = 0x83, - QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL = 0x85, - QQ_NORMAL_IM_FILE_EX_NOTIFY_IP = 0x87 -}; - -typedef struct _qq_im_header { - /* this is the common part of normal_text */ - guint16 version_from; - UID uid_from; - UID uid_to; - guint8 session_md5[QQ_KEY_LENGTH]; - guint16 im_type; -} qq_im_header; - -/* read the common parts of the normal_im, - * returns the bytes read if succeed, or -1 if there is any error */ -static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len) -{ - gint bytes; - g_return_val_if_fail(data != NULL && len > 0, -1); - - bytes = 0; - bytes += qq_get16(&(im_header->version_from), data + bytes); - bytes += qq_get32(&(im_header->uid_from), data + bytes); - bytes += qq_get32(&(im_header->uid_to), data + bytes); - bytes += qq_getdata(im_header->session_md5, QQ_KEY_LENGTH, data + bytes); - bytes += qq_get16(&(im_header->im_type), data + bytes); - return bytes; -} - -typedef struct _qq_emoticon { - guint8 symbol; - gchar *name; -} qq_emoticon; - -static gboolean emoticons_is_sorted = FALSE; -/* Map for purple smiley convert to qq, need qsort */ -static qq_emoticon emoticons_std[] = { - {0x4f, "/:)"}, {0x4f, "/wx"}, {0x4f, "/small_smile"}, - {0x42, "/:~"}, {0x42, "/pz"}, {0x42, "/curl_lip"}, - {0x43, "/:*"}, {0x43, "/se"}, {0x43, "/desire"}, - {0x44, "/:|"}, {0x44, "/fd"}, {0x44, "/dazed"}, - {0x45, "/8-)"}, {0x45, "/dy"}, {0x45, "/revel"}, - {0x46, "/:<"}, {0x46, "/ll"}, {0x46, "/cry"}, - {0x47, "/:$"}, {0x47, "/hx"}, {0x47, "/bashful"}, - {0x48, "/:x"}, {0x48, "/bz"}, {0x48, "/shut_mouth"}, - {0x8f, "/|-)"}, {0x8f, "/kun"}, {0x8f, "/sleepy"}, - {0x49, "/:z"}, {0x49, "/shui"}, {0x49, "/sleep"}, /* after sleepy */ - {0x4a, "/:'"}, {0x4a, "/dk"}, {0x4a, "/weep"}, - {0x4b, "/:-|"}, {0x4b, "/gg"}, {0x4b, "/embarassed"}, - {0x4c, "/:@"}, {0x4c, "/fn"}, {0x4c, "/pissed_off"}, - {0x4d, "/:P"}, {0x4d, "/tp"}, {0x4d, "/act_up"}, - {0x4e, "/:D"}, {0x4e, "/cy"}, {0x4e, "/toothy_smile"}, - {0x41, "/:O"}, {0x41, "/jy"}, {0x41, "/surprised"}, - {0x73, "/:("}, {0x73, "/ng"}, {0x73, "/sad"}, - {0x74, "/:+"}, {0x74, "/kuk"}, {0x74, "/cool"}, - {0xa1, "/--b"}, {0xa1, "/lengh"}, - {0x76, "/:Q"}, {0x76, "/zk"}, {0x76, "/crazy"}, - {0x8a, "/;P"}, {0x8a, "/tx"}, {0x8a, "/titter"}, - {0x8b, "/;-D"}, {0x8b, "/ka"}, {0x8b, "/cute"}, - {0x8c, "/;d"}, {0x8c, "/by"}, {0x8c, "/disdain"}, - {0x8d, "/;o"}, {0x8d, "/am"}, {0x8d, "/arrogant"}, - {0x8e, "/:g"}, {0x8e, "/jie"}, {0x8e, "/starving"}, - {0x78, "/:!"}, {0x78, "/jk"}, {0x78, "/terror"}, - {0x79, "/:L"}, {0x79, "/lh"}, {0x79, "/sweat"}, - {0x7a, "/:>"}, {0x7a, "/hanx"}, {0x7a, "/smirk"}, - {0x7b, "/:;"}, {0x7b, "/db"}, {0x7b, "/soldier"}, - {0x90, "/;f"}, {0x90, "/fendou"}, {0x90, "/struggle"}, - {0x91, "/:-S"}, {0x91, "/zhm"}, {0x91, "/curse"}, - {0x92, "/?"}, {0x92, "/yiw"}, {0x92, "/question"}, - {0x93, "/;x"}, {0x93, "/xu"}, {0x93, "/shh"}, - {0x94, "/;@"}, {0x94, "/yun"}, {0x94, "/dizzy"}, - {0x95, "/:8"}, {0x95, "/zhem"}, {0x95, "/excrutiating"}, - {0x96, "/;!"}, {0x96, "/shuai"}, {0x96, "/freaked_out"}, - {0x97, "/!!!"}, {0x97, "/kl"}, {0x97, "/skeleton"}, - {0x98, "/xx"}, {0x98, "/qiao"}, {0x98, "/hammer"}, - {0x99, "/bye"}, {0x99, "/zj"}, {0x99, "/bye"}, - {0xa2, "/wipe"}, {0xa2, "/ch"}, - {0xa3, "/dig"}, {0xa3, "/kb"}, - {0xa4, "/handclap"},{0xa4, "/gz"}, - {0xa5, "/&-("}, {0xa5, "/qd"}, - {0xa6, "/B-)"}, {0xa6, "/huaix"}, - {0xa7, "/<@"}, {0xa7, "/zhh"}, - {0xa8, "/@>"}, {0xa8, "/yhh"}, - {0xa9, "/:-O"}, {0xa9, "/hq"}, - {0xaa, "/>-|"}, {0xaa, "/bs"}, - {0xab, "/P-("}, {0xab, "/wq"}, - {0xac, "/:'|"}, {0xac, "/kk"}, - {0xad, "/X-)"}, {0xad, "/yx"}, - {0xae, "/:*"}, {0xae, "/qq"}, - {0xaf, "/@x"}, {0xaf, "/xia"}, - {0xb0, "/8*"}, {0xb0, "/kel"}, - {0xb1, "/pd"}, {0xb1, "/cd"}, - {0x61, "/"}, {0x61, "/xig"}, {0x61, "/watermelon"}, - {0xb2, "/beer"}, {0xb2, "/pj"}, - {0xb3, "/basketb"}, {0xb3, "/lq"}, - {0xb4, "/oo"}, {0xb4, "/pp"}, - {0x80, "/coffee"}, {0x80, "/kf"}, - {0x81, "/eat"}, {0x81, "/fan"}, - {0x62, "/rose"}, {0x62, "/mg"}, - {0x63, "/fade"}, {0x63, "/dx"}, {0x63, "/wilt"}, - {0xb5, "/showlove"},{0xb5, "/sa"}, /* after sad */ - {0x65, "/heart"}, {0x65, "/xin"}, - {0x66, "/break"}, {0x66, "/xs"}, {0x66, "/broken_heart"}, - {0x67, "/cake"}, {0x67, "/dg"}, - {0x9c, "/li"}, {0x9c, "/shd"}, {0x9c, "/lightning"}, - {0x9d, "/bome"}, {0x9d, "/zhd"}, {0x9d, "/bomb"}, - {0x9e, "/kn"}, {0x9e, "/dao"}, {0x9e, "/knife"}, - {0x5e, "/footb"}, {0x5e, "/zq"}, {0x5e, "/soccer"}, - {0xb6, "/ladybug"}, {0xb6, "/pc"}, - {0x89, "/shit"}, {0x89, "/bb"}, - {0x6e, "/moon"}, {0x6e, "/yl"}, - {0x6b, "/sun"}, {0x6b, "/ty"}, - {0x68, "/gift"}, {0x68, "/lw"}, - {0x7f, "/hug"}, {0x7f, "/yb"}, - {0x6f, "/strong"}, {0x6f, "/qiang"}, {0x6f, "/thumbs_up"}, - {0x70, "/weak"}, {0x70, "/ruo"}, {0x70, "/thumbs_down"}, - {0x88, "/share"}, {0x88, "/ws"}, {0x88, "/handshake"}, - {0xb7, "/@)"}, {0xb7, "/bq"}, - {0xb8, "/jj"}, {0xb8, "/gy"}, - {0xb9, "/@@"}, {0xb9, "/qt"}, - {0xba, "/bad"}, {0xba, "/cj"}, - {0xbb, "/loveu"}, {0xbb, "/aini"}, - {0xbc, "/no"}, {0xbc, "/bu"}, - {0xbd, "/ok"}, {0xbd, "/hd"}, - {0x5c, "/love"}, {0x5c, "/aiq"}, /* after loveu */ - {0x56, "/"}, {0x56, "/fw"}, {0x56, "/blow_kiss"}, - {0x58, "/jump"}, {0x58, "/tiao"}, - {0x5a, "/shake"}, {0x5a, "/fad"}, /* after fade */ - {0x5b, "/"}, {0x5b, "/oh"}, {0x5b, "/angry"}, - {0xbe, "/circle"}, {0xbe, "/zhq"}, - {0xbf, "/kotow"}, {0xbf, "/kt"}, - {0xc0, "/turn"}, {0xc0, "/ht"}, - {0x77, "/:t"}, {0x77, "/tu"}, {0x77, "/vomit"}, /* after turn */ - {0xa0, "/victory"}, {0xa0, "/shl"}, {0xa0, "/v"}, /* end of v */ - {0xc1, "/skip"}, {0xc1, "/tsh"}, - {0xc2, "/oY"}, {0xc2, "/hsh"}, - {0xc3, "/#-O"}, {0xc3, "/jd"}, - {0xc4, "/hiphop"}, {0xc4, "/jw"}, - {0xc5, "/kiss"}, {0xc5, "/xw"}, - {0xc6, "/<&"}, {0xc6, "/ztj"}, - {0x7c, "/pig"}, {0x7c, "/zt"}, /* after ztj */ - {0xc7, "/&>"}, {0xc7, "/ytj"}, /* must be end of "&" */ - {0x75, "/:#"}, {0x75, "/feid"}, {0x75, "/SARS"}, - {0x59, "/go"}, {0x59, "/shan"}, - {0x57, "/find"}, {0x57, "/zhao"}, {0x57, "/search"}, - {0x55, "/&"}, {0x55, "/mm"}, {0x55, "/beautiful_eyebrows"}, - {0x7d, "/cat"}, {0x7d, "/maom"}, - {0x7e, "/dog"}, {0x7e, "/xg"}, - {0x9a, "/$"}, {0x9a, "/qianc"}, {0x9a, "/money"}, - {0x9b, "/(!)"}, {0x9b, "/dp"}, {0x9b, "/lightbulb"}, - {0x60, "/cup"}, {0x60, "/bei"}, - {0x9f, "/music"}, {0x9f, "/yy"}, - {0x82, "/pill"}, {0x82, "/yw"}, - {0x64, "/kiss"}, {0x64, "/wen"}, - {0x83, "/meeting"}, {0x83, "/hy"}, - {0x84, "/phone"}, {0x84, "/dh"}, - {0x85, "/time"}, {0x85, "/sj"}, - {0x86, "/email"}, {0x86, "/yj"}, - {0x87, "/tv"}, {0x87, "/ds"}, - {0x50, "/"}, {0x50, "/dd"}, - {0x51, "/"}, {0x51, "/mn"}, {0x51, "/beauty"}, - {0x52, "/"}, {0x52, "/hl"}, - {0x53, "/"}, {0x53, "/mamao"}, - {0x54, "/"}, {0x54, "/qz"}, {0x54, "/qq"}, - {0x5d, "/"}, {0x5d, "/bj"}, {0x5d, "/baijiu"}, - {0x5f, "/"}, {0x5f, "/qsh"}, {0x5f, "/soda"}, - {0x69, "/"}, {0x69, "/xy"}, {0x69, "/rain"}, - {0x6a, "/<~>"}, {0x6a, "/duoy"}, {0x6a, "/cloudy"}, - {0x6c, "/"}, {0x6c, "/xr"}, {0x6c, "/snowman"}, - {0x6d, "/<*>"}, {0x6d, "/xixing"}, {0x6d, "/star"}, /* after starving */ - {0x71, "/<00>"}, {0x71, "/nv"}, {0x71, "/woman"}, - {0x72, "/<11>"}, {0x72, "/nan"}, {0x72, "/man"}, - {0, NULL} -}; -gint emoticons_std_num = sizeof(emoticons_std) / sizeof(qq_emoticon) - 1; - -/* Map for purple smiley convert to qq, need qsort */ -static qq_emoticon emoticons_ext[] = { - {0xc7, "/&>"}, {0xa5, "/&-("}, - {0xbb, "/loveu"}, - {0x63, "/fade"}, - {0x8f, "/sleepy"}, {0x73, "/sad"}, {0x8e, "/starving"}, - {0xc0, "/turn"}, - {0xa0, "/victory"}, {0x77, "/vomit"}, - {0xc6, "/ztj"}, - {0, NULL} -}; -gint emoticons_ext_num = sizeof(emoticons_ext) / sizeof(qq_emoticon) - 1; - -/* Map for qq smiley convert to purple */ -static qq_emoticon emoticons_sym[] = { - {0x41, "/jy"}, - {0x42, "/pz"}, - {0x43, "/se"}, - {0x44, "/fd"}, - {0x45, "/dy"}, - {0x46, "/ll"}, - {0x47, "/hx"}, - {0x48, "/bz"}, - {0x49, "/shui"}, - {0x4a, "/dk"}, - {0x4b, "/gg"}, - {0x4c, "/fn"}, - {0x4d, "/tp"}, - {0x4e, "/cy"}, - {0x4f, "/wx"}, - {0x50, "/dd"}, - {0x51, "/mn"}, - {0x52, "/hl"}, - {0x53, "/mamao"}, - {0x54, "/qz"}, - {0x55, "/mm"}, - {0x56, "/fw"}, - {0x57, "/zhao"}, - {0x58, "/tiao"}, - {0x59, "/shan"}, - {0x5a, "/fad"}, - {0x5b, "/oh"}, - {0x5c, "/aiq"}, - {0x5d, "/bj"}, - {0x5e, "/zq"}, - {0x5f, "/qsh"}, - {0x60, "/bei"}, - {0x61, "/xig"}, - {0x62, "/mg"}, - {0x63, "/dx"}, - {0x64, "/wen"}, - {0x65, "/xin"}, - {0x66, "/xs"}, - {0x67, "/dg"}, - {0x68, "/lw"}, - {0x69, "/xy"}, - {0x6a, "/duoy"}, - {0x6b, "/ty"}, - {0x6c, "/xr"}, - {0x6d, "/xixing"}, - {0x6e, "/yl"}, - {0x6f, "/qiang"}, - {0x70, "/ruo"}, - {0x71, "/nv"}, - {0x72, "/nan"}, - {0x73, "/ng"}, - {0x74, "/kuk"}, - {0x75, "/feid"}, - {0x76, "/zk"}, - {0x77, "/tu"}, - {0x78, "/jk"}, - {0x79, "/sweat"}, - {0x7a, "/hanx"}, - {0x7b, "/db"}, - {0x7c, "/zt"}, - {0x7d, "/maom"}, - {0x7e, "/xg"}, - {0x7f, "/yb"}, - {0x80, "/coffee"}, - {0x81, "/fan"}, - {0x82, "/yw"}, - {0x83, "/hy"}, - {0x84, "/dh"}, - {0x85, "/sj"}, - {0x86, "/yj"}, - {0x87, "/ds"}, - {0x88, "/ws"}, - {0x89, "/bb"}, - {0x8a, "/tx"}, - {0x8b, "/ka"}, - {0x8c, "/by"}, - {0x8d, "/am"}, - {0x8e, "/jie"}, - {0x8f, "/kun"}, - {0x90, "/fendou"}, - {0x91, "/zhm"}, - {0x92, "/yiw"}, - {0x93, "/xu"}, - {0x94, "/yun"}, - {0x95, "/zhem"}, - {0x96, "/shuai"}, - {0x97, "/kl"}, - {0x98, "/qiao"}, - {0x99, "/zj"}, - {0x9a, "/qianc"}, - {0x9b, "/dp"}, - {0x9c, "/shd"}, - {0x9d, "/zhd"}, - {0x9e, "/dao"}, - {0x9f, "/yy"}, - {0xa0, "/shl"}, - {0xa1, "/lengh"}, - {0xa2, "/wipe"}, - {0xa3, "/kb"}, - {0xa4, "/gz"}, - {0xa5, "/qd"}, - {0xa6, "/huaix"}, - {0xa7, "/zhh"}, - {0xa8, "/yhh"}, - {0xa9, "/hq"}, - {0xaa, "/bs"}, - {0xab, "/wq"}, - {0xac, "/kk"}, - {0xad, "/yx"}, - {0xae, "/qq"}, - {0xaf, "/xia"}, - {0xb0, "/kel"}, - {0xb1, "/cd"}, - {0xb2, "/pj"}, - {0xb3, "/lq"}, - {0xb4, "/pp"}, - {0xb5, "/sa"}, - {0xb6, "/pc"}, - {0xb7, "/bq"}, - {0xb8, "/gy"}, - {0xb9, "/qt"}, - {0xba, "/cj"}, - {0xbb, "/aini"}, - {0xbc, "/bu"}, - {0xbd, "/hd"}, - {0xbe, "/zhq"}, - {0xbf, "/kt"}, - {0xc0, "/ht"}, - {0xc1, "/tsh"}, - {0xc2, "/hsh"}, - {0xc3, "/jd"}, - {0xc4, "/jw"}, - {0xc5, "/xw"}, - {0xc6, "/ztj"}, - {0xc7, "/ytj"}, - {0, NULL} -}; -gint emoticons_sym_num = sizeof(emoticons_sym) / sizeof(qq_emoticon) - 1;; - -static int emoticon_cmp(const void *k1, const void *k2) -{ - const qq_emoticon *e1 = (const qq_emoticon *) k1; - const qq_emoticon *e2 = (const qq_emoticon *) k2; - if (e1->symbol == 0) { - /* purple_debug_info("QQ", "emoticon_cmp len %d\n", strlen(e2->name)); */ - return strncmp(e1->name, e2->name, strlen(e2->name)); - } - if (e2->symbol == 0) { - /* purple_debug_info("QQ", "emoticon_cmp len %d\n", strlen(e1->name)); */ - return strncmp(e1->name, e2->name, strlen(e1->name)); - } - return strcmp(e1->name, e2->name); -} - -static void emoticon_try_sort() -{ - if (emoticons_is_sorted) - return; - - purple_debug_info("QQ", "qsort stand emoticons\n"); - qsort(emoticons_std, emoticons_std_num, sizeof(qq_emoticon), emoticon_cmp); - purple_debug_info("QQ", "qsort extend emoticons\n"); - qsort(emoticons_ext, emoticons_ext_num, sizeof(qq_emoticon), emoticon_cmp); - emoticons_is_sorted = TRUE; -} - -static qq_emoticon *emoticon_find(gchar *name) -{ - qq_emoticon *ret = NULL; - qq_emoticon key; - - g_return_val_if_fail(name != NULL, NULL); - emoticon_try_sort(); - - key.name = name; - key.symbol = 0; - - /* purple_debug_info("QQ", "bsearch emoticon %.20s\n", name); */ - ret = (qq_emoticon *)bsearch(&key, emoticons_ext, emoticons_ext_num, - sizeof(qq_emoticon), emoticon_cmp); - if (ret != NULL) { - return ret; - } - ret = (qq_emoticon *)bsearch(&key, emoticons_std, emoticons_std_num, - sizeof(qq_emoticon), emoticon_cmp); - return ret; -} - -static gchar *emoticon_get(guint8 symbol) -{ - g_return_val_if_fail(symbol >= emoticons_sym[0].symbol, NULL); - g_return_val_if_fail(symbol <= emoticons_sym[emoticons_sym_num - 2].symbol, NULL); - - return emoticons_sym[symbol - emoticons_sym[0].symbol].name; -} - -/* convert qq emote icon to purple sytle - Notice: text is in qq charset, GB18030 or utf8 */ -gchar *qq_emoticon_to_purple(gchar *text) -{ - gchar *ret; - GString *converted; - gchar **segments; - gboolean have_smiley; - gchar *purple_smiley; - gchar *cur; - guint8 symbol; - - /* qq_show_packet("text", (guint8 *)text, strlen(text)); */ - g_return_val_if_fail(text != NULL && strlen(text) != 0, g_strdup("")); - - while ((cur = strchr(text, '\x14')) != NULL) - *cur = '\x15'; - - segments = g_strsplit(text, "\x15", 0); - if(segments == NULL) { - return g_strdup(""); - } - - converted = g_string_new(""); - have_smiley = FALSE; - if (segments[0] != NULL) { - g_string_append(converted, segments[0]); - } else { - purple_debug_info("QQ", "segments[0] is NULL\n"); - } - while ((*(++segments)) != NULL) { - have_smiley = TRUE; - - cur = *segments; - if (cur == NULL) { - purple_debug_info("QQ", "current segment is NULL\n"); - break; - } - if (strlen(cur) == 0) { - purple_debug_info("QQ", "current segment length is 0\n"); - break; - } - symbol = (guint8)cur[0]; - - purple_smiley = emoticon_get(symbol); - if (purple_smiley == NULL) { - purple_debug_info("QQ", "Not found smiley of 0x%02X\n", symbol); - g_string_append(converted, ""); - } else { - purple_debug_info("QQ", "Found 0x%02X smiley is %s\n", symbol, purple_smiley); - g_string_append(converted, purple_smiley); - g_string_append(converted, cur + 1); - } - /* purple_debug_info("QQ", "next segment\n"); */ - } - - /* purple_debug_info("QQ", "end of convert\n"); */ - if (!have_smiley) { - g_string_prepend(converted, ""); - g_string_append(converted, ""); - } - ret = converted->str; - g_string_free(converted, FALSE); - return ret; -} - -void qq_im_fmt_free(qq_im_format *fmt) -{ - g_return_if_fail(fmt != NULL); - if (fmt->font) g_free(fmt->font); - g_free(fmt); -} - -qq_im_format *qq_im_fmt_new(void) -{ - qq_im_format *fmt; - /* '0xcb, 0xce, 0xcc, 0xe5' means Chinese '宋体' in utf8 */ - const gchar simsun[] = { 0xcb, 0xce, 0xcc, 0xe5, 0}; - - fmt = g_new0(qq_im_format, 1); - memset(fmt, 0, sizeof(qq_im_format)); - fmt->font_len = strlen(simsun); - fmt->font = g_strdup(simsun); - fmt->attr = 10; - /* encoding, 0x8602=GB, 0x0000=EN, define BIG5 support here */ - fmt->charset = 0x8602; - - return fmt; -} - -void qq_im_fmt_reset_font(qq_im_format *fmt) -{ - const gchar simsun[] = {0xcb, 0xce, 0xcc, 0xe5, 0x00}; - g_return_if_fail(NULL != fmt); - - if (NULL != fmt->font) { - g_free(fmt->font); - fmt->font = g_strdup(simsun); - } -} - -qq_im_format *qq_im_fmt_new_by_purple(const gchar *msg) -{ - qq_im_format *fmt; - const gchar *start, *end, *last; - GData *attribs; - gchar *tmp; - - g_return_val_if_fail(msg != NULL, NULL); - - fmt = qq_im_fmt_new(); - - last = msg; - while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { - tmp = g_datalist_get_data(&attribs, "face"); - if (tmp && strlen(tmp) > 0) { - if (fmt->font) g_free(fmt->font); - fmt->font_len = strlen(tmp); - fmt->font = g_strdup(tmp); - } - - tmp = g_datalist_get_data(&attribs, "size"); - if (tmp) { - fmt->attr = atoi(tmp) * 3 + 1; - fmt->attr &= 0x0f; - } - - tmp = g_datalist_get_data(&attribs, "color"); - if (tmp && strlen(tmp) > 1) { - unsigned char *rgb; - gsize rgb_len; - rgb = purple_base16_decode(tmp + 1, &rgb_len); - if (rgb != NULL && rgb_len >= 3) - g_memmove(fmt->rgb, rgb, 3); - g_free(rgb); - } - - g_datalist_clear(&attribs); - last = end + 1; - } - - if (purple_markup_find_tag("b", msg, &start, &end, &attribs)) { - fmt->attr |= 0x20; - g_datalist_clear(&attribs); - } - - if (purple_markup_find_tag("i", msg, &start, &end, &attribs)) { - fmt->attr |= 0x40; - g_datalist_clear(&attribs); - } - - if (purple_markup_find_tag("u", msg, &start, &end, &attribs)) { - fmt->attr |= 0x80; - g_datalist_clear(&attribs); - } - - return fmt; -} - -/* convert qq format to purple - Notice: text is in qq charset, GB18030 or utf8 */ -gchar *qq_im_fmt_to_purple(qq_im_format *fmt, gchar *text) -{ - GString *converted, *tmp; - gchar *ret; - gint size; - - converted = g_string_new(text); - tmp = g_string_new(""); - g_string_append_printf(tmp, "", - fmt->rgb[0], fmt->rgb[1], fmt->rgb[2]); - g_string_prepend(converted, tmp->str); - g_string_set_size(tmp, 0); - g_string_append(converted, ""); - - /* Fixme: - * check font face can be convert to utf8 or not? - * If failed, prepending font face cause msg display as "(NULL)" */ - if (fmt->font != NULL) { - g_string_append_printf(tmp, "", fmt->font); - g_string_prepend(converted, tmp->str); - g_string_set_size(tmp, 0); - g_string_append(converted, ""); - } - size = (fmt->attr & 0x1f) / 3; - if (size >= 0) { - g_string_append_printf(tmp, "", size); - g_string_prepend(converted, tmp->str); - g_string_set_size(tmp, 0); - g_string_append(converted, ""); - } - if (fmt->attr & 0x20) { - /* bold */ - g_string_prepend(converted, ""); - g_string_append(converted, ""); - } - if (fmt->attr & 0x40) { - /* italic */ - g_string_prepend(converted, ""); - g_string_append(converted, ""); - } - if (fmt->attr & 0x80) { - /* underline */ - g_string_prepend(converted, ""); - g_string_append(converted, ""); - } - - g_string_free(tmp, TRUE); - ret = converted->str; - g_string_free(converted, FALSE); - return ret; -} - -gint qq_put_im_tail(guint8 *buf, qq_im_format *fmt) -{ - gint bytes; - - g_return_val_if_fail(buf != NULL && fmt != NULL, 0); - - bytes = 0; - bytes += qq_put8(buf + bytes, 0); - bytes += qq_put8(buf + bytes, fmt->attr); - bytes += qq_putdata(buf + bytes, fmt->rgb, sizeof(fmt->rgb)); - bytes += qq_put8(buf + bytes, 0); - bytes += qq_put16(buf + bytes, fmt->charset); - if (fmt->font != NULL && fmt->font_len > 0) { - bytes += qq_putdata(buf + bytes, (guint8 *)fmt->font, fmt->font_len); - } else { - purple_debug_warning("QQ", "Font name is empty\n"); - } - bytes += qq_put8(buf + bytes, bytes + 1); - /* qq_show_packet("IM tail", buf, bytes); */ - return bytes; -} - -/* data includes text msg and font attr*/ -gint qq_get_im_tail(qq_im_format *fmt, guint8 *data, gint data_len) -{ - gint bytes, text_len; - guint8 tail_len; - guint8 font_len; - - g_return_val_if_fail(fmt != NULL && data != NULL, 0); - g_return_val_if_fail(data_len > 1, 0); - tail_len = data[data_len - 1]; - g_return_val_if_fail(tail_len > 2, 0); - text_len = data_len - tail_len; - g_return_val_if_fail(text_len >= 0, 0); - - bytes = text_len; - /* qq_show_packet("IM tail", data + bytes, tail_len); */ - bytes += 1; /* skip 0x00 */ - bytes += qq_get8(&fmt->attr, data + bytes); - bytes += qq_getdata(fmt->rgb, sizeof(fmt->rgb), data + bytes); /* red,green,blue */ - bytes += 1; /* skip 0x00 */ - bytes += qq_get16(&fmt->charset, data + bytes); - - font_len = data_len - bytes - 1; - g_return_val_if_fail(font_len > 0, bytes + 1); - - fmt->font_len = font_len; - if (fmt->font != NULL) g_free(fmt->font); - fmt->font = g_strndup((gchar *)data + bytes, fmt->font_len); - return tail_len; -} - -void qq_got_message(PurpleConnection *gc, const gchar *msg) -{ - qq_data *qd; - gchar *from; - time_t now = time(NULL); - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = gc->proto_data; - - g_return_if_fail(qd->uid > 0); - - qq_buddy_find_or_new(gc, qd->uid); - - from = uid_to_purple_name(qd->uid); - serv_got_im(gc, from, msg, PURPLE_MESSAGE_SYSTEM, now); - g_free(from); -} - -/* process received normal text IM */ -static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) -{ - guint16 purple_msg_type; - gchar *who; - gchar *msg_smiley, *msg_fmt, *msg_utf8; - PurpleBuddy *buddy; - qq_buddy_data *bd; - gint bytes, tail_len; - qq_im_format *fmt = NULL; - - struct { - /* now comes the part for text only */ - guint16 msg_seq; - guint32 send_time; - guint16 sender_icon; - guint8 unknown1[3]; - guint8 has_font_attr; - guint8 fragment_count; - guint8 fragment_index; - guint8 msg_id; - guint8 unknown2; - guint8 msg_type; - gchar *msg; /* no fixed length, ends with 0x00 */ - } im_text; - - g_return_if_fail(data != NULL && len > 0); - g_return_if_fail(im_header != NULL); - - memset(&im_text, 0, sizeof(im_text)); - - /* qq_show_packet("IM text", data, len); */ - bytes = 0; - bytes += qq_get16(&(im_text.msg_seq), data + bytes); - bytes += qq_get32(&(im_text.send_time), data + bytes); - bytes += qq_get16(&(im_text.sender_icon), data + bytes); - bytes += qq_getdata(im_text.unknown1, sizeof(im_text.unknown1), data + bytes); /* 0x(00 00 00)*/ - bytes += qq_get8(&(im_text.has_font_attr), data + bytes); - bytes += qq_get8(&(im_text.fragment_count), data + bytes); - bytes += qq_get8(&(im_text.fragment_index), data + bytes); - bytes += qq_get8(&(im_text.msg_id), data + bytes); - bytes += 1; /* skip 0x00 */ - bytes += qq_get8(&(im_text.msg_type), data + bytes); - purple_debug_info("QQ", "IM Seq %u, id %04X, fragment %d-%d, type %d, %s\n", - im_text.msg_seq, im_text.msg_id, - im_text.fragment_count, im_text.fragment_index, - im_text.msg_type, - im_text.has_font_attr ? "exist font atrr" : ""); - - if (im_text.has_font_attr) { - fmt = qq_im_fmt_new(); - tail_len = qq_get_im_tail(fmt, data + bytes, len - bytes); - im_text.msg = g_strndup((gchar *)(data + bytes), len - tail_len); - } else { - im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); - } - /* qq_show_packet("IM text", (guint8 *)im_text.msg , strlen(im_text.msg) ); */ - - who = uid_to_purple_name(im_header->uid_from); - buddy = purple_find_buddy(gc->account, who); - if (buddy == NULL) { - /* create no-auth buddy */ - buddy = qq_buddy_new(gc, im_header->uid_from); - } - bd = (buddy == NULL) ? NULL : purple_buddy_get_protocol_data(buddy); - if (bd != NULL) { - bd->client_tag = im_header->version_from; - bd->face = im_text.sender_icon; - qq_update_buddy_icon(gc->account, who, bd->face); - } - - purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY) - ? PURPLE_MESSAGE_AUTO_RESP : 0; - - msg_smiley = qq_emoticon_to_purple(im_text.msg); - if (fmt != NULL) { - msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley); - msg_utf8 = qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT); - g_free(msg_fmt); - qq_im_fmt_free(fmt); - } else { - msg_utf8 = qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT); - } - g_free(msg_smiley); - - /* send encoded to purple, note that we use im_text.send_time, - * not the time we receive the message - * as it may have been delayed when I am not online. */ - purple_debug_info("QQ", "IM from %u: %s\n", im_header->uid_from,msg_utf8); - serv_got_im(gc, who, msg_utf8, purple_msg_type, (time_t) im_text.send_time); - - g_free(msg_utf8); - g_free(who); - g_free(im_text.msg); -} - -/* process received extended (2007) text IM */ -static void process_extend_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) -{ - guint16 purple_msg_type; - gchar *who; - gchar *msg_smiley, *msg_fmt, *msg_utf8; - PurpleBuddy *buddy; - qq_buddy_data *bd; - gint bytes, tail_len; - qq_im_format *fmt = NULL; - - struct { - /* now comes the part for text only */ - guint16 msg_seq; - guint32 send_time; - guint16 sender_icon; - guint32 has_font_attr; - guint8 unknown1[8]; - guint8 fragment_count; - guint8 fragment_index; - guint8 msg_id; - guint8 unknown2; - guint8 msg_type; - gchar *msg; /* no fixed length, ends with 0x00 */ - guint8 fromMobileQQ; - } im_text; - - g_return_if_fail(data != NULL && len > 0); - g_return_if_fail(im_header != NULL); - - memset(&im_text, 0, sizeof(im_text)); - - /* qq_show_packet("Extend IM text", data, len); */ - bytes = 0; - bytes += qq_get16(&(im_text.msg_seq), data + bytes); - bytes += qq_get32(&(im_text.send_time), data + bytes); - bytes += qq_get16(&(im_text.sender_icon), data + bytes); - bytes += qq_get32(&(im_text.has_font_attr), data + bytes); - bytes += qq_getdata(im_text.unknown1, sizeof(im_text.unknown1), data + bytes); - bytes += qq_get8(&(im_text.fragment_count), data + bytes); - bytes += qq_get8(&(im_text.fragment_index), data + bytes); - bytes += qq_get8(&(im_text.msg_id), data + bytes); - bytes += 1; /* skip 0x00 */ - bytes += qq_get8(&(im_text.msg_type), data + bytes); - purple_debug_info("QQ", "IM Seq %u, id %04X, fragment %d-%d, type %d, %s\n", - im_text.msg_seq, im_text.msg_id, - im_text.fragment_count, im_text.fragment_index, - im_text.msg_type, - im_text.has_font_attr ? "exist font atrr" : ""); - - if (im_text.has_font_attr) { - fmt = qq_im_fmt_new(); - tail_len = qq_get_im_tail(fmt, data + bytes, len - bytes); - im_text.msg = g_strndup((gchar *)(data + bytes), len - tail_len); - } else { - im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); - } - /* qq_show_packet("IM text", (guint8 *)im_text.msg , strlen(im_text.msg)); */ - - if(im_text.fragment_count == 0) im_text.fragment_count = 1; - - who = uid_to_purple_name(im_header->uid_from); - buddy = purple_find_buddy(gc->account, who); - if (buddy == NULL) { - /* create no-auth buddy */ - buddy = qq_buddy_new(gc, im_header->uid_from); - } - bd = (buddy == NULL) ? NULL : purple_buddy_get_protocol_data(buddy); - if (bd != NULL) { - bd->client_tag = im_header->version_from; - bd->face = im_text.sender_icon; - qq_update_buddy_icon(gc->account, who, bd->face); - } - - purple_msg_type = 0; - - msg_smiley = qq_emoticon_to_purple(im_text.msg); - if (fmt != NULL) { - msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley); - msg_utf8 = qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT); - g_free(msg_fmt); - qq_im_fmt_free(fmt); - } else { - msg_utf8 = qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT); - } - g_free(msg_smiley); - - /* send encoded to purple, note that we use im_text.send_time, - * not the time we receive the message - * as it may have been delayed when I am not online. */ - serv_got_im(gc, who, msg_utf8, purple_msg_type, (time_t) im_text.send_time); - - g_free(msg_utf8); - g_free(who); - g_free(im_text.msg); -} - -/* it is a normal IM, maybe text or video request */ -void qq_process_im(PurpleConnection *gc, guint8 *data, gint len) -{ - gint bytes = 0; - qq_im_header im_header; - - g_return_if_fail (data != NULL && len > 0); - - bytes = get_im_header(&im_header, data, len); - if (bytes < 0) { - purple_debug_error("QQ", "Fail read im header, len %d\n", len); - qq_show_packet ("IM Header", data, len); - return; - } - purple_debug_info("QQ", - "Got IM to %u, type: %02X from: %u ver: %s (%04X)\n", - im_header.uid_to, im_header.im_type, im_header.uid_from, - qq_get_ver_desc(im_header.version_from), im_header.version_from); - - switch (im_header.im_type) { - case QQ_NORMAL_IM_TEXT: - if (bytes >= len - 1) { - purple_debug_warning("QQ", "Received normal IM text is empty\n"); - return; - } - process_im_text(gc, data + bytes, len - bytes, &im_header); - break; - case QQ_NORMAL_IM_FILE_REJECT_UDP: - qq_process_recv_file_reject(data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_APPROVE_UDP: - qq_process_recv_file_accept(data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_REQUEST_UDP: - qq_process_recv_file_request(data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_CANCEL: - qq_process_recv_file_cancel(data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_NOTIFY: - qq_process_recv_file_notify(data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_REQUEST_TCP: - /* Check ReceivedFileIM::parseContents in eva*/ - /* some client use this function for detect invisable buddy*/ - case QQ_NORMAL_IM_FILE_APPROVE_TCP: - case QQ_NORMAL_IM_FILE_REJECT_TCP: - case QQ_NORMAL_IM_FILE_PASV: - case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP: - case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT: - case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL: - case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP: - qq_show_packet ("Not support", data, len); - break; - default: - /* a simple process here, maybe more later */ - qq_show_packet ("Unknow", data + bytes, len - bytes); - return; - } -} - -/* it is a extended IM, maybe text or video request */ -void qq_process_extend_im(PurpleConnection *gc, guint8 *data, gint len) -{ - gint bytes; - qq_im_header im_header; - - g_return_if_fail (data != NULL && len > 0); - - bytes = get_im_header(&im_header, data, len); - if (bytes < 0) { - purple_debug_error("QQ", "Fail read im header, len %d\n", len); - qq_show_packet ("IM Header", data, len); - return; - } - purple_debug_info("QQ", - "Got Extend IM to %u, type: %02X from: %u ver: %s (%04X)\n", - im_header.uid_to, im_header.im_type, im_header.uid_from, - qq_get_ver_desc(im_header.version_from), im_header.version_from); - - switch (im_header.im_type) { - case QQ_NORMAL_IM_TEXT: - process_extend_im_text(gc, data + bytes, len - bytes, &im_header); - break; - case QQ_NORMAL_IM_FILE_REJECT_UDP: - qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_APPROVE_UDP: - qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_REQUEST_UDP: - qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_CANCEL: - qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_NOTIFY: - qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc); - break; - case QQ_NORMAL_IM_FILE_REQUEST_TCP: - /* Check ReceivedFileIM::parseContents in eva*/ - /* some client use this function for detect invisable buddy*/ - case QQ_NORMAL_IM_FILE_APPROVE_TCP: - case QQ_NORMAL_IM_FILE_REJECT_TCP: - case QQ_NORMAL_IM_FILE_PASV: - case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP: - case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT: - case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL: - case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP: - qq_show_packet ("Not support", data, len); - break; - default: - /* a simple process here, maybe more later */ - qq_show_packet ("Unknow", data + bytes, len - bytes); - break; - } -} - -/* send an IM to uid_to */ -static void request_send_im(PurpleConnection *gc, UID uid_to, gint type, - qq_im_format *fmt, gchar *msg, guint8 id, guint8 frag_count, guint8 frag_index) -{ - qq_data *qd; - guint8 raw_data[MAX_PACKET_SIZE - 16]; - gint bytes; - time_t now; - - qd = (qq_data *) gc->proto_data; - - /* purple_debug_info("QQ", "Send IM %d-%d\n", frag_count, frag_index); */ - bytes = 0; - /* 000-003: receiver uid */ - bytes += qq_put32(raw_data + bytes, qd->uid); - /* 004-007: sender uid */ - bytes += qq_put32(raw_data + bytes, uid_to); - /* 008-009: sender client version */ - bytes += qq_put16(raw_data + bytes, qd->client_tag); - /* 010-013: receiver uid */ - bytes += qq_put32(raw_data + bytes, qd->uid); - /* 014-017: sender uid */ - bytes += qq_put32(raw_data + bytes, uid_to); - /* 018-033: md5 of (uid+session_key) */ - bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); - /* 034-035: message type */ - bytes += qq_put16(raw_data + bytes, QQ_NORMAL_IM_TEXT); - /* 036-037: sequence number */ - bytes += qq_put16(raw_data + bytes, qd->send_seq); - /* 038-041: send time */ - now = time(NULL); - bytes += qq_put32(raw_data + bytes, (guint32) now); - /* 042-043: sender icon */ - bytes += qq_put16(raw_data + bytes, qd->my_icon); - /* 044-046: always 0x00 */ - bytes += qq_put16(raw_data + bytes, 0x0000); - bytes += qq_put8(raw_data + bytes, 0x00); - /* 047-047: always use font attr */ - bytes += qq_put8(raw_data + bytes, 0x01); - /* 048-051: always 0x00 */ - /* Fixme: frag_count, frag_index not working now */ - bytes += qq_put8(raw_data + bytes, frag_count); - bytes += qq_put8(raw_data + bytes, frag_index); - bytes += qq_put8(raw_data + bytes, id); - bytes += qq_put8(raw_data + bytes, 0); - /* 052-052: text message type (normal/auto-reply) */ - bytes += qq_put8(raw_data + bytes, type); - /* 053- : msg ends with 0x00 */ - bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg)); - if (frag_count == frag_index + 1) { - bytes += qq_put8(raw_data + bytes, 0x20); /* add extra SPACE */ - } - bytes += qq_put_im_tail(raw_data + bytes, fmt); - - /* qq_show_packet("QQ_CMD_SEND_IM", raw_data, bytes); */ - qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes); -} - -static void im_convert_and_merge(GString *dest, GString *append) -{ - gchar *converted; - g_return_if_fail(dest != NULL && append != NULL); - - if (append->str == NULL || append->len <= 0) { - return; - } - /* purple_debug_info("QQ", "Append:\n%s\n", append->str); */ - converted = utf8_to_qq(append->str, QQ_CHARSET_DEFAULT); - g_string_append(dest, converted); - g_string_set_size(append, 0); - g_free(converted); -} - -GSList *qq_im_get_segments(gchar *msg_stripped, gboolean is_smiley_none) -{ - GSList *string_list = NULL; - GString *new_string; - GString *append_utf8; - gchar *start, *p; - gint len; - qq_emoticon *emoticon; - - g_return_val_if_fail(msg_stripped != NULL, NULL); - - start = msg_stripped; - new_string = g_string_new(""); - append_utf8 = g_string_new(""); - while (*start) { - p = start; - - /* Convert emoticon */ - if (!is_smiley_none && *p == '/') { - if (new_string->len + append_utf8->len + 2 > QQ_MSG_IM_MAX) { - /* enough chars to send */ - im_convert_and_merge(new_string, append_utf8); - string_list = g_slist_append(string_list, strdup(new_string->str)); - g_string_set_size(new_string, 0); - continue; - } - emoticon = emoticon_find(p); - if (emoticon != NULL) { - purple_debug_info("QQ", "found emoticon %s as 0x%02X\n", - emoticon->name, emoticon->symbol); - /* QQ emoticon code prevent converting from utf8 to QQ charset - * convert append_utf8 to QQ charset - * merge the result to dest - * append qq QQ emoticon code to dest */ - im_convert_and_merge(new_string, append_utf8); - g_string_append_c(new_string, 0x14); - g_string_append_c(new_string, emoticon->symbol); - start += strlen(emoticon->name); - continue; - } else { - purple_debug_info("QQ", "Not found emoticon %.20s\n", p); - } - } - - /* Get next char */ - start = g_utf8_next_char(p); - len = start - p; - if (new_string->len + append_utf8->len + len > QQ_MSG_IM_MAX) { - /* enough chars to send */ - im_convert_and_merge(new_string, append_utf8); - string_list = g_slist_append(string_list, strdup(new_string->str)); - g_string_set_size(new_string, 0); - } - g_string_append_len(append_utf8, p, len); - } - - if (new_string->len + append_utf8->len > 0) { - im_convert_and_merge(new_string, append_utf8); - string_list = g_slist_append(string_list, strdup(new_string->str)); - } - g_string_free(new_string, TRUE); - g_string_free(append_utf8, TRUE); - return string_list; -} - -gboolean qq_im_smiley_none(const gchar *msg) -{ - const gchar *start, *end, *last; - GData *attribs; - gchar *tmp; - gboolean ret = FALSE; - - g_return_val_if_fail(msg != NULL, TRUE); - - last = msg; - while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { - tmp = g_datalist_get_data(&attribs, "sml"); - if (tmp && strlen(tmp) > 0) { - if (strcmp(tmp, "none") == 0) { - ret = TRUE; - break; - } - } - g_datalist_clear(&attribs); - last = end + 1; - } - return ret; -} - -/* Grab custom emote icons -static GSList* qq_grab_emoticons(const char *msg, const char*username) -{ - GSList *list; - GList *smileys; - PurpleSmiley *smiley; - const char *smiley_shortcut; - char *ptr; - int length; - PurpleStoredImage *img; - - smileys = purple_smileys_get_all(); - length = strlen(msg); - - for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { - smiley = smileys->data; - smiley_shortcut = purple_smiley_get_shortcut(smiley); - purple_debug_info("QQ", "Smiley shortcut [%s]\n", smiley_shortcut); - - ptr = g_strstr_len(msg, length, smiley_shortcut); - - if (!ptr) - continue; - - purple_debug_info("QQ", "Found Smiley shortcut [%s]\n", smiley_shortcut); - - img = purple_smiley_get_stored_image(smiley); - - emoticon = g_new0(MsnEmoticon, 1); - emoticon->smile = g_strdup(purple_smiley_get_shortcut(smiley)); - emoticon->obj = msn_object_new_from_image(img, - purple_imgstore_get_filename(img), - username, MSN_OBJECT_EMOTICON); - - purple_imgstore_unref(img); - list = g_slist_prepend(list, emoticon); - } - return list; -} -*/ - -gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *what, PurpleMessageFlags flags) -{ - qq_data *qd; - UID uid_to; - gint type; - qq_im_format *fmt; - gchar *msg_stripped, *tmp; - GSList *segments, *it; - gint msg_len; - const gchar *start_invalid; - gboolean is_smiley_none; - guint8 frag_count, frag_index; - guint8 msg_id; - - g_return_val_if_fail(NULL != gc && NULL != gc->proto_data, -1); - g_return_val_if_fail(who != NULL && what != NULL, -1); - - qd = (qq_data *) gc->proto_data; - purple_debug_info("QQ", "Send IM to %s, len %" G_GSIZE_FORMAT ":\n%s\n", who, strlen(what), what); - - uid_to = purple_name_to_uid(who); - if (uid_to == qd->uid) { - /* if msg is to myself, bypass the network */ - serv_got_im(gc, who, what, flags, time(NULL)); - return 1; - } - - type = (flags == PURPLE_MESSAGE_AUTO_RESP ? QQ_IM_AUTO_REPLY : QQ_IM_TEXT); - /* qq_show_packet("IM UTF8", (guint8 *)what, strlen(what)); */ - - msg_stripped = purple_markup_strip_html(what); - g_return_val_if_fail(msg_stripped != NULL, -1); - /* qq_show_packet("IM Stripped", (guint8 *)what, strlen(what)); */ - - /* Check and valid utf8 string */ - msg_len = strlen(msg_stripped); - g_return_val_if_fail(msg_len > 0, -1); - if (!g_utf8_validate(msg_stripped, msg_len, &start_invalid)) { - if (start_invalid > msg_stripped) { - tmp = g_strndup(msg_stripped, start_invalid - msg_stripped); - g_free(msg_stripped); - msg_stripped = g_strconcat(tmp, _("(Invalid UTF-8 string)"), NULL); - g_free(tmp); - } else { - g_free(msg_stripped); - msg_stripped = g_strdup(_("(Invalid UTF-8 string)")); - } - } - - is_smiley_none = qq_im_smiley_none(what); - segments = qq_im_get_segments(msg_stripped, is_smiley_none); - g_free(msg_stripped); - - if (segments == NULL) { - return -1; - } - - qd->send_im_id++; - msg_id = (guint8)(qd->send_im_id && 0xFF); - fmt = qq_im_fmt_new_by_purple(what); - frag_count = g_slist_length(segments); - frag_index = 0; - for (it = segments; it; it = it->next) { - /* - request_send_im(gc, uid_to, type, fmt, (gchar *)it->data, - msg_id, frag_count, frag_index); - */ - request_send_im(gc, uid_to, type, fmt, (gchar *)it->data, 0, 0, 0); - g_free(it->data); - frag_index++; - } - g_slist_free(segments); - qq_im_fmt_free(fmt); - return 1; -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/im.h --- a/libpurple/protocols/qq/im.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/** - * @file im.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_IM_H_ -#define _QQ_IM_H_ - -#include -#include "connection.h" - -enum { - QQ_MSG_TO_BUDDY = 0x0009, - QQ_MSG_TO_UNKNOWN = 0x000a, - QQ_MSG_SMS = 0x0014, /* not sure */ - QQ_MSG_NEWS = 0x0018, - QQ_MSG_QUN_IM_UNKNOWN = 0x0020, - QQ_MSG_ADD_TO_QUN = 0x0021, - QQ_MSG_DEL_FROM_QUN = 0x0022, - QQ_MSG_APPLY_ADD_TO_QUN = 0x0023, - QQ_MSG_APPROVE_APPLY_ADD_TO_QUN = 0x0024, - QQ_MSG_REJCT_APPLY_ADD_TO_QUN = 0x0025, - QQ_MSG_CREATE_QUN = 0x0026, - QQ_MSG_TEMP_QUN_IM = 0x002A, - QQ_MSG_QUN_IM = 0x002B, - QQ_MSG_SYS_30 = 0x0030, - QQ_MSG_SYS_4C = 0x004C, - QQ_MSG_EXTEND = 0x0084, - QQ_MSG_EXTEND_85 = 0x0085 -}; - -typedef struct { - /* attr: - bit0-4 for font size, bit5 for bold, - bit6 for italic, bit7 for underline - */ - guint8 attr; - guint8 rgb[3]; - guint16 charset; - gchar *font; /* Attension: font may NULL. font name is in QQ charset */ - guint8 font_len; -} qq_im_format; - -gint qq_put_im_tail(guint8 *buf, qq_im_format *fmt); -gint qq_get_im_tail(qq_im_format *fmt, guint8 *data, gint data_len); - -qq_im_format *qq_im_fmt_new(void); -void qq_im_fmt_free(qq_im_format *fmt); -void qq_im_fmt_reset_font(qq_im_format *fmt); -qq_im_format *qq_im_fmt_new_by_purple(const gchar *msg); -gchar *qq_im_fmt_to_purple(qq_im_format *fmt, gchar *text); -gboolean qq_im_smiley_none(const gchar *msg); -GSList *qq_im_get_segments(gchar *msg_stripped, gboolean is_smiley_none); - -void qq_got_message(PurpleConnection *gc, const gchar *msg); -gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags); - -void qq_process_im(PurpleConnection *gc, guint8 *data, gint len); -void qq_process_extend_im(PurpleConnection *gc, guint8 *data, gint len); - -gchar *qq_emoticon_to_purple(gchar *text); -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/packet_parse.c --- a/libpurple/protocols/qq/packet_parse.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,190 +0,0 @@ -/** - * @file packet_parse.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 - -#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 'undef' to 'define' to get more info about the packet parsing. */ - -#undef PARSER_DEBUG - -/* read one byte from buf, - * return the number of bytes read if succeeds, otherwise return -1 */ -gint qq_get8(guint8 *b, guint8 *buf) -{ - guint8 b_dest; - memcpy(&b_dest, buf, sizeof(b_dest)); - *b = b_dest; -#ifdef PARSER_DEBUG - purple_debug_info("QQ", "[DBG][get8] buf %p\n", (void *)buf); - 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 qq_get16(guint16 *w, guint8 *buf) -{ - guint16 w_dest; - memcpy(&w_dest, buf, sizeof(w_dest)); - *w = g_ntohs(w_dest); -#ifdef PARSER_DEBUG - purple_debug_info("QQ", "[DBG][get16] buf %p\n", (void *)buf); - 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 qq_get32(guint32 *dw, guint8 *buf) -{ - guint32 dw_dest; - memcpy(&dw_dest, buf, sizeof(dw_dest)); - *dw = g_ntohl(dw_dest); -#ifdef PARSER_DEBUG - purple_debug_info("QQ", "[DBG][get32] buf %p\n", (void *)buf); - purple_debug_info("QQ", "[DBG][get32] dw_dest 0x%08x, *dw 0x%08x\n", dw_dest, *dw); -#endif - return sizeof(dw_dest); -} - -gint qq_getIP(struct in_addr *ip, guint8 *buf) -{ - memcpy(ip, buf, sizeof(struct in_addr)); - return sizeof(struct in_addr); -} - -/* 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_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 qq_getime(time_t *t, guint8 *buf) -{ - guint32 dw_dest; - memcpy(&dw_dest, buf, sizeof(dw_dest)); -#ifdef PARSER_DEBUG - purple_debug_info("QQ", "[DBG][getime] buf %p\n", (void *)buf); - 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_info("QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest); -#endif - memcpy(t, &dw_dest, sizeof(dw_dest)); - return sizeof(dw_dest); -} - -/*------------------------------------------------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_info("QQ", "[DBG][put8] buf %p\n", (void *)buf); - purple_debug_info("QQ", "[DBG][put8] b 0x%02x\n", b); -#endif - return sizeof(b); -} - - -/* pack two bytes as "guint16" into buf - * return the number of bytes packed, otherwise return -1 */ -gint qq_put16(guint8 *buf, guint16 w) -{ - guint16 w_porter; - w_porter = g_htons(w); -#ifdef PARSER_DEBUG - purple_debug_info("QQ", "[DBG][put16] buf %p\n", (void *)buf); - 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 qq_put32(guint8 *buf, guint32 dw) -{ - guint32 dw_porter; - dw_porter = g_htonl(dw); -#ifdef PARSER_DEBUG - purple_debug_info("QQ", "[DBG][put32] buf %p\n", (void *)buf); - 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); -} - -gint qq_putime(guint8 *buf, time_t *t) -{ - guint32 dw, dw_porter; - memcpy(&dw, t, sizeof(dw)); - dw_porter = g_htonl(dw); -#ifdef PARSER_DEBUG - purple_debug_info("QQ", "[DBG][put32] buf %p\n", (void *)buf); - 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); -} - -gint qq_putIP(guint8* buf, struct in_addr *ip) -{ - memcpy(buf, ip, sizeof(struct in_addr)); - return sizeof(struct in_addr); -} - -/* pack datalen bytes into buf - * return the number of bytes packed, otherwise return -1 */ -gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen) -{ - memcpy(buf, data, datalen); -#ifdef PARSER_DEBUG - purple_debug_info("QQ", "[DBG][putdata] buf %p\n", (void *)buf); -#endif - return datalen; -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/packet_parse.h --- a/libpurple/protocols/qq/packet_parse.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/** - * @file packet_parse.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_PACKET_PARSE_H_ -#define _QQ_PACKET_PARSE_H_ - -#include -#include - -/* According to "UNIX Network Programming", all TCP/IP implementations - * must support a minimum IP datagram size of 576 bytes, regardless of the MTU. - * Assuming a 20 byte IP header and 8 byte UDP header, this leaves 548 bytes - * as a safe maximum size for UDP messages. - * - * TCP allows packet 64K - */ -#define MAX_PACKET_SIZE 65535 - -#ifndef _WIN32 -#include -#else -#include "win32dep.h" -#endif - -gint qq_get8(guint8 *b, guint8 *buf); -gint qq_get16(guint16 *w, guint8 *buf); -gint qq_get32(guint32 *dw, guint8 *buf); -gint qq_getIP(struct in_addr *ip, 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_putIP(guint8* buf, struct in_addr *ip); -gint qq_putime(guint8 *buf, time_t *t); -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 diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq.c --- a/libpurple/protocols/qq/qq.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1171 +0,0 @@ -/** - * @file qq.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 "accountopt.h" -#include "debug.h" -#include "notify.h" -#include "prefs.h" -#include "prpl.h" -#include "privacy.h" -#include "request.h" -#include "roomlist.h" -#include "server.h" -#include "util.h" - -#include "buddy_info.h" -#include "buddy_memo.h" -#include "buddy_opt.h" -#include "buddy_list.h" -#include "char_conv.h" -#include "group.h" -#include "group_im.h" -#include "group_info.h" -#include "group_join.h" -#include "group_opt.h" -#include "group_internal.h" -#include "qq_define.h" -#include "im.h" -#include "qq_process.h" -#include "qq_base.h" -#include "packet_parse.h" -#include "qq.h" -#include "qq_network.h" -#include "send_file.h" -#include "utils.h" -#include "version.h" - -#define OPENQ_VERSION "0.3.2-p20" - -static GList *server_list_build(gchar select) -{ - GList *list = NULL; - - if ( select == 'T' || select == 'A') { - list = g_list_append(list, "tcpconn.tencent.com:8000"); - list = g_list_append(list, "tcpconn2.tencent.com:8000"); - list = g_list_append(list, "tcpconn3.tencent.com:8000"); - list = g_list_append(list, "tcpconn4.tencent.com:8000"); - list = g_list_append(list, "tcpconn5.tencent.com:8000"); - list = g_list_append(list, "tcpconn6.tencent.com:8000"); - } - if ( select == 'U' || select == 'A') { - list = g_list_append(list, "sz.tencent.com:8000"); - list = g_list_append(list, "sz2.tencent.com:8000"); - list = g_list_append(list, "sz3.tencent.com:8000"); - list = g_list_append(list, "sz4.tencent.com:8000"); - list = g_list_append(list, "sz5.tencent.com:8000"); - list = g_list_append(list, "sz6.tencent.com:8000"); - list = g_list_append(list, "sz7.tencent.com:8000"); - list = g_list_append(list, "sz8.tencent.com:8000"); - list = g_list_append(list, "sz9.tencent.com:8000"); - } - return list; -} - -static void server_list_create(PurpleAccount *account) -{ - PurpleConnection *gc; - qq_data *qd; - const gchar *custom_server; - - gc = purple_account_get_connection(account); - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = gc->proto_data; - - qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE); - - custom_server = purple_account_get_string(account, "server", NULL); - - if (custom_server != NULL) { - purple_debug_info("QQ", "Select server '%s'\n", custom_server); - if (*custom_server != '\0' && g_ascii_strcasecmp(custom_server, "auto") != 0) { - qd->servers = g_list_append(qd->servers, g_strdup(custom_server)); - return; - } - } - - if (qd->use_tcp) { - qd->servers = server_list_build('T'); - return; - } - - qd->servers = server_list_build('U'); -} - -static void server_list_remove_all(qq_data *qd) -{ - g_return_if_fail(qd != NULL); - - purple_debug_info("QQ", "free server list\n"); - g_list_free(qd->servers); - qd->curr_server = NULL; -} - -static void qq_login(PurpleAccount *account) -{ - PurpleConnection *gc; - qq_data *qd; - PurplePresence *presence; - const gchar *version_str; - - g_return_if_fail(account != NULL); - - gc = purple_account_get_connection(account); - g_return_if_fail(gc != NULL); - - gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_AUTO_RESP; - - qd = g_new0(qq_data, 1); - memset(qd, 0, sizeof(qq_data)); - qd->gc = gc; - gc->proto_data = qd; - - presence = purple_account_get_presence(account); - 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) - || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)) { - qd->login_mode = QQ_LOGIN_MODE_AWAY; - } else { - qd->login_mode = QQ_LOGIN_MODE_NORMAL; - } - - server_list_create(account); - purple_debug_info("QQ", "Server list has %d\n", g_list_length(qd->servers)); - - version_str = purple_account_get_string(account, "client_version", NULL); - qd->client_tag = QQ_CLIENT_115B; /* set default as QQ2008 */ - qd->client_version = 2008; - if (version_str != NULL && strlen(version_str) != 0) { - if (strcmp(version_str, "qq2005") == 0) { - qd->client_tag = QQ_CLIENT_0D55; - qd->client_version = 2005; - } else if (strcmp(version_str, "qq2007") == 0) { - qd->client_tag = QQ_CLIENT_111D; - qd->client_version = 2007; - } - } - - qd->is_show_notice = purple_account_get_bool(account, "show_notice", TRUE); - qd->is_show_news = purple_account_get_bool(account, "show_news", TRUE); - qd->is_show_chat = purple_account_get_bool(account, "show_chat", TRUE); - - if (purple_account_get_bool(account, "default_font", FALSE)) { - qd->custom = QQ_CUSTOM_USE_DEFAULT_FONT; - } - else { - qd->custom = QQ_CUSTOM_NONE; - } - - qd->resend_times = purple_prefs_get_int("/plugins/prpl/qq/resend_times"); - if (qd->resend_times <= 1) qd->itv_config.resend = 4; - - qd->itv_config.resend = purple_prefs_get_int("/plugins/prpl/qq/resend_interval"); - if (qd->itv_config.resend <= 0) qd->itv_config.resend = 3; - purple_debug_info("QQ", "Resend interval %d, retries %d\n", - qd->itv_config.resend, qd->resend_times); - - qd->itv_config.keep_alive = purple_account_get_int(account, "keep_alive_interval", 60); - if (qd->itv_config.keep_alive < 30) qd->itv_config.keep_alive = 30; - qd->itv_config.keep_alive /= qd->itv_config.resend; - qd->itv_count.keep_alive = qd->itv_config.keep_alive; - - qd->itv_config.update = purple_account_get_int(account, "update_interval", 300); - if (qd->itv_config.update > 0) { - if (qd->itv_config.update < qd->itv_config.keep_alive) { - qd->itv_config.update = qd->itv_config.keep_alive; - } - qd->itv_config.update /= qd->itv_config.resend; - qd->itv_count.update = qd->itv_config.update; - } else { - qd->itv_config.update = 0; - } - - qd->connect_watcher = purple_timeout_add_seconds(0, qq_connect_later, gc); -} - -/* clean up the given QQ connection and free all resources */ -static void qq_close(PurpleConnection *gc) -{ - qq_data *qd; - - g_return_if_fail(gc != NULL && gc->proto_data); - qd = gc->proto_data; - - if (qd->check_watcher > 0) { - purple_timeout_remove(qd->check_watcher); - qd->check_watcher = 0; - } - - if (qd->connect_watcher > 0) { - purple_timeout_remove(qd->connect_watcher); - qd->connect_watcher = 0; - } - - /* This is cancelled by _purple_connection_destroy */ - qd->conn_data = NULL; - - qq_disconnect(gc); - - if (qd->redirect) g_free(qd->redirect); - if (qd->ld.token) g_free(qd->ld.token); - if (qd->ld.token_ex) g_free(qd->ld.token_ex); - if (qd->captcha.token) g_free(qd->captcha.token); - if (qd->captcha.data) g_free(qd->captcha.data); - - server_list_remove_all(qd); - - g_free(qd); - gc->proto_data = NULL; -} - -/* returns the icon name for a buddy or protocol */ -static const gchar *qq_list_icon(PurpleAccount *a, PurpleBuddy *b) -{ - return "qq"; -} - - -/* a short status text beside buddy icon*/ -static gchar *qq_status_text(PurpleBuddy *b) -{ - qq_buddy_data *bd; - GString *status; - - bd = purple_buddy_get_protocol_data(b); - if (bd == NULL) - return NULL; - - status = g_string_new(""); - - switch(bd->status) { - case QQ_BUDDY_OFFLINE: - g_string_append(status, _("Offline")); - break; - case QQ_BUDDY_ONLINE_NORMAL: - g_string_append(status, _("Online")); - break; - /* TODO What does this status mean? Labelling it as offline... */ - case QQ_BUDDY_CHANGE_TO_OFFLINE: - g_string_append(status, _("Offline")); - break; - case QQ_BUDDY_ONLINE_AWAY: - g_string_append(status, _("Away")); - break; - case QQ_BUDDY_ONLINE_INVISIBLE: - g_string_append(status, _("Invisible")); - break; - case QQ_BUDDY_ONLINE_BUSY: - g_string_append(status, _("Busy")); - break; - default: - g_string_printf(status, _("Unknown-%d"), bd->status); - } - - return g_string_free(status, FALSE); -} - - -/* a floating text when mouse is on the icon, show connection status here */ -static void qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) -{ - qq_buddy_data *bd; - gchar *tmp; - GString *str; - - g_return_if_fail(b != NULL); - - bd = purple_buddy_get_protocol_data(b); - if (bd == NULL) - return; - - /* if (PURPLE_BUDDY_IS_ONLINE(b) && bd != NULL) */ - if (bd->ip.s_addr != 0) { - str = g_string_new(NULL); - g_string_printf(str, "%s:%d", inet_ntoa(bd->ip), bd->port); - if (bd->comm_flag & QQ_COMM_FLAG_TCP_MODE) { - g_string_append(str, " TCP"); - } else { - g_string_append(str, " UDP"); - } - g_string_free(str, TRUE); - } - - tmp = g_strdup_printf("%d", bd->age); - purple_notify_user_info_add_pair(user_info, _("Age"), tmp); - g_free(tmp); - - switch (bd->gender) { - case QQ_BUDDY_GENDER_GG: - purple_notify_user_info_add_pair(user_info, _("Gender"), _("Male")); - break; - case QQ_BUDDY_GENDER_MM: - purple_notify_user_info_add_pair(user_info, _("Gender"), _("Female")); - break; - case QQ_BUDDY_GENDER_UNKNOWN: - purple_notify_user_info_add_pair(user_info, _("Gender"), _("Unknown")); - break; - default: - tmp = g_strdup_printf("Error (%d)", bd->gender); - purple_notify_user_info_add_pair(user_info, _("Gender"), tmp); - g_free(tmp); - } - - if (bd->level) { - tmp = g_strdup_printf("%d", bd->level); - purple_notify_user_info_add_pair(user_info, _("Level"), tmp); - g_free(tmp); - } - - str = g_string_new(NULL); - if (bd->comm_flag & QQ_COMM_FLAG_QQ_MEMBER) { - g_string_append( str, _("Member") ); - } - if (bd->comm_flag & QQ_COMM_FLAG_QQ_VIP) { - g_string_append( str, _(" VIP") ); - } - if (bd->comm_flag & QQ_COMM_FLAG_TCP_MODE) { - g_string_append( str, _(" TCP") ); - } - if (bd->comm_flag & QQ_COMM_FLAG_MOBILE) { - g_string_append( str, _(" FromMobile") ); - } - if (bd->comm_flag & QQ_COMM_FLAG_BIND_MOBILE) { - g_string_append( str, _(" BindMobile") ); - } - if (bd->comm_flag & QQ_COMM_FLAG_VIDEO) { - g_string_append( str, _(" Video") ); - } - - if (bd->ext_flag & QQ_EXT_FLAG_ZONE) { - g_string_append( str, _(" Zone") ); - } - purple_notify_user_info_add_pair(user_info, _("Flag"), str->str); - - g_string_free(str, TRUE); - -#ifdef DEBUG - tmp = g_strdup_printf( "%s (%04X)", - qq_get_ver_desc(bd->client_tag), - bd->client_tag ); - purple_notify_user_info_add_pair(user_info, _("Ver"), tmp); - g_free(tmp); - - tmp = g_strdup_printf( "Ext 0x%X, Comm 0x%X", - bd->ext_flag, bd->comm_flag ); - purple_notify_user_info_add_pair(user_info, _("Flag"), tmp); - g_free(tmp); -#endif -} - -/* we can show tiny icons on the four corners of buddy icon, */ -static const char *qq_list_emblem(PurpleBuddy *b) -{ - PurpleAccount *account; - qq_buddy_data *buddy; - - if (!b || !(account = purple_buddy_get_account(b)) || - !purple_account_get_connection(account)) - return NULL; - - buddy = purple_buddy_get_protocol_data(b); - if (!buddy) { - return "not-authorized"; - } - - if (buddy->comm_flag & QQ_COMM_FLAG_MOBILE) - return "mobile"; - if (buddy->comm_flag & QQ_COMM_FLAG_VIDEO) - return "video"; - if (buddy->comm_flag & QQ_COMM_FLAG_QQ_MEMBER) - return "qq_member"; - - return NULL; -} - -/* QQ away status (used to initiate QQ away packet) */ -static GList *qq_status_types(PurpleAccount *ga) -{ - PurpleStatusType *status; - GList *types = NULL; - - status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, - "available", _("Available"), TRUE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_AWAY, - "away", _("Away"), TRUE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, - "invisible", _("Invisible"), TRUE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, - "busy", _("Busy"), TRUE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, - "offline", _("Offline"), TRUE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_MOBILE, - "mobile", NULL, FALSE, FALSE, TRUE); - types = g_list_append(types, status); - - return types; -} - -/* initiate QQ away with proper change_status packet */ -static void qq_change_status(PurpleAccount *account, PurpleStatus *status) -{ - PurpleConnection *gc = purple_account_get_connection(account); - - qq_request_change_status(gc, 0); -} - -/* send packet to get who's detailed information */ -static void qq_show_buddy_info(PurpleConnection *gc, const gchar *who) -{ - UID uid; - qq_data *qd; - - qd = gc->proto_data; - uid = purple_name_to_uid(who); - - if (uid <= 0) { - purple_debug_error("QQ", "Not valid QQid: %s\n", who); - purple_notify_error(gc, NULL, _("Invalid name"), NULL); - return; - } - - if (qd->client_version >= 2007) { - qq_request_get_level_2007(gc, uid); - } else { - qq_request_get_level(gc, uid); - } - qq_request_buddy_info(gc, uid, 0, QQ_BUDDY_INFO_DISPLAY); -} - -static void action_update_all_rooms(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - qq_data *qd; - - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - qd = (qq_data *) gc->proto_data; - - if ( !qd->is_login ) { - return; - } - - qq_update_all_rooms(gc, 0, 0); -} - -static void action_change_icon(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - qq_data *qd; - gchar *icon_name; - gchar *icon_path; - - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - qd = (qq_data *) gc->proto_data; - - if ( !qd->is_login ) { - return; - } - - icon_name = qq_get_icon_name(qd->my_icon); - icon_path = qq_get_icon_path(icon_name); - g_free(icon_name); - - purple_debug_info("QQ", "Change prev icon %s to...\n", icon_path); - purple_request_file(action, _("Select icon..."), icon_path, - FALSE, - G_CALLBACK(qq_change_icon_cb), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); - g_free(icon_path); -} - -static void action_modify_info_base(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - qq_data *qd; - - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - qd = (qq_data *) gc->proto_data; - qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_BASE); -} - -static void action_modify_info_ext(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - qq_data *qd; - - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - qd = (qq_data *) gc->proto_data; - qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_EXT); -} - -static void action_modify_info_addr(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - qq_data *qd; - - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - qd = (qq_data *) gc->proto_data; - qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_ADDR); -} - -static void action_modify_info_contact(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - qq_data *qd; - - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - qd = (qq_data *) gc->proto_data; - qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_CONTACT); -} - -static void action_change_password(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - purple_notify_uri(NULL, "https://password.qq.com"); -} - -/* show a brief summary of what we get from login packet */ -static void action_show_account_info(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - qq_data *qd; - GString *info; - struct tm *tm_local; - int index; - - g_return_if_fail(NULL != gc && NULL != gc->proto_data); - qd = (qq_data *) gc->proto_data; - info = g_string_new(""); - - tm_local = localtime(&qd->login_time); - g_string_append_printf(info, _("Login time: %d-%d-%d, %d:%d:%d
\n"), - (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday, - tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec); - g_string_append_printf(info, _("Total Online Buddies: %d
\n"), qd->online_total); - tm_local = localtime(&qd->online_last_update); - g_string_append_printf(info, _("Last Refresh: %d-%d-%d, %d:%d:%d
\n"), - (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday, - tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec); - - g_string_append(info, "
"); - - g_string_append_printf(info, _("Server: %s
\n"), qd->curr_server); - g_string_append_printf(info, _("Client Tag: %s
\n"), qq_get_ver_desc(qd->client_tag)); - g_string_append_printf(info, _("Connection Mode: %s
\n"), qd->use_tcp ? "TCP" : "UDP"); - g_string_append_printf(info, _("My Internet IP: %s:%d
\n"), inet_ntoa(qd->my_ip), qd->my_port); - - g_string_append(info, "
"); - g_string_append(info, "Network Status
\n"); - g_string_append_printf(info, _("Sent: %lu
\n"), qd->net_stat.sent); - g_string_append_printf(info, _("Resend: %lu
\n"), qd->net_stat.resend); - g_string_append_printf(info, _("Lost: %lu
\n"), qd->net_stat.lost); - g_string_append_printf(info, _("Received: %lu
\n"), qd->net_stat.rcved); - g_string_append_printf(info, _("Received Duplicate: %lu
\n"), qd->net_stat.rcved_dup); - - g_string_append(info, "
"); - g_string_append(info, "Last Login Information
\n"); - - for (index = 0; index < sizeof(qd->last_login_time) / sizeof(time_t); index++) { - tm_local = localtime(&qd->last_login_time[index]); - g_string_append_printf(info, _("Time: %d-%d-%d, %d:%d:%d
\n"), - (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday, - tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec); - } - if (qd->last_login_ip.s_addr != 0) { - g_string_append_printf(info, _("IP: %s
\n"), inet_ntoa(qd->last_login_ip)); - } - - g_string_append(info, ""); - - purple_notify_formatted(gc, NULL, _("Login Information"), NULL, info->str, NULL, NULL); - - g_string_free(info, TRUE); -} - -static void action_about_openq(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - GString *info; - gchar *title; - - g_return_if_fail(NULL != gc); - - info = g_string_new(""); - g_string_append(info, _("

Original Author:
\n")); - g_string_append(info, "puzzlebird
\n"); - g_string_append(info, "
\n"); - - g_string_append(info, _("

Code Contributors:
\n")); - g_string_append(info, "gfhuang(poppyer) : patches for libpurple 2.0.0beta2, maintainer
\n"); - g_string_append(info, "Yuan Qingyun : patches for libpurple 1.5.0, maintainer
\n"); - g_string_append(info, "henryouly : file transfer, udp sock5 proxy and qq_show, maintainer
\n"); - g_string_append(info, "hzhr : maintainer
\n"); - g_string_append(info, "joymarquis : maintainer
\n"); - g_string_append(info, "arfankai : fixed bugs in char_conv.c
\n"); - g_string_append(info, "rakescar : provided filter for HTML tag
\n"); - g_string_append(info, "yyw : improved performance on PPC linux
\n"); - g_string_append(info, "lvxiang : provided ip to location original code
\n"); - g_string_append(info, "markhuetsch : OpenQ merge into libpurple, maintainer 2006-2007
\n"); - g_string_append(info, "ccpaging : maintainer since 2007
\n"); - g_string_append(info, "icesky : maintainer since 2007
\n"); - g_string_append(info, "csyfek : faces, maintainer since 2007
\n"); - g_string_append(info, "
\n"); - - g_string_append(info, _("

Lovely Patch Writers:
\n")); - g_string_append(info, "gnap : message displaying, documentation
\n"); - g_string_append(info, "manphiz : qun processing
\n"); - g_string_append(info, "moo : qun processing
\n"); - g_string_append(info, "Coly Li : qun processing
\n"); - g_string_append(info, "Emil Alexiev : captcha verification on login based on LumaQQ for MAC (2007), login, add buddy, remove buddy, message exchange and logout
\n"); - g_string_append(info, "Chengming Wang : buddy memo
\n"); - g_string_append(info, "lonicerae : chat room window bugfix, server list bugfix, buddy memo
\n"); - g_string_append(info, "
\n"); - - g_string_append(info, _("

Acknowledgement:
\n")); - g_string_append(info, "Shufeng Tan : http://sf.net/projects/perl-oicq
\n"); - g_string_append(info, "Jeff Ye : http://www.sinomac.com
\n"); - g_string_append(info, "Hu Zheng : http://forlinux.yeah.net
\n"); - g_string_append(info, "yunfan : http://www.myswear.net
\n"); - g_string_append(info, "OpenQ Team : http://openq.linuxsir.org
\n"); - g_string_append(info, "LumaQQ Team : http://lumaqq.linuxsir.org
\n"); - g_string_append(info, "Pidgin Team : http://www.pidgin.im
\n"); - g_string_append(info, "Huang Guan : http://home.xxsyzx.com
\n"); - g_string_append(info, "OpenQ Google Group : http://groups.google.com/group/openq
\n"); - g_string_append(info, "
\n"); - - g_string_append(info, _("

Scrupulous Testers:
\n")); - g_string_append(info, "yegle
\n"); - g_string_append(info, "cnzhangbx
\n"); - g_string_append(info, "casparant
\n"); - g_string_append(info, "wd
\n"); - g_string_append(info, "x6719620
\n"); - g_string_append(info, "netelk
\n"); - g_string_append(info, _("and more, please let me know... thank you!))")); - g_string_append(info, "
\n
\n"); - g_string_append(info, _("

And, all the boys in the backroom...
\n")); - g_string_append(info, _("Feel free to join us! :)")); - g_string_append(info, ""); - - title = g_strdup_printf(_("About OpenQ %s"), OPENQ_VERSION); - purple_notify_formatted(gc, title, title, NULL, info->str, NULL, NULL); - - g_free(title); - g_string_free(info, TRUE); -} - -/* - static void _qq_menu_search_or_add_permanent_group(PurplePluginAction *action) - { - purple_roomlist_show_with_account(NULL); - } -*/ - -/* - static void _qq_menu_create_permanent_group(PurplePluginAction * action) - { - PurpleConnection *gc = (PurpleConnection *) action->context; - purple_request_input(gc, _("Create QQ Qun"), - _("Input Qun name here"), - _("Only QQ members can create permanent Qun"), - "OpenQ", FALSE, FALSE, NULL, - _("Create"), G_CALLBACK(qq_create_room), _("Cancel"), NULL, gc); - } -*/ - -static void action_chat_quit(PurpleBlistNode * node) -{ - PurpleChat *chat = (PurpleChat *)node; - PurpleAccount *account = purple_chat_get_account(chat); - PurpleConnection *gc = purple_account_get_connection(account); - GHashTable *components = purple_chat_get_components(chat); - gchar *num_str; - guint32 room_id; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - g_return_if_fail(components != NULL); - - num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID); - room_id = strtoul(num_str, NULL, 10); - g_return_if_fail(room_id != 0); - - qq_room_quit(gc, room_id); -} - -static void action_chat_get_info(PurpleBlistNode * node) -{ - PurpleChat *chat = (PurpleChat *)node; - PurpleAccount *account = purple_chat_get_account(chat); - PurpleConnection *gc = purple_account_get_connection(account); - GHashTable *components = purple_chat_get_components(chat); - gchar *num_str; - guint32 room_id; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - g_return_if_fail(components != NULL); - - num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID); - room_id = strtoul(num_str, NULL, 10); - g_return_if_fail(room_id != 0); - - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0, - 0, QQ_ROOM_INFO_DISPLAY); -} - -#if 0 -/* TODO: re-enable this */ -static void _qq_menu_send_file(PurpleBlistNode * node, gpointer ignored) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - qq_buddy_data *bd; - - g_return_if_fail (PURPLE_BLIST_NODE_IS_BUDDY (node)); - buddy = (PurpleBuddy *) node; - bd = (qq_buddy_data *) buddy->proto_data; - /* if (is_online (bd->status)) { */ - gc = purple_account_get_connection (buddy->account); - g_return_if_fail (gc != NULL && gc->proto_data != NULL); - qq_send_file(gc, buddy->name, NULL); - /* } */ -} -#endif - -/* protocol related menus */ -static GList *qq_actions(PurplePlugin *plugin, gpointer context) -{ - GList *m; - PurplePluginAction *act; - - m = NULL; - act = purple_plugin_action_new(_("Change Icon"), action_change_icon); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Modify Information"), action_modify_info_base); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Modify Extended Information"), action_modify_info_ext); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Modify Address"), action_modify_info_addr); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Modify Contact"), action_modify_info_contact); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Change Password"), action_change_password); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Account Information"), action_show_account_info); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Update all QQ Quns"), action_update_all_rooms); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("About OpenQ"), action_about_openq); - m = g_list_append(m, act); - /* - act = purple_plugin_action_new(_("Qun: Search a permanent Qun"), _qq_menu_search_or_add_permanent_group); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Qun: Create a permanent Qun"), _qq_menu_create_permanent_group); - m = g_list_append(m, act); - */ - - return m; -} - -static void qq_add_buddy_from_menu_cb(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - - qq_add_buddy(gc, buddy, NULL); -} - -static void qq_modify_buddy_memo_from_menu_cb(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - qq_buddy_data *bd; - PurpleConnection *gc; - UID bd_uid; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *)node; - g_return_if_fail(NULL != buddy); - - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - g_return_if_fail(NULL != gc); - - bd = (qq_buddy_data *)purple_buddy_get_protocol_data(buddy); - g_return_if_fail(NULL != bd); - bd_uid = bd->uid; - - /* param: gc, uid, update_class, action - * here, update_class is set to bd_uid. because some memo packages returned - * without uid, which will make us confused */ - qq_request_buddy_memo(gc, bd_uid, bd_uid, QQ_BUDDY_MEMO_MODIFY); -} - -static GList *qq_buddy_menu(PurpleBuddy *buddy) -{ - GList *m = NULL; - PurpleMenuAction *act; - qq_buddy_data *bd = purple_buddy_get_protocol_data(buddy); - - if (bd == NULL) { - act = purple_menu_action_new(_("Add Buddy"), - PURPLE_CALLBACK(qq_add_buddy_from_menu_cb), - NULL, NULL); - m = g_list_append(m, act); - return m; - } - - - act = purple_menu_action_new(_("Modify Buddy Memo"), - PURPLE_CALLBACK(qq_modify_buddy_memo_from_menu_cb), - NULL, NULL); - m = g_list_append(m, act); - - - /* TODO : not working, temp commented out by gfhuang */ -#if 0 - if (bd && is_online(bd->status)) { - act = purple_menu_action_new(_("Send File"), PURPLE_CALLBACK(_qq_menu_send_file), NULL, NULL); /* add NULL by gfhuang */ - m = g_list_append(m, act); - } -#endif - return m; -} - -/* chat-related (QQ Qun) menu shown up with right-click */ -static GList *qq_chat_menu(PurpleBlistNode *node) -{ - GList *m; - PurpleMenuAction *act; - - m = NULL; - act = purple_menu_action_new(_("Get Info"), PURPLE_CALLBACK(action_chat_get_info), NULL, NULL); - m = g_list_append(m, act); - - act = purple_menu_action_new(_("Quit Qun"), PURPLE_CALLBACK(action_chat_quit), NULL, NULL); - m = g_list_append(m, act); - return m; -} - -/* buddy-related menu shown up with right-click */ -static GList *qq_blist_node_menu(PurpleBlistNode * node) -{ - if(PURPLE_BLIST_NODE_IS_CHAT(node)) - return qq_chat_menu(node); - - if(PURPLE_BLIST_NODE_IS_BUDDY(node)) - return qq_buddy_menu((PurpleBuddy *) node); - - return NULL; -} - -/* convert name displayed in a chat channel to original QQ UID */ -static gchar *chat_name_to_purple_name(const gchar *const name) -{ - const char *start; - const char *end; - gchar *ret; - - g_return_val_if_fail(name != NULL, NULL); - - /* Sample: (1234567)*/ - start = strchr(name, '('); - g_return_val_if_fail(start != NULL, NULL); - end = strchr(start, ')'); - g_return_val_if_fail(end != NULL && (end - start) > 1, NULL); - - ret = g_strndup(start + 1, end - start - 1); - - return ret; -} - -/* convert chat nickname to uid to get this buddy info */ -/* who is the nickname of buddy in QQ chat-room (Qun) */ -static void qq_get_chat_buddy_info(PurpleConnection *gc, gint channel, const gchar *who) -{ - qq_data *qd; - gchar *uid_str; - UID uid; - - purple_debug_info("QQ", "Get chat buddy info of %s\n", who); - g_return_if_fail(who != NULL); - - uid_str = chat_name_to_purple_name(who); - if (uid_str == NULL) { - return; - } - - qd = gc->proto_data; - uid = purple_name_to_uid(uid_str); - g_free(uid_str); - - if (uid <= 0) { - purple_debug_error("QQ", "Not valid chat name: %s\n", who); - purple_notify_error(gc, NULL, _("Invalid name"), NULL); - return; - } - - if (qd->client_version < 2007) { - qq_request_get_level(gc, uid); - } - qq_request_buddy_info(gc, uid, 0, QQ_BUDDY_INFO_DISPLAY); -} - -/* convert chat nickname to uid to invite individual IM to buddy */ -/* who is the nickname of buddy in QQ chat-room (Qun) */ -static gchar *qq_get_chat_buddy_real_name(PurpleConnection *gc, gint channel, const gchar *who) -{ - g_return_val_if_fail(who != NULL, NULL); - return chat_name_to_purple_name(who); -} - -static PurplePluginProtocolInfo prpl_info = -{ - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_USE_POINTSIZE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */ - qq_list_icon, /* list_icon */ - qq_list_emblem, /* list_emblems */ - qq_status_text, /* status_text */ - qq_tooltip_text, /* tooltip_text */ - qq_status_types, /* away_states */ - qq_blist_node_menu, /* blist_node_menu */ - qq_chat_info, /* chat_info */ - qq_chat_info_defaults, /* chat_info_defaults */ - qq_login, /* open */ - qq_close, /* close */ - qq_send_im, /* send_im */ - NULL, /* set_info */ - NULL, /* send_typing */ - qq_show_buddy_info, /* get_info */ - qq_change_status, /* change status */ - NULL, /* set_idle */ - NULL, /* change_passwd */ - qq_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - qq_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* rem_permit */ - NULL, /* rem_deny */ - NULL, /* set_permit_deny */ - qq_group_join, /* join_chat */ - NULL, /* reject chat invite */ - NULL, /* get_chat_name */ - NULL, /* chat_invite */ - NULL, /* chat_leave */ - NULL, /* chat_whisper */ - qq_chat_send, /* chat_send */ - NULL, /* keepalive */ - NULL, /* register_user */ - qq_get_chat_buddy_info, /* get_cb_info */ - NULL, /* get_cb_away */ - NULL, /* alias_buddy */ - NULL, /* change buddy's group */ - NULL, /* rename_group */ - NULL, /* buddy_free */ - NULL, /* convo_closed */ - NULL, /* normalize */ - qq_set_custom_icon, - NULL, /* remove_group */ - qq_get_chat_buddy_real_name, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - qq_roomlist_get_list, /* roomlist_get_list */ - qq_roomlist_cancel, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - NULL, /* qq_send_file send_file */ - NULL, /* new xfer */ - NULL, /* offline_message */ - NULL, /* PurpleWhiteboardPrplOps */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get attention_types */ - - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL, /* add_buddy_with_invite */ - NULL /* add_buddies_with_invite */ -}; - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-qq", /**< id */ - "QQ", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("QQ Protocol Plugin"), - /** description */ - N_("QQ Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - qq_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - - -static void init_plugin(PurplePlugin *plugin) -{ - PurpleAccountOption *option; - PurpleKeyValuePair *kvp; - GList *server_list = NULL; - GList *server_kv_list = NULL; - GList *it; - /* #ifdef DEBUG */ - GList *version_kv_list = NULL; - /* #endif */ - - server_list = server_list_build('A'); - - purple_prefs_remove("/plugins/prpl/qq/serverlist"); - - server_kv_list = NULL; - kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(_("Auto")); - kvp->value = g_strdup("auto"); - server_kv_list = g_list_append(server_kv_list, kvp); - - it = server_list; - while(it) { - if (it->data != NULL && strlen(it->data) > 0) { - kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(it->data); - kvp->value = g_strdup(it->data); - server_kv_list = g_list_append(server_kv_list, kvp); - } - it = it->next; - } - - g_list_free(server_list); - - option = purple_account_option_list_new(_("Select Server"), "server", server_kv_list); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(_("QQ2008")); - kvp->value = g_strdup("qq2008"); - version_kv_list = g_list_append(version_kv_list, kvp); - - kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(_("QQ2007")); - kvp->value = g_strdup("qq2007"); - version_kv_list = g_list_append(version_kv_list, kvp); - - kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(_("QQ2005")); - kvp->value = g_strdup("qq2005"); - version_kv_list = g_list_append(version_kv_list, kvp); - - option = purple_account_option_list_new(_("Client Version"), "client_version", version_kv_list); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Connect by TCP"), "use_tcp", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Show server notice"), "show_notice", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Show server news"), "show_news", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Show chat room when msg comes"), "show_chat", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Use default font"), "default_font", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_int_new(_("Keep alive interval (seconds)"), "keep_alive_interval", 60); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_int_new(_("Update interval (seconds)"), "update_interval", 300); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - purple_prefs_add_none("/plugins/prpl/qq"); - purple_prefs_add_bool("/plugins/prpl/qq/show_status_by_icon", TRUE); - purple_prefs_add_bool("/plugins/prpl/qq/show_fake_video", FALSE); - purple_prefs_add_bool("/plugins/prpl/qq/auto_get_authorize_info", TRUE); - purple_prefs_add_int("/plugins/prpl/qq/resend_interval", 3); - purple_prefs_add_int("/plugins/prpl/qq/resend_times", 10); -} - -PURPLE_INIT_PLUGIN(qq, init_plugin, info); diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq.h --- a/libpurple/protocols/qq/qq.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,192 +0,0 @@ -/** - * @file qq.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_QQ_H_ -#define _QQ_QQ_H_ - -#include "internal.h" -#include "ft.h" -#include "circbuffer.h" -#include "dnsquery.h" -#include "dnssrv.h" -#include "proxy.h" -#include "roomlist.h" - -#define QQ_KEY_LENGTH 16 - -/* steal from kazehakase :) */ -#define qq_strlen(s) ((s)!=NULL?strlen(s):0) -#define qq_strcmp(s1,s2) ((s1)!=NULL && (s2)!=NULL?strcmp(s1,s2):0) - -/* business logic layer */ -typedef guint32 UPDCLS; -typedef guint32 UID; - -typedef struct _qq_captcha_data { - guint8 *token; - guint16 token_len; - guint8 next_index; - guint8 *data; - guint16 data_len; -} qq_captcha_data; - -typedef struct _qq_login_data { - guint8 random_key[QQ_KEY_LENGTH]; /* first encrypt key generated by client */ - guint8 *token; /* get from server */ - guint8 token_len; - guint8 *token_ex; /* get from server */ - guint16 token_ex_len; - - guint8 pwd_md5[QQ_KEY_LENGTH]; /* password in md5 (or md5' md5) */ - guint8 pwd_twice_md5[QQ_KEY_LENGTH]; - - guint8 *login_token; - guint16 login_token_len; - guint8 login_key[QQ_KEY_LENGTH]; -} qq_login_data; - -typedef struct _qq_interval { - gint resend; - gint keep_alive; - gint update; -} qq_interval; - -typedef struct _qq_net_stat { - glong sent; - glong resend; - glong lost; - glong rcved; - glong rcved_dup; -} qq_net_stat; - -typedef struct _qq_buddy_data { - UID uid; - guint16 face; /* index: 0 - 299 */ - guint8 age; - guint8 gender; - gchar *nickname; - struct in_addr ip; - guint16 port; - guint8 status; - guint8 ext_flag; - guint8 comm_flag; /* details in qq_buddy_list.c */ - guint16 client_tag; - guint8 onlineTime; - guint16 level; - guint16 timeRemainder; - time_t signon; - time_t idle; - time_t last_update; - gchar** memo; - - gint8 role; /* role in group, used only in group->members list */ -} qq_buddy_data; - -typedef struct _qq_connection qq_connection; -struct _qq_connection { - int fd; /* socket file handler */ - int input_handler; - - /* tcp related */ - int can_write_handler; /* use in tcp_send_out */ - PurpleCircBuffer *tcp_txbuf; - guint8 *tcp_rxqueue; - int tcp_rxlen; -}; - -typedef struct _qq_data { - PurpleConnection *gc; - - GSList *openconns; - gboolean use_tcp; /* network in tcp or udp */ - PurpleProxyConnectData *conn_data; -#ifndef purple_proxy_connect_udp - PurpleDnsQueryData *udp_query_data; /* udp related */ - gint udp_can_write_handler; /* socket can_write handle, use in udp connecting and tcp send out */ -#endif - gint fd; /* socket file handler */ - qq_net_stat net_stat; - - GList *servers; - gchar *curr_server; /* point to servers->data, do not free*/ - - guint16 client_tag; - gint client_version; - - struct in_addr redirect_ip; - guint16 redirect_port; - guint8 *redirect; - guint8 redirect_len; - - guint check_watcher; - guint connect_watcher; - gint connect_retry; - - qq_interval itv_config; - qq_interval itv_count; - guint network_watcher; - gint resend_times; - - GList *transactions; /* check ack packet and resend */ - - UID uid; /* QQ number */ - - qq_login_data ld; - qq_captcha_data captcha; - - guint8 session_key[QQ_KEY_LENGTH]; /* later use this as key in this session */ - guint8 session_md5[QQ_KEY_LENGTH]; /* concatenate my uid with session_key and md5 it */ - - guint16 send_seq; /* send sequence number */ - guint8 login_mode; /* online of invisible */ - gboolean is_login; /* used by qq_add_buddy */ - - PurpleXfer *xfer; /* file transfer handler */ - - /* get from login reply packet */ - struct in_addr my_local_ip; /* my local ip address detected by server */ - guint16 my_local_port; /* my lcoal port detected by server */ - time_t login_time; - time_t last_login_time[3]; - struct in_addr last_login_ip; - /* get from keep_alive packet */ - struct in_addr my_ip; /* my ip address detected by server */ - guint16 my_port; /* my port detected by server */ - guint16 my_icon; /* my icon index */ - guint32 online_total; /* the number of online QQ users */ - time_t online_last_update; /* last time send get_friends_online packet */ - - PurpleRoomlist *roomlist; - - GList *groups; - - gboolean is_show_notice; - gboolean is_show_news; - gboolean is_show_chat; - guint32 custom; - - guint16 send_im_id; /* send IM sequence number */ -} qq_data; - -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_base.c --- a/libpurple/protocols/qq/qq_base.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1491 +0,0 @@ -/** - * @file qq_base.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 "debug.h" -#include "server.h" -#include "cipher.h" -#include "request.h" - -#include "buddy_info.h" -#include "buddy_list.h" -#include "char_conv.h" -#include "qq_crypt.h" -#include "group.h" -#include "qq_define.h" -#include "qq_network.h" -#include "qq_base.h" -#include "packet_parse.h" -#include "qq.h" -#include "qq_network.h" -#include "utils.h" - -/* generate a md5 key using uid and session_key */ -static void get_session_md5(guint8 *session_md5, UID uid, guint8 *session_key) -{ - guint8 src[QQ_KEY_LENGTH + QQ_KEY_LENGTH]; - gint bytes = 0; - - bytes += qq_put32(src + bytes, uid); - bytes += qq_putdata(src + bytes, session_key, QQ_KEY_LENGTH); - - qq_get_md5(session_md5, QQ_KEY_LENGTH, src, bytes); -} - -/* process login reply which says OK */ -static gint8 process_login_ok(PurpleConnection *gc, guint8 *data, gint len) -{ - qq_data *qd; - gint bytes; - - guint8 ret; - UID uid; - struct in_addr ip; - guint16 port; - struct tm *tm_local; - - qd = (qq_data *) gc->proto_data; - /* qq_show_packet("Login reply", data, len); */ - - if (len < 148) { - qq_show_packet("Login reply OK, but length < 139", data, len); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("Unable to decrypt server reply")); - return QQ_LOGIN_REPLY_ERR; - } - - bytes = 0; - bytes += qq_get8(&ret, data + bytes); - bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes); - get_session_md5(qd->session_md5, qd->uid, qd->session_key); - purple_debug_info("QQ", "Got session_key\n"); - bytes += qq_get32(&uid, data + bytes); - if (uid != qd->uid) { - purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid); - } - bytes += qq_getIP(&qd->my_ip, data + bytes); - bytes += qq_get16(&qd->my_port, data + bytes); - purple_debug_info("QQ", "Internet IP: %s, %d\n", inet_ntoa(qd->my_ip), qd->my_port); - - bytes += qq_getIP(&qd->my_local_ip, data + bytes); - bytes += qq_get16(&qd->my_local_port, data + bytes); - purple_debug_info("QQ", "Local IP: %s, %d\n", inet_ntoa(qd->my_local_ip), qd->my_local_port); - - bytes += qq_getime(&qd->login_time, data + bytes); - tm_local = localtime(&qd->login_time); - purple_debug_info("QQ", "Login time: %d-%d-%d, %d:%d:%d\n", - (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday, - tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec); - /* skip unknown 2 bytes, 0x(03 0a) */ - bytes += 2; - /* skip unknown 24 bytes, maybe token to access Qun shared files */ - bytes += 24; - /* unknow ip and port */ - bytes += qq_getIP(&ip, data + bytes); - bytes += qq_get16(&port, data + bytes); - purple_debug_info("QQ", "Unknow IP: %s, %d\n", inet_ntoa(ip), port); - /* unknow ip and port */ - bytes += qq_getIP(&ip, data + bytes); - bytes += qq_get16(&port, data + bytes); - purple_debug_info("QQ", "Unknow IP: %s, %d\n", inet_ntoa(ip), port); - /* unknown 4 bytes, 0x(00 81 00 00)*/ - bytes += 4; - /* skip unknown 32 bytes, maybe key to access QQ Home */ - bytes += 32; - /* skip unknown 16 bytes, 0x(00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00) */ - bytes += 16; - /* time */ - bytes += qq_getime(&qd->last_login_time[0], data + bytes); - tm_local = localtime(&qd->last_login_time[0]); - purple_debug_info("QQ", "Last login time: %d-%d-%d, %d:%d:%d\n", - (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday, - tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec); - /* unknow time */ - g_return_val_if_fail(sizeof(qd->last_login_time) / sizeof(time_t) > 1, QQ_LOGIN_REPLY_OK); - bytes += qq_getime(&qd->last_login_time[1], data + bytes); - tm_local = localtime(&qd->last_login_time[1]); - purple_debug_info("QQ", "Time: %d-%d-%d, %d:%d:%d\n", - (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday, - tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec); - - g_return_val_if_fail(sizeof(qd->last_login_time) / sizeof(time_t) > 2, QQ_LOGIN_REPLY_OK); - bytes += qq_getime(&qd->last_login_time[2], data + bytes); - tm_local = localtime(&qd->last_login_time[2]); - purple_debug_info("QQ", "Time: %d-%d-%d, %d:%d:%d\n", - (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday, - tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec); - /* unknow 9 bytes, 0x(00 0a 00 0a 01 00 00 0e 10) */ - - if (len > 148) { - qq_show_packet("Login reply OK, but length > 139", data, len); - } - return QQ_LOGIN_REPLY_OK; -} - -/* process login reply packet which includes redirected new server address */ -static gint8 process_login_redirect(PurpleConnection *gc, guint8 *data, gint len) -{ - qq_data *qd; - gint bytes; - struct { - guint8 result; - UID uid; - struct in_addr new_server_ip; - guint16 new_server_port; - } packet; - - - if (len < 11) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("Unable to decrypt server reply")); - return QQ_LOGIN_REPLY_ERR; - } - - qd = (qq_data *) gc->proto_data; - bytes = 0; - /* 000-000: reply code */ - bytes += qq_get8(&packet.result, data + bytes); - /* 001-004: login uid */ - bytes += qq_get32(&packet.uid, data + bytes); - /* 005-008: redirected new server IP */ - bytes += qq_getIP(&packet.new_server_ip, data + bytes); - /* 009-010: redirected new server port */ - bytes += qq_get16(&packet.new_server_port, data + bytes); - - if (len > 11) { - purple_debug_error("QQ", "Login redirect more than expected %d bytes, read %d bytes\n", 11, bytes); - } - - /* redirect to new server, do not disconnect or connect here - * those connect should be called at packet_process */ - qd->redirect_ip.s_addr = packet.new_server_ip.s_addr; - qd->redirect_port = packet.new_server_port; - return QQ_LOGIN_REPLY_REDIRECT; -} - -/* request before login */ -void qq_request_token(PurpleConnection *gc) -{ - qq_data *qd; - guint8 buf[16] = {0}; - gint bytes = 0; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - bytes += qq_put8(buf + bytes, 0); - - qd->send_seq++; - qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN, qd->send_seq, buf, bytes, TRUE); -} - -/* send login packet to QQ server */ -void qq_request_login(PurpleConnection *gc) -{ - qq_data *qd; - guint8 *buf, *raw_data; - gint bytes; - guint8 *encrypted; - gint encrypted_len; - - /* for QQ 2005? copy from lumaqq */ - 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 guint8 login_53_68[16] = { - 0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A, - 0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D - }; - - 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 - }; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0); - - raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17); - memset(raw_data, 0, MAX_PACKET_SIZE - 17); - - encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */ - - bytes = 0; - /* now generate the encrypted data - * 000-015 use password_twice_md5 as key to encrypt empty string */ - encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5); - g_return_if_fail(encrypted_len == 16); - bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len); - - /* 016-016 */ - bytes += qq_put8(raw_data + bytes, 0x00); - /* 017-020, used to be IP, now zero */ - bytes += qq_put32(raw_data + bytes, 0x00000000); - /* 021-022, used to be port, now zero */ - bytes += qq_put16(raw_data + bytes, 0x0000); - /* 023-051, fixed value, unknown */ - bytes += qq_putdata(raw_data + bytes, login_23_51, 29); - /* 052-052, login mode */ - bytes += qq_put8(raw_data + bytes, qd->login_mode); - /* 053-068, fixed value, maybe related to per machine */ - bytes += qq_putdata(raw_data + bytes, login_53_68, 16); - /* 069, login token length */ - bytes += qq_put8(raw_data + bytes, qd->ld.token_len); - /* 070-093, login token, normally 24 bytes */ - bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len); - /* 100 bytes unknown */ - bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100); - /* all zero left */ - memset(raw_data + bytes, 0, 416 - bytes); - bytes = 416; - - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - memset(buf, 0, MAX_PACKET_SIZE); - bytes = 0; - bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH); - bytes += qq_putdata(buf + bytes, encrypted, encrypted_len); - - qd->send_seq++; - qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE); -} - -guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len) -{ - qq_data *qd; - gint bytes; - guint8 ret; - guint8 token_len; - gchar *msg; - - g_return_val_if_fail(buf != NULL && buf_len != 0, QQ_LOGIN_REPLY_ERR); - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR); - qd = (qq_data *) gc->proto_data; - - bytes = 0; - bytes += qq_get8(&ret, buf + bytes); - bytes += qq_get8(&token_len, buf + bytes); - - if (ret != QQ_LOGIN_REPLY_OK) { - qq_show_packet("Failed requesting token", buf, buf_len); - - msg = g_strdup_printf( _("Failed requesting token, 0x%02X"), ret ); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - msg); - g_free(msg); - return QQ_LOGIN_REPLY_ERR; - } - - if (bytes + token_len < buf_len) { - msg = g_strdup_printf( _("Invalid token len, %d"), token_len); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - msg); - g_free(msg); - return QQ_LOGIN_REPLY_ERR; - } - - if (bytes + token_len > buf_len) { - purple_debug_info("QQ", "Extra token data, %d %d\n", token_len, buf_len - bytes); - } - /* qq_show_packet("Got token", buf + bytes, buf_len - bytes); */ - - if (qd->ld.token != NULL) { - g_free(qd->ld.token); - qd->ld.token = NULL; - qd->ld.token_len = 0; - } - qd->ld.token = g_new0(guint8, token_len); - qd->ld.token_len = token_len; - g_memmove(qd->ld.token, buf + 2, qd->ld.token_len); - return ret; -} - -/* send logout packets to QQ server */ -void qq_request_logout(PurpleConnection *gc) -{ - gint i; - qq_data *qd; - - qd = (qq_data *) gc->proto_data; - for (i = 0; i < 4; i++) - qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->ld.pwd_twice_md5, QQ_KEY_LENGTH); - - qd->is_login = FALSE; /* update login status AFTER sending logout packets */ -} - -/* for QQ 2003iii 0117, fixed value */ -/* static const guint8 login_23_51[29] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20, - 0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13, - 0x95, 0x67, 0xda, 0x2c, 0x01 -}; */ - -/* for QQ 2003iii 0304, fixed value */ -/* -static const guint8 login_23_51[29] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85, - 0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6, - 0x40, 0xb8, 0xac, 0x32, 0x01 -}; -*/ - -/* fixed value, not affected by version, or mac address */ -/* -static const guint8 login_53_68[16] = { - 0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c, - 0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf -}; -*/ - -/* process the login reply packet */ -guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len) -{ - guint8 ret = data[0]; - gchar *msg, *msg_utf8; - gchar *error; - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - - g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR); - - switch (ret) { - case QQ_LOGIN_REPLY_OK: - purple_debug_info("QQ", "Login OK\n"); - return process_login_ok(gc, data, data_len); - case QQ_LOGIN_REPLY_REDIRECT: - purple_debug_info("QQ", "Redirect new server\n"); - return process_login_redirect(gc, data, data_len); - - case 0x0A: /* extend redirect used in QQ2006 */ - error = g_strdup( _("Redirect_EX is not currently supported") ); - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - case 0x05: /* invalid password */ - if (!purple_account_get_remember_password(gc->account)) { - purple_account_set_password(gc->account, NULL); - } - error = g_strdup( _("Incorrect password")); - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - case 0x06: /* need activation */ - error = g_strdup( _("Activation required")); - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - - default: - qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, - ">>> [default] decrypt and dump"); - error = g_strdup_printf( - _("Unknown reply code when logging in (0x%02X)"), - ret ); - reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR; - break; - } - - msg = g_strndup((gchar *)data + 1, data_len - 1); - msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); - - purple_debug_error("QQ", "%s: %s\n", error, msg_utf8); - purple_connection_error_reason(gc, reason, msg_utf8); - - g_free(error); - g_free(msg); - g_free(msg_utf8); - return QQ_LOGIN_REPLY_ERR; -} - -/* send keep-alive packet to QQ server (it is a heart-beat) */ -void qq_request_keep_alive(PurpleConnection *gc) -{ - qq_data *qd; - guint8 raw_data[16] = {0}; - gint bytes= 0; - - qd = (qq_data *) gc->proto_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 */ - bytes += qq_put32(raw_data + bytes, qd->uid); - qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes); -} - -/* parse the return ofqq_process_keep_alive keep-alive packet, it includes some system information */ -gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc) -{ - qq_data *qd; - gchar **segments; - - g_return_val_if_fail(data != NULL, FALSE); - g_return_val_if_fail(data_len != 0, FALSE); - - qd = (qq_data *) gc->proto_data; - - /* qq_show_packet("Keep alive reply packet", data, len); */ - - /* the last one is 60, don't know what it is */ - segments = split_data(data, data_len, "\x1f", 6); - if (segments == NULL) - return TRUE; - - /* segments[0] and segment[1] are all 0x30 ("0") */ - qd->online_total = strtol(segments[2], NULL, 10); - if(0 == qd->online_total) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Lost connection with server")); - } - qd->my_ip.s_addr = inet_addr(segments[3]); - qd->my_port = strtol(segments[4], NULL, 10); - - purple_debug_info("QQ", "keep alive, %s:%d\n", - inet_ntoa(qd->my_ip), qd->my_port); - - g_strfreev(segments); - return TRUE; -} - -void qq_request_keep_alive_2007(PurpleConnection *gc) -{ - qq_data *qd; - guint8 raw_data[32] = {0}; - gint bytes= 0; - gchar *uid_str; - - qd = (qq_data *) gc->proto_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 */ - uid_str = g_strdup_printf("%u", qd->uid); - bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str)); - qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes); - - g_free(uid_str); -} - -gboolean qq_process_keep_alive_2007(guint8 *data, gint data_len, PurpleConnection *gc) -{ - qq_data *qd; - gint bytes= 0; - guint8 ret; - - g_return_val_if_fail(data != NULL && data_len != 0, FALSE); - - qd = (qq_data *) gc->proto_data; - - /* qq_show_packet("Keep alive reply packet", data, len); */ - - bytes = 0; - bytes += qq_get8(&ret, data + bytes); - bytes += qq_get32(&qd->online_total, data + bytes); - if(0 == qd->online_total) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Lost connection with server")); - } - - bytes += qq_getIP(&qd->my_ip, data + bytes); - bytes += qq_get16(&qd->my_port, data + bytes); - return TRUE; -} - -void qq_request_keep_alive_2008(PurpleConnection *gc) -{ - qq_data *qd; - guint8 raw_data[16] = {0}; - gint bytes= 0; - - qd = (qq_data *) gc->proto_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 */ - bytes += qq_put32(raw_data + bytes, qd->uid); - bytes += qq_putime(raw_data + bytes, &qd->login_time); - qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes); -} - -gboolean qq_process_keep_alive_2008(guint8 *data, gint data_len, PurpleConnection *gc) -{ - qq_data *qd; - gint bytes= 0; - guint8 ret; - time_t server_time; - struct tm *tm_local; - - g_return_val_if_fail(data != NULL && data_len != 0, FALSE); - - qd = (qq_data *) gc->proto_data; - - /* qq_show_packet("Keep alive reply packet", data, len); */ - - bytes = 0; - bytes += qq_get8(&ret, data + bytes); - bytes += qq_get32(&qd->online_total, data + bytes); - if(0 == qd->online_total) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Lost connection with server")); - } - - bytes += qq_getIP(&qd->my_ip, data + bytes); - bytes += qq_get16(&qd->my_port, data + bytes); - /* skip 2 byytes, 0x(00 3c) */ - bytes += 2; - bytes += qq_getime(&server_time, data + bytes); - /* skip 5 bytes, all are 0 */ - - purple_debug_info("QQ", "keep alive, %s:%d\n", - inet_ntoa(qd->my_ip), qd->my_port); - - tm_local = localtime(&server_time); - - if (tm_local != NULL) - purple_debug_info("QQ", "Server time: %d-%d-%d, %d:%d:%d\n", - (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday, - tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec); - else - purple_debug_error("QQ", "Server time could not be parsed\n"); - - return TRUE; -} - -/* For QQ2007/2008 */ -void qq_request_get_server(PurpleConnection *gc) -{ - qq_data *qd; - guint8 *buf, *raw_data; - gint bytes; - guint8 *encrypted; - gint encrypted_len; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - raw_data = g_newa(guint8, 128); - memset(raw_data, 0, 128); - - encrypted = g_newa(guint8, 128 + 17); /* 17 bytes more */ - - bytes = 0; - if (qd->redirect == NULL) { - /* first packet to get server */ - qd->redirect_len = 15; - qd->redirect = g_realloc(qd->redirect, qd->redirect_len); - memset(qd->redirect, 0, qd->redirect_len); - } - bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len); - - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - memset(buf, 0, MAX_PACKET_SIZE); - bytes = 0; - bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH); - bytes += qq_putdata(buf + bytes, encrypted, encrypted_len); - - qd->send_seq++; - qq_send_cmd_encrypted(gc, QQ_CMD_GET_SERVER, qd->send_seq, buf, bytes, TRUE); -} - -guint16 qq_process_get_server(PurpleConnection *gc, guint8 *data, gint data_len) -{ - qq_data *qd; - gint bytes; - guint16 ret; - - g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR); - qd = (qq_data *) gc->proto_data; - - g_return_val_if_fail (data != NULL, QQ_LOGIN_REPLY_ERR); - - /* qq_show_packet("Get Server", data, data_len); */ - bytes = 0; - bytes += qq_get16(&ret, data + bytes); - if (ret == 0) { - /* Notice: do not clear redirect_data here. It will be used in login*/ - qd->redirect_ip.s_addr = 0; - return QQ_LOGIN_REPLY_OK; - } - - if (data_len < 15) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("Unable to decrypt server reply")); - return QQ_LOGIN_REPLY_ERR; - } - - qd->redirect_len = data_len; - qd->redirect = g_realloc(qd->redirect, qd->redirect_len); - qq_getdata(qd->redirect, qd->redirect_len, data); - /* qq_show_packet("Redirect to", qd->redirect, qd->redirect_len); */ - - qq_getIP(&qd->redirect_ip, data + 11); - purple_debug_info("QQ", "Get server %s\n", inet_ntoa(qd->redirect_ip)); - return QQ_LOGIN_REPLY_REDIRECT; -} - -void qq_request_token_ex(PurpleConnection *gc) -{ - qq_data *qd; - guint8 *buf, *raw_data; - gint bytes; - guint8 *encrypted; - gint encrypted_len; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0); - - raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17); - memset(raw_data, 0, MAX_PACKET_SIZE - 17); - - encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */ - - bytes = 0; - bytes += qq_put8(raw_data + bytes, qd->ld.token_len); - bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len); - bytes += qq_put8(raw_data + bytes, 3); /* Subcommand */ - bytes += qq_put16(raw_data + bytes, 5); - bytes += qq_put32(raw_data + bytes, 0); - bytes += qq_put8(raw_data + bytes, 0); /* fragment index */ - bytes += qq_put16(raw_data + bytes, 0); /* captcha token */ - - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - memset(buf, 0, MAX_PACKET_SIZE); - bytes = 0; - bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH); - bytes += qq_putdata(buf + bytes, encrypted, encrypted_len); - - qd->send_seq++; - qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE); -} - -void qq_request_token_ex_next(PurpleConnection *gc) -{ - qq_data *qd; - guint8 *buf, *raw_data; - gint bytes; - guint8 *encrypted; - gint encrypted_len; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0); - - raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17); - memset(raw_data, 0, MAX_PACKET_SIZE - 17); - - encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */ - - bytes = 0; - bytes += qq_put8(raw_data + bytes, qd->ld.token_len); - bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len); - bytes += qq_put8(raw_data + bytes, 3); /* Subcommand */ - bytes += qq_put16(raw_data + bytes, 5); - bytes += qq_put32(raw_data + bytes, 0); - bytes += qq_put8(raw_data + bytes, qd->captcha.next_index); /* fragment index */ - bytes += qq_put16(raw_data + bytes, qd->captcha.token_len); /* captcha token */ - bytes += qq_putdata(raw_data + bytes, qd->captcha.token, qd->captcha.token_len); - - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - memset(buf, 0, MAX_PACKET_SIZE); - bytes = 0; - bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH); - bytes += qq_putdata(buf + bytes, encrypted, encrypted_len); - - qd->send_seq++; - qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE); - - purple_connection_update_progress(gc, _("Requesting captcha"), 3, QQ_CONNECT_STEPS); -} - -static void request_token_ex_code(PurpleConnection *gc, - guint8 *token, guint16 token_len, guint8 *code, guint16 code_len) -{ - qq_data *qd; - guint8 *buf, *raw_data; - gint bytes; - guint8 *encrypted; - gint encrypted_len; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0); - g_return_if_fail(code != NULL && code_len > 0); - - raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17); - memset(raw_data, 0, MAX_PACKET_SIZE - 17); - - encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */ - - bytes = 0; - bytes += qq_put8(raw_data + bytes, qd->ld.token_len); - bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len); - bytes += qq_put8(raw_data + bytes, 4); /* Subcommand */ - bytes += qq_put16(raw_data + bytes, 5); - bytes += qq_put32(raw_data + bytes, 0); - bytes += qq_put16(raw_data + bytes, code_len); - bytes += qq_putdata(raw_data + bytes, code, code_len); - bytes += qq_put16(raw_data + bytes, qd->ld.token_ex_len); /* login token ex */ - bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len); - - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - memset(buf, 0, MAX_PACKET_SIZE); - bytes = 0; - bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH); - bytes += qq_putdata(buf + bytes, encrypted, encrypted_len); - - qd->send_seq++; - qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE); - - purple_connection_update_progress(gc, _("Checking captcha"), 3, QQ_CONNECT_STEPS); -} - -typedef struct { - PurpleConnection *gc; - guint8 *token; - guint16 token_len; -} qq_captcha_request; - -static void captcha_request_destory(qq_captcha_request *captcha_req) -{ - g_return_if_fail(captcha_req != NULL); - if (captcha_req->token) g_free(captcha_req->token); - g_free(captcha_req); -} - -static void captcha_input_cancel_cb(qq_captcha_request *captcha_req, - PurpleRequestFields *fields) -{ - purple_connection_error_reason(captcha_req->gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("Failed captcha verification")); - - captcha_request_destory(captcha_req); -} - -static void captcha_input_ok_cb(qq_captcha_request *captcha_req, - PurpleRequestFields *fields) -{ - gchar *code; - - g_return_if_fail(captcha_req != NULL && captcha_req->gc != NULL); - - code = utf8_to_qq( - purple_request_fields_get_string(fields, "captcha_code"), - QQ_CHARSET_DEFAULT); - - if (strlen(code) <= 0) { - captcha_input_cancel_cb(captcha_req, fields); - return; - } - - request_token_ex_code(captcha_req->gc, - captcha_req->token, captcha_req->token_len, - (guint8 *)code, strlen(code)); - - captcha_request_destory(captcha_req); -} - -void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha) -{ - PurpleAccount *account; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - qq_captcha_request *captcha_req; - - g_return_if_fail(captcha->token != NULL && captcha->token_len > 0); - g_return_if_fail(captcha->data != NULL && captcha->data_len > 0); - - captcha_req = g_new0(qq_captcha_request, 1); - captcha_req->gc = gc; - captcha_req->token = g_new0(guint8, captcha->token_len); - g_memmove(captcha_req->token, captcha->token, captcha->token_len); - captcha_req->token_len = captcha->token_len; - - account = purple_connection_get_account(gc); - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_image_new("captcha_img", - _("Captcha Image"), (char *)captcha->data, captcha->data_len); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("captcha_code", - _("Enter code"), "", FALSE); - purple_request_field_string_set_masked(field, FALSE); - purple_request_field_group_add_field(group, field); - - purple_request_fields(account, - _("QQ Captcha Verification"), - _("QQ Captcha Verification"), - _("Enter the text from the image"), - fields, - _("OK"), G_CALLBACK(captcha_input_ok_cb), - _("Cancel"), G_CALLBACK(captcha_input_cancel_cb), - purple_connection_get_account(gc), NULL, NULL, - captcha_req); -} - -guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *data, gint data_len) -{ - qq_data *qd; - int bytes; - guint8 ret; - guint8 sub_cmd; - guint8 reply; - guint16 captcha_len; - guint8 curr_index; - - g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR); - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR); - qd = (qq_data *) gc->proto_data; - - ret = data[0]; - - bytes = 0; - bytes += qq_get8(&sub_cmd, data + bytes); /* 03: ok; 04: need verifying */ - bytes += 2; /* 0x(00 05) */ - bytes += qq_get8(&reply, data + bytes); - - bytes += qq_get16(&(qd->ld.token_ex_len), data + bytes); - qd->ld.token_ex = g_realloc(qd->ld.token_ex, qd->ld.token_ex_len); - bytes += qq_getdata(qd->ld.token_ex, qd->ld.token_ex_len, data + bytes); - /* qq_show_packet("Get token ex", qd->ld.token_ex, qd->ld.token_ex_len); */ - - if(reply != 1) - { - purple_debug_info("QQ", "Captcha verified, result %d\n", reply); - return QQ_LOGIN_REPLY_OK; - } - - bytes += qq_get16(&captcha_len, data + bytes); - - qd->captcha.data = g_realloc(qd->captcha.data, qd->captcha.data_len + captcha_len); - bytes += qq_getdata(qd->captcha.data + qd->captcha.data_len, captcha_len, data + bytes); - qd->captcha.data_len += captcha_len; - - bytes += qq_get8(&curr_index, data + bytes); - bytes += qq_get8(&qd->captcha.next_index, data + bytes); - - bytes += qq_get16(&qd->captcha.token_len, data + bytes); - qd->captcha.token = g_realloc(qd->captcha.token, qd->captcha.token_len); - bytes += qq_getdata(qd->captcha.token, qd->captcha.token_len, data + bytes); - /* qq_show_packet("Get captcha token", qd->captcha.token, qd->captcha.token_len); */ - - purple_debug_info("QQ", "Request next captcha %d, new %d, total %d\n", - qd->captcha.next_index, captcha_len, qd->captcha.data_len); - if(qd->captcha.next_index > 0) - { - return QQ_LOGIN_REPLY_NEXT_TOKEN_EX; - } - - return QQ_LOGIN_REPLY_CAPTCHA_DLG; -} - -/* source copy from gg's common.c */ -static guint32 crc32_table[256]; -static int crc32_initialized = 0; - -static void crc32_make_table() -{ - guint32 h = 1; - unsigned int i, j; - - memset(crc32_table, 0, sizeof(crc32_table)); - - for (i = 128; i; i >>= 1) { - h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0); - - for (j = 0; j < 256; j += 2 * i) - crc32_table[i + j] = crc32_table[j] ^ h; - } - - crc32_initialized = 1; -} - -static guint32 crc32(guint32 crc, const guint8 *buf, int len) -{ - if (!crc32_initialized) - crc32_make_table(); - - if (!buf || len < 0) - return crc; - - crc ^= 0xffffffffL; - - while (len--) - crc = (crc >> 8) ^ crc32_table[(crc ^ *buf++) & 0xff]; - - return crc ^ 0xffffffffL; -} - -void qq_request_check_pwd(PurpleConnection *gc) -{ - qq_data *qd; - guint8 *buf, *raw_data; - gint bytes; - guint8 *encrypted; - gint encrypted_len; - static guint8 header[] = { - 0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0xE0 - }; - static guint8 unknown[] = { - 0xDB, 0xB9, 0xF3, 0x0B, 0xF9, 0x13, 0x87, 0xB2, - 0xE6, 0x20, 0x43, 0xBE, 0x53, 0xCA, 0x65, 0x03 - }; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - g_return_if_fail(qd->ld.token_ex != NULL && qd->ld.token_ex_len > 0); - - raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17); - memset(raw_data, 0, MAX_PACKET_SIZE - 17); - - encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */ - - /* Encrypted password and put in encrypted */ - bytes = 0; - bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5)); - bytes += qq_put16(raw_data + bytes, 0); - bytes += qq_put16(raw_data + bytes, rand() & 0xffff); - - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5); - - /* create packet */ - bytes = 0; - bytes += qq_putdata(raw_data + bytes, header, sizeof(header)); - /* token get from qq_request_token_ex */ - bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff)); - bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len); - /* password encrypted */ - bytes += qq_put16(raw_data + bytes, encrypted_len); - bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len); - /* len of unknown + len of CRC32 */ - bytes += qq_put16(raw_data + bytes, sizeof(unknown) + 4); - bytes += qq_putdata(raw_data + bytes, unknown, sizeof(unknown)); - bytes += qq_put32( - raw_data + bytes, crc32(0xFFFFFFFF, unknown, sizeof(unknown))); - - /* put length into first 2 bytes */ - qq_put8(raw_data + 1, bytes - 2); - - /* tail */ - bytes += qq_put16(raw_data + bytes, 0x0003); - bytes += qq_put8(raw_data + bytes, 0); - bytes += qq_put8(raw_data + bytes, qd->ld.pwd_md5[1]); - bytes += qq_put8(raw_data + bytes, qd->ld.pwd_md5[2]); - - /* qq_show_packet("Check password", raw_data, bytes); */ - /* Encrypted by random key*/ - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - memset(buf, 0, MAX_PACKET_SIZE); - bytes = 0; - bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH); - bytes += qq_putdata(buf + bytes, encrypted, encrypted_len); - - qd->send_seq++; - qq_send_cmd_encrypted(gc, QQ_CMD_CHECK_PWD, qd->send_seq, buf, bytes, TRUE); -} - -guint8 qq_process_check_pwd( PurpleConnection *gc, guint8 *data, gint data_len) -{ - qq_data *qd; - int bytes; - guint8 ret; - gchar *error = NULL; - guint16 unknow_token_len; - gchar *msg, *msg_utf8; - guint16 msg_len; - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - - g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR); - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR); - qd = (qq_data *) gc->proto_data; - - /* qq_show_packet("Check password reply", data, data_len); */ - - bytes = 0; - bytes += qq_get16(&unknow_token_len, data + bytes); /* maybe total length */ - bytes += qq_get8(&ret, data + bytes); - bytes += 4; /* 0x(00 00 6d b9) */ - /* unknow_token_len may 0 when not reply ok*/ - bytes += qq_get16(&unknow_token_len, data + bytes); /* 0x0020 */ - bytes += unknow_token_len; - bytes += qq_get16(&unknow_token_len, data + bytes); /* 0x0020 */ - bytes += unknow_token_len; - - if (ret == 0) { - /* get login_token */ - bytes += qq_get16(&qd->ld.login_token_len, data + bytes); - if (qd->ld.login_token != NULL) g_free(qd->ld.login_token); - qd->ld.login_token = g_new0(guint8, qd->ld.login_token_len); - bytes += qq_getdata(qd->ld.login_token, qd->ld.login_token_len, data + bytes); - /* qq_show_packet("Get login token", qd->ld.login_token, qd->ld.login_token_len); */ - - /* get login_key */ - bytes += qq_getdata(qd->ld.login_key, sizeof(qd->ld.login_key), data + bytes); - /* qq_show_packet("Get login key", qd->ld.login_key, sizeof(qd->ld.login_key)); */ - return QQ_LOGIN_REPLY_OK; - } - - switch (ret) - { - case 0x34: /* invalid password */ - if (!purple_account_get_remember_password(gc->account)) { - purple_account_set_password(gc->account, NULL); - } - error = g_strdup(_("Incorrect password")); - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - case 0x33: /* need activation */ - case 0x51: /* need activation */ - error = g_strdup(_("Activation required")); - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - case 0xBF: /* uid is not exist */ - error = g_strdup(_("Username does not exist")); - reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME; - break; - default: - qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, - ">>> [default] decrypt and dump"); - error = g_strdup_printf( - _("Unknown reply when checking password (0x%02X)"), - ret ); - reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR; - break; - } - - bytes += qq_get16(&msg_len, data + bytes); - - msg = g_strndup((gchar *)data + bytes, msg_len); - msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); - - purple_debug_error("QQ", "%s: %s\n", error, msg_utf8); - purple_connection_error_reason(gc, reason, msg_utf8); - - g_free(error); - g_free(msg); - g_free(msg_utf8); - return QQ_LOGIN_REPLY_ERR; -} - -void qq_request_login_2007(PurpleConnection *gc) -{ - qq_data *qd; - guint8 *buf, *raw_data; - gint bytes; - guint8 *encrypted; - gint encrypted_len; - static const guint8 login_1_16[] = { - 0x56, 0x4E, 0xC8, 0xFB, 0x0A, 0x4F, 0xEF, 0xB3, - 0x7A, 0x5D, 0xD8, 0x86, 0x0F, 0xAC, 0xE5, 0x1A - }; - static const guint8 login_2_16[] = { - 0x5E, 0x22, 0x3A, 0xBE, 0x13, 0xBF, 0xDA, 0x4C, - 0xA9, 0xB7, 0x0B, 0x43, 0x63, 0x51, 0x8E, 0x28 - }; - static const guint8 login_3_83[] = { - 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x40, 0x01, 0x01, 0x58, 0x83, - 0xD0, 0x00, 0x10, 0x9D, 0x14, 0x64, 0x0A, 0x2E, - 0xE2, 0x11, 0xF7, 0x90, 0xF0, 0xB5, 0x5F, 0x16, - 0xFB, 0x41, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x76, 0x3C, 0xEE, - 0x4A, 0x00, 0x10, 0x86, 0x81, 0xAD, 0x1F, 0xC8, - 0xC9, 0xCC, 0xCF, 0xCA, 0x9F, 0xFF, 0x88, 0xC0, - 0x5C, 0x88, 0xD5 - }; - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0); - - raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17); - memset(raw_data, 0, MAX_PACKET_SIZE - 17); - - encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */ - - /* Encrypted password and put in encrypted */ - bytes = 0; - bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5)); - bytes += qq_put16(raw_data + bytes, 0); - bytes += qq_put16(raw_data + bytes, 0xffff); - - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5); - - /* create packet */ - bytes = 0; - bytes += qq_put16(raw_data + bytes, 0); /* Unknow */ - /* password encrypted */ - bytes += qq_put16(raw_data + bytes, encrypted_len); - bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len); - /* put data which NULL string encrypted by key pwd_twice_md5 */ - encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5); - g_return_if_fail(encrypted_len == 16); - bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len); - /* unknow fill 0 */ - memset(raw_data + bytes, 0, 19); - bytes += 19; - bytes += qq_putdata(raw_data + bytes, login_1_16, sizeof(login_1_16)); - - bytes += qq_put8(raw_data + bytes, rand() & 0xff); - bytes += qq_put8(raw_data + bytes, qd->login_mode); - /* unknow 10 bytes zero filled*/ - memset(raw_data + bytes, 0, 10); - bytes += 10; - /* redirect data, 15 bytes */ - /* qq_show_packet("Redirect", qd->redirect, qd->redirect_len); */ - bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len); - /* unknow fill */ - bytes += qq_putdata(raw_data + bytes, login_2_16, sizeof(login_2_16)); - /* captcha token get from qq_process_token_ex */ - bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff)); - bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len); - /* unknow fill */ - bytes += qq_putdata(raw_data + bytes, login_3_83, sizeof(login_3_83)); - memset(raw_data + bytes, 0, 332 - sizeof(login_3_83)); - bytes += 332 - sizeof(login_3_83); - - /* qq_show_packet("Login", raw_data, bytes); */ - - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - memset(buf, 0, MAX_PACKET_SIZE); - bytes = 0; - /* logint token get from qq_process_check_pwd_2007 */ - bytes += qq_put16(buf + bytes, qd->ld.login_token_len); - bytes += qq_putdata(buf + bytes, qd->ld.login_token, qd->ld.login_token_len); - bytes += qq_putdata(buf + bytes, encrypted, encrypted_len); - - qd->send_seq++; - qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE); -} - -/* process the login reply packet */ -guint8 qq_process_login_2007( PurpleConnection *gc, guint8 *data, gint data_len) -{ - qq_data *qd; - gint bytes; - guint8 ret; - UID uid; - gchar *error; - gchar *msg; - gchar *msg_utf8; - - g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR); - - qd = (qq_data *) gc->proto_data; - - bytes = 0; - bytes += qq_get8(&ret, data + bytes); - if (ret != 0) { - msg = g_strndup((gchar *)data + bytes, data_len - bytes); - msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); - g_free(msg); - - switch (ret) { - case 0x05: - purple_debug_error("QQ", "Server busy for %s\n", msg_utf8); - return QQ_LOGIN_REPLY_REDIRECT; - case 0x0A: - /* 0a 2d 9a 4b 9a 01 01 00 00 00 05 00 00 00 00 79 0e 5f fd */ - /* Missing get server before login*/ - default: - error = g_strdup_printf( - _("Unknown reply code when logging in (0x%02X):\n%s"), - ret, msg_utf8); - break; - } - - purple_debug_error("QQ", "%s\n", error); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_OTHER_ERROR, - error); - - qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, error); - - g_free(error); - g_free(msg_utf8); - return QQ_LOGIN_REPLY_ERR; - } - - bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes); - purple_debug_info("QQ", "Got session_key\n"); - get_session_md5(qd->session_md5, qd->uid, qd->session_key); - - bytes += qq_get32(&uid, data + bytes); - if (uid != qd->uid) { - purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid); - } - bytes += qq_getIP(&qd->my_ip, data + bytes); - bytes += qq_get16(&qd->my_port, data + bytes); - bytes += qq_getIP(&qd->my_local_ip, data + bytes); - bytes += qq_get16(&qd->my_local_port, data + bytes); - bytes += qq_getime(&qd->login_time, data + bytes); - /* skip unknow 50 byte */ - bytes += 50; - /* skip client key 32 byte */ - bytes += 32; - /* skip unknow 12 byte */ - bytes += 12; - /* last login */ - bytes += qq_getIP(&qd->last_login_ip, data + bytes); - bytes += qq_getime(&qd->last_login_time[0], data + bytes); - purple_debug_info("QQ", "Last Login: %s, %s\n", - inet_ntoa(qd->last_login_ip), ctime(&qd->last_login_time[0])); - return QQ_LOGIN_REPLY_OK; -} - -void qq_request_login_2008(PurpleConnection *gc) -{ - qq_data *qd; - guint8 *buf, *raw_data; - gint bytes; - guint8 *encrypted; - gint encrypted_len; - guint8 index, count; - - static const guint8 login_1_16[] = { - 0xD2, 0x41, 0x75, 0x12, 0xC2, 0x86, 0x57, 0x10, - 0x78, 0x57, 0xDC, 0x24, 0x8C, 0xAA, 0x8F, 0x4E - }; - - static const guint8 login_2_16[] = { - 0x94, 0x0B, 0x73, 0x7A, 0xA2, 0x51, 0xF0, 0x4B, - 0x95, 0x2F, 0xC6, 0x0A, 0x5B, 0xF6, 0x76, 0x52 - }; - static const guint8 login_3_18[] = { - 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x40, 0x01, 0x1b, 0x02, 0x84, - 0x50, 0x00 - }; - static const guint8 login_4_16[] = { - 0x2D, 0x49, 0x15, 0x55, 0x78, 0xFC, 0xF3, 0xD4, - 0x53, 0x55, 0x60, 0x9C, 0x37, 0x9F, 0xE9, 0x59 - }; - static const guint8 login_5_6[] = { - 0x02, 0x68, 0xe8, 0x07, 0x83, 0x00 - }; - static const guint8 login_6_16[] = { - 0x3B, 0xCE, 0x43, 0xF1, 0x8B, 0xA4, 0x2B, 0xB5, - 0xB3, 0x51, 0x57, 0xF7, 0x06, 0x4B, 0x18, 0xFC - }; - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0); - - raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17); - memset(raw_data, 0, MAX_PACKET_SIZE - 17); - - encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */ - - /* Encrypted password and put in encrypted */ - bytes = 0; - bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5)); - bytes += qq_put16(raw_data + bytes, 0); - bytes += qq_put16(raw_data + bytes, 0xffff); - - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5); - - /* create packet */ - bytes = 0; - bytes += qq_put16(raw_data + bytes, 0); /* Unknow */ - /* password encrypted */ - bytes += qq_put16(raw_data + bytes, encrypted_len); - bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len); - /* put data which NULL string encrypted by key pwd_twice_md5 */ - encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5); - g_return_if_fail(encrypted_len == 16); - bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len); - /* unknow 19 bytes zero filled*/ - memset(raw_data + bytes, 0, 19); - bytes += 19; - bytes += qq_putdata(raw_data + bytes, login_1_16, sizeof(login_1_16)); - - index = rand() % 3; /* can be set as 1 */ - for( count = 0; count < encrypted_len; count++ ) - index ^= encrypted[count]; - for( count = 0; count < sizeof(login_1_16); count++ ) - index ^= login_1_16[count]; - bytes += qq_put8(raw_data + bytes, index); /* random in QQ 2007*/ - - bytes += qq_put8(raw_data + bytes, qd->login_mode); - /* unknow 10 bytes zero filled*/ - memset(raw_data + bytes, 0, 10); - bytes += 10; - /* redirect data, 15 bytes */ - bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len); - /* unknow fill */ - bytes += qq_putdata(raw_data + bytes, login_2_16, sizeof(login_2_16)); - /* captcha token get from qq_process_token_ex */ - bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff)); - bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len); - /* unknow fill */ - bytes += qq_putdata(raw_data + bytes, login_3_18, sizeof(login_3_18)); - bytes += qq_put8(raw_data + bytes , sizeof(login_4_16)); - bytes += qq_putdata(raw_data + bytes, login_4_16, sizeof(login_4_16)); - /* unknow 10 bytes zero filled*/ - memset(raw_data + bytes, 0, 10); - bytes += 10; - /* redirect data, 15 bytes */ - bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len); - /* unknow fill */ - bytes += qq_putdata(raw_data + bytes, login_5_6, sizeof(login_5_6)); - bytes += qq_put8(raw_data + bytes , sizeof(login_6_16)); - bytes += qq_putdata(raw_data + bytes, login_6_16, sizeof(login_6_16)); - /* unknow 249 bytes zero filled*/ - memset(raw_data + bytes, 0, 249); - bytes += 249; - - /* qq_show_packet("Login request", raw_data, bytes); */ - encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - memset(buf, 0, MAX_PACKET_SIZE); - bytes = 0; - /* logint token get from qq_process_check_pwd_2007 */ - bytes += qq_put16(buf + bytes, qd->ld.login_token_len); - bytes += qq_putdata(buf + bytes, qd->ld.login_token, qd->ld.login_token_len); - bytes += qq_putdata(buf + bytes, encrypted, encrypted_len); - - qd->send_seq++; - qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE); -} - -guint8 qq_process_login_2008( PurpleConnection *gc, guint8 *data, gint data_len) -{ - qq_data *qd; - gint bytes; - guint8 ret; - UID uid; - gchar *error; - gchar *msg; - gchar *msg_utf8; - - g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR); - - qd = (qq_data *) gc->proto_data; - - bytes = 0; - bytes += qq_get8(&ret, data + bytes); - if (ret != 0) { - msg = g_strndup((gchar *)data + bytes, data_len - bytes); - msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); - g_free(msg); - - switch (ret) { - case 0x05: - purple_debug_error("QQ", "Server busy for %s\n", msg_utf8); - return QQ_LOGIN_REPLY_REDIRECT; - break; - default: - error = g_strdup_printf( - _("Unknown reply code when logging in (0x%02X):\n%s"), - ret, msg_utf8); - break; - } - - purple_debug_error("QQ", "%s\n", error); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_OTHER_ERROR, - error); - - qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, error); - - g_free(error); - g_free(msg_utf8); - return QQ_LOGIN_REPLY_ERR; - } - - bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes); - purple_debug_info("QQ", "Got session_key\n"); - get_session_md5(qd->session_md5, qd->uid, qd->session_key); - - bytes += qq_get32(&uid, data + bytes); - if (uid != qd->uid) { - purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid); - } - bytes += qq_getIP(&qd->my_ip, data + bytes); - bytes += qq_get16(&qd->my_port, data + bytes); - bytes += qq_getIP(&qd->my_local_ip, data + bytes); - bytes += qq_get16(&qd->my_local_port, data + bytes); - bytes += qq_getime(&qd->login_time, data + bytes); - /* skip 1 byte, always 0x03 */ - /* skip 1 byte, login mode */ - bytes = 131; - bytes += qq_getIP(&qd->last_login_ip, data + bytes); - bytes += qq_getime(&qd->last_login_time[0], data + bytes); - purple_debug_info("QQ", "Last Login: %s, %s\n", - inet_ntoa(qd->last_login_ip), ctime(&qd->last_login_time[0])); - return QQ_LOGIN_REPLY_OK; -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_base.h --- a/libpurple/protocols/qq/qq_base.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/** - * file qq_base.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_BASE_H_ -#define _QQ_BASE_H_ - -#include -#include "connection.h" - -#define QQ_LOGIN_REPLY_OK 0x00 -#define QQ_LOGIN_REPLY_REDIRECT 0x01 -/* defined by myself */ -#define QQ_LOGIN_REPLY_CAPTCHA_DLG 0xfd -#define QQ_LOGIN_REPLY_NEXT_TOKEN_EX 0xfe -#define QQ_LOGIN_REPLY_ERR 0xff - -#define QQ_LOGIN_MODE_NORMAL 0x0a -#define QQ_LOGIN_MODE_AWAY 0x1e -#define QQ_LOGIN_MODE_HIDDEN 0x28 - -#define QQ_UPDATE_ONLINE_INTERVAL 300 /* in sec */ - -void qq_request_token(PurpleConnection *gc); -guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len); - -void qq_request_login(PurpleConnection *gc); -guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len); - -void qq_request_logout(PurpleConnection *gc); - -void qq_request_keep_alive(PurpleConnection *gc); -gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc); - -void qq_request_keep_alive_2007(PurpleConnection *gc); -gboolean qq_process_keep_alive_2007(guint8 *data, gint data_len, PurpleConnection *gc); - -void qq_request_keep_alive_2008(PurpleConnection *gc); -gboolean qq_process_keep_alive_2008(guint8 *data, gint data_len, PurpleConnection *gc); - -/* for QQ2007/2008 */ -void qq_request_get_server(PurpleConnection *gc); -guint16 qq_process_get_server(PurpleConnection *gc, guint8 *rcved, gint rcved_len); - -void qq_request_token_ex(PurpleConnection *gc); -void qq_request_token_ex_next(PurpleConnection *gc); -guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *buf, gint buf_len); -void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha); - -void qq_request_check_pwd(PurpleConnection *gc); -guint8 qq_process_check_pwd( PurpleConnection *gc, guint8 *data, gint data_len); - -void qq_request_login_2007(PurpleConnection *gc); -guint8 qq_process_login_2007( PurpleConnection *gc, guint8 *data, gint data_len); - -void qq_request_login_2008(PurpleConnection *gc); -guint8 qq_process_login_2008( PurpleConnection *gc, guint8 *data, gint data_len); -#endif - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_crypt.c --- a/libpurple/protocols/qq/qq_crypt.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,337 +0,0 @@ -/** - * @file qq_crypt.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 02110-1301, USA - * - * - * QQ encryption algorithm - * Convert from ASM code provided by PerlOICQ - * - * Puzzlebird, Nov-Dec 2002 - */ - -/* Notes: (QQ uses 16 rounds, and modified something...) - -IN : 64 bits of data in v[0] - v[1]. -OUT: 64 bits of data in w[0] - w[1]. -KEY: 128 bits of key in k[0] - k[3]. - -delta is chosen to be the real part of -the golden ratio: Sqrt(5/4) - 1/2 ~ 0.618034 multiplied by 2^32. - -0x61C88647 is what we can track on the ASM codes.!! -*/ - -#include - -#include "debug.h" -#include "qq_crypt.h" - -#if 0 -void show_binary(char *psztitle, const guint8 *const buffer, gint bytes) -{ - printf("== %s %d ==\r\n", psztitle, bytes); - gint i, j, ch; - for (i = 0; i < bytes; i += 16) { - /* length label */ - printf("%07x: ", i); - - /* dump hex value */ - for (j = 0; j < 16; j++) { - if (j == 8) { - printf(" -"); - } - if ((i + j) < bytes) - printf(" %02x", buffer[i + j]); - else - printf(" "); - } - - printf(" "); - - - /* dump ascii value */ - for (j = 0; j < 16 && (i + j) < bytes; j++) { - ch = buffer[i + j] & 127; - if (ch < ' ' || ch == 127) - printf("."); - else - printf("%c", ch); - } - printf("\r\n"); - } - printf("========\r\n"); -} -#else - -#define show_binary(args... ) /* nothing */ - -#endif - -/******************************************************************** - * encryption - *******************************************************************/ - -/* Tiny Encryption Algorithm (TEA) */ -static inline void qq_encipher(guint32 *const v, const guint32 *const k, guint32 *const w) -{ - register guint32 - y = g_ntohl(v[0]), - z = g_ntohl(v[1]), - a = g_ntohl(k[0]), - b = g_ntohl(k[1]), - c = g_ntohl(k[2]), - d = g_ntohl(k[3]), - n = 0x10, - sum = 0, - delta = 0x9E3779B9; /* 0x9E3779B9 - 0x100000000 = -0x61C88647 */ - - while (n-- > 0) { - sum += delta; - y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); - z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); - } - - w[0] = g_htonl(y); - w[1] = g_htonl(z); -} - -/* it can be the real random seed function */ -/* override with number, convenient for debug */ -#ifdef DEBUG -static gint crypt_rand(void) { - return 0xdead; -} -#else -#include -#define crypt_rand() rand() -#endif - -/* 64-bit blocks and some kind of feedback mode of operation */ -static inline void encrypt_out(guint8 *crypted, const gint crypted_len, const guint8 *key) -{ - /* ships in encipher */ - guint32 plain32[2]; - guint32 p32_prev[2]; - guint32 key32[4]; - guint32 crypted32[2]; - guint32 c32_prev[2]; - - guint8 *crypted_ptr; - gint count64; - - /* prepare at first */ - crypted_ptr = crypted; - - memcpy(crypted32, crypted_ptr, sizeof(crypted32)); - c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1]; - - p32_prev[0] = 0; p32_prev[1] = 0; - plain32[0] = crypted32[0] ^ p32_prev[0]; plain32[1] = crypted32[1] ^ p32_prev[1]; - - g_memmove(key32, key, 16); - count64 = crypted_len / 8; - while (count64-- > 0){ - /* encrypt it */ - qq_encipher(plain32, key32, crypted32); - - crypted32[0] ^= p32_prev[0]; crypted32[1] ^= p32_prev[1]; - - /* store curr 64 bits crypted */ - g_memmove(crypted_ptr, crypted32, sizeof(crypted32)); - - /* set prev */ - p32_prev[0] = plain32[0]; p32_prev[1] = plain32[1]; - c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1]; - - /* set next 64 bits want to crypt*/ - if (count64 > 0) { - crypted_ptr += 8; - memcpy(crypted32, crypted_ptr, sizeof(crypted32)); - plain32[0] = crypted32[0] ^ c32_prev[0]; plain32[1] = crypted32[1] ^ c32_prev[1]; - } - } -} - -/* length of crypted buffer must be plain_len + 17*/ -/* - * The above comment used to say "plain_len + 16", but based on the - * behavior of the function that is wrong. If you give this function - * a plain string with len%8 = 7 then the returned length is len+17 - */ -gint qq_encrypt(guint8* crypted, const guint8* const plain, const gint plain_len, const guint8* const key) -{ - guint8 *crypted_ptr = crypted; /* current position of dest */ - gint pos, padding; - - padding = (plain_len + 10) % 8; - if (padding) { - padding = 8 - padding; - } - - pos = 0; - - /* set first byte as padding len */ - crypted_ptr[pos] = (rand() & 0xf8) | padding; - pos++; - - /* extra 2 bytes */ - padding += 2; - - /* faster a little - memset(crypted_ptr + pos, rand() & 0xff, padding); - pos += padding; - */ - - /* more random */ - while (padding--) { - crypted_ptr[pos++] = rand() & 0xff; - } - - g_memmove(crypted_ptr + pos, plain, plain_len); - pos += plain_len; - - /* header padding len + plain len must be multiple of 8 - * tail pading len is always 8 - (1st byte) - */ - memset(crypted_ptr + pos, 0x00, 7); - pos += 7; - - show_binary("After padding", crypted, pos); - - encrypt_out(crypted, pos, key); - - show_binary("Encrypted", crypted, pos); - return pos; -} - -/******************************************************************** - * decryption - ********************************************************************/ - -static inline void qq_decipher(guint32 *const v, const guint32 *const k, guint32 *const w) -{ - register guint32 - y = g_ntohl(v[0]), - z = g_ntohl(v[1]), - a = g_ntohl(k[0]), - b = g_ntohl(k[1]), - c = g_ntohl(k[2]), - d = g_ntohl(k[3]), - n = 0x10, - sum = 0xE3779B90, /* why this ? must be related with n value */ - delta = 0x9E3779B9; - - /* sum = delta<<5, in general sum = delta * n */ - while (n-- > 0) { - z -= ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); - y -= ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); - sum -= delta; - } - - w[0] = g_htonl(y); - w[1] = g_htonl(z); -} - -static inline gint decrypt_out(guint8 *dest, gint crypted_len, const guint8* const key) -{ - gint plain_len; - guint32 key32[4]; - guint32 crypted32[2]; - guint32 c32_prev[2]; - guint32 plain32[2]; - guint32 p32_prev[2]; - gint count64; - gint padding; - guint8 *crypted_ptr = dest; - - /* decrypt first 64 bit */ - memcpy(key32, key, sizeof(key32)); - memcpy(crypted32, crypted_ptr, sizeof(crypted32)); - c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1]; - - qq_decipher(crypted32, key32, p32_prev); - memcpy(crypted_ptr, p32_prev, sizeof(p32_prev)); - - /* check padding len */ - padding = 2 + (crypted_ptr[0] & 0x7); - if (padding < 2) { - padding += 8; - } - plain_len = crypted_len - 1 - padding - 7; - if( plain_len < 0 ) { - return -2; - } - - count64 = crypted_len / 8; - while (--count64 > 0){ - c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1]; - crypted_ptr += 8; - - memcpy(crypted32, crypted_ptr, sizeof(crypted32)); - p32_prev[0] ^= crypted32[0]; p32_prev[1] ^= crypted32[1]; - - qq_decipher(p32_prev, key32, p32_prev); - - plain32[0] = p32_prev[0] ^ c32_prev[0]; plain32[1] = p32_prev[1] ^ c32_prev[1]; - memcpy(crypted_ptr, plain32, sizeof(plain32)); - } - - return plain_len; -} - -/* length of plain buffer must be equal to crypted_len */ -gint qq_decrypt(guint8 *plain, const guint8* const crypted, const gint crypted_len, const guint8* const key) -{ - gint plain_len = 0; - gint hdr_padding; - gint pos; - - /* at least 16 bytes and %8 == 0 */ - if ((crypted_len % 8) || (crypted_len < 16)) { - return -1; - } - - memcpy(plain, crypted, crypted_len); - - plain_len = decrypt_out(plain, crypted_len, key); - if (plain_len < 0) { - return plain_len; /* invalid first 64 bits */ - } - - show_binary("Decrypted with padding", plain, crypted_len); - - /* check last 7 bytes is zero or not? */ - for (pos = crypted_len - 1; pos > crypted_len - 8; pos--) { - if (plain[pos] != 0) { - return -3; - } - } - if (plain_len == 0) { - return plain_len; - } - - hdr_padding = crypted_len - plain_len - 7; - g_memmove(plain, plain + hdr_padding, plain_len); - - return plain_len; -} - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_crypt.h --- a/libpurple/protocols/qq/qq_crypt.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ - /** - * @file qq_crypt.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 02110-1301, USA - */ - -#ifndef _QQ_CRYPT_H_ -#define _QQ_CRYPT_H_ - -#include - -gint qq_encrypt(guint8* crypted, const guint8* const plain, const gint plain_len, const guint8* const key); - -gint qq_decrypt(guint8 *plain, const guint8* const crypted, const gint crypted_len, const guint8* const key); -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_define.c --- a/libpurple/protocols/qq/qq_define.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,270 +0,0 @@ -/** - * @file qq_define.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 "qq_define.h" - -#define QQ_CLIENT_062E 0x062e /* GB QQ2000c build 0630 */ -#define QQ_CLIENT_072E 0x072e /* EN QQ2000c build 0305 */ -#define QQ_CLIENT_0801 0x0801 /* EN QQ2000c build 0630 */ -#define QQ_CLIENT_0A1D 0x0a1d /* GB QQ2003c build 0808 */ -#define QQ_CLIENT_0B07 0x0b07 /* GB QQ2003c build 0925 */ -#define QQ_CLIENT_0B2F 0x0b2f /* GB QQ2003iii build 0117 */ -#define QQ_CLIENT_0B35 0x0b35 /* GB QQ2003iii build 0304 (offical release) */ -#define QQ_CLIENT_0B37 0x0b37 /* GB QQ2003iii build 0304 (April 05 updates) */ -#define QQ_CLIENT_0E1B 0x0e1b /* QQ2005 ? */ -#define QQ_CLIENT_0E35 0x0e35 /* EN QQ2005 V05.0.200.020 */ -#define QQ_CLIENT_0F15 0x0f15 /* QQ2006 Spring Festival build */ -#define QQ_CLIENT_0F5F 0x0f5f /* QQ2006 final build */ - -#define QQ_CLIENT_0C0B 0x0C0B /* QQ2004 */ -#define QQ_CLIENT_0C0D 0x0C0D /* QQ2004 preview*/ -#define QQ_CLIENT_0C21 0x0C21 /* QQ2004 */ -#define QQ_CLIENT_0C49 0x0C49 /* QQ2004II */ -#define QQ_CLIENT_0D05 0x0D05 /* QQ2005 beta1 */ -#define QQ_CLIENT_0D51 0x0D51 /* QQ2005 beta2 */ -#define QQ_CLIENT_0D61 0x0D61 /* QQ2005 */ -#define QQ_CLIENT_05A5 0x05A5 /* ? */ -#define QQ_CLIENT_05F1 0x0F15 /* QQ2006 Spring Festival */ -#define QQ_CLIENT_0F4B 0x0F4B /* QQ2006 Beta 3 */ - -#define QQ_CLIENT_1105 0x1105 /* QQ2007 beta4*/ -#define QQ_CLIENT_1203 0x1203 /* QQ2008 */ -#define QQ_CLIENT_1205 0x1205 /* QQ2008 Qi Fu */ -#define QQ_CLIENT_120B 0x120B /* QQ2008 July 8.0.978.400 */ -#define QQ_CLIENT_1412 0x1412 /* QQMac 1.0 preview1 build 670 */ -#define QQ_CLIENT_1441 0x1441 /* QQ2009 preview2 */ - -#define QQ_SERVER_0100 0x0100 /* server */ - - -/* given source tag, return its description accordingly */ -const gchar *qq_get_ver_desc(gint source) -{ - switch (source) { - case QQ_CLIENT_062E: - return "GB QQ2000c build 0630"; - case QQ_CLIENT_072E: - return "En QQ2000c build 0305"; - case QQ_CLIENT_0801: - return "En QQ2000c build 0630"; - case QQ_CLIENT_0A1D: - return "GB QQ2003ii build 0808"; - case QQ_CLIENT_0B07: - return "GB QQ2003ii build 0925"; - case QQ_CLIENT_0B2F: - return "GB QQ2003iii build 0117"; - case QQ_CLIENT_0B35: - return "GB QQ2003iii build 0304"; - case QQ_CLIENT_0B37: - return "GB QQ2003iii build 0304 (April 5 update)"; - case QQ_CLIENT_0C0B: - return "QQ2004"; - case QQ_CLIENT_0C0D: - return "QQ2004 preview"; - case QQ_CLIENT_0C21: - return "QQ2004"; - case QQ_CLIENT_0C49: - return "QQ2004II"; - case QQ_CLIENT_0D05: - return "QQ2005 beta1"; - case QQ_CLIENT_0D51: - return "QQ2005 beta2"; - case QQ_CLIENT_0D55: - case QQ_CLIENT_0D61: - return "QQ2005"; - case QQ_CLIENT_0E1B: - return "QQ2005 or QQ2006"; - case QQ_CLIENT_0E35: - return "En QQ2005 V05.0.200.020"; - case QQ_CLIENT_0F15: - return "QQ2006 Spring Festival"; - case QQ_CLIENT_0F4B: - return "QQ2006 beta3"; - case QQ_CLIENT_0F5F: - return "QQ2006 final build"; - case QQ_CLIENT_1105: - return "QQ2007 beta4"; - case QQ_CLIENT_111D: - return "QQ2007"; - case QQ_CLIENT_115B: - case QQ_CLIENT_1203: - case QQ_CLIENT_1205: - case QQ_CLIENT_120B: - return "QQ2008"; - case QQ_CLIENT_1412: - return "QQMac 1.0 preview1 build 670"; - case QQ_CLIENT_1441: - return "QQ2009 preview2"; - case QQ_CLIENT_1663: - return "QQ2009"; - case QQ_SERVER_0100: - return "QQ Server 0100"; - default: - return "Unknown Version"; - } -} - -/* given command alias, return the command name accordingly */ -const gchar *qq_get_cmd_desc(gint cmd) -{ - switch (cmd) { - case QQ_CMD_LOGOUT: - return "QQ_CMD_LOGOUT"; - case QQ_CMD_KEEP_ALIVE: - return "CMD_KEEP_ALIVE"; - case QQ_CMD_UPDATE_INFO: - return "CMD_UPDATE_INFO"; - case QQ_CMD_SEARCH_USER: - return "CMD_SEARCH_USER"; - case QQ_CMD_GET_BUDDY_INFO: - return "CMD_GET_BUDDY_INFO"; - case QQ_CMD_ADD_BUDDY_NO_AUTH: - return "CMD_ADD_BUDDY_NO_AUTH"; - case QQ_CMD_REMOVE_BUDDY: - return "CMD_REMOVE_BUDDY"; - case QQ_CMD_ADD_BUDDY_AUTH: - return "CMD_ADD_BUDDY_AUTH"; - case QQ_CMD_CHANGE_STATUS: - return "CMD_CHANGE_STATUS"; - case QQ_CMD_ACK_SYS_MSG: - return "CMD_ACK_SYS_MSG"; - case QQ_CMD_SEND_IM: - return "CMD_SEND_IM"; - case QQ_CMD_RECV_IM: - return "CMD_RECV_IM"; - case QQ_CMD_REMOVE_ME: - return "CMD_REMOVE_ME"; - case QQ_CMD_LOGIN: - return "CMD_LOGIN"; - case QQ_CMD_GET_BUDDIES_LIST: - return "CMD_GET_BUDDIES_LIST"; - case QQ_CMD_GET_BUDDIES_ONLINE: - return "CMD_GET_BUDDIES_ONLINE"; - case QQ_CMD_ROOM: - return "CMD_ROOM"; - case QQ_CMD_GET_BUDDIES_AND_ROOMS: - return "CMD_GET_BUDDIES_AND_ROOMS"; - case QQ_CMD_GET_LEVEL: - return "CMD_GET_LEVEL"; - case QQ_CMD_TOKEN: - return "CMD_TOKEN"; - case QQ_CMD_RECV_MSG_SYS: - return "CMD_RECV_MSG_SYS"; - case QQ_CMD_BUDDY_CHANGE_STATUS: - return "CMD_BUDDY_CHANGE_STATUS"; - case QQ_CMD_GET_SERVER: - return "CMD_GET_SERVER"; - case QQ_CMD_TOKEN_EX: - return "CMD_TOKEN_EX"; - case QQ_CMD_CHECK_PWD: - return "CMD_CHECK_PWD"; - case QQ_CMD_AUTH_CODE: - return "CMD_AUTH_CODE"; - case QQ_CMD_ADD_BUDDY_NO_AUTH_EX: - return "CMD_ADD_BUDDY_NO_AUTH_EX"; - case QQ_CMD_ADD_BUDDY_AUTH_EX: - return "CMD_BUDDY_ADD_AUTH_EX"; - case QQ_CMD_BUDDY_CHECK_CODE: - return "CMD_BUDDY_CHECK_CODE"; - case QQ_CMD_BUDDY_QUESTION: - return "CMD_BUDDY_QUESTION"; - case QQ_CMD_BUDDY_MEMO: - return "CMD_BUDDY_MEMO"; - default: - return "CMD_UNKNOW"; - } -} - -const gchar *qq_get_room_cmd_desc(gint room_cmd) -{ - switch (room_cmd) { - case QQ_ROOM_CMD_CREATE: - return "ROOM_CMD_CREATE"; - case QQ_ROOM_CMD_MEMBER_OPT: - return "ROOM_CMD_MEMBER_OPT"; - case QQ_ROOM_CMD_CHANGE_INFO: - return "ROOM_CMD_CHANGE_INFO"; - case QQ_ROOM_CMD_GET_INFO: - return "ROOM_CMD_GET_INFO"; - case QQ_ROOM_CMD_ACTIVATE: - return "ROOM_CMD_ACTIVATE"; - case QQ_ROOM_CMD_SEARCH: - return "ROOM_CMD_SEARCH"; - case QQ_ROOM_CMD_JOIN: - return "ROOM_CMD_JOIN"; - case QQ_ROOM_CMD_AUTH: - return "ROOM_CMD_AUTH"; - case QQ_ROOM_CMD_QUIT: - return "ROOM_CMD_QUIT"; - case QQ_ROOM_CMD_SEND_IM: - return "ROOM_CMD_SEND_IM"; - case QQ_ROOM_CMD_GET_ONLINES: - return "ROOM_CMD_GET_ONLINES"; - case QQ_ROOM_CMD_GET_BUDDIES: - return "ROOM_CMD_GET_BUDDIES"; - case QQ_ROOM_CMD_CHANGE_CARD: - return "ROOM_CMD_CHANGE_CARD"; - case QQ_ROOM_CMD_GET_REALNAMES: - return "ROOM_CMD_GET_REALNAMES"; - case QQ_ROOM_CMD_GET_CARD: - return "ROOM_CMD_GET_CARD"; - case QQ_ROOM_CMD_SEND_IM_EX: - return "ROOM_CMD_SEND_IM_EX"; - case QQ_ROOM_CMD_ADMIN: - return "ROOM_CMD_ADMIN"; - case QQ_ROOM_CMD_TRANSFER: - return "ROOM_CMD_TRANSFER"; - case QQ_ROOM_CMD_TEMP_CREATE: - return "ROOM_CMD_TEMP_CREATE"; - case QQ_ROOM_CMD_TEMP_CHANGE_MEMBER: - return "ROOM_CMD_TEMP_CHANGE_MEMBER"; - case QQ_ROOM_CMD_TEMP_QUIT: - return "ROOM_CMD_TEMP_QUIT"; - case QQ_ROOM_CMD_TEMP_GET_INFO: - return "ROOM_CMD_TEMP_GET_INFO"; - case QQ_ROOM_CMD_TEMP_SEND_IM: - return "ROOM_CMD_TEMP_SEND_IM"; - case QQ_ROOM_CMD_TEMP_GET_MEMBERS: - return "ROOM_CMD_TEMP_GET_MEMBERS"; - default: - return "ROOM_CMD_UNKNOW"; - } -} - -/* check if status means online or offline */ -gboolean is_online(guint8 status) -{ - switch(status) { - case QQ_BUDDY_ONLINE_NORMAL: - case QQ_BUDDY_ONLINE_AWAY: - case QQ_BUDDY_ONLINE_INVISIBLE: - case QQ_BUDDY_ONLINE_BUSY: - return TRUE; - case QQ_BUDDY_CHANGE_TO_OFFLINE: - return FALSE; - } - return FALSE; -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_define.h --- a/libpurple/protocols/qq/qq_define.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -/** - * @file qq_define.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_HEADER_INFO_H_ -#define _QQ_HEADER_INFO_H_ - -#include - -#define QQ_UDP_HEADER_LENGTH 7 -#define QQ_TCP_HEADER_LENGTH 9 - -#define QQ_PACKET_TAG 0x02 /* all QQ text packets starts with it */ -#define QQ_PACKET_TAIL 0x03 /* all QQ text packets end with it */ - -#define QQ_CLIENT_0D55 0x0d55 /* QQ2005 used by openq before */ -#define QQ_CLIENT_111D 0x111D /* QQ2007 */ -#define QQ_CLIENT_115B 0x115B /* QQ2008 He Sui */ -#define QQ_CLIENT_1663 0x1663 /* QQ2009 Release */ -#define QQ_CLIENT_1801 0x1801 /* QQ2009 International Beta1 */ - -const gchar *qq_get_ver_desc(gint source); - -/* list of known QQ commands */ -enum { - QQ_CMD_LOGOUT = 0x0001, /* log out */ - QQ_CMD_KEEP_ALIVE = 0x0002, /* get onlines from tencent */ - QQ_CMD_UPDATE_INFO = 0x0004, /* update information */ - QQ_CMD_SEARCH_USER = 0x0005, /* search for user */ - QQ_CMD_GET_BUDDY_INFO = 0x0006, /* get user information */ - QQ_CMD_ADD_BUDDY_NO_AUTH = 0x0009, /* add buddy without auth */ - QQ_CMD_REMOVE_BUDDY = 0x000a, /* delete a buddy */ - QQ_CMD_ADD_BUDDY_AUTH = 0x000b, /* buddy authentication */ - QQ_CMD_CHANGE_STATUS = 0x000d, /* change my online status */ - QQ_CMD_ACK_SYS_MSG = 0x0012, /* ack system message */ - QQ_CMD_SEND_IM = 0x0016, /* send message */ - QQ_CMD_RECV_IM = 0x0017, /* receive message */ - QQ_CMD_REMOVE_ME = 0x001c, /* remove self */ - QQ_CMD_REQUEST_KEY = 0x001d, /* request key for file transfer */ - QQ_CMD_CELL_PHONE_1 = 0x0021, /* cell phone 1 */ - QQ_CMD_LOGIN = 0x0022, /* login */ - QQ_CMD_GET_BUDDIES_LIST = 0x0026, /* get buddies list */ - QQ_CMD_GET_BUDDIES_ONLINE = 0x0027, /* get online buddies list */ - QQ_CMD_CELL_PHONE_2 = 0x0029, /* cell phone 2 */ - QQ_CMD_ROOM = 0x0030, /* room command */ - QQ_CMD_GET_BUDDIES_AND_ROOMS = 0x0058, - QQ_CMD_GET_LEVEL = 0x005C, /* get level for one or more buddies */ - QQ_CMD_TOKEN = 0x0062, /* get login token */ - QQ_CMD_RECV_MSG_SYS = 0x0080, /* receive a system message */ - QQ_CMD_BUDDY_CHANGE_STATUS = 0x0081, /* buddy change status */ - QQ_CMD_BUDDY_MEMO = 0x003E, /* the message about buddy memo */ - - /* for QQ2007*/ - QQ_CMD_GET_SERVER = 0x0091, /* select login server */ - QQ_CMD_TOKEN_EX = 0x00BA, /* get LOGIN token */ - QQ_CMD_CHECK_PWD = 0x00DD, /* Password verify */ - QQ_CMD_AUTH_CODE = 0x00AE, /* the request verification of information */ - QQ_CMD_ADD_BUDDY_NO_AUTH_EX = 0x00A7, /* add friend without auth */ - QQ_CMD_ADD_BUDDY_AUTH_EX = 0x00A8, /* add buddy with auth */ - QQ_CMD_BUDDY_CHECK_CODE = 0x00B5, - QQ_CMD_BUDDY_QUESTION = 0x00B7 -}; - -const gchar *qq_get_cmd_desc(gint type); - -enum { - QQ_ROOM_CMD_CREATE = 0x01, - QQ_ROOM_CMD_MEMBER_OPT = 0x02, - QQ_ROOM_CMD_CHANGE_INFO = 0x03, - QQ_ROOM_CMD_GET_INFO = 0x04, - QQ_ROOM_CMD_ACTIVATE = 0x05, - QQ_ROOM_CMD_SEARCH = 0x06, - QQ_ROOM_CMD_JOIN = 0x07, - QQ_ROOM_CMD_AUTH = 0x08, - QQ_ROOM_CMD_QUIT = 0x09, - QQ_ROOM_CMD_SEND_IM = 0x0a, - QQ_ROOM_CMD_GET_ONLINES = 0x0b, - QQ_ROOM_CMD_GET_BUDDIES = 0x0c, - - QQ_ROOM_CMD_CHANGE_CARD = 0x0E, - QQ_ROOM_CMD_GET_REALNAMES = 0x0F, - QQ_ROOM_CMD_GET_CARD = 0x10, - QQ_ROOM_CMD_SEND_IM_EX = 0x1A, - QQ_ROOM_CMD_ADMIN = 0x1B, - QQ_ROOM_CMD_TRANSFER = 0x1C, - QQ_ROOM_CMD_TEMP_CREATE = 0x30, - QQ_ROOM_CMD_TEMP_CHANGE_MEMBER = 0x31, - QQ_ROOM_CMD_TEMP_QUIT = 0x32, - QQ_ROOM_CMD_TEMP_GET_INFO = 0x33, - QQ_ROOM_CMD_TEMP_SEND_IM = 0x35, - QQ_ROOM_CMD_TEMP_GET_MEMBERS = 0x37 -}; - -const gchar *qq_get_room_cmd_desc(gint room_cmd); - -enum { - QQ_SERVER_BUDDY_ADDED = 1, - QQ_SERVER_BUDDY_ADD_REQUEST = 2, - QQ_SERVER_BUDDY_ADDED_ME = 3, - QQ_SERVER_BUDDY_REJECTED_ME = 4, - QQ_SERVER_NOTICE= 6, - QQ_SERVER_NEW_CLIENT = 9, - QQ_SERVER_BUDDY_ADDING_EX = 40, - QQ_SERVER_BUDDY_ADD_REQUEST_EX = 41, - QQ_SERVER_BUDDY_ADDED_ANSWER = 42, - QQ_SERVER_BUDDY_ADDED_EX = 43 -}; - -enum { - QQ_BUDDY_OFFLINE = 0x00, - QQ_BUDDY_ONLINE_NORMAL = 10, - QQ_BUDDY_CHANGE_TO_OFFLINE = 20, - QQ_BUDDY_ONLINE_AWAY = 30, - QQ_BUDDY_ONLINE_INVISIBLE = 40, - QQ_BUDDY_ONLINE_BUSY = 50 -}; - - -gboolean is_online(guint8 status); - -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_network.c --- a/libpurple/protocols/qq/qq_network.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1322 +0,0 @@ -/** - * @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 "internal.h" -#include "cipher.h" -#include "debug.h" - -#include "buddy_info.h" -#include "group_info.h" -#include "group_internal.h" -#include "qq_crypt.h" -#include "qq_define.h" -#include "qq_base.h" -#include "buddy_list.h" -#include "packet_parse.h" -#include "qq_network.h" -#include "qq_trans.h" -#include "utils.h" -#include "qq_process.h" - -#define QQ_DEFAULT_PORT 8000 - -/* set QQ_CONNECT_MAX to 1, when test reconnecting */ -#define QQ_CONNECT_MAX 3 -#define QQ_CONNECT_INTERVAL 2 -#define QQ_CONNECT_CHECK 5 -#define QQ_KEEP_ALIVE_INTERVAL 60 -#define QQ_TRANS_INTERVAL 10 - -gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port); - -static qq_connection *connection_find(qq_data *qd, int fd) { - qq_connection *ret = NULL; - GSList *entry = qd->openconns; - while(entry) { - ret = entry->data; - if(ret->fd == fd) return ret; - entry = entry->next; - } - return NULL; -} - -static qq_connection *connection_create(qq_data *qd, int fd) { - qq_connection *ret = g_new0(qq_connection, 1); - g_return_val_if_fail(ret != NULL, NULL); - ret->fd = fd; - qd->openconns = g_slist_append(qd->openconns, ret); - return ret; -} - -static void connection_remove(qq_data *qd, int fd) { - qq_connection *conn = connection_find(qd, fd); - qd->openconns = g_slist_remove(qd->openconns, conn); - - g_return_if_fail( conn != NULL ); - - purple_debug_info("QQ", "Close socket %d\n", conn->fd); - if(conn->input_handler > 0) purple_input_remove(conn->input_handler); - if(conn->can_write_handler > 0) purple_input_remove(conn->can_write_handler); - - if (conn->fd >= 0) close(conn->fd); - if(conn->tcp_txbuf != NULL) purple_circ_buffer_destroy(conn->tcp_txbuf); - if (conn->tcp_rxqueue != NULL) g_free(conn->tcp_rxqueue); - - g_free(conn); -} - -static void connection_free_all(qq_data *qd) { - qq_connection *ret = NULL; - GSList *entry = qd->openconns; - while(entry) { - ret = entry->data; - connection_remove(qd, ret->fd); - entry = qd->openconns; - } -} -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_info("QQ", "Server list is NULL\n"); - return FALSE; - } - - /* remove server used before */ - if (qd->curr_server != NULL) { - purple_debug_info("QQ", - "Remove current [%s] from server list\n", qd->curr_server); - qd->servers = g_list_remove(qd->servers, qd->curr_server); - qd->curr_server = NULL; - } - - count = g_list_length(qd->servers); - 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 = rand() % count; - it = g_list_nth(qd->servers, index); - qd->curr_server = it->data; /* do not free server_name */ - if (qd->curr_server == NULL || strlen(qd->curr_server) <= 0 ) { - purple_debug_info("QQ", "Server name at %d is empty\n", index); - return FALSE; - } - - purple_debug_info("QQ", "set new server to %s\n", qd->curr_server); - return TRUE; -} - -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; -} - -static gboolean connect_check(gpointer data) -{ - PurpleConnection *gc = (PurpleConnection *) data; - qq_data *qd; - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); - qd = (qq_data *) gc->proto_data; - - if (qd->connect_watcher > 0) { - purple_timeout_remove(qd->connect_watcher); - qd->connect_watcher = 0; - } - - if (qd->fd >= 0 && qd->ld.token != NULL && qd->ld.token_len > 0) { - purple_debug_info("QQ", "Connect ok\n"); - return FALSE; - } - - qd->connect_watcher = purple_timeout_add_seconds(0, qq_connect_later, gc); - return FALSE; -} - -/* Warning: qq_connect_later destory all connection - * Any function should be care of use qq_data after call this function - * Please conside tcp_pending and udp_pending */ -gboolean qq_connect_later(gpointer data) -{ - PurpleConnection *gc; - char *tmp_server; - int port; - gchar **segments; - qq_data *qd; - - gc = (PurpleConnection *) data; - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); - qd = (qq_data *) gc->proto_data; - tmp_server = NULL; - - if (qd->check_watcher > 0) { - purple_timeout_remove(qd->check_watcher); - qd->check_watcher = 0; - } - qq_disconnect(gc); - - if (qd->redirect_ip.s_addr != 0) { - /* redirect to new server */ - tmp_server = g_strdup_printf("%s:%d", inet_ntoa(qd->redirect_ip), qd->redirect_port); - qd->servers = g_list_append(qd->servers, tmp_server); - - qd->curr_server = tmp_server; - tmp_server = NULL; - - qd->redirect_ip.s_addr = 0; - qd->redirect_port = 0; - qd->connect_retry = QQ_CONNECT_MAX; - } - - if (qd->curr_server == NULL || strlen (qd->curr_server) == 0 || qd->connect_retry <= 0) { - if ( set_new_server(qd) != TRUE) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - return FALSE; - } - qd->connect_retry = QQ_CONNECT_MAX; - } - - segments = g_strsplit_set(qd->curr_server, ":", 0); - tmp_server = g_strdup(segments[0]); - if (NULL != segments[1]) { - port = atoi(segments[1]); - if (port <= 0) { - purple_debug_info("QQ", "Port not define in %s, use default.\n", qd->curr_server); - port = QQ_DEFAULT_PORT; - } - } else { - purple_debug_info("QQ", "Error splitting server string: %s, setting port to default.\n", qd->curr_server); - port = QQ_DEFAULT_PORT; - } - - g_strfreev(segments); - - qd->connect_retry--; - if ( !connect_to_server(gc, tmp_server, port) ) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - } - - g_free(tmp_server); - tmp_server = NULL; - - qd->check_watcher = purple_timeout_add_seconds(QQ_CONNECT_CHECK, connect_check, gc); - return FALSE; /* timeout callback stops */ -} - -static void redirect_server(PurpleConnection *gc) -{ - qq_data *qd; - qd = (qq_data *) gc->proto_data; - - if (qd->check_watcher > 0) { - purple_timeout_remove(qd->check_watcher); - qd->check_watcher = 0; - } - if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher); - qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc); -} - -/* process the incoming packet from qq_pending */ -static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) -{ - qq_data *qd; - gint bytes, bytes_not_read; - - guint8 header_tag; - guint16 source_tag; - guint16 cmd; - guint16 seq; /* May be ack_seq or send_seq, depends on cmd */ - guint8 room_cmd; - guint32 room_id; - UPDCLS update_class; - guint32 ship32; - int ret; - - qq_transaction *trans; - - g_return_val_if_fail(buf != NULL && buf_len > 0, TRUE); - - qd = (qq_data *) gc->proto_data; - - qd->net_stat.rcved++; - if (qd->net_stat.rcved <= 0) memset(&(qd->net_stat), 0, sizeof(qd->net_stat)); - - /* Len, header and tail tag have been checked before */ - bytes = 0; - bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); - -#if 1 - purple_debug_info("QQ", "==> [%05d] %s 0x%04X, source tag 0x%04X len %d\n", - seq, qq_get_cmd_desc(cmd), cmd, source_tag, buf_len); -#endif - /* this is the length of all the encrypted data (also remove tail tag) */ - bytes_not_read = buf_len - bytes - 1; - - /* ack packet, we need to update send tranactions */ - /* we do not check duplication for server ack */ - trans = qq_trans_find_rcved(gc, cmd, seq); - if (trans == NULL) { - /* new server command */ - if ( !qd->is_login ) { - qq_trans_add_remain(gc, cmd, seq, buf + bytes, bytes_not_read); - } else { - qq_trans_add_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read); - qq_proc_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read); - } - return TRUE; - } - - if (qq_trans_is_dup(trans)) { - qd->net_stat.rcved_dup++; - purple_debug_info("QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); - return TRUE; - } - - update_class = qq_trans_get_class(trans); - ship32 = qq_trans_get_ship(trans); - if (update_class != 0 || ship32 != 0) { - purple_debug_info("QQ", "Update class %d, ship32 %d\n", update_class, ship32); - } - - switch (cmd) { - case QQ_CMD_TOKEN: - case QQ_CMD_GET_SERVER: - case QQ_CMD_TOKEN_EX: - case QQ_CMD_CHECK_PWD: - case QQ_CMD_LOGIN: - ret = qq_proc_login_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); - if (ret != QQ_LOGIN_REPLY_OK) { - if (ret == QQ_LOGIN_REPLY_REDIRECT) { - redirect_server(gc); - } - return FALSE; /* do nothing after this function and return now */ - } - break; - case QQ_CMD_ROOM: - room_cmd = qq_trans_get_room_cmd(trans); - room_id = qq_trans_get_room_id(trans); - qq_proc_room_cmds(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32); - break; - default: - qq_proc_client_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); - break; - } - - return TRUE; -} - -static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) -{ - PurpleConnection *gc = (PurpleConnection *) data; - qq_data *qd; - qq_connection *conn; - 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; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - if(cond != PURPLE_INPUT_READ) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Socket error")); - return; - } - - conn = connection_find(qd, source); - g_return_if_fail(conn != NULL); - - /* 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(source, buf, sizeof(buf)); - if (buf_len < 0) { - if (errno == EAGAIN) - /* No worries */ - return; - - error_msg = g_strdup_printf(_("Lost connection with server: %s"), 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; - } - - /* keep alive will be sent in 30 seconds since last_receive - * QQ need a keep alive packet in every 60 seconds - gc->last_received = time(NULL); - */ - /* purple_debug_info("TCP_PENDING", "Read %d bytes, rxlen is %d\n", buf_len, conn->tcp_rxlen); */ - conn->tcp_rxqueue = g_realloc(conn->tcp_rxqueue, buf_len + conn->tcp_rxlen); - memcpy(conn->tcp_rxqueue + conn->tcp_rxlen, buf, buf_len); - conn->tcp_rxlen += buf_len; - - pkt = g_newa(guint8, MAX_PACKET_SIZE); - while (PURPLE_CONNECTION_IS_VALID(gc)) { - if (qd->openconns == NULL) { - break; - } - if (conn->tcp_rxqueue == NULL) { - conn->tcp_rxlen = 0; - break; - } - if (conn->tcp_rxlen < QQ_TCP_HEADER_LENGTH) { - break; - } - - bytes = 0; - bytes += qq_get16(&pkt_len, conn->tcp_rxqueue + bytes); - if (conn->tcp_rxlen < pkt_len) { - break; - } - - /* purple_debug_info("TCP_PENDING", "Packet len=%d, rxlen=%d\n", pkt_len, conn->tcp_rxlen); */ - if ( pkt_len < QQ_TCP_HEADER_LENGTH - || *(conn->tcp_rxqueue + bytes) != QQ_PACKET_TAG - || *(conn->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { - /* HEY! This isn't even a QQ. What are you trying to pull? */ - purple_debug_warning("TCP_PENDING", "Packet error, no header or tail tag\n"); - - jump = memchr(conn->tcp_rxqueue + 1, QQ_PACKET_TAIL, conn->tcp_rxlen - 1); - if ( !jump ) { - purple_debug_warning("TCP_PENDING", "Failed to find next tail, clear receive buffer\n"); - g_free(conn->tcp_rxqueue); - conn->tcp_rxqueue = NULL; - conn->tcp_rxlen = 0; - return; - } - - /* jump and over QQ_PACKET_TAIL */ - jump_len = (jump - conn->tcp_rxqueue) + 1; - purple_debug_warning("TCP_PENDING", "Find next tail at %d, jump %d\n", jump_len, jump_len + 1); - g_memmove(conn->tcp_rxqueue, jump, conn->tcp_rxlen - jump_len); - conn->tcp_rxlen -= jump_len; - continue; - } - - memset(pkt, 0, MAX_PACKET_SIZE); - g_memmove(pkt, conn->tcp_rxqueue + bytes, pkt_len - bytes); - - /* jump to next packet */ - conn->tcp_rxlen -= pkt_len; - if (conn->tcp_rxlen) { - /* purple_debug_info("TCP_PENDING", "shrink tcp_rxqueue to %d\n", conn->tcp_rxlen); */ - jump = g_memdup(conn->tcp_rxqueue + pkt_len, conn->tcp_rxlen); - g_free(conn->tcp_rxqueue); - conn->tcp_rxqueue = jump; - } else { - /* purple_debug_info("TCP_PENDING", "free tcp_rxqueue\n"); */ - g_free(conn->tcp_rxqueue); - conn->tcp_rxqueue = NULL; - } - - /* packet_process may call disconnect and destory data like conn - * do not call packet_process before jump, - * break if packet_process return FALSE */ - if (packet_process(gc, pkt, pkt_len - bytes) == FALSE) { - purple_debug_info("TCP_PENDING", "Connection has been destory\n"); - break; - } - } -} - -static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) -{ - PurpleConnection *gc = NULL; - guint8 *buf; - gint buf_len; - - gc = (PurpleConnection *) data; - g_return_if_fail(gc != NULL); - - if(cond != PURPLE_INPUT_READ) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Socket error")); - return; - } - - buf = g_newa(guint8, MAX_PACKET_SIZE); - - /* here we have UDP proxy suppport */ - buf_len = read(source, buf, MAX_PACKET_SIZE); - if (buf_len <= 0) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to read from socket")); - return; - } - - /* keep alive will be sent in 30 seconds since last_receive - * QQ need a keep alive packet in every 60 seconds - 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 may call disconnect and destory data like conn - * do not call packet_process before jump, - * break if packet_process return FALSE */ - packet_process(gc, buf, buf_len); -} - -static gint udp_send_out(PurpleConnection *gc, guint8 *data, gint data_len) -{ - qq_data *qd; - gint ret; - - g_return_val_if_fail(data != NULL && data_len > 0, -1); - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - qd = (qq_data *) gc->proto_data; - -#if 0 - purple_debug_info("UDP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); -#endif - - 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_error("UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno)); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - g_strerror(errno)); - } - return ret; -} - -static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond) -{ - PurpleConnection *gc = (PurpleConnection *) data; - qq_data *qd; - qq_connection *conn; - int ret, writelen; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - conn = connection_find(qd, source); - g_return_if_fail(conn != NULL); - - writelen = purple_circ_buffer_get_max_read(conn->tcp_txbuf); - if (writelen == 0) { - purple_input_remove(conn->can_write_handler); - conn->can_write_handler = 0; - return; - } - - ret = write(source, conn->tcp_txbuf->outptr, writelen); - purple_debug_info("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? */ - gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"), - g_strerror(errno)); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return; - } - - purple_circ_buffer_mark_read(conn->tcp_txbuf, ret); -} - -static gint tcp_send_out(PurpleConnection *gc, guint8 *data, gint data_len) -{ - qq_data *qd; - qq_connection *conn; - gint ret; - - g_return_val_if_fail(data != NULL && data_len > 0, -1); - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - qd = (qq_data *) gc->proto_data; - - conn = connection_find(qd, qd->fd); - g_return_val_if_fail(conn, -1); - -#if 0 - purple_debug_info("TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); -#endif - - if (conn->can_write_handler == 0) { - ret = write(qd->fd, data, data_len); - } else { - ret = -1; - errno = EAGAIN; - } - - /* - 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_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? */ - gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"), - g_strerror(errno)); - purple_debug_error("TCP_SEND_OUT", - "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno)); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return ret; - } - - if (ret < data_len) { - purple_debug_info("TCP_SEND_OUT", "Add %d bytes to buffer\n", data_len - ret); - if (conn->can_write_handler == 0) { - conn->can_write_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, gc); - } - if (conn->tcp_txbuf == NULL) { - conn->tcp_txbuf = purple_circ_buffer_new(4096); - } - purple_circ_buffer_append(conn->tcp_txbuf, data + ret, data_len - ret); - } - return ret; -} - -static gboolean network_timeout(gpointer data) -{ - PurpleConnection *gc = (PurpleConnection *) data; - qq_data *qd; - gboolean is_lost_conn; - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE); - qd = (qq_data *) gc->proto_data; - - is_lost_conn = qq_trans_scan(gc); - if (is_lost_conn) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Lost connection with server")); - return TRUE; - } - - if ( !qd->is_login ) { - return TRUE; - } - - qd->itv_count.keep_alive--; - if (qd->itv_count.keep_alive <= 0) { - qd->itv_count.keep_alive = qd->itv_config.keep_alive; - if (qd->client_version >= 2008) { - qq_request_keep_alive_2008(gc); - } else if (qd->client_version >= 2007) { - qq_request_keep_alive_2007(gc); - } else { - qq_request_keep_alive(gc); - } - return TRUE; - } - - if (qd->itv_config.update <= 0) { - return TRUE; - } - - qd->itv_count.update--; - if (qd->itv_count.update <= 0) { - qd->itv_count.update = qd->itv_config.update; - qq_update_online(gc, 0); - return TRUE; - } - - return TRUE; /* if return FALSE, timeout callback stops */ -} - -static void set_all_keys(PurpleConnection *gc) -{ - qq_data *qd; - const gchar *passwd; - guint8 *dest; - int dest_len = QQ_KEY_LENGTH; -#ifndef DEBUG - int bytes; -#endif - /* _qq_show_socket("Got login socket", source); */ - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - /* QQ use random seq, to minimize duplicated packets */ - srand(time(NULL)); - qd->send_seq = rand() & 0xffff; - - qd->is_login = FALSE; - qd->uid = strtoul(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); - -#ifdef DEBUG - memset(qd->ld.random_key, 0x01, sizeof(qd->ld.random_key)); -#else - for (bytes = 0; bytes < sizeof(qd->ld.random_key); bytes++) { - qd->ld.random_key[bytes] = (guint8) (rand() & 0xff); - } -#endif - - /* now generate md5 processed passwd */ - passwd = purple_account_get_password(purple_connection_get_account(gc)); - - /* use twice-md5 of user password as session key since QQ 2003iii */ - dest = qd->ld.pwd_md5; - qq_get_md5(dest, dest_len, (guint8 *)passwd, strlen(passwd)); - - dest = qd->ld.pwd_twice_md5; - qq_get_md5(dest, dest_len, qd->ld.pwd_md5, dest_len); -} - -/* the callback function after socket is built - * we setup the qq protocol related configuration here */ -static void connect_cb(gpointer data, gint source, const gchar *error_message) -{ - PurpleConnection *gc; - qq_data *qd; - qq_connection *conn; - - gc = (PurpleConnection *) data; - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - - qd = (qq_data *) gc->proto_data; - - /* conn_data will be destoryed */ - qd->conn_data = NULL; - - if (!PURPLE_CONNECTION_IS_VALID(gc)) { - purple_debug_info("QQ_CONN", "Invalid connection\n"); - close(source); - return; - } - - if (source < 0) { /* socket returns -1 */ - purple_debug_info("QQ_CONN", - "Could not establish a connection with the server:\n%s\n", - error_message); - if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher); - qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc); - return; - } - - /* _qq_show_socket("Got login socket", source); */ - /* ok, already connected to the server */ - qd->fd = source; - conn = connection_create(qd, source); - g_return_if_fail( conn != NULL ); - - if (qd->use_tcp) { - /* events which match "PURPLE_INPUT_READ" of - * "source" would trigger the callback function */ - conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, tcp_pending, gc); - } else { - conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, udp_pending, gc); - } - - g_return_if_fail(qd->network_watcher == 0); - qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc); - - set_all_keys( gc ); - - if (qd->client_version >= 2007) { - purple_connection_update_progress(gc, _("Getting server"), 2, QQ_CONNECT_STEPS); - qq_request_get_server(gc); - return; - } - - purple_connection_update_progress(gc, _("Requesting token"), 2, QQ_CONNECT_STEPS); - qq_request_token(gc); -} - -#ifndef purple_proxy_connect_udp -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->udp_can_write_handler); - qd->udp_can_write_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)); - - connect_cb(gc, -1, _("Unable to connect")); - return; - } - - 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, - _("Unable to resolve hostname")); - 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_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); -#ifndef _WIN32 - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif - - /* 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_info("QQ", "Connected.\n"); - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); - 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_warning( "QQ", "Connect in asynchronous mode.\n"); - qd->udp_can_write_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc); - return; - } - - purple_debug_error("QQ", "Connection failed: %s\n", g_strerror(errno)); - close(fd); -} -#endif - -gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port) -{ - PurpleAccount *account ; - qq_data *qd; - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); - account = purple_connection_get_account(gc); - qd = (qq_data *) gc->proto_data; - - if (server == NULL || server[0] == '\0' || port == 0) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid server or port")); - return FALSE; - } - - purple_connection_update_progress(gc, _("Connecting to server"), 1, QQ_CONNECT_STEPS); - - purple_debug_info("QQ", "Connect to %s:%d\n", server, port); - - if (qd->conn_data != NULL) { - purple_proxy_connect_cancel(qd->conn_data); - qd->conn_data = NULL; - } - -#ifdef purple_proxy_connect_udp - if (qd->use_tcp) { - qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc); - } else { - qd->conn_data = purple_proxy_connect_udp(gc, account, server, port, connect_cb, gc); - } - if ( qd->conn_data == NULL ) { - purple_debug_error("QQ", "Couldn't create socket\n"); - return FALSE; - } -#else - /* 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) { - qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc); - if ( qd->conn_data == NULL ) { - purple_debug_error("QQ", "Unable to connect.\n"); - return FALSE; - } - return TRUE; - } - - purple_debug_info("QQ", "UDP Connect to %s:%d\n", server, port); - qd->udp_query_data = purple_dnsquery_a(server, port, udp_host_resolved, gc); - if ( qd->udp_query_data == NULL ) { - purple_debug_error("QQ", "Could not resolve hostname\n"); - return FALSE; - } -#endif - return TRUE; -} - -/* 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_info("QQ", "Disconnecting...\n"); - - if (qd->network_watcher > 0) { - purple_debug_info("QQ", "Remove network watcher\n"); - purple_timeout_remove(qd->network_watcher); - qd->network_watcher = 0; - } - - /* finish all I/O */ - if (qd->fd >= 0 && qd->is_login) { - qq_request_logout(gc); - } - - /* not connected */ - if (qd->conn_data != NULL) { - purple_debug_info("QQ", "Connect cancel\n"); - purple_proxy_connect_cancel(qd->conn_data); - qd->conn_data = NULL; - } -#ifndef purple_proxy_connect_udp - if (qd->udp_can_write_handler) { - purple_input_remove(qd->udp_can_write_handler); - qd->udp_can_write_handler = 0; - } - if (qd->udp_query_data != NULL) { - purple_debug_info("QQ", "destroy udp_query_data\n"); - purple_dnsquery_destroy(qd->udp_query_data); - qd->udp_query_data = NULL; - } -#endif - connection_free_all(qd); - qd->fd = -1; - - qq_trans_remove_all(gc); - - memset(qd->ld.random_key, 0, sizeof(qd->ld.random_key)); - memset(qd->ld.pwd_md5, 0, sizeof(qd->ld.pwd_md5)); - memset(qd->ld.pwd_twice_md5, 0, sizeof(qd->ld.pwd_twice_md5)); - memset(qd->ld.login_key, 0, sizeof(qd->ld.login_key)); - memset(qd->session_key, 0, sizeof(qd->session_key)); - memset(qd->session_md5, 0, sizeof(qd->session_md5)); - - qd->my_local_ip.s_addr = 0; - qd->my_local_port = 0; - qd->my_ip.s_addr = 0; - qd->my_port = 0; - - qq_room_data_free_all(gc); - qq_buddy_data_free_all(gc); -} - -static gint packet_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); - g_return_val_if_fail(data != NULL && data_len > 0, -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, qd->client_tag); - 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; -} - -/* data has been encrypted before */ -static gint packet_send_out(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) -{ - qq_data *qd; - guint8 *buf; - gint buf_len; - gint bytes_sent; - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - qd = (qq_data *)gc->proto_data; - g_return_val_if_fail(data != NULL && data_len > 0, -1); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - memset(buf, 0, MAX_PACKET_SIZE); - buf_len = packet_encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); - if (buf_len <= 0) { - return -1; - } - - qd->net_stat.sent++; - if (qd->use_tcp) { - bytes_sent = tcp_send_out(gc, buf, buf_len); - } else { - bytes_sent = udp_send_out(gc, buf, buf_len); - } - - return bytes_sent; -} - -gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *encrypted, gint encrypted_len, gboolean is_save2trans) -{ - gint sent_len; - -#if 1 - /* qq_show_packet("qq_send_cmd_encrypted", data, data_len); */ - purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n", - seq, qq_get_cmd_desc(cmd), cmd, encrypted_len); -#endif - - sent_len = packet_send_out(gc, cmd, seq, encrypted, encrypted_len); - if (is_save2trans) { - qq_trans_add_client_cmd(gc, cmd, seq, encrypted, encrypted_len, 0, 0); - } - return sent_len; -} - -/* Encrypt data with session_key, and send packet out */ -static gint send_cmd_detail(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *data, gint data_len, gboolean is_save2trans, - UPDCLS update_class, guint32 ship32) -{ - qq_data *qd; - guint8 *encrypted; - gint encrypted_len; - gint bytes_sent; - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - qd = (qq_data *)gc->proto_data; - g_return_val_if_fail(data != NULL && data_len > 0, -1); - - /* at most 17 bytes more */ - encrypted = g_newa(guint8, data_len + 17); - encrypted_len = qq_encrypt(encrypted, data, data_len, qd->session_key); - if (encrypted_len < 16) { - purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n", - encrypted_len, seq, cmd, qq_get_cmd_desc(cmd)); - return -1; - } - - bytes_sent = packet_send_out(gc, cmd, seq, encrypted, encrypted_len); - - if (is_save2trans) { - qq_trans_add_client_cmd(gc, cmd, seq, encrypted, encrypted_len, - update_class, ship32); - } - return bytes_sent; -} - -gint qq_send_cmd_mess(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len, - UPDCLS update_class, guint32 ship32) -{ - qq_data *qd; - guint16 seq; - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - qd = (qq_data *) gc->proto_data; - g_return_val_if_fail(data != NULL && data_len > 0, -1); - - seq = ++qd->send_seq; -#if 1 - purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n", - seq, qq_get_cmd_desc(cmd), cmd, data_len); -#endif - return send_cmd_detail(gc, cmd, seq, data, data_len, TRUE, update_class, ship32); -} - -/* set seq and is_save2trans, then call send_cmd_detail */ -gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len) -{ - qq_data *qd; - guint16 seq; - gboolean is_save2trans; - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - qd = (qq_data *) gc->proto_data; - g_return_val_if_fail(data != NULL && data_len > 0, -1); - - if (cmd != QQ_CMD_LOGOUT) { - seq = ++qd->send_seq; - is_save2trans = TRUE; - } else { - seq = 0xFFFF; - is_save2trans = FALSE; - } -#if 1 - purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n", - seq, qq_get_cmd_desc(cmd), cmd, data_len); -#endif - return send_cmd_detail(gc, cmd, seq, data, data_len, is_save2trans, 0, 0); -} - -/* set seq and is_save2trans, then call send_cmd_detail */ -gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) -{ - qq_data *qd; - guint8 *encrypted; - gint encrypted_len; - gint bytes_sent; - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - qd = (qq_data *)gc->proto_data; - g_return_val_if_fail(data != NULL && data_len > 0, -1); - -#if 1 - purple_debug_info("QQ", "<== [SRV-%05d] %s(0x%04X), datalen %d\n", - seq, qq_get_cmd_desc(cmd), cmd, data_len); -#endif - /* at most 17 bytes more */ - encrypted = g_newa(guint8, data_len + 17); - encrypted_len = qq_encrypt(encrypted, data, data_len, qd->session_key); - if (encrypted_len < 16) { - purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n", - encrypted_len, seq, cmd, qq_get_cmd_desc(cmd)); - return -1; - } - - bytes_sent = packet_send_out(gc, cmd, seq, encrypted, encrypted_len); - qq_trans_add_server_reply(gc, cmd, seq, encrypted, encrypted_len); - - return bytes_sent; -} - -static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, - guint8 *data, gint data_len, UPDCLS update_class, guint32 ship32) -{ - qq_data *qd; - guint8 *buf; - gint buf_len; - guint8 *encrypted; - gint encrypted_len; - gint bytes_sent; - guint16 seq; - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - qd = (qq_data *) gc->proto_data; - - buf = g_newa(guint8, MAX_PACKET_SIZE); - memset(buf, 0, MAX_PACKET_SIZE); - - /* encap room_cmd and room id to buf*/ - buf_len = 0; - buf_len += qq_put8(buf + buf_len, room_cmd); - if (room_id != 0) { - /* id 0 is for QQ Demo Group, now they are closed*/ - buf_len += qq_put32(buf + buf_len, room_id); - } - if (data != NULL && data_len > 0) { - buf_len += qq_putdata(buf + buf_len, data, data_len); - } - - qd->send_seq++; - seq = qd->send_seq; - - /* Encrypt to encrypted with session_key */ - /* at most 17 bytes more */ - encrypted = g_newa(guint8, buf_len + 17); - encrypted_len = qq_encrypt(encrypted, buf, buf_len, qd->session_key); - if (encrypted_len < 16) { - purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] %s (0x%02X)\n", - encrypted_len, seq, qq_get_room_cmd_desc(room_cmd), room_cmd); - return -1; - } - - bytes_sent = packet_send_out(gc, QQ_CMD_ROOM, seq, encrypted, encrypted_len); -#if 1 - /* qq_show_packet("send_room_cmd", buf, buf_len); */ - purple_debug_info("QQ", - "<== [%05d] %s (0x%02X) to room %d, datalen %d\n", - seq, qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); -#endif - - qq_trans_add_room_cmd(gc, seq, room_cmd, room_id, encrypted, encrypted_len, - update_class, ship32); - return bytes_sent; -} - -gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, - guint8 *data, gint data_len, UPDCLS update_class, guint32 ship32) -{ - g_return_val_if_fail(room_cmd > 0, -1); - return send_room_cmd(gc, room_cmd, room_id, data, data_len, update_class, ship32); -} - -gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, - guint8 *data, gint data_len) -{ - g_return_val_if_fail(room_cmd > 0 && room_id > 0, -1); - return send_room_cmd(gc, room_cmd, room_id, data, data_len, 0, 0); -} - -gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd, - guint8 *data, gint data_len) -{ - g_return_val_if_fail(room_cmd > 0, -1); - return send_room_cmd(gc, room_cmd, 0, data, data_len, 0, 0); -} - -gint qq_send_room_cmd_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) -{ - g_return_val_if_fail(room_cmd > 0 && room_id > 0, -1); - return send_room_cmd(gc, room_cmd, room_id, NULL, 0, 0, 0); -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_network.h --- a/libpurple/protocols/qq/qq_network.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/** - * @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_NETWORK_H -#define _QQ_NETWORK_H - -#include -#include "connection.h" - -#include "qq.h" - -#define QQ_CONNECT_STEPS 4 /* steps in connection */ - -gboolean qq_connect_later(gpointer data); -void qq_disconnect(PurpleConnection *gc); - -gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *encrypted_data, gint encrypted_len, gboolean is_save2trans); -gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, guint8 *data, gint datalen); -gint qq_send_cmd_mess(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len, - UPDCLS update_class, guint32 ship32); - -gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *data, gint data_len); - -gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, - guint8 *data, gint data_len); -gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, - guint8 *data, gint data_len, UPDCLS update_class, guint32 ship32); -gint qq_send_room_cmd_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id); -gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd, - guint8 *data, gint data_len); -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_process.c --- a/libpurple/protocols/qq/qq_process.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1157 +0,0 @@ -/** - * @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 "internal.h" -#include "cipher.h" -#include "debug.h" - -#include "buddy_info.h" -#include "buddy_list.h" -#include "buddy_opt.h" -#include "group_info.h" -#include "char_conv.h" -#include "qq_crypt.h" - -#include "group_internal.h" -#include "group_im.h" -#include "group_info.h" -#include "group_join.h" -#include "group_opt.h" - -#include "qq_define.h" -#include "qq_base.h" -#include "im.h" -#include "qq_process.h" -#include "packet_parse.h" -#include "qq_network.h" -#include "qq_trans.h" -#include "utils.h" -#include "buddy_memo.h" - -enum { - QQ_ROOM_CMD_REPLY_OK = 0x00, - QQ_ROOM_CMD_REPLY_SEARCH_ERROR = 0x02, - QQ_ROOM_CMD_REPLY_NOT_MEMBER = 0x0a -}; - -/* default process, decrypt and dump */ -static void process_unknow_cmd(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq) -{ - gchar *msg; - - g_return_if_fail(data != NULL && data_len != 0); - - qq_show_packet(title, data, data_len); - - qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", - data, data_len, - ">>> [%d] %s -> [default] decrypt and dump", - seq, qq_get_cmd_desc(cmd)); - - msg = g_strdup_printf("Unknow command 0x%02X, %s", cmd, qq_get_cmd_desc(cmd)); - purple_notify_info(gc, _("QQ Error"), title, msg); - g_free(msg); -} - -/* parse the reply to send_im */ -static void do_im_ack(guint8 *data, gint data_len, PurpleConnection *gc) -{ - g_return_if_fail(data != NULL && data_len != 0); - - if (data[0] != 0) { - purple_debug_warning("QQ", "Failed sent IM\n"); - purple_notify_error(gc, _("Error"), _("Unable to send message."), NULL); - return; - } - - purple_debug_info("QQ", "OK sent IM\n"); -} - -static void do_server_news(PurpleConnection *gc, guint8 *data, gint data_len) -{ - qq_data *qd = (qq_data *) gc->proto_data; - gint bytes; - gchar *title, *brief, *url; - gchar *content; - - g_return_if_fail(data != NULL && data_len != 0); - - /* qq_show_packet("Rcv news", data, data_len); */ - - bytes = 4; /* skip unknown 4 bytes */ - - bytes += qq_get_vstr(&title, QQ_CHARSET_DEFAULT, data + bytes); - bytes += qq_get_vstr(&brief, QQ_CHARSET_DEFAULT, data + bytes); - bytes += qq_get_vstr(&url, QQ_CHARSET_DEFAULT, data + bytes); - - content = g_strdup_printf(_("Server News:\n%s\n%s\n%s"), title, brief, url); - - if (qd->is_show_news) { - qq_got_message(gc, content); - } else { - purple_debug_info("QQ", "QQ Server news:\n%s\n", content); - } - g_free(title); - g_free(brief); - g_free(url); - g_free(content); -} - -static void do_got_sms(PurpleConnection *gc, guint8 *data, gint data_len) -{ - gint bytes; - gchar *mobile = NULL; - gchar *msg = NULL; - gchar *msg_utf8 = NULL; - gchar *msg_formated; - - g_return_if_fail(data != NULL && data_len > 26); - - qq_show_packet("Rcv sms", data, data_len); - - bytes = 0; - bytes += 1; /* skip 0x00 */ - mobile = g_strndup((gchar *)data + bytes, 20); - bytes += 20; - bytes += 5; /* skip 0x(49 11 98 d5 03)*/ - if (bytes < data_len) { - msg = g_strndup((gchar *)data + bytes, data_len - bytes); - msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); - g_free(msg); - } else { - msg_utf8 = g_strdup(""); - } - - msg_formated = g_strdup_printf(_("%s:%s"), mobile, msg_utf8); - - qq_got_message(gc, msg_formated); - - g_free(msg_formated); - g_free(msg_utf8); - g_free(mobile); -} - -static void do_msg_sys_30(PurpleConnection *gc, guint8 *data, gint data_len) -{ - gint len; - guint8 reply; - gchar **segments, *msg_utf8; - - g_return_if_fail(data != NULL && data_len != 0); - - len = data_len; - - if (NULL == (segments = split_data(data, len, "\x2f", 2))) - return; - - reply = strtol(segments[0], NULL, 10); - if (reply == 1) - purple_debug_warning("QQ", "We are kicked out by QQ server\n"); - - msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); - qq_got_message(gc, msg_utf8); -} - -static void do_msg_sys_4c(PurpleConnection *gc, guint8 *data, gint data_len) -{ - gint bytes; - gint msg_len; - GString *content; - gchar *msg = NULL; - - g_return_if_fail(data != NULL && data_len > 0); - - bytes = 6; /* skip 0x(06 00 01 1e 01 1c)*/ - - content = g_string_new(""); - while (bytes < data_len) { - msg_len = qq_get_vstr(&msg, QQ_CHARSET_DEFAULT, data + bytes); - g_string_append(content, msg); - g_string_append(content, "\n"); - g_free(msg); - - if (msg_len <= 1) { - break; - } - bytes += msg_len; - } - if (bytes != data_len) { - purple_debug_warning("QQ", "Failed to read QQ_MSG_SYS_4C\n"); - qq_show_packet("do_msg_sys_4c", data, data_len); - } - qq_got_message(gc, content->str); - g_string_free(content, FALSE); -} - -static const gchar *get_im_type_desc(gint type) -{ - switch (type) { - case QQ_MSG_TO_BUDDY: - return "QQ_MSG_TO_BUDDY"; - case QQ_MSG_TO_UNKNOWN: - return "QQ_MSG_TO_UNKNOWN"; - case QQ_MSG_QUN_IM_UNKNOWN: - return "QQ_MSG_QUN_IM_UNKNOWN"; - case QQ_MSG_ADD_TO_QUN: - return "QQ_MSG_ADD_TO_QUN"; - case QQ_MSG_DEL_FROM_QUN: - return "QQ_MSG_DEL_FROM_QUN"; - case QQ_MSG_APPLY_ADD_TO_QUN: - return "QQ_MSG_APPLY_ADD_TO_QUN"; - case QQ_MSG_CREATE_QUN: - return "QQ_MSG_CREATE_QUN"; - case QQ_MSG_SYS_30: - return "QQ_MSG_SYS_30"; - case QQ_MSG_SYS_4C: - return "QQ_MSG_SYS_4C"; - case QQ_MSG_APPROVE_APPLY_ADD_TO_QUN: - return "QQ_MSG_APPROVE_APPLY_ADD_TO_QUN"; - case QQ_MSG_REJCT_APPLY_ADD_TO_QUN: - return "QQ_MSG_REJCT_APPLY_ADD_TO_QUN"; - case QQ_MSG_TEMP_QUN_IM: - return "QQ_MSG_TEMP_QUN_IM"; - case QQ_MSG_QUN_IM: - return "QQ_MSG_QUN_IM"; - case QQ_MSG_NEWS: - return "QQ_MSG_NEWS"; - case QQ_MSG_SMS: - return "QQ_MSG_SMS"; - case QQ_MSG_EXTEND: - return "QQ_MSG_EXTEND"; - case QQ_MSG_EXTEND_85: - return "QQ_MSG_EXTEND_85"; - default: - return "QQ_MSG_UNKNOWN"; - } -} - -/* I receive a message, mainly it is text msg, - * but we need to proess other types (group etc) */ -static void process_private_msg(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc) -{ - qq_data *qd; - gint bytes; - - struct { - UID uid_from; - UID uid_to; - guint32 seq; - struct in_addr ip_from; - guint16 port_from; - guint16 msg_type; - } header; - - g_return_if_fail(data != NULL && data_len != 0); - - qd = (qq_data *) gc->proto_data; - - if (data_len < 16) { /* we need to ack with the first 16 bytes */ - purple_debug_error("QQ", "MSG is too short\n"); - return; - } else { - /* when we receive a message, - * we send an ACK which is the first 16 bytes of incoming packet */ - qq_send_server_reply(gc, QQ_CMD_RECV_IM, seq, data, 16); - } - - /* check len first */ - if (data_len < 20) { /* length of im_header */ - purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len); - return; - } - - bytes = 0; - bytes += qq_get32(&(header.uid_from), data + bytes); - bytes += qq_get32(&(header.uid_to), data + bytes); - bytes += qq_get32(&(header.seq), data + bytes); - /* if the message is delivered via server, it is server IP/port */ - bytes += qq_getIP(&(header.ip_from), data + bytes); - bytes += qq_get16(&(header.port_from), data + bytes); - bytes += qq_get16(&(header.msg_type), data + bytes); - /* im_header prepared */ - - if (header.uid_to != qd->uid) { /* should not happen */ - purple_debug_error("QQ", "MSG to %u, NOT me\n", header.uid_to); - return; - } - - /* check bytes */ - if (bytes >= data_len - 1) { - purple_debug_warning("QQ", "Empty MSG\n"); - return; - } - - switch (header.msg_type) { - case QQ_MSG_NEWS: - do_server_news(gc, data + bytes, data_len - bytes); - break; - case QQ_MSG_SMS: - do_got_sms(gc, data + bytes, data_len - bytes); - break; - case QQ_MSG_EXTEND: - case QQ_MSG_EXTEND_85: - purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from); - qq_process_extend_im(gc, data + bytes, data_len - bytes); - break; - case QQ_MSG_TO_UNKNOWN: - case QQ_MSG_TO_BUDDY: - purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from); - qq_process_im(gc, data + bytes, data_len - bytes); - break; - case QQ_MSG_QUN_IM_UNKNOWN: - case QQ_MSG_TEMP_QUN_IM: - case QQ_MSG_QUN_IM: - purple_debug_info("QQ", "MSG from room [%d]\n", header.uid_from); - qq_process_room_im(data + bytes, data_len - bytes, header.uid_from, gc, header.msg_type); - break; - case QQ_MSG_ADD_TO_QUN: - purple_debug_info("QQ", "Notice from [%d], Added\n", header.uid_from); - /* uid_from is group id - * we need this to create a dummy group and add to blist */ - qq_process_room_buddy_joined(data + bytes, data_len - bytes, header.uid_from, gc); - break; - case QQ_MSG_DEL_FROM_QUN: - purple_debug_info("QQ", "Notice from room [%d], Removed\n", header.uid_from); - /* uid_from is group id */ - qq_process_room_buddy_removed(data + bytes, data_len - bytes, header.uid_from, gc); - break; - case QQ_MSG_APPLY_ADD_TO_QUN: - purple_debug_info("QQ", "Notice from room [%d], Joined\n", header.uid_from); - /* uid_from is group id */ - qq_process_room_buddy_request_join(data + bytes, data_len - bytes, header.uid_from, gc); - break; - case QQ_MSG_APPROVE_APPLY_ADD_TO_QUN: - purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n", - header.uid_from); - /* uid_from is group id */ - qq_process_room_buddy_approved(data + bytes, data_len - bytes, header.uid_from, gc); - break; - case QQ_MSG_REJCT_APPLY_ADD_TO_QUN: - purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n", - header.uid_from); - /* uid_from is group id */ - qq_process_room_buddy_rejected(data + bytes, data_len - bytes, header.uid_from, gc); - break; - case QQ_MSG_SYS_30: - do_msg_sys_30(gc, data + bytes, data_len - bytes); - break; - case QQ_MSG_SYS_4C: - do_msg_sys_4c(gc, data + bytes, data_len - bytes); - break; - default: - purple_debug_warning("QQ", "MSG from %u, unknown type %s [0x%04X]\n", - header.uid_from, get_im_type_desc(header.msg_type), header.msg_type); - qq_show_packet("MSG header", data, bytes); - if (data_len - bytes > 0) { - qq_show_packet("MSG data", data + bytes, data_len - bytes); - } - break; - } -} - -/* Send ACK if the sys message needs an ACK */ -static void request_server_ack(PurpleConnection *gc, gchar *funct_str, gchar *from, guint16 seq) -{ - guint8 *raw_data; - gint bytes; - guint8 bar; - - g_return_if_fail(funct_str != NULL && from != NULL); - - bar = 0x1e; - raw_data = g_newa(guint8, strlen(funct_str) + strlen(from) + 16); - - bytes = 0; - bytes += qq_putdata(raw_data + bytes, (guint8 *)funct_str, strlen(funct_str)); - bytes += qq_put8(raw_data + bytes, bar); - bytes += qq_putdata(raw_data + bytes, (guint8 *)from, strlen(from)); - bytes += qq_put8(raw_data + bytes, bar); - bytes += qq_put16(raw_data + bytes, seq); - - qq_send_server_reply(gc, QQ_CMD_ACK_SYS_MSG, 0, raw_data, bytes); -} - -static void do_server_notice(PurpleConnection *gc, gchar *from, gchar *to, - guint8 *data, gint data_len) -{ - qq_data *qd = (qq_data *) gc->proto_data; - gchar *msg, *msg_utf8; - gchar *title, *content; - - g_return_if_fail(from != NULL && to != NULL && data_len > 0); - - msg = g_strndup((gchar *)data, data_len); - msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); - g_free(msg); - if (msg_utf8 == NULL) { - purple_debug_error("QQ", "Recv NULL sys msg from %s to %s, discard\n", - from, to); - return; - } - - title = g_strdup_printf(_("From %s:"), from); - content = g_strdup_printf(_("Server notice From %s: \n%s"), from, msg_utf8); - - if (qd->is_show_notice) { - qq_got_message(gc, content); - } else { - purple_debug_info("QQ", "QQ Server notice from %s:\n%s\n", from, msg_utf8); - } - g_free(msg_utf8); - g_free(title); - g_free(content); -} - -static void process_server_msg(PurpleConnection *gc, guint8 *data, gint data_len, guint16 seq) -{ - qq_data *qd; - guint8 *data_str, i = 0; - gchar **segments, **seg; - gchar *funct_str, *from, *to; - gint bytes, funct; - - g_return_if_fail(data != NULL && data_len != 0); - - qd = (qq_data *) gc->proto_data; - - data_str = g_newa(guint8, data_len + 1); - g_memmove(data_str, data, data_len); - data_str[data_len] = 0x00; - - segments = g_strsplit((gchar *) data_str, "\x1f", 0); - g_return_if_fail(segments != NULL); - for (seg = segments; *seg != NULL; seg++) - i++; - if (i < 3) { - purple_debug_warning("QQ", "Server message segments is less than 3\n"); - g_strfreev(segments); - return; - } - - bytes = 0; - funct_str = segments[0]; - bytes += strlen(funct_str) + 1; - from = segments[1]; - bytes += strlen(from) + 1; - to = segments[2]; - bytes += strlen(to) + 1; - - request_server_ack(gc, funct_str, from, seq); - - /* qq_show_packet("Server MSG", data, data_len); */ - if (strtoul(to, NULL, 10) != qd->uid) { /* not to me */ - purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to); - g_strfreev(segments); - return; - } - - funct = strtol(funct_str, NULL, 10); - switch (funct) { - case QQ_SERVER_BUDDY_ADDED: - case QQ_SERVER_BUDDY_ADD_REQUEST: - case QQ_SERVER_BUDDY_ADDED_ME: - case QQ_SERVER_BUDDY_REJECTED_ME: - case QQ_SERVER_BUDDY_ADD_REQUEST_EX: - case QQ_SERVER_BUDDY_ADDING_EX: - case QQ_SERVER_BUDDY_ADDED_ANSWER: - case QQ_SERVER_BUDDY_ADDED_EX: - qq_process_buddy_from_server(gc, funct, from, to, data + bytes, data_len - bytes); - break; - case QQ_SERVER_NOTICE: - do_server_notice(gc, from, to, data + bytes, data_len - bytes); - break; - case QQ_SERVER_NEW_CLIENT: - purple_debug_warning("QQ", "QQ Server has newer client version\n"); - break; - default: - qq_show_packet("Unknown sys msg", data, data_len); - purple_debug_warning("QQ", "Recv unknown sys msg code: %s\n", funct_str); - break; - } - g_strfreev(segments); -} - -void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len) -{ - qq_data *qd; - - guint8 *data; - gint data_len; - - g_return_if_fail (gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - data = g_newa(guint8, rcved_len); - data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key); - if (data_len < 0) { - purple_debug_warning("QQ", - "Can not decrypt server cmd by session key, [%05d], 0x%04X %s, len %d\n", - seq, cmd, qq_get_cmd_desc(cmd), rcved_len); - qq_show_packet("Can not decrypted", rcved, rcved_len); - return; - } - - if (data_len <= 0) { - purple_debug_warning("QQ", - "Server cmd decrypted is empty, [%05d], 0x%04X %s, len %d\n", - seq, cmd, qq_get_cmd_desc(cmd), rcved_len); - return; - } - - /* now process the packet */ - switch (cmd) { - case QQ_CMD_RECV_IM: - process_private_msg(data, data_len, seq, gc); - break; - case QQ_CMD_RECV_MSG_SYS: - process_server_msg(gc, data, data_len, seq); - break; - case QQ_CMD_BUDDY_CHANGE_STATUS: - qq_process_buddy_change_status(data, data_len, gc); - break; - default: - process_unknow_cmd(gc, _("Unknown SERVER CMD"), data, data_len, cmd, seq); - break; - } -} - -static void process_room_cmd_notify(PurpleConnection *gc, - guint8 room_cmd, guint8 room_id, guint8 reply, guint8 *data, gint data_len) -{ - gchar *prim; - gchar *msg, *msg_utf8; - g_return_if_fail(data != NULL && data_len > 0); - - msg = g_strndup((gchar *) data, data_len); /* it will append 0x00 */ - msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); - g_free(msg); - - prim = g_strdup_printf(_("Error reply of %s(0x%02X)\nRoom %u, reply 0x%02X"), - qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, reply); - - purple_notify_error(gc, _("QQ Qun Command"), prim, msg_utf8); - - g_free(prim); - g_free(msg_utf8); -} - -void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) -{ - gint ret; - - g_return_if_fail (gc != NULL); - - switch (room_cmd) { - case 0: - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0, - QQ_CMD_CLASS_UPDATE_ROOM, 0); - break; - case QQ_ROOM_CMD_GET_INFO: - ret = qq_request_room_get_buddies(gc, room_id, QQ_CMD_CLASS_UPDATE_ROOM); - if (ret <= 0) { - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, room_id, NULL, 0, - QQ_CMD_CLASS_UPDATE_ROOM, 0); - } - break; - case QQ_ROOM_CMD_GET_BUDDIES: - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, room_id, NULL, 0, - QQ_CMD_CLASS_UPDATE_ROOM, 0); - break; - case QQ_ROOM_CMD_GET_ONLINES: - /* last command */ - default: - break; - } -} - -void qq_update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) -{ - gboolean is_new_turn = FALSE; - guint32 next_id; - - g_return_if_fail(gc != NULL); - - next_id = qq_room_get_next(gc, room_id); - purple_debug_info("QQ", "Update rooms, next id %u, prev id %u\n", next_id, room_id); - - if (next_id <= 0) { - if (room_id > 0) { - is_new_turn = TRUE; - next_id = qq_room_get_next(gc, 0); - purple_debug_info("QQ", "New turn, id %u\n", next_id); - } else { - purple_debug_info("QQ", "No room. Finished update\n"); - return; - } - } - - switch (room_cmd) { - case 0: - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_id, NULL, 0, - QQ_CMD_CLASS_UPDATE_ALL, 0); - break; - case QQ_ROOM_CMD_GET_INFO: - if (!is_new_turn) { - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_id, NULL, 0, - QQ_CMD_CLASS_UPDATE_ALL, 0); - } else { - qq_request_room_get_buddies(gc, next_id, QQ_CMD_CLASS_UPDATE_ALL); - } - break; - case QQ_ROOM_CMD_GET_BUDDIES: - /* last command */ - if (!is_new_turn) { - qq_request_room_get_buddies(gc, next_id, QQ_CMD_CLASS_UPDATE_ALL); - } else { - purple_debug_info("QQ", "Finished update\n"); - } - break; - default: - break; - } -} - -void qq_update_all(PurpleConnection *gc, guint16 cmd) -{ - qq_data *qd; - - g_return_if_fail (gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - switch (cmd) { - case 0: - qq_request_buddy_info(gc, qd->uid, QQ_CMD_CLASS_UPDATE_ALL, 0); - break; - case QQ_CMD_GET_BUDDY_INFO: - qq_request_change_status(gc, QQ_CMD_CLASS_UPDATE_ALL); - break; - case QQ_CMD_CHANGE_STATUS: - qq_request_get_buddies(gc, 0, QQ_CMD_CLASS_UPDATE_ALL); - break; - case QQ_CMD_GET_BUDDIES_LIST: - qq_request_get_buddies_and_rooms(gc, 0, QQ_CMD_CLASS_UPDATE_ALL); - break; - case QQ_CMD_GET_BUDDIES_AND_ROOMS: - if (qd->client_version >= 2007) { - /* QQ2007/2008 can not get buddies level*/ - qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ALL); - } else { - qq_request_get_buddies_level(gc, QQ_CMD_CLASS_UPDATE_ALL); - } - break; - case QQ_CMD_GET_LEVEL: - qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ALL); - break; - case QQ_CMD_GET_BUDDIES_ONLINE: - /* last command */ - qq_update_all_rooms(gc, 0, 0); - break; - default: - break; - } - qd->online_last_update = time(NULL); -} - -static void update_all_rooms_online(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) -{ - guint32 next_id; - - g_return_if_fail (gc != NULL); - - next_id = qq_room_get_next_conv(gc, room_id); - if (next_id <= 0 && room_id <= 0) { - purple_debug_info("QQ", "No room in conversation, no update online buddies\n"); - return; - } - if (next_id <= 0 ) { - purple_debug_info("QQ", "finished update rooms' online buddies\n"); - return; - } - - switch (room_cmd) { - case 0: - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_id, NULL, 0, - QQ_CMD_CLASS_UPDATE_ALL, 0); - break; - case QQ_ROOM_CMD_GET_ONLINES: - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_id, NULL, 0, - QQ_CMD_CLASS_UPDATE_ALL, 0); - break; - default: - break; - } -} - -void qq_update_online(PurpleConnection *gc, guint16 cmd) -{ - qq_data *qd; - g_return_if_fail (gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - switch (cmd) { - case 0: - qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ONLINE); - break; - case QQ_CMD_GET_BUDDIES_ONLINE: - /* last command */ - update_all_rooms_online(gc, 0, 0); - break; - default: - break; - } - qd->online_last_update = time(NULL); -} - -void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq, - guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len, - UPDCLS update_class, guint32 ship32) -{ - qq_data *qd; - guint8 *data; - gint data_len; - qq_room_data *rmd; - gint bytes; - guint8 reply_cmd, reply; - - g_return_if_fail (gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - data = g_newa(guint8, rcved_len); - data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key); - if (data_len < 0) { - purple_debug_warning("QQ", - "Can not decrypt room cmd by session key, [%05d], 0x%02X %s for %d, len %d\n", - seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); - qq_show_packet("Can not decrypted", rcved, rcved_len); - return; - } - - if (room_id <= 0) { - purple_debug_warning("QQ", - "Invaild room id, [%05d], 0x%02X %s for %d, len %d\n", - seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); - /* Some room cmd has no room id, like QQ_ROOM_CMD_SEARCH */ - } - - if (data_len <= 2) { - purple_debug_warning("QQ", - "Invaild len of room cmd decrypted, [%05d], 0x%02X %s for %d, len %d\n", - seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); - return; - } - - bytes = 0; - bytes += qq_get8(&reply_cmd, data + bytes); - bytes += qq_get8(&reply, data + bytes); - - if (reply_cmd != room_cmd) { - purple_debug_warning("QQ", - "Missing room cmd in reply 0x%02X %s, [%05d], 0x%02X %s for %d, len %d\n", - reply_cmd, qq_get_room_cmd_desc(reply_cmd), - seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); - } - - /* now process the packet */ - if (reply != QQ_ROOM_CMD_REPLY_OK) { - switch (reply) { /* this should be all errors */ - case QQ_ROOM_CMD_REPLY_NOT_MEMBER: - rmd = qq_room_data_find(gc, room_id); - if (rmd == NULL) { - purple_debug_warning("QQ", - "Missing room id in [%05d], 0x%02X %s for %d, len %d\n", - seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); - } else { - purple_debug_warning("QQ", - "Not a member of room \"%s\"\n", rmd->title_utf8); - rmd->my_role = QQ_ROOM_ROLE_NO; - } - break; - case QQ_ROOM_CMD_REPLY_SEARCH_ERROR: - if (qd->roomlist != NULL) { - if (purple_roomlist_get_in_progress(qd->roomlist)) - purple_roomlist_set_in_progress(qd->roomlist, FALSE); - } - default: - process_room_cmd_notify(gc, reply_cmd, room_id, reply, data + bytes, data_len - bytes); - } - return; - } - - /* seems ok so far, so we process the reply according to sub_cmd */ - switch (reply_cmd) { - case QQ_ROOM_CMD_GET_INFO: - qq_process_room_cmd_get_info(data + bytes, data_len - bytes, ship32, gc); - break; - case QQ_ROOM_CMD_CREATE: - qq_group_process_create_group_reply(data + bytes, data_len - bytes, gc); - break; - case QQ_ROOM_CMD_CHANGE_INFO: - qq_group_process_modify_info_reply(data + bytes, data_len - bytes, gc); - break; - case QQ_ROOM_CMD_MEMBER_OPT: - qq_group_process_modify_members_reply(data + bytes, data_len - bytes, gc); - break; - case QQ_ROOM_CMD_ACTIVATE: - qq_group_process_activate_group_reply(data + bytes, data_len - bytes, gc); - break; - case QQ_ROOM_CMD_SEARCH: - qq_process_room_search(gc, data + bytes, data_len - bytes, ship32); - break; - case QQ_ROOM_CMD_JOIN: - qq_process_group_cmd_join_group(data + bytes, data_len - bytes, gc); - break; - case QQ_ROOM_CMD_AUTH: - qq_process_group_cmd_join_group_auth(data + bytes, data_len - bytes, gc); - break; - case QQ_ROOM_CMD_QUIT: - qq_process_group_cmd_exit_group(data + bytes, data_len - bytes, gc); - break; - case QQ_ROOM_CMD_SEND_IM: - qq_process_room_send_im(gc, data + bytes, data_len - bytes); - break; - case QQ_ROOM_CMD_SEND_IM_EX: - qq_process_room_send_im_ex(gc, data + bytes, data_len - bytes); - break; - case QQ_ROOM_CMD_GET_ONLINES: - qq_process_room_cmd_get_onlines(data + bytes, data_len - bytes, gc); - break; - case QQ_ROOM_CMD_GET_BUDDIES: - qq_process_room_cmd_get_buddies(data + bytes, data_len - bytes, gc); - break; - default: - purple_debug_warning("QQ", "Unknow room cmd 0x%02X %s\n", - reply_cmd, qq_get_room_cmd_desc(reply_cmd)); - } - - if (update_class == QQ_CMD_CLASS_NONE) - return; - - if (update_class == QQ_CMD_CLASS_UPDATE_ALL) { - qq_update_all_rooms(gc, room_cmd, room_id); - return; - } - if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) { - update_all_rooms_online(gc, room_cmd, room_id); - return; - } - if (update_class == QQ_CMD_CLASS_UPDATE_ROOM) { - qq_update_room(gc, room_cmd, room_id); - } -} - -guint8 qq_proc_login_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *rcved, gint rcved_len, UPDCLS update_class, guint32 ship32) -{ - qq_data *qd; - guint8 *data = NULL; - gint data_len = 0; - guint ret_8 = QQ_LOGIN_REPLY_ERR; - - g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR); - qd = (qq_data *) gc->proto_data; - - g_return_val_if_fail(rcved_len > 0, QQ_LOGIN_REPLY_ERR); - data = g_newa(guint8, rcved_len); - - switch (cmd) { - case QQ_CMD_TOKEN: - if (qq_process_token(gc, rcved, rcved_len) == QQ_LOGIN_REPLY_OK) { - if (qd->client_version >= 2007) { - qq_request_token_ex(gc); - } else { - qq_request_login(gc); - } - return QQ_LOGIN_REPLY_OK; - } - return QQ_LOGIN_REPLY_ERR; - case QQ_CMD_GET_SERVER: - case QQ_CMD_TOKEN_EX: - data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key); - break; - case QQ_CMD_CHECK_PWD: - /* May use password_twice_md5 in the past version like QQ2005 */ - data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key); - if (data_len >= 0) { - purple_debug_warning("QQ", "Decrypt login packet by random_key, %d bytes\n", data_len); - } else { - data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5); - if (data_len >= 0) { - purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5, %d bytes\n", data_len); - } - } - break; - case QQ_CMD_LOGIN: - default: - if (qd->client_version >= 2007) { - data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5); - if (data_len >= 0) { - purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n"); - } else { - data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.login_key); - if (data_len >= 0) { - purple_debug_warning("QQ", "Decrypt login packet by login_key\n"); - } - } - } else { - /* May use password_twice_md5 in the past version like QQ2005 */ - data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key); - if (data_len >= 0) { - purple_debug_warning("QQ", "Decrypt login packet by random_key\n"); - } else { - data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5); - if (data_len >= 0) { - purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n"); - } - } - } - break; - } - - if (data_len < 0) { - purple_debug_warning("QQ", - "Can not decrypt login cmd, [%05d], 0x%04X %s, len %d\n", - seq, cmd, qq_get_cmd_desc(cmd), rcved_len); - qq_show_packet("Can not decrypted", rcved, rcved_len); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("Unable to decrypt login reply")); - return QQ_LOGIN_REPLY_ERR; - } - - switch (cmd) { - case QQ_CMD_GET_SERVER: - ret_8 = qq_process_get_server(gc, data, data_len); - if ( ret_8 == QQ_LOGIN_REPLY_OK) { - qq_request_token(gc); - } else if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) { - return QQ_LOGIN_REPLY_REDIRECT; - } - break; - case QQ_CMD_TOKEN_EX: - ret_8 = qq_process_token_ex(gc, data, data_len); - if (ret_8 == QQ_LOGIN_REPLY_OK) { - qq_request_check_pwd(gc); - } else if (ret_8 == QQ_LOGIN_REPLY_NEXT_TOKEN_EX) { - qq_request_token_ex_next(gc); - } else if (ret_8 == QQ_LOGIN_REPLY_CAPTCHA_DLG) { - qq_captcha_input_dialog(gc, &(qd->captcha)); - g_free(qd->captcha.token); - g_free(qd->captcha.data); - memset(&qd->captcha, 0, sizeof(qd->captcha)); - } - break; - case QQ_CMD_CHECK_PWD: - ret_8 = qq_process_check_pwd(gc, data, data_len); - if (ret_8 != QQ_LOGIN_REPLY_OK) { - return ret_8; - } - if (qd->client_version >= 2008) { - qq_request_login_2008(gc); - } else { - qq_request_login_2007(gc); - } - break; - case QQ_CMD_LOGIN: - if (qd->client_version >= 2008) { - ret_8 = qq_process_login_2008(gc, data, data_len); - if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) { - qq_request_get_server(gc); - return QQ_LOGIN_REPLY_OK; - } - } else if (qd->client_version >= 2007) { - ret_8 = qq_process_login_2007(gc, data, data_len); - if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) { - qq_request_get_server(gc); - return QQ_LOGIN_REPLY_OK; - } - } else { - ret_8 = qq_process_login(gc, data, data_len); - } - if (ret_8 != QQ_LOGIN_REPLY_OK) { - return ret_8; - } - - purple_connection_update_progress(gc, _("Logging in"), QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS); - purple_debug_info("QQ", "Login replies OK; everything is fine\n"); - purple_connection_set_state(gc, PURPLE_CONNECTED); - qd->is_login = TRUE; /* must be defined after sev_finish_login */ - - /* now initiate QQ Qun, do it first as it may take longer to finish */ - qq_room_data_initial(gc); - - /* is_login, but we have packets before login */ - qq_trans_process_remained(gc); - - qq_update_all(gc, 0); - break; - default: - process_unknow_cmd(gc, _("Unknown LOGIN CMD"), data, data_len, cmd, seq); - return QQ_LOGIN_REPLY_ERR; - } - return QQ_LOGIN_REPLY_OK; -} - -void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *rcved, gint rcved_len, UPDCLS update_class, guint32 ship32) -{ - qq_data *qd; - - guint8 *data; - gint data_len; - - guint8 ret_8 = 0; - guint16 ret_16 = 0; - guint32 ret_32 = 0; - gboolean is_unknow = FALSE; - - g_return_if_fail(rcved_len > 0); - - g_return_if_fail (gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - data = g_newa(guint8, rcved_len); - data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key); - if (data_len < 0) { - purple_debug_warning("QQ", - "Reply can not be decrypted by session key, [%05d], 0x%04X %s, len %d\n", - seq, cmd, qq_get_cmd_desc(cmd), rcved_len); - qq_show_packet("Can not decrypted", rcved, rcved_len); - return; - } - - if (data_len <= 0) { - purple_debug_warning("QQ", - "Reply decrypted is empty, [%05d], 0x%04X %s, len %d\n", - seq, cmd, qq_get_cmd_desc(cmd), rcved_len); - return; - } - - switch (cmd) { - case QQ_CMD_UPDATE_INFO: - qq_process_change_info(gc, data, data_len); - break; - case QQ_CMD_ADD_BUDDY_NO_AUTH: - qq_process_add_buddy_no_auth(gc, data, data_len, ship32); - break; - case QQ_CMD_REMOVE_BUDDY: - qq_process_remove_buddy(gc, data, data_len, ship32); - break; - case QQ_CMD_REMOVE_ME: - qq_process_buddy_remove_me(gc, data, data_len, ship32); - break; - case QQ_CMD_ADD_BUDDY_AUTH: - qq_process_add_buddy_auth(data, data_len, gc); - break; - case QQ_CMD_GET_BUDDY_INFO: - qq_process_get_buddy_info(data, data_len, ship32, gc); - break; - case QQ_CMD_CHANGE_STATUS: - qq_process_change_status(data, data_len, gc); - break; - case QQ_CMD_SEND_IM: - do_im_ack(data, data_len, gc); - break; - case QQ_CMD_KEEP_ALIVE: - if (qd->client_version >= 2008) { - qq_process_keep_alive_2008(data, data_len, gc); - } else if (qd->client_version >= 2007) { - qq_process_keep_alive_2007(data, data_len, gc); - } else { - qq_process_keep_alive(data, data_len, gc); - } - break; - case QQ_CMD_GET_BUDDIES_ONLINE: - ret_8 = qq_process_get_buddies_online(data, data_len, gc); - if (ret_8 > 0 && ret_8 < 0xff) { - purple_debug_info("QQ", "Requesting for more online buddies\n"); - qq_request_get_buddies_online(gc, ret_8, update_class); - return; - } - purple_debug_info("QQ", "All online buddies received\n"); - qq_update_buddyies_status(gc); - break; - case QQ_CMD_GET_LEVEL: - qq_process_get_level_reply(data, data_len, gc); - break; - case QQ_CMD_GET_BUDDIES_LIST: - ret_16 = qq_process_get_buddies(data, data_len, gc); - if (ret_16 > 0 && ret_16 < 0xffff) { - purple_debug_info("QQ", "Requesting for more buddies\n"); - qq_request_get_buddies(gc, ret_16, update_class); - return; - } - purple_debug_info("QQ", "All buddies received. Requesting buddies' levels\n"); - break; - case QQ_CMD_GET_BUDDIES_AND_ROOMS: - ret_32 = qq_process_get_buddies_and_rooms(data, data_len, gc); - if (ret_32 > 0 && ret_32 < 0xffffffff) { - purple_debug_info("QQ", "Requesting for more buddies and groups\n"); - qq_request_get_buddies_and_rooms(gc, ret_32, update_class); - return; - } - purple_debug_info("QQ", "All buddies and groups received\n"); - break; - case QQ_CMD_AUTH_CODE: - qq_process_auth_code(gc, data, data_len, ship32); - break; - case QQ_CMD_BUDDY_QUESTION: - qq_process_question(gc, data, data_len, ship32); - break; - case QQ_CMD_ADD_BUDDY_NO_AUTH_EX: - qq_process_add_buddy_no_auth_ex(gc, data, data_len, ship32); - break; - case QQ_CMD_ADD_BUDDY_AUTH_EX: - qq_process_add_buddy_auth_ex(gc, data, data_len, ship32); - break; - case QQ_CMD_BUDDY_CHECK_CODE: - qq_process_buddy_check_code(gc, data, data_len); - break; - case QQ_CMD_BUDDY_MEMO: - purple_debug_info("QQ", "Receive memo from server!\n"); - qq_process_get_buddy_memo(gc, data, data_len, update_class, ship32); - return; - purple_debug_info("QQ", "Should NOT be here...\n"); - break; - default: - process_unknow_cmd(gc, _("Unknown CLIENT CMD"), data, data_len, cmd, seq); - is_unknow = TRUE; - break; - } - if (is_unknow) - return; - - if (update_class == QQ_CMD_CLASS_NONE) - return; - - purple_debug_info("QQ", "Update class %d\n", update_class); - if (update_class == QQ_CMD_CLASS_UPDATE_ALL) { - qq_update_all(gc, cmd); - return; - } - if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) { - qq_update_online(gc, cmd); - return; - } -} - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_process.h --- a/libpurple/protocols/qq/qq_process.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * @file qq_process.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_PROCESS_H -#define _QQ_PROCESS_H - -#include -#include "connection.h" - -#include "qq.h" - -enum { - QQ_CMD_CLASS_NONE = 0, - QQ_CMD_CLASS_UPDATE_ALL, - QQ_CMD_CLASS_UPDATE_ONLINE, - QQ_CMD_CLASS_UPDATE_BUDDY, - QQ_CMD_CLASS_UPDATE_ROOM -}; - -guint8 qq_proc_login_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *rcved, gint rcved_len, UPDCLS update_class, guint32 ship32); -void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *rcved, gint rcved_len, UPDCLS update_class, guint32 ship32); -void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq, - guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len, - UPDCLS update_class, guint32 ship32); - -void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len); - -void qq_update_all(PurpleConnection *gc, guint16 cmd); -void qq_update_online(PurpleConnection *gc, guint16 cmd); -void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id); -void qq_update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id); -#endif - diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_trans.c --- a/libpurple/protocols/qq/qq_trans.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,395 +0,0 @@ -/** - * @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 "qq_define.h" -#include "qq_network.h" -#include "qq_process.h" -#include "qq_trans.h" - -enum { - QQ_TRANS_IS_SERVER = 0x01, /* Is server command or client command */ - QQ_TRANS_IS_IMPORT = 0x02, /* Only notice if not get reply; or resend, disconn if reties get 0*/ - QQ_TRANS_REMAINED = 0x04, /* server command before login*/ - QQ_TRANS_IS_REPLY = 0x08 /* server command before login*/ -}; - - -gboolean qq_trans_is_server(qq_transaction *trans) -{ - g_return_val_if_fail(trans != NULL, FALSE); - - if (trans->flag & QQ_TRANS_IS_SERVER) - return TRUE; - else - return FALSE; -} - -gboolean qq_trans_is_dup(qq_transaction *trans) -{ - g_return_val_if_fail(trans != NULL, TRUE); - - if (trans->rcved_times > 1) - return TRUE; - else - return FALSE; -} - -guint8 qq_trans_get_room_cmd(qq_transaction *trans) -{ - g_return_val_if_fail(trans != NULL, 0); - return trans->room_cmd; -} - -guint32 qq_trans_get_room_id(qq_transaction *trans) -{ - g_return_val_if_fail(trans != NULL, 0); - return trans->room_id; -} - -guint32 qq_trans_get_class(qq_transaction *trans) -{ - g_return_val_if_fail(trans != NULL, QQ_CMD_CLASS_NONE); - return trans->update_class; -} - -guint32 qq_trans_get_ship(qq_transaction *trans) -{ - g_return_val_if_fail(trans != NULL, 0); - return trans->ship32; -} - -static qq_transaction *trans_create(PurpleConnection *gc, gint fd, - guint16 cmd, guint16 seq, guint8 *data, gint data_len, UPDCLS update_class, guint32 ship32) -{ - qq_transaction *trans; - - g_return_val_if_fail(gc != NULL, NULL); - - trans = g_new0(qq_transaction, 1); - - memset(trans, 0, sizeof(qq_transaction)); - trans->fd = fd; - trans->cmd = cmd; - trans->seq = seq; - - trans->data = NULL; - trans->data_len = 0; - if (data != NULL && data_len > 0) { - /* don't use g_strdup, may have 0x00 */ - trans->data = g_memdup(data, data_len); - trans->data_len = data_len; - } - - trans->update_class = update_class; - trans->ship32 = ship32; - return trans; -} - -/* Remove a packet with seq from send trans */ -static void trans_remove(PurpleConnection *gc, qq_transaction *trans) -{ - qq_data *qd; - - g_return_if_fail(gc != NULL); - qd = (qq_data *) gc->proto_data; - g_return_if_fail(qd != NULL); - - g_return_if_fail(trans != NULL); -#if 0 - purple_debug_info("QQ_TRANS", - "Remove [%s%05d] retry %d rcved %d scan %d %s\n", - (trans->flag & QQ_TRANS_IS_SERVER) ? "SRV-" : "", - trans->seq, - trans->send_retries, trans->rcved_times, trans->scan_times, - qq_get_cmd_desc(trans->cmd)); -#endif - if (trans->data) g_free(trans->data); - qd->transactions = g_list_remove(qd->transactions, trans); - g_free(trans); -} - -static qq_transaction *trans_find(PurpleConnection *gc, guint16 cmd, guint16 seq) -{ - qq_data *qd; - GList *list; - qq_transaction *trans; - - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, NULL); - qd = (qq_data *) gc->proto_data; - - list = qd->transactions; - while (list != NULL) { - trans = (qq_transaction *) list->data; - if(trans->cmd == cmd && trans->seq == seq) { - return trans; - } - list = list->next; - } - - return NULL; -} - -void qq_trans_add_client_cmd(PurpleConnection *gc, - guint16 cmd, guint16 seq, guint8 *data, gint data_len, UPDCLS update_class, guint32 ship32) -{ - qq_data *qd = (qq_data *)gc->proto_data; - qq_transaction *trans = trans_create(gc, qd->fd, cmd, seq, data, data_len, update_class, ship32); - - if (cmd == QQ_CMD_TOKEN || cmd == QQ_CMD_LOGIN || cmd == QQ_CMD_KEEP_ALIVE) { - trans->flag |= QQ_TRANS_IS_IMPORT; - } - trans->send_retries = qd->resend_times; -#if 0 - purple_debug_info("QQ_TRANS", "Add client cmd, seq %d, data %p, len %d\n", - trans->seq, trans->data, trans->data_len); -#endif - qd->transactions = g_list_append(qd->transactions, trans); -} - -qq_transaction *qq_trans_find_rcved(PurpleConnection *gc, guint16 cmd, guint16 seq) -{ - qq_transaction *trans; - - trans = trans_find(gc, cmd, seq); - if (trans == NULL) { - return NULL; - } - - if (trans->rcved_times == 0) { - trans->scan_times = 0; - } - trans->rcved_times++; - /* server may not get our confirm reply before, send reply again*/ - if (qq_trans_is_server(trans) && (trans->flag & QQ_TRANS_IS_REPLY)) { - if (trans->data != NULL && trans->data_len > 0) { - qq_send_cmd_encrypted(gc, trans->cmd, trans->seq, trans->data, trans->data_len, FALSE); - } - } - return trans; -} - -void qq_trans_add_room_cmd(PurpleConnection *gc, - guint16 seq, guint8 room_cmd, guint32 room_id, guint8 *data, gint data_len, - UPDCLS update_class, guint32 ship32) -{ - qq_data *qd = (qq_data *)gc->proto_data; - qq_transaction *trans = trans_create(gc, qd->fd, QQ_CMD_ROOM, seq, data, data_len, - update_class, ship32); - - trans->room_cmd = room_cmd; - trans->room_id = room_id; - trans->send_retries = qd->resend_times; -#if 0 - purple_debug_info("QQ_TRANS", "Add room cmd, seq %d, data %p, len %d\n", - trans->seq, trans->data, trans->data_len); -#endif - qd->transactions = g_list_append(qd->transactions, trans); -} - -void qq_trans_add_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *rcved, gint rcved_len) -{ - qq_data *qd = (qq_data *)gc->proto_data; - qq_transaction *trans = trans_create(gc, qd->fd, cmd, seq, rcved, rcved_len, QQ_CMD_CLASS_NONE, 0); - - trans->flag = QQ_TRANS_IS_SERVER; - trans->send_retries = 0; - trans->rcved_times = 1; -#if 0 - purple_debug_info("QQ_TRANS", "Add server cmd, seq %d, data %p, len %d\n", - trans->seq, trans->data, trans->data_len); -#endif - qd->transactions = g_list_append(qd->transactions, trans); -} - -void qq_trans_add_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *reply, gint reply_len) -{ - qq_transaction *trans; - - g_return_if_fail(reply != NULL && reply_len > 0); - - trans = trans_find(gc, cmd, seq); - if (trans == NULL) { - return; - } - - g_return_if_fail(trans->flag & QQ_TRANS_IS_SERVER); - trans->flag |= QQ_TRANS_IS_REPLY; - - if (trans->data) g_free(trans->data); - - trans->data = g_memdup(reply, reply_len); - trans->data_len = reply_len; -} - -void qq_trans_add_remain(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *data, gint data_len) -{ - qq_data *qd = (qq_data *)gc->proto_data; - qq_transaction *trans = trans_create(gc, qd->fd, cmd, seq, data, data_len, QQ_CMD_CLASS_NONE, 0); - - trans->flag = QQ_TRANS_IS_SERVER; - trans->flag |= QQ_TRANS_REMAINED; - trans->send_retries = 0; - trans->rcved_times = 1; -#if 1 - purple_debug_info("QQ_TRANS", "Add server cmd and remained, seq %d, data %p, len %d\n", - trans->seq, trans->data, trans->data_len); -#endif - qd->transactions = g_list_append(qd->transactions, trans); -} - -void qq_trans_process_remained(PurpleConnection *gc) -{ - qq_data *qd = (qq_data *)gc->proto_data; - GList *curr; - GList *next; - qq_transaction *trans; - - g_return_if_fail(qd != NULL); - - next = qd->transactions; - while( (curr = next) ) { - next = curr->next; - trans = (qq_transaction *) (curr->data); -#if 0 - purple_debug_info("QQ_TRANS", "Scan [%d]\n", trans->seq); -#endif - if ( !(trans->flag & QQ_TRANS_IS_SERVER) ) { - continue; - } - if ( !(trans->flag & QQ_TRANS_REMAINED) ) { - continue; - } - /* set QQ_TRANS_REMAINED off */ - trans->flag &= ~QQ_TRANS_REMAINED; - -#if 1 - purple_debug_info("QQ_TRANS", - "Process server cmd remained, seq %d, data %p, len %d, send_retries %d\n", - trans->seq, trans->data, trans->data_len, trans->send_retries); -#endif - qq_proc_server_cmd(gc, trans->cmd, trans->seq, trans->data, trans->data_len); - } - - /* purple_debug_info("QQ_TRANS", "Scan finished\n"); */ - return; -} - -gboolean qq_trans_scan(PurpleConnection *gc) -{ - qq_data *qd = (qq_data *)gc->proto_data; - GList *curr; - GList *next; - qq_transaction *trans; - - g_return_val_if_fail(qd != NULL, FALSE); - - next = qd->transactions; - while( (curr = next) ) { - next = curr->next; - trans = (qq_transaction *) (curr->data); - /* purple_debug_info("QQ_TRANS", "Scan [%d]\n", trans->seq); */ - - if (trans->flag & QQ_TRANS_REMAINED) { - /* keep server cmd before login*/ - continue; - } - - trans->scan_times++; - if (trans->scan_times <= 1) { - /* skip in 10 seconds */ - continue; - } - - if (trans->rcved_times > 0) { - /* Has been received */ - trans_remove(gc, trans); - continue; - } - - if (trans->flag & QQ_TRANS_IS_SERVER) { - continue; - } - - /* Never get reply */ - trans->send_retries--; - if (trans->send_retries <= 0) { - purple_debug_warning("QQ_TRANS", - "[%d] %s is lost.\n", - trans->seq, qq_get_cmd_desc(trans->cmd)); - if (trans->flag & QQ_TRANS_IS_IMPORT) { - return TRUE; - } - - qd->net_stat.lost++; - purple_debug_error("QQ_TRANS", - "Lost [%d] %s, data %p, len %d, retries %d\n", - trans->seq, qq_get_cmd_desc(trans->cmd), - trans->data, trans->data_len, trans->send_retries); - trans_remove(gc, trans); - continue; - } - - qd->net_stat.resend++; - purple_debug_warning("QQ_TRANS", - "Resend [%d] %s data %p, len %d, send_retries %d\n", - trans->seq, qq_get_cmd_desc(trans->cmd), - trans->data, trans->data_len, trans->send_retries); - qq_send_cmd_encrypted(gc, trans->cmd, trans->seq, trans->data, trans->data_len, FALSE); - } - - /* purple_debug_info("QQ_TRANS", "Scan finished\n"); */ - return FALSE; -} - -/* clean up send trans and free all contents */ -void qq_trans_remove_all(PurpleConnection *gc) -{ - qq_data *qd = (qq_data *)gc->proto_data; - qq_transaction *trans; - gint count = 0; - - while(qd->transactions != NULL) { - trans = (qq_transaction *) (qd->transactions->data); - qd->transactions = g_list_remove(qd->transactions, trans); - - if (trans->data) g_free(trans->data); - g_free(trans); - - count++; - } - if (count > 0) { - purple_debug_info("QQ_TRANS", "Free all %d packets\n", count); - } -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/qq_trans.h --- a/libpurple/protocols/qq/qq_trans.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -/** - * @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 -#include "qq.h" - -typedef struct _qq_transaction { - guint8 flag; - guint16 seq; - guint16 cmd; - - guint8 room_cmd; - guint32 room_id; - - guint8 *data; - gint data_len; - - gint fd; - gint send_retries; - gint rcved_times; - gint scan_times; - - UPDCLS update_class; - guint32 ship32; -} qq_transaction; - -qq_transaction *qq_trans_find_rcved(PurpleConnection *gc, guint16 cmd, guint16 seq); -gboolean qq_trans_is_server(qq_transaction *trans) ; -gboolean qq_trans_is_dup(qq_transaction *trans); -guint8 qq_trans_get_room_cmd(qq_transaction *trans); -guint32 qq_trans_get_room_id(qq_transaction *trans); -guint32 qq_trans_get_class(qq_transaction *trans); -guint32 qq_trans_get_ship(qq_transaction *trans); - -void qq_trans_add_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *data, gint data_len, UPDCLS update_class, guint32 ship32); -void qq_trans_add_room_cmd(PurpleConnection *gc, - guint16 seq, guint8 room_cmd, guint32 room_id, - guint8 *data, gint data_len, UPDCLS update_class, guint32 ship32); -void qq_trans_add_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *rcved, gint rcved_len); -void qq_trans_add_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *reply, gint reply_len); -void qq_trans_add_remain(PurpleConnection *gc, guint16 cmd, guint16 seq, - guint8 *data, gint data_len); - -void qq_trans_process_remained(PurpleConnection *gc); -gboolean qq_trans_scan(PurpleConnection *gc); -void qq_trans_remove_all(PurpleConnection *gc); - -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/send_file.c --- a/libpurple/protocols/qq/send_file.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,940 +0,0 @@ -/** - * @file send_file.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 "qq.h" - -#include "send_file.h" -#include "debug.h" -#include "network.h" -#include "notify.h" - -#include "buddy_list.h" -#include "file_trans.h" -#include "qq_define.h" -#include "im.h" -#include "qq_base.h" -#include "packet_parse.h" -#include "qq_network.h" -#include "utils.h" - -enum -{ - QQ_FILE_TRANS_REQ = 0x0035, - QQ_FILE_TRANS_ACC_UDP = 0x0037, - QQ_FILE_TRANS_ACC_TCP = 0x0003, - QQ_FILE_TRANS_DENY_UDP = 0x0039, - QQ_FILE_TRANS_DENY_TCP = 0x0005, - QQ_FILE_TRANS_NOTIFY = 0x003b, - QQ_FILE_TRANS_NOTIFY_ACK = 0x003c, - QQ_FILE_TRANS_CANCEL = 0x0049, - QQ_FILE_TRANS_PASV = 0x003f -}; - -static int _qq_in_same_lan(ft_info *info) -{ - if (info->remote_internet_ip == info->local_internet_ip) return 1; - purple_debug_info("QQ", - "Not in the same LAN, remote internet ip[%x], local internet ip[%x]\n", - info->remote_internet_ip - , info->local_internet_ip); - return 0; -} - -static int _qq_xfer_init_udp_channel(ft_info *info) -{ - struct sockaddr_in sin; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - if (!_qq_in_same_lan(info)) { - sin.sin_port = g_htons(info->remote_major_port); - sin.sin_addr.s_addr = g_htonl(info->remote_internet_ip); - } else { - sin.sin_port = g_htons(info->remote_minor_port); - sin.sin_addr.s_addr = g_htonl(info->remote_real_ip); - } - return 0; -} - -/* these 2 functions send and recv buffer from/to UDP channel */ -static ssize_t _qq_xfer_udp_recv(guint8 *buf, size_t len, PurpleXfer *xfer) -{ - struct sockaddr_in sin; - socklen_t sinlen; - ft_info *info; - gint r; - - info = (ft_info *) xfer->data; - sinlen = sizeof(sin); - r = recvfrom(info->recv_fd, buf, len, 0, (struct sockaddr *) &sin, &sinlen); - purple_debug_info("QQ", - "==> recv %d bytes from File UDP Channel, remote ip[%s], remote port[%d]\n", - r, inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); - return r; -} - -/* -static ssize_t _qq_xfer_udp_send(const char *buf, size_t len, PurpleXfer *xfer) -{ - ft_info *info; - - info = (ft_info *) xfer->data; - 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; - ft_info *info; - - info = (ft_info *) xfer->data; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - if (!_qq_in_same_lan(info)) { - sin.sin_port = g_htons(info->remote_major_port); - sin.sin_addr.s_addr = g_htonl(info->remote_internet_ip); - } else if (info->use_major) { - sin.sin_port = g_htons(info->remote_major_port); - sin.sin_addr.s_addr = g_htonl(info->remote_real_ip); - } else { - sin.sin_port = g_htons(info->remote_minor_port); - sin.sin_addr.s_addr = g_htonl(info->remote_real_ip); - } - purple_debug_info("QQ", "sending to channel: %s:%d\n", - inet_ntoa(sin.sin_addr), - (int)g_ntohs(sin.sin_port) - ); - return sendto(info->sender_fd, buf, len, 0, (struct sockaddr *) &sin, sizeof(sin)); -} - -/* user-defined functions for purple_xfer_read and purple_xfer_write */ - -/* -static ssize_t _qq_xfer_read(char **buf, PurpleXfer *xfer) -{ - *buf = g_newa(char, QQ_FILE_FRAGMENT_MAXLEN + 100); - return _qq_xfer_udp_recv(*buf, QQ_FILE_FRAGMENT_MAXLEN + 100, xfer); -} -*/ - -gssize _qq_xfer_write(const guint8 *buf, size_t len, PurpleXfer *xfer) -{ - return _qq_xfer_udp_send(buf, len, xfer); -} - -static void _qq_xfer_recv_packet(gpointer data, gint source, PurpleInputCondition condition) -{ - PurpleXfer *xfer = (PurpleXfer *) data; - PurpleAccount *account = purple_xfer_get_account(xfer); - PurpleConnection *gc = purple_account_get_connection(account); - guint8 *buf; - gint size; - /* FIXME: It seems that the transfer never use a packet - * larger than 1500 bytes, so if it happened to be a - * larger packet, either error occurred or protocol should - * be modified - */ - ft_info *info; - info = xfer->data; - g_return_if_fail (source == info->recv_fd); - buf = g_newa(guint8, 1500); - size = _qq_xfer_udp_recv(buf, 1500, xfer); - qq_process_recv_file(gc, buf, size); -} - -/* start file transfer process */ -/* -static void _qq_xfer_send_start (PurpleXfer *xfer) -{ - PurpleAccount *account; - PurpleConnection *gc; - ft_info *info; - - account = purple_xfer_get_account(xfer); - gc = purple_account_get_connection(account); - info = (ft_info *) xfer->data; -} -*/ - -/* -static void _qq_xfer_send_ack (PurpleXfer *xfer, const char *buffer, size_t len) -{ - PurpleAccount *account; - PurpleConnection *gc; - - account = purple_xfer_get_account(xfer); - gc = purple_account_get_connection(account); - qq_process_recv_file(gc, (guint8 *) buffer, len); -} -*/ - -/* -static void _qq_xfer_recv_start(PurpleXfer *xfer) -{ -} -*/ - -static void _qq_xfer_end(PurpleXfer *xfer) -{ - ft_info *info; - g_return_if_fail(xfer != NULL && xfer->data != NULL); - info = (ft_info *) xfer->data; - - qq_xfer_close_file(xfer); - if (info->dest_fp != NULL) { - fclose(info->dest_fp); - purple_debug_info("QQ", "file closed\n"); - } - if (info->major_fd != 0) { - close(info->major_fd); - purple_debug_info("QQ", "major port closed\n"); - } - if (info->minor_fd != 0) { - close(info->minor_fd); - purple_debug_info("QQ", "minor port closed\n"); - } - /* - if (info->buffer != NULL) { - munmap(info->buffer, purple_xfer_get_size(xfer)); - purple_debug_info("QQ", "file mapping buffer is freed.\n"); - } - */ - g_free(info); -} - -static void qq_show_conn_info(ft_info *info) -{ - gchar *internet_ip_str, *real_ip_str; - guint32 ip; - - ip = g_htonl(info->remote_real_ip); - real_ip_str = gen_ip_str((guint8 *) &ip); - ip = g_htonl(info->remote_internet_ip); - internet_ip_str = gen_ip_str((guint8 *) &ip); - purple_debug_info("QQ", "remote internet ip[%s:%d], major port[%d], real ip[%s], minor port[%d]\n", - internet_ip_str, info->remote_internet_port, - info->remote_major_port, real_ip_str, info->remote_minor_port - ); - g_free(real_ip_str); - g_free(internet_ip_str); -} - -#define QQ_CONN_INFO_LEN 61 -gint qq_get_conn_info(ft_info *info, guint8 *data) -{ - 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, ft_info *info) -{ - gint bytes = 0; - /* 064: connection method, UDP 0x00, TCP 0x03 */ - bytes += qq_put8 (raw_data + bytes, info->conn_method); - /* 065-068: outer ip address of sender (proxy address) */ - bytes += qq_put32 (raw_data + bytes, info->local_internet_ip); - /* 069-070: sender port */ - bytes += qq_put16 (raw_data + bytes, info->local_internet_port); - /* 071-072: the first listening port(TCP doesn't have this part) */ - bytes += qq_put16 (raw_data + bytes, info->local_major_port); - /* 073-076: real ip */ - bytes += qq_put32 (raw_data + bytes, info->local_real_ip); - /* 077-078: the second listening 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, UID to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack) -{ - gint bytes; - time_t now; - guint16 seq; - ft_info *info; - - bytes = 0; - now = time(NULL); - if (!seq_ack) seq = qd->send_seq; - else { - info = (ft_info *) qd->xfer->data; - seq = info->send_seq; - } - - /* 000-003: receiver uid */ - bytes += qq_put32 (raw_data + bytes, qd->uid); - /* 004-007: sender uid */ - bytes += qq_put32 (raw_data + bytes, to_uid); - /* 008-009: sender client version */ - bytes += qq_put16 (raw_data + bytes, qd->client_tag); - /* 010-013: receiver uid */ - bytes += qq_put32 (raw_data + bytes, qd->uid); - /* 014-017: sender uid */ - bytes += qq_put32 (raw_data + bytes, to_uid); - /* 018-033: md5 of (uid+session_key) */ - bytes += qq_putdata (raw_data + bytes, qd->session_md5, 16); - /* 034-035: message type */ - bytes += qq_put16 (raw_data + bytes, message_type); - /* 036-037: sequence number */ - bytes += qq_put16 (raw_data + bytes, seq); - /* 038-041: send time */ - bytes += qq_put32 (raw_data + bytes, (guint32) now); - /* 042-042: always 0x00 */ - bytes += qq_put8 (raw_data + bytes, 0x00); - /* 043-043: sender icon */ - bytes += qq_put8 (raw_data + bytes, qd->my_icon); - /* 044-046: always 0x00 */ - bytes += qq_put16 (raw_data + bytes, 0x0000); - bytes += qq_put8 (raw_data + bytes, 0x00); - /* 047-047: we use font attr */ - bytes += qq_put8 (raw_data + bytes, 0x01); - /* 048-051: always 0x00 */ - bytes += qq_put32 (raw_data + bytes, 0x00000000); - - /* 052-062: always 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 += qq_put8 (raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME */ - - return bytes; -} - -#if 0 -in_addr_t get_real_ip() -{ - char hostname[40]; - struct hostent *host; - - gethostname(hostname, sizeof(hostname)); - host = gethostbyname(hostname); - return *(host->h_addr); -} - - -#include -#include - -#define MAXINTERFACES 16 -in_addr_t get_real_ip() -{ - int fd, intrface, i; - struct ifconf ifc; - struct ifreq buf[MAXINTERFACES]; - in_addr_t ret; - - if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return 0; - ifc.ifc_len = sizeof(buf); - ifc.ifc_buf = (caddr_t) buf; - if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0) return 0; - intrface = ifc.ifc_len / sizeof(struct ifreq); - for (i = 0; i < intrface; i++) { - /* buf[intrface].ifr_name */ - if (ioctl(fd, SIOCGIFADDR, (char *) &buf[i]) >= 0) - { - ret = (((struct sockaddr_in *)(&buf[i].ifr_addr))->sin_addr).s_addr; - if (ret == g_ntohl(0x7f000001)) continue; - return ret; - } - } - return 0; -} -#endif - -static void _qq_xfer_init_socket(PurpleXfer *xfer) -{ - gint sockfd, listen_port = 0, i; - socklen_t sin_len; - struct sockaddr_in sin; - ft_info *info; - - g_return_if_fail(xfer != NULL); - g_return_if_fail(xfer->data != NULL); - info = (ft_info *) xfer->data; - - /* debug - info->local_real_ip = 0x7f000001; - */ - info->local_real_ip = g_ntohl(inet_addr(purple_network_get_my_ip(-1))); - purple_debug_info("QQ", "local real ip is %x\n", info->local_real_ip); - - for (i = 0; i < 2; i++) { - sockfd = socket(PF_INET, SOCK_DGRAM, 0); - g_return_if_fail(sockfd >= 0); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = 0; - sin.sin_addr.s_addr = INADDR_ANY; - sin_len = sizeof(sin); - bind(sockfd, (struct sockaddr *) &sin, sin_len); - getsockname(sockfd, (struct sockaddr *) &sin, &sin_len); - listen_port = g_ntohs(sin.sin_port); - - switch (i) { - case 0: - info->local_major_port = listen_port; - info->major_fd = sockfd; - purple_debug_info("QQ", "UDP Major Channel created on port[%d]\n", - info->local_major_port); - break; - case 1: - info->local_minor_port = listen_port; - info->minor_fd = sockfd; - purple_debug_info("QQ", "UDP Minor Channel created on port[%d]\n", - info->local_minor_port); - break; - } - } - - if (_qq_in_same_lan(info)) { - info->sender_fd = info->recv_fd = info->minor_fd; - } else { - info->sender_fd = info->recv_fd = info->major_fd; - } -/* xfer->watcher = purple_input_add(info->recv_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer); */ -} - -/* create the QQ_FILE_TRANS_REQ packet with file infomations */ -static void _qq_send_packet_file_request (PurpleConnection *gc, UID to_uid, gchar *filename, gint filesize) -{ - qq_data *qd; - guint8 *raw_data; - gchar *filelen_str; - gint filename_len, filelen_strlen, packet_len, bytes; - ft_info *info; - - qd = (qq_data *) gc->proto_data; - - info = g_new0(ft_info, 1); - info->to_uid = to_uid; - info->send_seq = qd->send_seq; - info->local_internet_ip = qd->my_ip.s_addr; - info->local_internet_port = qd->my_port; - info->local_real_ip = 0x00000000; - info->conn_method = 0x00; - qd->xfer->data = info; - - filename_len = strlen(filename); - filelen_str = g_strdup_printf("%d ?ֽ?", filesize); - filelen_strlen = strlen(filelen_str); - - packet_len = 82 + filename_len + filelen_strlen; - raw_data = g_newa(guint8, packet_len); - bytes = 0; - - bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, - QQ_FILE_TRANS_REQ, qd, FALSE); - bytes += qq_fill_conn_info(raw_data + bytes, info); - /* 079: 0x20 */ - bytes += qq_put8 (raw_data + bytes, 0x20); - /* 080: 0x1f */ - bytes += qq_put8 (raw_data + bytes, 0x1f); - /* undetermined len: filename */ - bytes += qq_putdata (raw_data + bytes, (guint8 *) filename, filename_len); - /* 0x1f */ - bytes += qq_put8 (raw_data + bytes, 0x1f); - /* file length */ - bytes += qq_putdata (raw_data + bytes, (guint8 *) filelen_str, filelen_strlen); - - if (packet_len == bytes) - qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes); - else - purple_debug_info("qq_send_packet_file_request", - "%d bytes expected but got %d bytes\n", - packet_len, bytes); - - g_free (filelen_str); -} - -/* tell the buddy we want to accept the file */ -static void _qq_send_packet_file_accept(PurpleConnection *gc, UID to_uid) -{ - qq_data *qd; - guint8 *raw_data; - guint16 minor_port; - guint32 real_ip; - gint packet_len, bytes; - ft_info *info; - - qd = (qq_data *) gc->proto_data; - info = (ft_info *) qd->xfer->data; - - purple_debug_info("QQ", "I've accepted the file transfer request from %d\n", to_uid); - _qq_xfer_init_socket(qd->xfer); - - packet_len = 79; - raw_data = g_newa (guint8, packet_len); - 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 + 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, raw_data, bytes); - else - purple_debug_info("qq_send_packet_file_accept", - "%d bytes expected but got %d bytes\n", - packet_len, bytes); -} - -static void _qq_send_packet_file_notifyip(PurpleConnection *gc, UID to_uid) -{ - PurpleXfer *xfer; - ft_info *info; - qq_data *qd; - guint8 *raw_data; - gint packet_len, bytes; - - qd = (qq_data *) gc->proto_data; - xfer = qd->xfer; - info = xfer->data; - - packet_len = 79; - raw_data = g_newa (guint8, packet_len); - bytes = 0; - - purple_debug_info("QQ", "<== sending qq file notify ip packet\n"); - 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, raw_data, bytes); - else - purple_debug_info("qq_send_packet_file_notify", - "%d bytes expected but got %d bytes\n", - packet_len, bytes); - - if (xfer->watcher) purple_input_remove(xfer->watcher); - xfer->watcher = purple_input_add(info->recv_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer); - purple_input_add(info->major_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer); -} - -/* tell the buddy we don't want the file */ -static void _qq_send_packet_file_reject (PurpleConnection *gc, UID to_uid) -{ - qq_data *qd; - guint8 *raw_data; - gint packet_len, bytes; - - purple_debug_info("_qq_send_packet_file_reject", "start\n"); - qd = (qq_data *) gc->proto_data; - - packet_len = 64; - raw_data = g_newa (guint8, packet_len); - bytes = 0; - - 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, raw_data, bytes); - else - purple_debug_info("qq_send_packet_file", - "%d bytes expected but got %d bytes\n", - packet_len, bytes); -} - -/* tell the buddy to cancel transfer */ -static void _qq_send_packet_file_cancel (PurpleConnection *gc, UID to_uid) -{ - qq_data *qd; - guint8 *raw_data; - gint packet_len, bytes; - - purple_debug_info("_qq_send_packet_file_cancel", "start\n"); - qd = (qq_data *) gc->proto_data; - - packet_len = 64; - raw_data = g_newa (guint8, packet_len); - bytes = 0; - - purple_debug_info("_qq_send_packet_file_cancel", "before create header\n"); - bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE); - purple_debug_info("_qq_send_packet_file_cancel", "end create header\n"); - - if (packet_len == bytes) { - purple_debug_info("_qq_send_packet_file_cancel", "before send cmd\n"); - qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes); - } - else - purple_debug_info("qq_send_packet_file", - "%d bytes expected but got %d bytes\n", - packet_len, bytes); - - purple_debug_info("qq_send_packet_file_cancel", "end\n"); -} - -/* request to send a file */ -static void -_qq_xfer_init (PurpleXfer * xfer) -{ - PurpleConnection *gc; - PurpleAccount *account; - UID to_uid; - const gchar *filename; - gchar *base_filename; - - g_return_if_fail (xfer != NULL); - account = purple_xfer_get_account(xfer); - gc = purple_account_get_connection(account); - - to_uid = purple_name_to_uid (xfer->who); - g_return_if_fail (to_uid != 0); - - filename = purple_xfer_get_local_filename (xfer); - g_return_if_fail (filename != NULL); - - base_filename = g_path_get_basename(filename); - - _qq_send_packet_file_request (gc, to_uid, base_filename, - purple_xfer_get_size(xfer)); - g_free(base_filename); -} - -/* cancel the transfer of receiving files */ -static void _qq_xfer_cancel(PurpleXfer *xfer) -{ - PurpleConnection *gc; - PurpleAccount *account; - - g_return_if_fail (xfer != NULL); - account = purple_xfer_get_account(xfer); - gc = purple_account_get_connection(account); - - switch (purple_xfer_get_status(xfer)) { - case PURPLE_XFER_STATUS_CANCEL_LOCAL: - _qq_send_packet_file_cancel(gc, purple_name_to_uid(xfer->who)); - break; - case PURPLE_XFER_STATUS_CANCEL_REMOTE: - _qq_send_packet_file_cancel(gc, purple_name_to_uid(xfer->who)); - break; - case PURPLE_XFER_STATUS_NOT_STARTED: - break; - case PURPLE_XFER_STATUS_UNKNOWN: - _qq_send_packet_file_reject(gc, purple_name_to_uid(xfer->who)); - break; - case PURPLE_XFER_STATUS_DONE: - break; - case PURPLE_XFER_STATUS_ACCEPTED: - break; - case PURPLE_XFER_STATUS_STARTED: - break; - } -} - -/* init the transfer of receiving files */ -static void _qq_xfer_recv_init(PurpleXfer *xfer) -{ - PurpleConnection *gc; - PurpleAccount *account; - - g_return_if_fail(xfer != NULL); - account = purple_xfer_get_account(xfer); - gc = purple_account_get_connection(account); - - _qq_send_packet_file_accept(gc, purple_name_to_uid(xfer->who)); -} - -/* process reject im for file transfer request */ -void qq_process_recv_file_reject (guint8 *data, gint data_len, - UID sender_uid, PurpleConnection *gc) -{ - gchar *msg, *filename; - qq_data *qd; - - g_return_if_fail (data != NULL && data_len != 0); - 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_warning("QQ", - "Received file reject message is empty\n"); - return; - } - */ - filename = g_path_get_basename(purple_xfer_get_local_filename(qd->xfer)); - msg = g_strdup_printf(_("%d has declined the file %s"), - sender_uid, filename); - - purple_notify_warning (gc, _("File Send"), msg, NULL); - purple_xfer_request_denied(qd->xfer); - qd->xfer = NULL; - - g_free(filename); - g_free(msg); -} - -/* process cancel im for file transfer request */ -void qq_process_recv_file_cancel (guint8 *data, gint data_len, - UID sender_uid, PurpleConnection *gc) -{ - gchar *msg, *filename; - qq_data *qd; - - g_return_if_fail (data != NULL && data_len != 0); - qd = (qq_data *) gc->proto_data; - 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_warning("QQ", "Received file reject message is empty\n"); - return; - } - */ - filename = g_path_get_basename(purple_xfer_get_local_filename(qd->xfer)); - msg = g_strdup_printf - (_("%d cancelled the transfer of %s"), - sender_uid, filename); - - purple_notify_warning (gc, _("File Send"), msg, NULL); - purple_xfer_cancel_remote(qd->xfer); - qd->xfer = NULL; - - g_free(filename); - g_free(msg); -} - -/* process accept im for file transfer request */ -void qq_process_recv_file_accept(guint8 *data, gint data_len, UID 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 *) xfer->data; - - if (data_len <= 30 + QQ_CONN_INFO_LEN) { - purple_debug_warning("QQ", "Received file reject message is empty\n"); - return; - } - - bytes = 18 + 12; /* skip 30 bytes */ - qq_get_conn_info(info, data + bytes); - _qq_xfer_init_socket(xfer); - - _qq_xfer_init_udp_channel(info); - _qq_send_packet_file_notifyip(gc, sender_uid); -} - -/* process request from buddy's im for file transfer request */ -void qq_process_recv_file_request(guint8 *data, gint data_len, UID sender_uid, PurpleConnection * gc) -{ - qq_data *qd; - PurpleXfer *xfer; - gchar *sender_name, **fileinfo; - ft_info *info; - PurpleBuddy *b; - qq_buddy_data *bd; - gint bytes; - - g_return_if_fail (data != NULL && data_len != 0); - qd = (qq_data *) gc->proto_data; - - info = g_newa(ft_info, 1); - info->local_internet_ip = qd->my_ip.s_addr; - info->local_internet_port = qd->my_port; - info->local_real_ip = 0x00000000; - info->to_uid = sender_uid; - - if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) { - purple_debug_warning("QQ", "Received file request message is empty\n"); - return; - } - bytes = 0; - bytes += qq_get16(&(info->send_seq), data + bytes); - - 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); - - sender_name = uid_to_purple_name(sender_uid); - - /* FACE from IP detector, ignored by gfhuang */ - if(g_ascii_strcasecmp(fileinfo[0], "FACE") == 0) { - purple_debug_warning("QQ", - "Received a FACE ip detect from %d, so he/she must be online :)\n", sender_uid); - - b = purple_find_buddy(gc->account, sender_name); - bd = (b == NULL) ? NULL : purple_buddy_get_protocol_data(b); - if (bd) { - if(0 != info->remote_real_ip) { - g_memmove(&(bd->ip), &info->remote_real_ip, sizeof(bd->ip)); - bd->port = info->remote_minor_port; - } - else if (0 != info->remote_internet_ip) { - g_memmove(&(bd->ip), &info->remote_internet_ip, sizeof(bd->ip)); - bd->port = info->remote_major_port; - } - - if(!is_online(bd->status)) { - bd->status = QQ_BUDDY_ONLINE_INVISIBLE; - bd->last_update = time(NULL); - qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag); - } - else - purple_debug_info("QQ", "buddy %d is already online\n", sender_uid); - - } - else - purple_debug_warning("QQ", "buddy %d is not in list\n", sender_uid); - - g_free(sender_name); - g_strfreev(fileinfo); - return; - } - - xfer = purple_xfer_new(purple_connection_get_account(gc), - PURPLE_XFER_RECEIVE, - sender_name); - if (xfer) - { - purple_xfer_set_filename(xfer, fileinfo[0]); - purple_xfer_set_size(xfer, atoi(fileinfo[1])); - - purple_xfer_set_init_fnc(xfer, _qq_xfer_recv_init); - purple_xfer_set_request_denied_fnc(xfer, _qq_xfer_cancel); - purple_xfer_set_cancel_recv_fnc(xfer, _qq_xfer_cancel); - purple_xfer_set_end_fnc(xfer, _qq_xfer_end); - purple_xfer_set_write_fnc(xfer, _qq_xfer_write); - - xfer->data = info; - qd->xfer = xfer; - - purple_xfer_request(xfer); - } - - g_free(sender_name); - g_strfreev(fileinfo); -} - -static void _qq_xfer_send_notify_ip_ack(gpointer data, gint source, PurpleInputCondition cond) -{ - PurpleXfer *xfer = (PurpleXfer *) data; - PurpleAccount *account = purple_xfer_get_account(xfer); - PurpleConnection *gc = purple_account_get_connection(account); - ft_info *info = (ft_info *) xfer->data; - - purple_input_remove(xfer->watcher); - xfer->watcher = purple_input_add(info->recv_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer); - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_NOTIFY_IP_ACK, info->to_uid, 0); - /* - info->use_major = TRUE; - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_NOTIFY_IP_ACK, info->to_uid, 0); - info->use_major = FALSE; - */ -} - -void qq_process_recv_file_notify(guint8 *data, gint data_len, - UID sender_uid, PurpleConnection *gc) -{ - gint bytes; - qq_data *qd; - 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 (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) { - purple_debug_warning("QQ", "Received file notify message is empty\n"); - return; - } - - bytes = 0; - bytes += qq_get16(&(info->send_seq), data + bytes); - - bytes += 18 + 12; - bytes += qq_get_conn_info(info, data + bytes); - - _qq_xfer_init_udp_channel(info); - - xfer->watcher = purple_input_add(info->sender_fd, PURPLE_INPUT_WRITE, _qq_xfer_send_notify_ip_ack, xfer); -} - -/* temp placeholder until a working function can be implemented */ -gboolean qq_can_receive_file(PurpleConnection *gc, const char *who) -{ - return TRUE; -} - -void qq_send_file(PurpleConnection *gc, const char *who, const char *file) -{ - qq_data *qd; - PurpleXfer *xfer; - - qd = (qq_data *) gc->proto_data; - - xfer = purple_xfer_new (gc->account, PURPLE_XFER_SEND, - who); - if (xfer) - { - purple_xfer_set_init_fnc (xfer, _qq_xfer_init); - purple_xfer_set_cancel_send_fnc (xfer, _qq_xfer_cancel); - purple_xfer_set_write_fnc(xfer, _qq_xfer_write); - - qd->xfer = xfer; - purple_xfer_request(xfer); - } -} - -/* -static void qq_send_packet_request_key(PurpleConnection *gc, guint8 key) -{ - qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, &key, 1); -} - -static void qq_process_recv_request_key(PurpleConnection *gc) -{ -} -*/ diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/send_file.h --- a/libpurple/protocols/qq/send_file.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/** - * @file send_file.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_QQ_SEND_FILE_H_ -#define _QQ_QQ_SEND_FILE_H_ - -#include "ft.h" -#include "qq.h" - -typedef struct _ft_info { - UID to_uid; - guint16 send_seq; - guint8 file_session_key[QQ_KEY_LENGTH]; - guint8 conn_method; - guint32 remote_internet_ip; - guint16 remote_internet_port; - guint16 remote_major_port; - guint32 remote_real_ip; - guint16 remote_minor_port; - guint32 local_internet_ip; - guint16 local_internet_port; - guint16 local_major_port; - guint32 local_real_ip; - guint16 local_minor_port; - /* we use these to control the packets sent or received */ - guint32 fragment_num; - guint32 fragment_len; - /* The max index of sending/receiving fragment - * for sender, it is the lower bolder of a slide window for sending - * for receiver, it seems that packets having a fragment index lower - * than max_fragment_index have been received already - */ - guint32 max_fragment_index; - guint32 window; - - /* It seems that using xfer's function is not enough for our - * transfer module. So I will use our own structure instead - * of xfer provided - */ - int major_fd; - int minor_fd; - int sender_fd; - int recv_fd; - FILE *dest_fp; - /* guint8 *buffer; */ - gboolean use_major; -} ft_info; - -void qq_process_recv_file_accept(guint8 *data, gint data_len, UID sender_uid, PurpleConnection *gc); -void qq_process_recv_file_reject(guint8 *data, gint data_len, UID sender_uid, PurpleConnection *gc); -void qq_process_recv_file_cancel(guint8 *data, gint data_len, UID sender_uid, PurpleConnection *gc); -void qq_process_recv_file_request(guint8 *data, gint data_len, UID sender_uid, PurpleConnection *gc); -void qq_process_recv_file_notify(guint8 *data, gint data_len, UID 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); -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 diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/utils.c --- a/libpurple/protocols/qq/utils.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,359 +0,0 @@ -/** - * @file utils.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 "limits.h" -#include "stdlib.h" -#include "string.h" - -#ifdef _WIN32 -#include "win32dep.h" -#endif - -#include "cipher.h" - -#include "char_conv.h" -#include "debug.h" -#include "prefs.h" -#include "qq.h" -#include "util.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_info(desc, "%s:%d\n", - inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); - } - */ - -void qq_get_md5(guint8 *md5, gint md5_len, const guint8* const src, gint src_len) -{ - PurpleCipher *cipher; - PurpleCipherContext *context; - - g_return_if_fail(md5 != NULL && md5_len > 0); - g_return_if_fail(src != NULL && src_len > 0); - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, src, src_len); - purple_cipher_context_digest(context, md5_len, md5, NULL); - purple_cipher_context_destroy(context); -} - -gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount) -{ - gint index; - - index = atoi(index_str); - if (index < 0 || index >= amount) - index = 0; - - return array[index]; -} - -gchar *get_index_str_by_name(gchar **array, const gchar *name, gint amount) -{ - gint index; - - for (index = 0; index <= amount; index++) - if (g_ascii_strcasecmp(array[index], name) == 0) - break; - - if (index >= amount) - index = 0; /* meaning no match */ - return g_strdup_printf("%d", index); -} - -/* split the given data(len) with delimit, - * check the number of field matches the expected_fields (<=0 means all) - * return gchar* array (needs to be freed by g_strfreev later), or NULL */ -gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_fields) -{ - guint8 *input; - gchar **segments, **seg; - gint count = 0, j; - - g_return_val_if_fail(data != NULL && len != 0 && delimit != 0, NULL); - - /* as the last field would be string, but data is not ended with 0x00 - * we have to duplicate the data and append a 0x00 at the end */ - input = g_newa(guint8, len + 1); - g_memmove(input, data, len); - input[len] = 0x00; - - segments = g_strsplit((gchar *) input, delimit, 0); - if (expected_fields <= 0) - return segments; - - for (seg = segments; *seg != NULL; seg++) - count++; - if (count < expected_fields) { /* not enough fields */ - purple_debug_error("QQ", "Less fields %d then %d\n", count, expected_fields); - return NULL; - } else if (count > expected_fields) { /* more fields, OK */ - purple_debug_warning("QQ", "More fields %d than %d\n", count, expected_fields); - /* free up those not used */ - for (j = expected_fields; j < count; j++) { - purple_debug_warning("QQ", "field[%d] is %s\n", j, segments[j]); - g_free(segments[j]); - } - segments[expected_fields] = NULL; - } - - return segments; -} - -/* convert Purple name to original QQ UID */ -UID purple_name_to_uid(const gchar *const name) -{ - UID ret; - g_return_val_if_fail(name != NULL, 0); - - ret = strtoul(name, NULL, 10); - if (errno == ERANGE) - return 0; - else - return ret; -} - -gchar *gen_ip_str(guint8 *ip) { - gchar *ret; - if (ip == NULL || ip[0] == 0) { - ret = g_new(gchar, 1); - *ret = '\0'; - return ret; - } else { - return g_strdup_printf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); - } -} - -guint8 *str_ip_gen(gchar *str) { - guint8 *ip = g_new(guint8, 4); - gint a, b, c, d; - - sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d); - ip[0] = a; - ip[1] = b; - ip[2] = c; - ip[3] = d; - return ip; -} - -/* convert a QQ UID to a unique name of Purple - * the return needs to be freed */ -gchar *uid_to_purple_name(UID uid) -{ - return g_strdup_printf("%u", uid); -} - -/* try to dump the data as GBK */ -gchar* try_dump_as_gbk(const guint8 *const data, gint len) -{ - gint i; - guint8 *incoming; - gchar *msg_utf8; - - incoming = g_newa(guint8, len + 1); - g_memmove(incoming, data, len); - incoming[len] = 0x00; - /* GBK code: - * Single-byte ASCII: 0x21-0x7E - * GBK first byte range: 0x81-0xFE - * GBK second byte range: 0x40-0x7E and 0x80-0xFE */ - for (i = 0; i < len; i++) - if (incoming[i] >= 0x81) - break; - - msg_utf8 = i < len ? qq_to_utf8((gchar *) &incoming[i], QQ_CHARSET_DEFAULT) : NULL; - - if (msg_utf8 != NULL) { - purple_debug_warning("QQ", "Try extract GB msg: %s\n", msg_utf8); - } - return msg_utf8; -} - -/* strips whitespace */ -static gchar *strstrip(const gchar *const buffer) -{ - GString *stripped; - gchar *ret, cur; - gint i; - - g_return_val_if_fail(buffer != NULL, NULL); - - stripped = g_string_new(""); - for (i=0; istr; - g_string_free(stripped, FALSE); - - return ret; -} - -/* Attempts to dump an ASCII hex string to a string of bytes. - * The return should be freed later. */ -guint8 *hex_str_to_bytes(const gchar *const buffer, gint *out_len) -{ - gchar *hex_str, *hex_buffer, *cursor; - gchar tmp[2]; - guint8 *bytes, nibble1, nibble2; - gint index; - - g_return_val_if_fail(buffer != NULL, NULL); - - hex_buffer = strstrip(buffer); - - if (strlen(hex_buffer) % 2 != 0) { - purple_debug_warning("QQ", - "Unable to convert an odd number of nibbles to a string of bytes!\n"); - g_free(hex_buffer); - return NULL; - } - bytes = g_newa(guint8, strlen(hex_buffer) / 2); - hex_str = g_ascii_strdown(hex_buffer, -1); - g_free(hex_buffer); - index = 0; - for (cursor = hex_str; cursor < hex_str + sizeof(gchar) * (strlen(hex_str)) - 1; cursor++) { - if (g_ascii_isdigit(*cursor)) { - tmp[0] = *cursor; - tmp[1] = '\0'; - nibble1 = atoi(tmp); - } else if (g_ascii_isalpha(*cursor) && (gint) *cursor - 87 < 16) { - nibble1 = (gint) *cursor - 87; - } else { - purple_debug_warning("QQ", "Invalid char \'%c\' found in hex string!\n", - *cursor); - g_free(hex_str); - return NULL; - } - nibble1 = nibble1 << 4; - cursor++; - if (g_ascii_isdigit(*cursor)) { - tmp[0] = *cursor; - tmp[1] = '\0'; - nibble2 = atoi(tmp); - } else if (g_ascii_isalpha(*cursor) && (gint) (*cursor - 87) < 16) { - nibble2 = (gint) *cursor - 87; - } else { - purple_debug_warning("QQ", "Invalid char found in hex string!\n"); - g_free(hex_str); - return NULL; - } - bytes[index++] = nibble1 + nibble2; - } - *out_len = strlen(hex_str) / 2; - g_free(hex_str); - return g_memdup(bytes, *out_len); -} - -/* Dumps a chunk of raw data into an ASCII hex string. - * The return should be freed later. */ -static gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes) -{ - GString *str; - gchar *ret; - gint i, j, ch; - - str = g_string_new(""); - for (i = 0; i < bytes; i += 16) { - /* length label */ - g_string_append_printf(str, "%07x: ", i); - - /* dump hex value */ - for (j = 0; j < 16; j++) - if ((i + j) < bytes) - g_string_append_printf(str, " %02x", buffer[i + j]); - else - g_string_append(str, " --"); - g_string_append(str, " "); - - /* dump ascii value */ - for (j = 0; j < 16 && (i + j) < bytes; j++) { - ch = buffer[i + j] & 127; - if (ch < ' ' || ch == 127) - g_string_append_c(str, '.'); - else - g_string_append_c(str, ch); - } - g_string_append_c(str, '\n'); - } - - ret = str->str; - /* GString can be freed without freeing it character data */ - g_string_free(str, FALSE); - - 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, "%s", 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) -{ - qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", buf, len, desc); -} - -void qq_filter_str(gchar *str) { - gchar *temp; - if (str == NULL) { - return; - } - - for (temp = str; *temp != 0; temp++) { - /*if (*temp == '\r' || *temp == '\n') *temp = ' ';*/ - if (*temp > 0 && *temp < 0x20) *temp = ' '; - } - g_strstrip(str); -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/qq/utils.h --- a/libpurple/protocols/qq/utils.h Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * @file utils.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_MY_UTILS_H_ -#define _QQ_MY_UTILS_H_ - -#include -#include -#include "qq.h" - -#include "debug.h" - -void qq_get_md5(guint8 *md5, gint md5_len, const guint8* const src, gint src_len); - -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); - -gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_fields); - -gchar *gen_ip_str(guint8 *ip); -guint8 *str_ip_gen(gchar *str); - -UID purple_name_to_uid(const gchar *name); -gchar *uid_to_purple_name(UID uid); - -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); - -void qq_filter_str(gchar *str); -#endif diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/simple/simple.c --- a/libpurple/protocols/simple/simple.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/simple/simple.c Mon May 02 03:42:24 2011 +0000 @@ -1903,7 +1903,8 @@ } else { /* UDP */ purple_debug_info("simple", "using udp with server %s and port %d\n", hostname, port); - sip->query_data = purple_dnsquery_a(hostname, port, simple_udp_host_resolved, sip); + sip->query_data = purple_dnsquery_a_account(sip->account, hostname, + port, simple_udp_host_resolved, sip); if (sip->query_data == NULL) { purple_connection_error_reason(sip->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, @@ -1965,7 +1966,7 @@ hosttoconnect = purple_account_get_string(account, "proxy", sip->servername); } - sip->srv_query_data = purple_srv_resolve("sip", + sip->srv_query_data = purple_srv_resolve_account(account, "sip", sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip); } diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/protocols/yahoo/yahoo_filexfer.c --- a/libpurple/protocols/yahoo/yahoo_filexfer.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Mon May 02 03:42:24 2011 +0000 @@ -1660,6 +1660,7 @@ } if(val_222 == 3) { + PurpleAccount *account; xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xfer_peer_idstring); if(!xfer) @@ -1679,14 +1680,17 @@ return; } + account = purple_connection_get_account(gc); if (yd->jp) { - purple_dnsquery_a(YAHOOJP_XFER_RELAY_HOST, YAHOOJP_XFER_RELAY_PORT, + purple_dnsquery_a_account(account, YAHOOJP_XFER_RELAY_HOST, + YAHOOJP_XFER_RELAY_PORT, yahoo_xfer_dns_connected_15, xfer); } else { - purple_dnsquery_a(YAHOO_XFER_RELAY_HOST, YAHOO_XFER_RELAY_PORT, + purple_dnsquery_a_account(account, YAHOO_XFER_RELAY_HOST, + YAHOO_XFER_RELAY_PORT, yahoo_xfer_dns_connected_15, xfer); } return; @@ -1784,7 +1788,6 @@ char *xfer_idstring_for_relay = NULL; GSList *l; struct yahoo_packet *pkt_to_send; - PurpleAccount *account; struct yahoo_p2p_data *p2p_data; yd = gc->proto_data; @@ -1838,7 +1841,8 @@ xfer_data->info_val_249 = val_249; xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay); - if(val_249 == 1 || val_249 == 3) { + if(val_249 == 1 || val_249 == 3) { + PurpleAccount *account; if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) { purple_xfer_cancel_remote(xfer); return; diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/proxy.c --- a/libpurple/proxy.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/proxy.c Mon May 02 03:42:24 2011 +0000 @@ -69,6 +69,7 @@ guchar *read_buffer; gsize read_buf_len; gsize read_len; + PurpleAccount *account; }; static const char * const socks5errors[] = { @@ -1367,7 +1368,8 @@ proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE); } else { - connect_data->query_data = purple_dnsquery_a(connect_data->host, + connect_data->query_data = purple_dnsquery_a_account( + connect_data->account, connect_data->host, connect_data->port, s4_host_resolved, connect_data); if (connect_data->query_data == NULL) { @@ -2137,6 +2139,7 @@ break; case PURPLE_PROXY_SOCKS5: + case PURPLE_PROXY_TOR: proxy_connect_socks5(connect_data, addr, addrlen); break; @@ -2281,6 +2284,7 @@ connect_data->host = g_strdup(host); connect_data->port = port; connect_data->gpi = purple_proxy_get_setup(account); + connect_data->account = account; if ((purple_proxy_info_get_type(connect_data->gpi) != PURPLE_PROXY_NONE) && (purple_proxy_info_get_host(connect_data->gpi) == NULL || @@ -2299,6 +2303,7 @@ case PURPLE_PROXY_HTTP: case PURPLE_PROXY_SOCKS4: case PURPLE_PROXY_SOCKS5: + case PURPLE_PROXY_TOR: case PURPLE_PROXY_USE_ENVVAR: connecthost = purple_proxy_info_get_host(connect_data->gpi); connectport = purple_proxy_info_get_port(connect_data->gpi); @@ -2311,7 +2316,7 @@ return NULL; } - connect_data->query_data = purple_dnsquery_a(connecthost, + connect_data->query_data = purple_dnsquery_a_account(account, connecthost, connectport, connection_host_resolved, connect_data); if (connect_data->query_data == NULL) { @@ -2347,6 +2352,7 @@ connect_data->host = g_strdup(host); connect_data->port = port; connect_data->gpi = purple_proxy_get_setup(account); + connect_data->account = account; if ((purple_proxy_info_get_type(connect_data->gpi) != PURPLE_PROXY_NONE) && (purple_proxy_info_get_host(connect_data->gpi) == NULL || @@ -2365,6 +2371,7 @@ case PURPLE_PROXY_HTTP: case PURPLE_PROXY_SOCKS4: case PURPLE_PROXY_SOCKS5: + case PURPLE_PROXY_TOR: case PURPLE_PROXY_USE_ENVVAR: purple_debug_info("proxy", "Ignoring Proxy type (%d) for UDP.\n", purple_proxy_info_get_type(connect_data->gpi)); @@ -2377,7 +2384,7 @@ return NULL; } - connect_data->query_data = purple_dnsquery_a(connecthost, + connect_data->query_data = purple_dnsquery_a_account(account, connecthost, connectport, connection_host_resolved, connect_data); if (connect_data->query_data == NULL) { @@ -2399,6 +2406,19 @@ PurpleProxyConnectFunction connect_cb, gpointer data) { + return purple_proxy_connect_socks5_account(NULL, handle, gpi, + host, port, connect_cb, data); +} +/* + * Combine some of this code with purple_proxy_connect() + */ +PurpleProxyConnectData * +purple_proxy_connect_socks5_account(void *handle, PurpleAccount *account, + PurpleProxyInfo *gpi, + const char *host, int port, + PurpleProxyConnectFunction connect_cb, + gpointer data) +{ PurpleProxyConnectData *connect_data; g_return_val_if_fail(host != NULL, NULL); @@ -2414,9 +2434,11 @@ connect_data->host = g_strdup(host); connect_data->port = port; connect_data->gpi = gpi; + connect_data->account = account; connect_data->query_data = - purple_dnsquery_a(purple_proxy_info_get_host(gpi), + purple_dnsquery_a_account(account, + purple_proxy_info_get_host(gpi), purple_proxy_info_get_port(gpi), connection_host_resolved, connect_data); if (connect_data->query_data == NULL) @@ -2470,6 +2492,8 @@ proxytype = PURPLE_PROXY_SOCKS4; else if (purple_strequal(type, "socks5")) proxytype = PURPLE_PROXY_SOCKS5; + else if (purple_strequal(type, "tor")) + proxytype = PURPLE_PROXY_TOR; else if (purple_strequal(type, "envvar")) proxytype = PURPLE_PROXY_USE_ENVVAR; else diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/proxy.h --- a/libpurple/proxy.h Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/proxy.h Mon May 02 03:42:24 2011 +0000 @@ -39,7 +39,8 @@ PURPLE_PROXY_HTTP, /**< HTTP proxy. */ PURPLE_PROXY_SOCKS4, /**< SOCKS 4 proxy. */ PURPLE_PROXY_SOCKS5, /**< SOCKS 5 proxy. */ - PURPLE_PROXY_USE_ENVVAR /**< Use environmental settings. */ + PURPLE_PROXY_USE_ENVVAR, /**< Use environmental settings. */ + PURPLE_PROXY_TOR /**< Use a Tor proxy (SOCKS 5 really) */ } PurpleProxyType; @@ -306,10 +307,39 @@ * opaque data structure that can be used to cancel * the pending connection, if needed. */ +PurpleProxyConnectData *purple_proxy_connect_socks5_account(void *handle, + PurpleAccount *account, PurpleProxyInfo *gpi, + const char *host, int port, + PurpleProxyConnectFunction connect_cb, gpointer data); + +#ifndef PURPLE_DISABLE_DEPRECATED +/** + * Makes a connection through a SOCKS5 proxy. + * + * @param handle A handle that should be associated with this + * connection attempt. The handle can be used + * to cancel the connection attempt using the + * purple_proxy_connect_cancel_with_handle() + * function. + * @param gpi The PurpleProxyInfo specifying the proxy settings + * @param host The destination host. + * @param port The destination port. + * @param connect_cb The function to call when the connection is + * established. If the connection failed then + * fd will be -1 and error message will be set + * to something descriptive (hopefully). + * @param data User-defined data. + * + * @return NULL if there was an error, or a reference to an + * opaque data structure that can be used to cancel + * the pending connection, if needed. + * @deprecated Use purple_proxy_connect_socks5_account instead + */ PurpleProxyConnectData *purple_proxy_connect_socks5(void *handle, PurpleProxyInfo *gpi, const char *host, int port, PurpleProxyConnectFunction connect_cb, gpointer data); +#endif /** * Cancel an in-progress connection attempt. This should be called diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/signals.c --- a/libpurple/signals.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/signals.c Mon May 02 03:42:24 2011 +0000 @@ -880,6 +880,20 @@ } void +purple_marshal_BOOLEAN__POINTER_BOOLEAN(PurpleCallback cb, va_list args, + void *data, void **return_val) +{ + gboolean ret_val; + void *arg1 = va_arg(args, void *); + gboolean arg2 = va_arg(args, gboolean); + + ret_val = ((gboolean (*)(void *, gboolean, void *))cb)(arg1, arg2, data); + + if (return_val != NULL) + *return_val = GINT_TO_POINTER(ret_val); +} + +void purple_marshal_BOOLEAN__POINTER_POINTER_POINTER(PurpleCallback cb, va_list args, void *data, void **return_val) { diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/signals.h --- a/libpurple/signals.h Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/signals.h Mon May 02 03:42:24 2011 +0000 @@ -339,6 +339,8 @@ PurpleCallback cb, va_list args, void *data, void **return_val); void purple_marshal_BOOLEAN__POINTER_POINTER( PurpleCallback cb, va_list args, void *data, void **return_val); +void purple_marshal_BOOLEAN__POINTER_BOOLEAN( + PurpleCallback cb, va_list args, void *data, void **return_val); void purple_marshal_BOOLEAN__POINTER_POINTER_POINTER( PurpleCallback cb, va_list args, void *data, void **return_val); void purple_marshal_BOOLEAN__POINTER_POINTER_UINT( diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/stun.c --- a/libpurple/stun.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/stun.c Mon May 02 03:42:24 2011 +0000 @@ -365,7 +365,7 @@ purple_debug_info("stun", "got %d SRV responses, server: %s, port: %d\n", results, servername, port); - purple_dnsquery_a(servername, port, hbn_cb, NULL); + purple_dnsquery_a_account(NULL, servername, port, hbn_cb, NULL); g_free(resp); } @@ -424,7 +424,7 @@ nattype.servername = g_strdup(servername); callbacks = g_slist_append(callbacks, cb); - purple_srv_resolve("stun", "udp", servername, do_test1, + purple_srv_resolve_account(NULL, "stun", "udp", servername, do_test1, (gpointer) servername); return &nattype; diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/tests/Makefile.am --- a/libpurple/tests/Makefile.am Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/tests/Makefile.am Mon May 02 03:42:24 2011 +0000 @@ -15,7 +15,6 @@ test_jabber_jutil.c \ test_jabber_scram.c \ test_oscar_util.c \ - test_qq.c \ test_yahoo_util.c \ test_util.c \ $(top_builddir)/libpurple/util.h @@ -32,7 +31,6 @@ check_libpurple_LDADD=\ $(top_builddir)/libpurple/protocols/jabber/libjabber.la \ $(top_builddir)/libpurple/protocols/oscar/liboscar.la \ - $(top_builddir)/libpurple/protocols/qq/libqq_tmp.la \ $(top_builddir)/libpurple/protocols/yahoo/libymsg.la \ $(top_builddir)/libpurple/libpurple.la \ @CHECK_LIBS@ \ diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/tests/check_libpurple.c --- a/libpurple/tests/check_libpurple.c Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/tests/check_libpurple.c Mon May 02 03:42:24 2011 +0000 @@ -89,7 +89,6 @@ srunner_add_suite(sr, jabber_jutil_suite()); srunner_add_suite(sr, jabber_scram_suite()); srunner_add_suite(sr, oscar_util_suite()); - srunner_add_suite(sr, qq_suite()); srunner_add_suite(sr, yahoo_util_suite()); srunner_add_suite(sr, util_suite()); diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/tests/test_qq.c --- a/libpurple/tests/test_qq.c Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -#include - -#include "tests.h" -#include "../protocols/qq/qq_crypt.h" - -START_TEST(test_qq_encrypt) -{ - const guint8 * const key = (guint8 *)"hamburger"; - guint8 crypted[80]; - gint ret; - - ret = qq_encrypt(crypted, (const guint8 * const)"a", 1, key); - assert_int_equal(16, ret); - - ret = qq_encrypt(crypted, (const guint8 * const)"aa", 2, key); - assert_int_equal(16, ret); - - ret = qq_encrypt(crypted, (const guint8 * const)"aaa", 3, key); - assert_int_equal(16, ret); - - ret = qq_encrypt(crypted, (const guint8 * const)"aaaa", 4, key); - assert_int_equal(16, ret); - - ret = qq_encrypt(crypted, (const guint8 * const)"aaaaa", 5, key); - assert_int_equal(16, ret); - - ret = qq_encrypt(crypted, (const guint8 * const)"aaaaaa", 6, key); - assert_int_equal(16, ret); - - ret = qq_encrypt(crypted, (const guint8 * const)"aaaaaaa", 7, key); - assert_int_equal(24, ret); - - ret = qq_encrypt(crypted, (const guint8 * const)"aaaaaaaa", 8, key); - assert_int_equal(24, ret); - - ret = qq_encrypt(crypted, (const guint8 * const)"aaaaaaaaa", 9, key); - assert_int_equal(24, ret); - - ret = qq_encrypt(crypted, (const guint8 * const)"aaaaaaaaaa", 10, key); - assert_int_equal(24, ret); - - ret = qq_encrypt(crypted, - (const guint8 * const)"aaaaaaaaaaa", 11, key); - assert_int_equal(24, ret); - - ret = qq_encrypt(crypted, - (const guint8 * const)"aaaaaaaaaaaa", 12, key); - assert_int_equal(24, ret); - - ret = qq_encrypt(crypted, - (const guint8 * const)"aaaaaaaaaaaaa", 13, key); - assert_int_equal(24, ret); - - ret = qq_encrypt(crypted, - (const guint8 * const)"aaaaaaaaaaaaaa", 14, key); - assert_int_equal(24, ret); - - ret = qq_encrypt(crypted, - (const guint8 * const)"aaaaaaaaaaaaaaa", 15, key); - assert_int_equal(32, ret); - - ret = qq_encrypt(crypted, - (const guint8 * const)"aaaaaaaaaaaaaaaa", 16, key); - assert_int_equal(32, ret); - - ret = qq_encrypt(crypted, - (const guint8 * const)"aaaaaaaaaaaaaaaaa", 17, key); - assert_int_equal(32, ret); - - ret = qq_encrypt(crypted, - (const guint8 * const)"aaaaaaaaaaaaaaaaaa", 18, key); - assert_int_equal(32, ret); - - ret = qq_encrypt(crypted, - (const guint8 * const)"aaaaaaaaaaaaaaaaaaa", 19, key); - assert_int_equal(32, ret); - - /* - fprintf(stderr, "crypted=%s\n", crypted); - assert_string_equal_free("plain", - yahoo_codes_to_html("plain")); - */ -} -END_TEST - -START_TEST(test_qq_decrypt) -{ -} -END_TEST - -Suite * -qq_suite(void) -{ - Suite *s; - TCase *tc; - - s = suite_create("QQ"); - - tc = tcase_create("QQ Crypt Functions"); - tcase_add_test(tc, test_qq_encrypt); - tcase_add_test(tc, test_qq_decrypt); - suite_add_tcase(s, tc); - - return s; -} diff -r 000aac6e42fe -r 3389c248e2b3 libpurple/tests/tests.h --- a/libpurple/tests/tests.h Mon Apr 18 21:20:33 2011 +0000 +++ b/libpurple/tests/tests.h Mon May 02 03:42:24 2011 +0000 @@ -14,7 +14,6 @@ Suite * jabber_jutil_suite(void); Suite * jabber_scram_suite(void); Suite * oscar_util_suite(void); -Suite * qq_suite(void); Suite * yahoo_util_suite(void); Suite * util_suite(void); diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/gtkaccount.c --- a/pidgin/gtkaccount.c Mon Apr 18 21:20:33 2011 +0000 +++ b/pidgin/gtkaccount.c Mon May 02 03:42:24 2011 +0000 @@ -1021,6 +1021,12 @@ gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, + 0, _("Tor/Privacy (SOCKS5)"), + 1, PURPLE_PROXY_TOR, + -1); + + gtk_list_store_append(model, &iter); + gtk_list_store_set(model, &iter, 0, _("Use Environmental Settings"), 1, PURPLE_PROXY_USE_ENVVAR, -1); @@ -2254,7 +2260,7 @@ G_CALLBACK(account_treeview_double_click_cb), dialog); gtk_notebook_append_page(GTK_NOTEBOOK(accounts_window->notebook), - pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, -1), + pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, -1), NULL); add_columns(treeview, dialog); diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Mon Apr 18 21:20:33 2011 +0000 +++ b/pidgin/gtkconv.c Mon May 02 03:42:24 2011 +0000 @@ -1792,6 +1792,15 @@ gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path); gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1); + /* emit chat-nick-clicked signal */ + if (event->type == GDK_BUTTON_PRESS) { + gint plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1( + pidgin_conversations_get_handle(), "chat-nick-clicked", + conv, who, event->button)); + if (plugin_return) + goto handled; + } + if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) { chat_do_im(gtkconv, who); } else if (event->button == 2 && event->type == GDK_BUTTON_PRESS) { @@ -1806,6 +1815,7 @@ event->button, event->time); } +handled: g_free(who); gtk_tree_path_free(path); @@ -2122,7 +2132,13 @@ case GDK_ISO_Left_Tab: if (gtkconv->entry != entry) break; - return tab_complete(conv); + { + gint plugin_return; + plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1( + pidgin_conversations_get_handle(), "chat-nick-autocomplete", + conv, event->state & GDK_SHIFT_MASK)); + return plugin_return ? TRUE : tab_complete(conv); + } break; case GDK_Page_Up: @@ -5586,6 +5602,15 @@ buddyname = (tag->name) + 6; + /* emit chat-nick-clicked signal */ + if (event->type == GDK_BUTTON_PRESS) { + gint plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1( + pidgin_conversations_get_handle(), "chat-nick-clicked", + data, buddyname, btn_event->button)); + if (plugin_return) + return TRUE; + } + if (btn_event->button == 1 && event->type == GDK_2BUTTON_PRESS) { chat_do_im(PIDGIN_CONVERSATION(conv), buddyname); @@ -8003,6 +8028,21 @@ purple_value_new(PURPLE_TYPE_BOXED, "PidginConversation *")); + purple_signal_register(handle, "chat-nick-autocomplete", + purple_marshal_BOOLEAN__POINTER_BOOLEAN, + purple_value_new(PURPLE_TYPE_BOOLEAN), 1, + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_CONVERSATION)); + + purple_signal_register(handle, "chat-nick-clicked", + purple_marshal_BOOLEAN__POINTER_POINTER_UINT, + purple_value_new(PURPLE_TYPE_BOOLEAN), 3, + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_CONVERSATION), + purple_value_new(PURPLE_TYPE_STRING), + purple_value_new(PURPLE_TYPE_UINT)); + + /********************************************************************** * Register commands **********************************************************************/ diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Mon Apr 18 21:20:33 2011 +0000 +++ b/pidgin/gtkdialogs.c Mon May 02 03:42:24 2011 +0000 @@ -102,8 +102,10 @@ /* Order: Alphabetical by Last Name */ static const struct developer patch_writers[] = { {"Jakub 'haakon' Adam", NULL, NULL}, + {"Krzysztof Klinikowski", NULL, NULL}, {"Peter 'Fmoo' Ruibal", NULL, NULL}, {"Gabriel 'Nix' Schulhof", NULL, NULL}, + {"Tomasz Wasilczyk", NULL, NULL}, {NULL, NULL, NULL} }; diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Mon Apr 18 21:20:33 2011 +0000 +++ b/pidgin/gtkprefs.c Mon May 02 03:42:24 2011 +0000 @@ -2041,9 +2041,10 @@ pidgin_prefs_dropdown(prefs_proxy_frame, _("Proxy t_ype:"), PURPLE_PREF_STRING, "/purple/proxy/type", _("No proxy"), "none", - "SOCKS 4", "socks4", - "SOCKS 5", "socks5", - "HTTP", "http", + _("SOCKS 4"), "socks4", + _("SOCKS 5"), "socks5", + _("Tor/Privacy (SOCKS5)"), "tor", + _("HTTP"), "http", _("Use Environmental Settings"), "envvar", NULL); gtk_box_pack_start(GTK_BOX(prefs_proxy_frame), prefs_proxy_subframe, 0, 0, 0); diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/Makefile.am --- a/pidgin/pixmaps/Makefile.am Mon Apr 18 21:20:33 2011 +0000 +++ b/pidgin/pixmaps/Makefile.am Mon May 02 03:42:24 2011 +0000 @@ -103,7 +103,6 @@ emblems/16/male.png \ emblems/16/not-authorized.png \ emblems/16/operator.png \ - emblems/16/qq-member.png \ emblems/16/secure.png \ emblems/16/unavailable.png \ emblems/16/video.png \ @@ -119,7 +118,6 @@ emblems/scalable/game.svg \ emblems/scalable/male.svg \ emblems/scalable/not-authorized.svg \ - emblems/scalable/qq-member.svg \ emblems/scalable/secure.svg \ emblems/scalable/unavailable.svg \ emblems/scalable/video.svg \ @@ -181,7 +179,6 @@ emotes/default/24/scalable/plate.svg \ emotes/default/24/scalable/poop.svg \ emotes/default/24/scalable/present.svg \ - emotes/default/24/scalable/qq.svg \ emotes/default/24/scalable/rainbow.svg \ emotes/default/24/scalable/rain.svg \ emotes/default/24/scalable/rose-dead.svg \ @@ -216,7 +213,6 @@ protocols/16/scalable/jabber.svg \ protocols/16/scalable/meanwhile.svg \ protocols/16/scalable/msn.svg \ - protocols/16/scalable/qq.svg \ protocols/16/scalable/silc.svg \ protocols/16/scalable/simple.svg \ protocols/16/scalable/yahoo.svg \ @@ -236,7 +232,6 @@ protocols/16/msn.png \ protocols/16/mxit.png \ protocols/16/myspace.png \ - protocols/16/qq.png \ protocols/16/silc.png \ protocols/16/simple.png \ protocols/16/yahoo.png \ @@ -273,7 +268,6 @@ protocols/22/scalable/jabber.svg \ protocols/22/scalable/meanwhile.svg \ protocols/22/scalable/msn.svg \ - protocols/22/scalable/qq.svg \ protocols/22/scalable/silc.svg \ protocols/22/scalable/simple.svg \ protocols/22/scalable/yahoo.svg \ @@ -292,7 +286,6 @@ protocols/22/meanwhile.png \ protocols/22/msn.png \ protocols/22/myspace.png \ - protocols/22/qq.png \ protocols/22/silc.png \ protocols/22/simple.png \ protocols/22/yahoo.png \ @@ -311,7 +304,6 @@ protocols/48/msn.png \ protocols/48/mxit.png \ protocols/48/myspace.png \ - protocols/48/qq.png \ protocols/48/silc.png \ protocols/48/simple.png \ protocols/48/yahoo.png \ @@ -329,7 +321,6 @@ protocols/scalable/meanwhile.svg \ protocols/scalable/msn.svg \ protocols/scalable/mxit.svg \ - protocols/scalable/qq.svg \ protocols/scalable/silc.svg \ protocols/scalable/simple.svg \ protocols/scalable/yahoo.svg \ diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/emblems/16/qq-member.png Binary file pidgin/pixmaps/emblems/16/qq-member.png has changed diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/emblems/scalable/qq-member.svg --- a/pidgin/pixmaps/emblems/scalable/qq-member.svg Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,198 +0,0 @@ - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/emotes/default/24/Makefile.am --- a/pidgin/pixmaps/emotes/default/24/Makefile.am Mon Apr 18 21:20:33 2011 +0000 +++ b/pidgin/pixmaps/emotes/default/24/Makefile.am Mon May 02 03:42:24 2011 +0000 @@ -135,7 +135,6 @@ pray.png \ present.png \ pumpkin.png \ - qq.png \ question.png \ quiet.png \ rainbow.png \ diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/emotes/default/24/qq.png Binary file pidgin/pixmaps/emotes/default/24/qq.png has changed diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/emotes/default/24/scalable/qq.svg --- a/pidgin/pixmaps/emotes/default/24/scalable/qq.svg Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/protocols/16/qq.png Binary file pidgin/pixmaps/protocols/16/qq.png has changed diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/protocols/16/scalable/qq.svg --- a/pidgin/pixmaps/protocols/16/scalable/qq.svg Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,285 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/protocols/22/qq.png Binary file pidgin/pixmaps/protocols/22/qq.png has changed diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/protocols/22/scalable/qq.svg --- a/pidgin/pixmaps/protocols/22/scalable/qq.svg Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,242 +0,0 @@ - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/protocols/48/qq.png Binary file pidgin/pixmaps/protocols/48/qq.png has changed diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/pixmaps/protocols/scalable/qq.svg --- a/pidgin/pixmaps/protocols/scalable/qq.svg Mon Apr 18 21:20:33 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,291 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 000aac6e42fe -r 3389c248e2b3 pidgin/plugins/vvconfig.c --- a/pidgin/plugins/vvconfig.c Mon Apr 18 21:20:33 2011 +0000 +++ b/pidgin/plugins/vvconfig.c Mon May 02 03:42:24 2011 +0000 @@ -90,7 +90,17 @@ } element = gst_element_factory_make(element_name, "test"); + if(!element) { + purple_debug_info("vvconfig", "'%s' - unable to find element\n", element_name); + return g_list_reverse(ret); + } + klass = G_OBJECT_GET_CLASS (element); + if(!klass) { + purple_debug_info("vvconfig", "'%s' - unable to find G_Object Class\n", element_name); + return g_list_reverse(ret); + } + if (!g_object_class_find_property(klass, "device") || !GST_IS_PROPERTY_PROBE(element) || !(probe = GST_PROPERTY_PROBE(element)) || diff -r 000aac6e42fe -r 3389c248e2b3 po/POTFILES.in --- a/po/POTFILES.in Mon Apr 18 21:20:33 2011 +0000 +++ b/po/POTFILES.in Mon May 02 03:42:24 2011 +0000 @@ -151,22 +151,6 @@ libpurple/protocols/oscar/userinfo.c libpurple/protocols/oscar/util.c libpurple/protocols/oscar/visibility.c -libpurple/protocols/qq/buddy_info.c -libpurple/protocols/qq/buddy_list.c -libpurple/protocols/qq/buddy_memo.c -libpurple/protocols/qq/buddy_opt.c -libpurple/protocols/qq/group.c -libpurple/protocols/qq/group_im.c -libpurple/protocols/qq/group_info.c -libpurple/protocols/qq/group_internal.c -libpurple/protocols/qq/group_join.c -libpurple/protocols/qq/group_opt.c -libpurple/protocols/qq/im.c -libpurple/protocols/qq/qq.c -libpurple/protocols/qq/qq_base.c -libpurple/protocols/qq/qq_network.c -libpurple/protocols/qq/qq_process.c -libpurple/protocols/qq/send_file.c libpurple/protocols/sametime/sametime.c libpurple/protocols/silc/buddy.c libpurple/protocols/silc/chat.c