Mercurial > pidgin.yaz
changeset 31760:02b0fbc627c0
merge of '385542a8270ac30d2121b533494264a28cd04423'
and '99c20d86165b24daadeb247977bbb6d4b80b94bf'
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Mon, 07 Mar 2011 06:43:26 +0000 |
parents | 5fa13ec086e0 (current diff) 356adba49dae (diff) |
children | 2e0cff411cc9 |
files | libpurple/protocols/jabber/jabber.c |
diffstat | 39 files changed, 1263 insertions(+), 390 deletions(-) [+] |
line wrap: on
line diff
--- a/COPYRIGHT Mon Mar 07 06:42:57 2011 +0000 +++ b/COPYRIGHT Mon Mar 07 06:43:26 2011 +0000 @@ -49,6 +49,7 @@ Carlos Bederian Dave Bell Igor Belyi +David Benjamin Brian Bernas Paul Betts Runa Bhattacharjee @@ -399,6 +400,7 @@ Eric Polino <aluink@gmail.com> Ari Pollak Stephen Pope +Cristi Posoiu Nathan Poznick Jory A. Pratt David Preece
--- a/ChangeLog Mon Mar 07 06:42:57 2011 +0000 +++ b/ChangeLog Mon Mar 07 06:43:26 2011 +0000 @@ -3,6 +3,17 @@ version 2.7.11 (??/??/????): General: * Our bundled libgadu should now build on HP-UX. + * Fix some instances of file transfers never completing. (Cristi Posoiu) + (#12472) + + Pidgin: + * Sort by Status no longer causes buddies to move around when you + click them. + * Fix embedding in the system tray on older GTK+ releases (such as on + CentOS 5.5 and older Fedora). + * No longer require libstartup-notification for startup notification + support. GTK+ has included support for years, so use it instead. (David + Benjamin) (#13245) AIM: * Fix a bug where some buddies from your buddy list might not show up. @@ -15,8 +26,13 @@ XMPP: * Fix building on platforms with an older glib (inadvertantly broken in 2.7.10). (#13329) - * Don't treat the on-join status storms as 'new arrivals'. (#a14527) - (Thijs Alkemade) + * Don't treat the on-join status storms as 'new arrivals'. (Thijs + Alkemade) (#a14527) + * Extend the /join command to support room JIDs, enabling you to join + a room on any server. (Solarius, Matěj Cepl, wyuka) (#4526) + * Add support for receiving a limited amount of history when joining a + room (not currently supported by Pidgin and Finch). (Thijs Alkemade) + (#10986, #a14219) version 2.7.10 (02/06/2011): General: @@ -323,6 +339,40 @@ * Bonjour support now requires Apple Bonjour Print Services version 2.0.0 or newer (http://support.apple.com/kb/dl999). + libpurple: + * Fall back to an ordinary request if a UI does not support showing a + request with an icon. Fixes receiving MSN file transfer requests + including a thumbnail in Finch. + + Pidgin: + * Add support for the Gadu-Gadu protocol in the gevolution plugin to + provide Evolution integration with contacts with GG IDs. (#10709) + * Remap the "Set User Mood" shortcut to Control-D, which does not + conflict with the previous shortcut for Get Buddy Info on the + selected buddy. + * Add a plugin action menu (under Tools) for the Voice and Video + Settings plugin. + + Finch: + * Add support for drop-down account options (like the SILC cipher + and HMAC options or the QQ protocol version). + + XMPP: + * Unify the connection security-related settings into one dropdown. + * Fix a crash when multiple accounts are simultaneously performing + SASL authentication when built with Cyrus SASL support. (thanks + to Jan Kaluza) (#11560) + * Restore the ability to connect to XMPP servers that do not offer + Stream ID. (#12331) + * Added support for using Google's relay servers when making voice and + video calls to Google clients. + + Yahoo/Yahoo JAPAN: + * Stop doing unnecessary lookups of certain alias information. This + solves deadlocks when a given Yahoo account has a ridiculously large + (>500 buddies) list and may improve login speed for those on slow + connections. (#12532) + version 2.7.3 (08/10/2010): General: * Use silent build rules for automake >1.11. You can enable verbose
--- a/ChangeLog.API Mon Mar 07 06:42:57 2011 +0000 +++ b/ChangeLog.API Mon Mar 07 06:43:26 2011 +0000 @@ -1,6 +1,15 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul version 2.7.11 (??/??/????): + * libpurple: + Added: + * Four entries in the GHashTable passed when joining + an XMPP chat room which allow the UI to request a limited + amount of history. See XEP-0045 7.1.16 for details; the + entries are named history_maxchars, history_maxstanzas, + history_seconds, and history_since. history_since must be + interpretable by purple_str_to_time, and the prpl takes care + of formatting the time properly. * Perl: Added: * Purple::find_conversation_with_account
--- a/configure.ac Mon Mar 07 06:42:57 2011 +0000 +++ b/configure.ac Mon Mar 07 06:43:26 2011 +0000 @@ -541,27 +541,6 @@ fi dnl ####################################################################### - dnl # Check for startup notification - dnl ####################################################################### - if test "x$enable_startup_notification" = "xyes"; then - PKG_CHECK_MODULES(STARTUP_NOTIFICATION, [libstartup-notification-1.0 >= 0.5], , [ - AC_MSG_RESULT(no) - enable_startup_notification="no" - if test "x$force_deps" = "xyes" ; then - AC_MSG_ERROR([ -Startup notification development headers not found. -Use --disable-startup-notification if you do not need it. -]) - fi]) - - if test "x$enable_startup_notification" = "xyes"; then - AC_DEFINE(HAVE_STARTUP_NOTIFICATION, 1, [Define if we're using libstartup-notification.]) - AC_SUBST(STARTUP_NOTIFICATION_CFLAGS) - AC_SUBST(STARTUP_NOTIFICATION_LIBS) - fi - fi - - dnl ####################################################################### dnl # Check for GtkSpell dnl ####################################################################### if test "x$enable_gtkspell" = "xyes" ; then
--- a/libpurple/ft.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/ft.c Mon Mar 07 06:43:26 2011 +0000 @@ -1100,9 +1100,11 @@ r = write(xfer->fd, buffer, s); if (r < 0 && errno == EAGAIN) r = 0; - if ((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer)) - purple_xfer_set_completed(xfer, TRUE); } + if (r >= 0 && (purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer) && + !purple_xfer_is_completed(xfer)) + purple_xfer_set_completed(xfer, TRUE); + return r; }
--- a/libpurple/protocols/jabber/chat.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/jabber/chat.c Mon Mar 07 06:43:26 2011 +0000 @@ -281,6 +281,14 @@ char *jid; + char *history_maxchars; + char *history_maxstanzas; + char *history_seconds; + char *history_since; + + struct tm history_since_datetime; + const char *history_since_string = NULL; + chat = jabber_chat_new(js, room, server, handle, password, data); if (chat == NULL) return NULL; @@ -297,6 +305,22 @@ xmlnode_set_attrib(presence, "to", jid); g_free(jid); + history_maxchars = g_hash_table_lookup(data, "history_maxchars"); + history_maxstanzas = g_hash_table_lookup(data, "history_maxstanzas"); + history_seconds = g_hash_table_lookup(data, "history_seconds"); + history_since = g_hash_table_lookup(data, "history_since"); + + if (history_since) { + if (purple_str_to_time(history_since, TRUE, &history_since_datetime, NULL, NULL) != 0) { + history_since_string = purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", &history_since_datetime); + } else { + history_since_string = NULL; + + purple_debug_error("jabber", "Invalid date format for history_since" + " while requesting history: %s", history_since); + } + } + x = xmlnode_new_child(presence, "x"); xmlnode_set_namespace(x, "http://jabber.org/protocol/muc"); @@ -305,6 +329,27 @@ xmlnode_insert_data(p, password, -1); } + if ((history_maxchars && *history_maxchars) + || (history_maxstanzas && *history_maxstanzas) + || (history_seconds && *history_seconds) + || (history_since_string && *history_since_string)) { + + xmlnode *history = xmlnode_new_child(x, "history"); + + if (history_maxchars && *history_maxchars) { + xmlnode_set_attrib(history, "maxchars", history_maxchars); + } + if (history_maxstanzas && *history_maxstanzas) { + xmlnode_set_attrib(history, "maxstanzas", history_maxstanzas); + } + if (history_seconds && *history_seconds) { + xmlnode_set_attrib(history, "seconds", history_seconds); + } + if (history_since_string && *history_since_string) { + xmlnode_set_attrib(history, "since", history_since_string); + } + } + jabber_send(js, presence); xmlnode_free(presence);
--- a/libpurple/protocols/jabber/jabber.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/jabber/jabber.c Mon Mar 07 06:43:26 2011 +0000 @@ -3004,21 +3004,44 @@ { JabberChat *chat = jabber_chat_find_by_conv(conv); GHashTable *components; - - if(!chat || !args || !args[0]) + JabberID *jid; + const char *room = NULL, *server = NULL, *handle = NULL; + + if (!chat || !args || !args[0]) return PURPLE_CMD_RET_FAILED; components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - g_hash_table_replace(components, "room", args[0]); - g_hash_table_replace(components, "server", chat->server); - g_hash_table_replace(components, "handle", chat->handle); - if(args[1]) - g_hash_table_replace(components, "password", args[1]); + jid = jabber_id_new(args[0]); + if (jid) { + room = jid->node; + server = jid->domain; + handle = jid->resource ? jid->resource : chat->handle; + } else { + /* If jabber_id_new failed, the user may have just passed in + * a room name. For backward compatibility, handle that here. + */ + if (strchr(args[0], '@')) { + *error = g_strdup(_("Invalid XMPP ID")); + return PURPLE_CMD_RET_FAILED; + } + + room = args[0]; + server = chat->server; + handle = chat->handle; + } + + g_hash_table_insert(components, "room", (gpointer)room); + g_hash_table_insert(components, "server", (gpointer)server); + g_hash_table_insert(components, "handle", (gpointer)handle); + + if (args[1]) + g_hash_table_insert(components, "password", args[1]); jabber_chat_join(purple_conversation_get_gc(conv), components); g_hash_table_destroy(components); + jabber_id_free(jid); return PURPLE_CMD_RET_OK; } @@ -3646,6 +3669,7 @@ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_join, _("join: <room> [password]: Join a chat on this server."), + /* _("join: <room[@server]> [password]: Join a chat."), */ NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
--- a/libpurple/protocols/jabber/jutil.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/jabber/jutil.c Mon Mar 07 06:43:26 2011 +0000 @@ -501,12 +501,9 @@ jabber_id_free(JabberID *jid) { if(jid) { - if(jid->node) - g_free(jid->node); - if(jid->domain) - g_free(jid->domain); - if(jid->resource) - g_free(jid->resource); + g_free(jid->node); + g_free(jid->domain); + g_free(jid->resource); g_free(jid); } }
--- a/libpurple/protocols/jabber/message.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/jabber/message.c Mon Mar 07 06:43:26 2011 +0000 @@ -1140,6 +1140,12 @@ if(!who || !msg) return 0; + if (purple_debug_is_verbose()) { + /* TODO: Maybe we need purple_debug_is_really_verbose? :) */ + purple_debug_misc("jabber", "jabber_message_send_im: who='%s'\n" + "\tmsg='%s'\n", who, msg); + } + resource = jabber_get_resource(who); jb = jabber_buddy_find(gc->proto_data, who, TRUE);
--- a/libpurple/protocols/msn/contact.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/msn/contact.c Mon Mar 07 06:43:26 2011 +0000 @@ -363,10 +363,9 @@ char *type; char *member_id; MsnUser *user; - xmlnode *annotation, *display; + xmlnode *annotation; guint nid = MSN_NETWORK_UNKNOWN; char *invite = NULL; - char *display_text; passport = xmlnode_get_data(xmlnode_get_child(member, node)); if (!msn_email_is_valid(passport)) { @@ -376,13 +375,8 @@ type = xmlnode_get_data(xmlnode_get_child(member, "Type")); member_id = xmlnode_get_data(xmlnode_get_child(member, "MembershipId")); - if ((display = xmlnode_get_child(member, "DisplayName"))) { - display_text = xmlnode_get_data(display); - } else { - display_text = NULL; - } - user = msn_userlist_find_add_user(session->userlist, passport, display_text); + user = msn_userlist_find_add_user(session->userlist, passport, NULL); for (annotation = xmlnode_get_child(member, "Annotations/Annotation"); annotation; @@ -423,7 +417,6 @@ g_free(type); g_free(member_id); g_free(invite); - g_free(display_text); } static void
--- a/libpurple/protocols/msn/msn.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/msn/msn.c Mon Mar 07 06:43:26 2011 +0000 @@ -110,11 +110,9 @@ g_return_val_if_fail(str != NULL, NULL); - g_snprintf(buf, sizeof(buf), "%s%s", str, - (strchr(str, '@') ? "" : "@hotmail.com")); - - tmp = g_utf8_strdown(buf, -1); - strncpy(buf, tmp, sizeof(buf)); + tmp = g_strchomp(g_utf8_strdown(str, -1)); + g_snprintf(buf, sizeof(buf), "%s%s", tmp, + (strchr(tmp, '@') ? "" : "@hotmail.com")); g_free(tmp); return buf;
--- a/libpurple/protocols/msn/p2p.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/msn/p2p.c Mon Mar 07 06:43:26 2011 +0000 @@ -23,72 +23,139 @@ */ #include "internal.h" +#include "debug.h" #include "p2p.h" #include "msnutils.h" MsnP2PInfo * -msn_p2p_info_new(void) +msn_p2p_info_new(MsnP2PVersion version) { - return g_new0(MsnP2PInfo, 1); + MsnP2PInfo *info = g_new0(MsnP2PInfo, 1); + info->version = version; + + switch (version) { + case MSN_P2P_VERSION_ONE: + case MSN_P2P_VERSION_TWO: + /* Nothing to do */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", version); + g_free(info); + info = NULL; + } + + return info; } MsnP2PInfo * msn_p2p_info_dup(MsnP2PInfo *info) { MsnP2PInfo *new_info = g_new0(MsnP2PInfo, 1); - *new_info = *info; + + new_info->version = info->version; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + case MSN_P2P_VERSION_TWO: + *new_info = *info; + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + g_free(new_info); + new_info = NULL; + } + return new_info; } void msn_p2p_info_free(MsnP2PInfo *info) { + switch (info->version) { + case MSN_P2P_VERSION_ONE: + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + g_free(info); } size_t msn_p2p_header_from_wire(MsnP2PInfo *info, const char *wire) { - MsnP2PHeader *header; + size_t len; - header = &info->header; + switch (info->version) { + case MSN_P2P_VERSION_ONE: { + MsnP2PHeader *header = &info->header.v1; - header->session_id = msn_pop32le(wire); - header->id = msn_pop32le(wire); - header->offset = msn_pop64le(wire); - header->total_size = msn_pop64le(wire); - header->length = msn_pop32le(wire); - header->flags = msn_pop32le(wire); - header->ack_id = msn_pop32le(wire); - header->ack_sub_id = msn_pop32le(wire); - header->ack_size = msn_pop64le(wire); + header->session_id = msn_pop32le(wire); + header->id = msn_pop32le(wire); + header->offset = msn_pop64le(wire); + header->total_size = msn_pop64le(wire); + header->length = msn_pop32le(wire); + header->flags = msn_pop32le(wire); + header->ack_id = msn_pop32le(wire); + header->ack_sub_id = msn_pop32le(wire); + header->ack_size = msn_pop64le(wire); - return P2P_PACKET_HEADER_SIZE; + len = P2P_PACKET_HEADER_SIZE; + break; + } + + case MSN_P2P_VERSION_TWO: + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return len; } char * msn_p2p_header_to_wire(MsnP2PInfo *info, size_t *len) { - MsnP2PHeader *header; - char *wire; + char *wire = NULL; char *tmp; - header = &info->header; - tmp = wire = g_new(char, P2P_PACKET_HEADER_SIZE); + switch (info->version) { + case MSN_P2P_VERSION_ONE: { + MsnP2PHeader *header = &info->header.v1; + tmp = wire = g_new(char, P2P_PACKET_HEADER_SIZE); + + msn_push32le(tmp, header->session_id); + msn_push32le(tmp, header->id); + msn_push64le(tmp, header->offset); + msn_push64le(tmp, header->total_size); + msn_push32le(tmp, header->length); + msn_push32le(tmp, header->flags); + msn_push32le(tmp, header->ack_id); + msn_push32le(tmp, header->ack_sub_id); + msn_push64le(tmp, header->ack_size); - msn_push32le(tmp, header->session_id); - msn_push32le(tmp, header->id); - msn_push64le(tmp, header->offset); - msn_push64le(tmp, header->total_size); - msn_push32le(tmp, header->length); - msn_push32le(tmp, header->flags); - msn_push32le(tmp, header->ack_id); - msn_push32le(tmp, header->ack_sub_id); - msn_push64le(tmp, header->ack_size); + if (len) + *len = P2P_PACKET_HEADER_SIZE; + + break; + } - if (len) - *len = P2P_PACKET_HEADER_SIZE; + case MSN_P2P_VERSION_TWO: + if (len) + *len = 0; + + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } return wire; @@ -127,15 +194,30 @@ void msn_p2p_info_to_string(MsnP2PInfo *info, GString *str) { - g_string_append_printf(str, "Session ID: %u\r\n", info->header.session_id); - g_string_append_printf(str, "ID: %u\r\n", info->header.id); - g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", info->header.offset); - g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", info->header.total_size); - g_string_append_printf(str, "Length: %u\r\n", info->header.length); - g_string_append_printf(str, "Flags: 0x%x\r\n", info->header.flags); - g_string_append_printf(str, "ACK ID: %u\r\n", info->header.ack_id); - g_string_append_printf(str, "SUB ID: %u\r\n", info->header.ack_sub_id); - g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", info->header.ack_size); + switch (info->version) { + case MSN_P2P_VERSION_ONE: { + MsnP2PHeader *header = &info->header.v1; + g_string_append_printf(str, "Session ID: %u\r\n", header->session_id); + g_string_append_printf(str, "ID: %u\r\n", header->id); + g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", header->offset); + g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", header->total_size); + g_string_append_printf(str, "Length: %u\r\n", header->length); + g_string_append_printf(str, "Flags: 0x%x\r\n", header->flags); + g_string_append_printf(str, "ACK ID: %u\r\n", header->ack_id); + g_string_append_printf(str, "SUB ID: %u\r\n", header->ack_sub_id); + g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", header->ack_size); + + break; + } + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + g_string_append_printf(str, "Footer: 0x%08X\r\n", info->footer.value); } @@ -150,67 +232,232 @@ gboolean msn_p2p_info_is_valid(MsnP2PInfo *info) { - return info->header.total_size >= info->header.length; + gboolean valid = FALSE; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + valid = info->header.v1.total_size >= info->header.v1.length; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return valid; } gboolean msn_p2p_info_is_final(MsnP2PInfo *info) { - return info->header.offset + info->header.length >= info->header.total_size; + gboolean final = FALSE; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + final = info->header.v1.offset + info->header.v1.length >= info->header.v1.total_size; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return final; } guint32 msn_p2p_info_get_session_id(MsnP2PInfo *info) { - return info->header.session_id; + guint32 session_id = 0; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + session_id = info->header.v1.session_id; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return session_id; } guint32 msn_p2p_info_get_id(MsnP2PInfo *info) { - return info->header.id; + guint32 id = 0; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + id = info->header.v1.id; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return id; } guint64 msn_p2p_info_get_offset(MsnP2PInfo *info) { - return info->header.offset; + guint64 offset = 0; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + offset = info->header.v1.offset; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return offset; } guint64 msn_p2p_info_get_total_size(MsnP2PInfo *info) { - return info->header.total_size; + guint64 total_size = 0; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + total_size = info->header.v1.total_size; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return total_size; } guint32 msn_p2p_info_get_length(MsnP2PInfo *info) { - return info->header.length; + guint32 length = 0; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + length = info->header.v1.length; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return length; } guint32 msn_p2p_info_get_flags(MsnP2PInfo *info) { - return info->header.flags; + guint32 flags = 0; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + flags = info->header.v1.flags; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return flags; } guint32 msn_p2p_info_get_ack_id(MsnP2PInfo *info) { - return info->header.ack_id; + guint32 ack_id = 0; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + ack_id = info->header.v1.ack_id; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return ack_id; } guint32 msn_p2p_info_get_ack_sub_id(MsnP2PInfo *info) { - return info->header.ack_sub_id; + guint32 ack_sub_id = 0; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + ack_sub_id = info->header.v1.ack_sub_id; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return ack_sub_id; } guint64 msn_p2p_info_get_ack_size(MsnP2PInfo *info) { - return info->header.ack_size; + guint64 ack_size = 0; + + switch (info->version) { + case MSN_P2P_VERSION_ONE: + ack_size = info->header.v1.ack_size; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + + return ack_size; } guint32 @@ -222,55 +469,156 @@ void msn_p2p_info_set_session_id(MsnP2PInfo *info, guint32 session_id) { - info->header.session_id = session_id; + switch (info->version) { + case MSN_P2P_VERSION_ONE: + info->header.v1.session_id = session_id; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + } void msn_p2p_info_set_id(MsnP2PInfo *info, guint32 id) { - info->header.id = id; + switch (info->version) { + case MSN_P2P_VERSION_ONE: + info->header.v1.id = id; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } + } void msn_p2p_info_set_offset(MsnP2PInfo *info, guint64 offset) { - info->header.offset = offset; + switch (info->version) { + case MSN_P2P_VERSION_ONE: + info->header.v1.offset = offset; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } } void msn_p2p_info_set_total_size(MsnP2PInfo *info, guint64 total_size) { - info->header.total_size = total_size; + switch (info->version) { + case MSN_P2P_VERSION_ONE: + info->header.v1.total_size = total_size; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } } void msn_p2p_info_set_length(MsnP2PInfo *info, guint32 length) { - info->header.length = length; + switch (info->version) { + case MSN_P2P_VERSION_ONE: + info->header.v1.length = length; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } } void msn_p2p_info_set_flags(MsnP2PInfo *info, guint32 flags) { - info->header.flags = flags; + switch (info->version) { + case MSN_P2P_VERSION_ONE: + info->header.v1.flags = flags; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } } void msn_p2p_info_set_ack_id(MsnP2PInfo *info, guint32 ack_id) { - info->header.ack_id = ack_id; + switch (info->version) { + case MSN_P2P_VERSION_ONE: + info->header.v1.ack_id = ack_id; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } } void msn_p2p_info_set_ack_sub_id(MsnP2PInfo *info, guint32 ack_sub_id) { - info->header.ack_sub_id = ack_sub_id; + switch (info->version) { + case MSN_P2P_VERSION_ONE: + info->header.v1.ack_sub_id = ack_sub_id; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } } void msn_p2p_info_set_ack_size(MsnP2PInfo *info, guint64 ack_size) { - info->header.ack_size = ack_size; + switch (info->version) { + case MSN_P2P_VERSION_ONE: + info->header.v1.ack_size = ack_size; + break; + + case MSN_P2P_VERSION_TWO: + /* Nothing to do! */ + break; + + default: + purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version); + } } void
--- a/libpurple/protocols/msn/p2p.h Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/msn/p2p.h Mon Mar 07 06:43:26 2011 +0000 @@ -58,8 +58,18 @@ } MsnP2PFooter; #define P2P_PACKET_FOOTER_SIZE (1 * 4) +typedef enum +{ + MSN_P2P_VERSION_ONE = 0, + MSN_P2P_VERSION_TWO = 1, +} MsnP2PVersion; + typedef struct { - MsnP2PHeader header; + MsnP2PVersion version; + union { + MsnP2PHeader v1; + MsnP2Pv2Header v2; + } header; MsnP2PFooter footer; } MsnP2PInfo; @@ -94,7 +104,7 @@ } MsnP2PAppId; MsnP2PInfo * -msn_p2p_info_new(void); +msn_p2p_info_new(MsnP2PVersion version); MsnP2PInfo * msn_p2p_info_dup(MsnP2PInfo *info);
--- a/libpurple/protocols/msn/slpmsg.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/msn/slpmsg.c Mon Mar 07 06:43:26 2011 +0000 @@ -48,7 +48,7 @@ else slpmsg->slplink = NULL; - slpmsg->p2p_info = msn_p2p_info_new(); + slpmsg->p2p_info = msn_p2p_info_new(MSN_P2P_VERSION_ONE); return slpmsg; }
--- a/libpurple/protocols/msn/slpmsg_part.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/msn/slpmsg_part.c Mon Mar 07 06:43:26 2011 +0000 @@ -54,7 +54,7 @@ } part = msn_slpmsgpart_new(NULL); - part->info = msn_p2p_info_new(); + part->info = msn_p2p_info_new(MSN_P2P_VERSION_ONE); /* Extract the binary SLP header */ len = msn_p2p_header_from_wire(part->info, data);
--- a/libpurple/protocols/msn/switchboard.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/msn/switchboard.c Mon Mar 07 06:43:26 2011 +0000 @@ -265,12 +265,6 @@ return; } - /* Don't add ourselves either... */ - if (g_str_equal(passport, purple_account_get_username(account))) { - g_free(passport); - return; - } - if (!msnuser) { purple_debug_info("msn","User %s is not on our list.\n", passport); msnuser = msn_user_new(userlist, passport, NULL);
--- a/libpurple/protocols/msn/user.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/msn/user.c Mon Mar 07 06:43:26 2011 +0000 @@ -223,7 +223,10 @@ { g_return_val_if_fail(user != NULL, FALSE); - if (user->friendly_name && name && (!strcmp(user->friendly_name, name) || + if (!name) + return FALSE; + + if (user->friendly_name && (!strcmp(user->friendly_name, name) || !strcmp(user->passport, name))) return FALSE;
--- a/libpurple/protocols/mxit/Makefile.am Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/Makefile.am Mon Mar 07 06:43:26 2011 +0000 @@ -33,7 +33,9 @@ roster.c \ roster.h \ splashscreen.c \ - splashscreen.h + splashscreen.h \ + voicevideo.c \ + voicevideo.h AM_CFLAGS = $(st)
--- a/libpurple/protocols/mxit/Makefile.mingw Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/Makefile.mingw Mon Mar 07 06:43:26 2011 +0000 @@ -51,7 +51,8 @@ profile.c \ protocol.c \ roster.c \ - splashscreen.c + splashscreen.c \ + voicevideo.c OBJECTS = $(C_SRC:%.c=%.o)
--- a/libpurple/protocols/mxit/actions.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/actions.c Mon Mar 07 06:43:26 2011 +0000 @@ -314,12 +314,11 @@ { char version[256]; - g_snprintf( version, sizeof( version ), "MXit libPurple Plugin v%s\n" + g_snprintf( version, sizeof( version ), "MXit Client Protocol v%i.%i\n\n" "Author:\nPieter Loubser\n\n" "Contributors:\nAndrew Victor\n\n" "Testers:\nBraeme Le Roux\n\n", - MXIT_PLUGIN_VERSION, ( MXIT_CP_PROTO_VESION / 10 ), ( MXIT_CP_PROTO_VESION % 10 ) ); mxit_popup( PURPLE_NOTIFY_MSG_INFO, _( "About" ), version );
--- a/libpurple/protocols/mxit/chunk.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/chunk.c Mon Mar 07 06:43:26 2011 +0000 @@ -406,10 +406,9 @@ * @param chunkdata Chunked-data buffer * @param mxitId The username who's avatar to download * @param avatarId The Id of the avatar image (as string) - * @param imgsize The resolution of the avatar image * @return The number of bytes encoded in the buffer */ -int mxit_chunk_create_get_avatar( char* chunkdata, const char* mxitId, const char* avatarId, unsigned int imgsize ) +int mxit_chunk_create_get_avatar( char* chunkdata, const char* mxitId, const char* avatarId ) { int pos = 0; @@ -432,7 +431,7 @@ pos += add_int16( &chunkdata[pos], 1 ); /* image size [4 bytes] */ - pos += add_int32( &chunkdata[pos], imgsize ); + pos += add_int32( &chunkdata[pos], MXIT_AVATAR_SIZE ); return pos; }
--- a/libpurple/protocols/mxit/chunk.h Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/chunk.h Mon Mar 07 06:43:26 2011 +0000 @@ -152,7 +152,7 @@ int mxit_chunk_create_get( char* chunkdata, const char* fileid, int filesize, int offset ); int mxit_chunk_create_received( char* chunkdata, const char* fileid, unsigned char status ); int mxit_chunk_create_set_avatar( char* chunkdata, const unsigned char* data, int datalen ); -int mxit_chunk_create_get_avatar( char* chunkdata, const char* mxitId, const char* avatarId, unsigned int imgsize ); +int mxit_chunk_create_get_avatar( char* chunkdata, const char* mxitId, const char* avatarId ); /* Decode chunk */ void mxit_chunk_parse_offer( char* chunkdata, int datalen, struct offerfile_chunk* offer );
--- a/libpurple/protocols/mxit/formcmds.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/formcmds.c Mon Mar 07 06:43:26 2011 +0000 @@ -47,7 +47,11 @@ MXIT_CMD_REPLY, /* Reply (reply) */ MXIT_CMD_PLATREQ, /* Platform Request (platreq) */ MXIT_CMD_SELECTCONTACT, /* Select Contact (selc) */ - MXIT_CMD_IMAGE /* Inline image (img) */ + MXIT_CMD_IMAGE, /* Inline image (img) */ + MXIT_CMD_SCREENCONFIG, /* Chat-screen config (csc) */ + MXIT_CMD_SCREENINFO, /* Chat-screen info (csi) */ + MXIT_CMD_IMAGESTRIP, /* Image Strip (is) */ + MXIT_CMD_TABLE /* Table (tbl) */ } MXitCommandType; @@ -87,7 +91,7 @@ goto done; } - /* lets first see if we dont have the inline image already in cache */ + /* lets first see if we don't have the inline image already in cache */ if (g_hash_table_lookup(iireq->mx->session->iimages, iireq->url)) { /* inline image found in the cache, so we just ignore this reply */ goto done; @@ -149,8 +153,16 @@ else if (strcmp(type, "selc") == 0) /* select contact */ return MXIT_CMD_SELECTCONTACT; } - else if (strcmp(op, "img") == 0) - return MXIT_CMD_IMAGE; + else if (strcmp(op, "img") == 0) /* inline image */ + return MXIT_CMD_IMAGE; + else if (strcmp(op, "csc") == 0) /* chat-screen config */ + return MXIT_CMD_SCREENCONFIG; + else if (strcmp(op, "csi") == 0) /* chat-screen info */ + return MXIT_CMD_SCREENINFO; + else if (strcmp(op, "is") == 0) /* image-strip */ + return MXIT_CMD_IMAGESTRIP; + else if (strcmp(op, "tbl") == 0) /* table */ + return MXIT_CMD_TABLE; } return MXIT_CMD_UNKNOWN; @@ -333,7 +345,7 @@ g_string_append_printf(msg, "%s%s>", MXIT_II_TAG, iireq->url); mx->got_img = TRUE; - /* lets first see if we dont have the inline image already in cache */ + /* lets first see if we don't have the inline image already in cache */ if (g_hash_table_lookup(mx->session->iimages, iireq->url)) { /* inline image found in the cache, so we do not have to request it from the web */ g_free(iireq);
--- a/libpurple/protocols/mxit/login.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/login.c Mon Mar 07 06:43:26 2011 +0000 @@ -84,7 +84,7 @@ session->iimages = g_hash_table_new( g_str_hash, g_str_equal ); session->rx_state = RX_STATE_RLEN; session->http_interval = MXIT_HTTP_POLL_MIN; - session->http_last_poll = time( NULL ); + session->http_last_poll = mxit_now_milli(); return session; } @@ -106,7 +106,7 @@ purple_connection_update_progress( session->con, _( "Logging In..." ), 2, 4 ); /* create a timer to send a ping packet if the connection is idle */ - session->last_tx = time( NULL ); + session->last_tx = mxit_now_milli(); /* encrypt the user password */ session->encpwd = mxit_encrypt_password( session ); @@ -141,9 +141,10 @@ } /* This timer might already exist if we're registering a new account */ - if ( session->q_timer == 0 ) + if ( session->q_timer == 0 ) { /* start the tx queue manager timer */ - session->q_timer = purple_timeout_add_seconds( 2, mxit_manage_queue, session ); + session->q_timer = purple_timeout_add_seconds( 2, mxit_manage_queue_slow, session ); + } } @@ -238,7 +239,7 @@ /* nickname */ str = purple_request_fields_get_string( fields, "nickname" ); if ( ( !str ) || ( strlen( str ) < 3 ) ) { - err = _( "The Display Name you entered is invalid." ); + err = _( "The Display Name you entered is too short." ); goto out; } g_strlcpy( profile->nickname, str, sizeof( profile->nickname ) ); @@ -546,8 +547,8 @@ /* get state */ state = purple_account_get_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN ); - url = g_strdup_printf( "%s?type=getpid&sessionid=%s&login=%s&ver=%s&clientid=%s&cat=%s&chalresp=%s&cc=%s&loc=%s&path=%i&brand=%s&model=%s&h=%i&w=%i&ts=%li", - session->logindata->wapserver, session->logindata->sessionid, purple_url_encode( session->acc->username ), MXIT_CP_RELEASE, MXIT_CLIENT_ID, MXIT_CP_ARCH, + url = g_strdup_printf( "%s?type=getpid&sessionid=%s&login=%s&ver=%i.%i.%i&clientid=%s&cat=%s&chalresp=%s&cc=%s&loc=%s&path=%i&brand=%s&model=%s&h=%i&w=%i&ts=%li", + session->logindata->wapserver, session->logindata->sessionid, purple_url_encode( session->acc->username ), PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CLIENT_ID, MXIT_CP_ARCH, captcha_resp, session->logindata->cc, session->logindata->locale, ( state == MXIT_STATE_REGISTER1 ) ? 0 : 1, MXIT_CP_PLATFORM, MXIT_CP_OS, MXIT_CAPTCHA_HEIGHT, MXIT_CAPTCHA_WIDTH, time( NULL ) ); url_data = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_clientinfo2, session ); @@ -762,6 +763,12 @@ { purple_debug_info( MXIT_PLUGIN_ID, "mxit_reconnect\n" ); + /* remove the input cb function */ + if ( session->con->inpa ) { + purple_input_remove( session->con->inpa ); + session->con->inpa = 0; + } + /* close existing connection */ session->flags &= ~MXIT_FLAG_CONNECTED; purple_proxy_connect_cancel_with_handle( session->con );
--- a/libpurple/protocols/mxit/mxit.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/mxit.c Mon Mar 07 06:43:26 2011 +0000 @@ -37,6 +37,7 @@ #include "filexfer.h" #include "actions.h" #include "multimx.h" +#include "voicevideo.h" #ifdef MXIT_LINK_CLICK @@ -524,7 +525,7 @@ if ( session->http ) return; - if ( session->last_tx <= time( NULL ) - MXIT_PING_INTERVAL ) { + if ( session->last_tx <= ( mxit_now_milli() - ( MXIT_PING_INTERVAL * 1000 ) ) ) { /* * this connection has been idle for too long, better ping * the server before it kills our connection. @@ -559,6 +560,8 @@ */ static void mxit_get_info( PurpleConnection *gc, const char *who ) { + PurpleBuddy* buddy; + struct contact* contact; struct MXitSession* session = (struct MXitSession*) gc->proto_data; const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_FULLNAME, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_REGCOUNTRY, CP_PROFILE_LASTSEEN, @@ -566,6 +569,22 @@ purple_debug_info( MXIT_PLUGIN_ID, "mxit_get_info: '%s'\n", who ); + /* find the buddy information for this contact (reference: "libpurple/blist.h") */ + buddy = purple_find_buddy( session->acc, who ); + if ( !buddy ) { + purple_debug_warning( MXIT_PLUGIN_ID, "mxit_get_info: unable to find the buddy '%s'\n", who ); + return; + } + + contact = purple_buddy_get_protocol_data( buddy ); + if ( !contact ) + return; + + /* only MXit users have profiles */ + if ( contact->type != MXIT_TYPE_MXIT ) { + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "No profile available" ), _( "This contact does not have a profile." ) ); + return; + } /* send profile request */ mxit_send_extprofile_request( session, who, ARRAY_SIZE( profilelist ), profilelist ); @@ -588,7 +607,33 @@ /*------------------------------------------------------------------------ - * Buddy list menu. + * Re-Invite was selected from the buddy-list menu. + * + * @param node The entry in the buddy list. + * @param ignored (not used) + */ +static void mxit_reinvite( PurpleBlistNode *node, gpointer ignored ) +{ + PurpleBuddy* buddy; + struct contact* contact; + PurpleConnection* gc; + struct MXitSession* session; + + buddy = (PurpleBuddy *)node; + gc = purple_account_get_connection( purple_buddy_get_account( buddy ) ); + session = gc->proto_data; + + contact = purple_buddy_get_protocol_data( (PurpleBuddy*) node ); + if ( !contact ) + return; + + /* send a new invite */ + mxit_send_invite( session, contact->username, contact->alias, contact->groupname ); +} + + +/*------------------------------------------------------------------------ + * Buddy-list menu. * * @param node The entry in the buddy list. */ @@ -597,6 +642,7 @@ PurpleBuddy* buddy; struct contact* contact; GList* m = NULL; + PurpleMenuAction* act; if ( !PURPLE_BLIST_NODE_IS_BUDDY( node ) ) return NULL; @@ -606,6 +652,12 @@ if ( !contact ) return NULL; + if ( ( contact->subtype == MXIT_SUBTYPE_DELETED ) || ( contact->subtype == MXIT_SUBTYPE_REJECTED ) || ( contact->subtype == MXIT_SUBTYPE_NONE ) ) { + /* contact is in Deleted, Rejected or None state */ + act = purple_menu_action_new( _( "Re-Invite" ), PURPLE_CALLBACK( mxit_reinvite ), NULL, NULL ); + m = g_list_append(m, act); + } + return m; } @@ -686,8 +738,8 @@ NULL, /* attention_types */ sizeof( PurplePluginProtocolInfo ), /* struct_size */ mxit_get_text_table, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ + mxit_media_initiate, /* initiate_media */ + mxit_media_caps, /* get_media_caps */ mxit_get_moods, /* get_moods */ NULL, /* set_public_alias */ NULL /* get_public_alias */ @@ -706,7 +758,7 @@ MXIT_PLUGIN_ID, /* plugin id (must be unique) */ MXIT_PLUGIN_NAME, /* plugin name (this will be displayed in the UI) */ - MXIT_PLUGIN_VERSION, /* version of the plugin */ + DISPLAY_VERSION, /* version of the plugin */ MXIT_PLUGIN_SUMMARY, /* short summary of the plugin */ MXIT_PLUGIN_DESC, /* description of the plugin (can be long) */
--- a/libpurple/protocols/mxit/mxit.h Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/mxit.h Mon Mar 07 06:43:26 2011 +0000 @@ -63,13 +63,12 @@ /* Plugin details */ #define MXIT_PLUGIN_ID "prpl-loubserp-mxit" #define MXIT_PLUGIN_NAME "MXit" -#define MXIT_PLUGIN_VERSION "2.4.0" #define MXIT_PLUGIN_EMAIL "Pieter Loubser <libpurple@mxit.com>" #define MXIT_PLUGIN_WWW "http://www.mxit.com" #define MXIT_PLUGIN_SUMMARY "MXit Protocol Plugin" #define MXIT_PLUGIN_DESC "MXit" -#define MXIT_HTTP_USERAGENT "libpurple-"MXIT_PLUGIN_VERSION +#define MXIT_HTTP_USERAGENT "libpurple-"DISPLAY_VERSION /* default connection settings */ @@ -111,7 +110,6 @@ /* define this to enable the link clicking support */ #define MXIT_LINK_CLICK - #ifdef MXIT_LINK_CLICK #define MXIT_LINK_PREFIX "gopher://" #define MXIT_LINK_KEY "MXIT" @@ -137,10 +135,13 @@ unsigned int http_seqno; /* HTTP request sequence number */ guint http_timer_id; /* timer resource id (pidgin) */ int http_interval; /* poll inverval */ - time_t http_last_poll; /* the last time a poll has been sent */ + gint64 http_last_poll; /* the last time a poll has been sent */ guint http_handler; /* HTTP connection handler */ void* http_out_req; /* HTTP outstanding request */ + /* other servers */ + char voip_server[HOST_NAME_MAX]; /* voice/video server */ + /* client */ struct login_data* logindata; char* encpwd; /* encrypted password */ @@ -159,7 +160,7 @@ /* transmit */ struct tx_queue queue; /* transmit packet queue (FIFO mode) */ - time_t last_tx; /* timestamp of last packet sent */ + gint64 last_tx; /* timestamp of last packet sent */ int outack; /* outstanding ack packet */ guint q_timer; /* timer handler for managing queue */ @@ -169,7 +170,7 @@ unsigned int rx_i; /* receive buffer current index */ int rx_res; /* amount of bytes still outstanding for the current packet */ char rx_state; /* current receiver state */ - time_t last_rx; /* timestamp of last packet received */ + gint64 last_rx; /* timestamp of last packet received */ GList* active_chats; /* list of all our contacts we received messages from (active chats) */ /* groupchat */
--- a/libpurple/protocols/mxit/profile.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/profile.c Mon Mar 07 06:43:26 2011 +0000 @@ -108,10 +108,10 @@ */ static const char* datetime( gint64 msecs ) { - time_t secs = msecs / 1000; + time_t secs = msecs / 1000; - struct tm t; - localtime_r( &secs, &t ); + struct tm t; + localtime_r( &secs, &t ); return purple_utf8_strftime( "%d-%m-%Y %H:%M:%S", &t ); } @@ -140,13 +140,10 @@ purple_notify_user_info_add_pair( info, _( "Display Name" ), profile->nickname ); purple_notify_user_info_add_pair( info, _( "Birthday" ), profile->birthday ); purple_notify_user_info_add_pair( info, _( "Gender" ), profile->male ? _( "Male" ) : _( "Female" ) ); -// purple_notify_user_info_add_pair( info, _( "Hidden Number" ), profile->hidden ? _( "Yes" ) : _( "No" ) ); /* optional information */ -// purple_notify_user_info_add_pair( info, _( "Title" ), profile->title ); purple_notify_user_info_add_pair( info, _( "First Name" ), profile->firstname ); purple_notify_user_info_add_pair( info, _( "Last Name" ), profile->lastname ); -// purple_notify_user_info_add_pair( info, _( "Email" ), profile->email ); purple_notify_user_info_add_pair( info, _( "Country" ), profile->regcountry ); purple_notify_user_info_add_section_break( info );
--- a/libpurple/protocols/mxit/protocol.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/protocol.c Mon Mar 07 06:43:26 2011 +0000 @@ -37,6 +37,7 @@ #include "login.h" #include "formcmds.h" #include "http.h" +#include "voicevideo.h" #define MXIT_MS_OFFSET 3 @@ -45,6 +46,18 @@ #define CP_REC_TERM ( ( session->http ) ? CP_HTTP_REC_TERM : CP_SOCK_REC_TERM ) +/*------------------------------------------------------------------------ + * return the current timestamp in milliseconds + */ +gint64 mxit_now_milli( void ) +{ + GTimeVal now; + + g_get_current_time( &now ); + + return ( ( now.tv_sec * 1000 ) + ( now.tv_usec / 1000 ) ); +} + /*------------------------------------------------------------------------ * Display a notification popup message to the user. @@ -411,7 +424,7 @@ } /* update the timestamp of the last-transmitted packet */ - session->last_tx = time( NULL ); + session->last_tx = mxit_now_milli(); /* * we need to remember that we are still waiting for the ACK from @@ -474,17 +487,13 @@ packet->datalen = datalen; - /* - * shortcut: first check if there are any commands still outstanding. - * if not, then we might as well just write this packet directly and - * skip the whole queueing thing - */ - if ( session->outack == 0 ) { - /* no outstanding ACKs, so we might as well write it directly */ + /* shortcut */ + if ( ( session->queue.count == 0 ) && ( session->outack == 0 ) ) { + /* the queue is empty and there are no outstanding acks so we can write it directly */ mxit_send_packet( session, packet ); } else { - /* ACK still outstanding, so we need to queue this request until we have the ACK */ + /* we need to queue this packet */ if ( ( packet->cmd == CP_CMD_PING ) || ( packet->cmd == CP_CMD_POLL ) ) { /* we do NOT queue HTTP poll nor socket ping packets */ @@ -503,42 +512,89 @@ /*------------------------------------------------------------------------ - * Callback to manage the packet send queue (send next packet, timeout's, etc). + * Manage the packet send queue (send next packet, timeout's, etc). * * @param session The MXit session object */ -gboolean mxit_manage_queue( gpointer user_data ) +static void mxit_manage_queue( struct MXitSession* session ) { - struct MXitSession* session = (struct MXitSession*) user_data; struct tx_packet* packet = NULL; + gint64 now = mxit_now_milli(); if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) { /* we are not connected, so ignore the queue */ - return TRUE; + return; } else if ( session->outack > 0 ) { /* we are still waiting for an outstanding ACK from the MXit server */ - if ( session->last_tx <= time( NULL ) - MXIT_ACK_TIMEOUT ) { + if ( session->last_tx <= mxit_now_milli() - ( MXIT_ACK_TIMEOUT * 1000 ) ) { /* ack timeout! so we close the connection here */ purple_debug_info( MXIT_PLUGIN_ID, "mxit_manage_queue: Timeout awaiting ACK for command '%i'\n", session->outack ); purple_connection_error( session->con, _( "Timeout while waiting for a response from the MXit server." ) ); } - return TRUE; + return; + } + + /* + * the mxit server has flood detection and it prevents you from sending messages to fast. + * this is a self defense mechanism, a very annoying feature. so the client must ensure that + * it does not send messages too fast otherwise mxit will ignore the user for 30 seconds. + * this is what we are trying to avoid here.. + */ + if ( session->last_tx > ( now - MXIT_TX_DELAY ) ) { + /* we need to wait a little before sending the next packet, so schedule a wakeup call */ + gint64 tdiff = now - ( session->last_tx ); + guint delay = ( MXIT_TX_DELAY - tdiff ) + 9; + if ( delay <= 0 ) + delay = MXIT_TX_DELAY; + purple_timeout_add( delay, mxit_manage_queue_fast, session ); + } + else { + /* get the next packet from the queue to send */ + packet = pop_tx_packet( session ); + if ( packet != NULL ) { + /* there was a packet waiting to be sent to the server, now is the time to do something about it */ + + /* send the packet to MXit server */ + mxit_send_packet( session, packet ); + } } - - packet = pop_tx_packet( session ); - if ( packet != NULL ) { - /* there was a packet waiting to be sent to the server, now is the time to do something about it */ - - /* send the packet to MXit server */ - mxit_send_packet( session, packet ); - } - +} + + +/*------------------------------------------------------------------------ + * Slow callback to manage the packet send queue. + * + * @param session The MXit session object + */ +gboolean mxit_manage_queue_slow( gpointer user_data ) +{ + struct MXitSession* session = (struct MXitSession*) user_data; + + mxit_manage_queue( session ); + + /* continue running */ return TRUE; } /*------------------------------------------------------------------------ + * Fast callback to manage the packet send queue. + * + * @param session The MXit session object + */ +gboolean mxit_manage_queue_fast( gpointer user_data ) +{ + struct MXitSession* session = (struct MXitSession*) user_data; + + mxit_manage_queue( session ); + + /* stop running */ + return FALSE; +} + + +/*------------------------------------------------------------------------ * Callback to manage HTTP server polling (HTTP connections ONLY) * * @param session The MXit session object @@ -547,9 +603,9 @@ { struct MXitSession* session = (struct MXitSession*) user_data; gboolean poll = FALSE; - time_t now = time( NULL ); + gint64 now = mxit_now_milli(); int polldiff; - int rxdiff; + gint64 rxdiff; if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) { /* we only poll if we are actually logged in */ @@ -579,7 +635,7 @@ if ( poll ) { /* send poll request */ - session->http_last_poll = time( NULL ); + session->http_last_poll = mxit_now_milli(); mxit_send_poll( session ); } @@ -638,21 +694,36 @@ const char* locale; char data[CP_MAX_PACKET]; int datalen; + char* clientVersion; + unsigned int features = MXIT_CP_FEATURES; locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE ); + /* Voice and Video supported */ + if (mxit_audio_enabled() && mxit_video_enabled()) + features |= (MXIT_CF_VOICE | MXIT_CF_VIDEO); + else if (mxit_audio_enabled()) + features |= MXIT_CF_VOICE; + + /* generate client version string (eg, P-2.7.10-Y-PURPLE) */ + clientVersion = g_strdup_printf( "%c-%i.%i.%i-%s-%s", MXIT_CP_DISTCODE, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CP_ARCH, MXIT_CP_PLATFORM ); + /* convert the packet to a byte stream */ datalen = snprintf( data, sizeof( data ), "ms=%s%c%s%c%i%c%s%c" /* "ms"=password\1version\1maxreplyLen\1name\1 */ "%s%c%i%c%s%c%s%c" /* dateOfBirth\1gender\1location\1capabilities\1 */ - "%s%c%i%c%s%c%s", /* dc\1features\1dialingcode\1locale */ - session->encpwd, CP_FLD_TERM, MXIT_CP_VERSION, CP_FLD_TERM, CP_MAX_FILESIZE, CP_FLD_TERM, profile->nickname, CP_FLD_TERM, + "%s%c%i%c%s%c%s" /* dc\1features\1dialingcode\1locale */ + "%c%i%c%i", /* \1protocolVer\1lastRosterUpdate */ + session->encpwd, CP_FLD_TERM, clientVersion, CP_FLD_TERM, CP_MAX_FILESIZE, CP_FLD_TERM, profile->nickname, CP_FLD_TERM, profile->birthday, CP_FLD_TERM, ( profile->male ) ? 1 : 0, CP_FLD_TERM, MXIT_DEFAULT_LOC, CP_FLD_TERM, MXIT_CP_CAP, CP_FLD_TERM, - session->distcode, CP_FLD_TERM, MXIT_CP_FEATURES, CP_FLD_TERM, session->dialcode, CP_FLD_TERM, locale + session->distcode, CP_FLD_TERM, features, CP_FLD_TERM, session->dialcode, CP_FLD_TERM, locale, + CP_FLD_TERM, MXIT_CP_PROTO_VESION, CP_FLD_TERM, 0 ); /* queue packet for transmission */ mxit_queue_packet( session, data, datalen, CP_CMD_REGISTER ); + + g_free( clientVersion ); } @@ -663,21 +734,32 @@ */ void mxit_send_login( struct MXitSession* session ) { - const char* splashId; - const char* locale; - char data[CP_MAX_PACKET]; - int datalen; + const char* splashId; + const char* locale; + char data[CP_MAX_PACKET]; + int datalen; + char* clientVersion; + unsigned int features = MXIT_CP_FEATURES; locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE ); + /* Voice and Video supported */ + if (mxit_audio_enabled() && mxit_video_enabled()) + features |= (MXIT_CF_VOICE | MXIT_CF_VIDEO); + else if (mxit_audio_enabled()) + features |= MXIT_CF_VOICE; + + /* generate client version string (eg, P-2.7.10-Y-PURPLE) */ + clientVersion = g_strdup_printf( "%c-%i.%i.%i-%s-%s", MXIT_CP_DISTCODE, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CP_ARCH, MXIT_CP_PLATFORM ); + /* convert the packet to a byte stream */ datalen = snprintf( data, sizeof( data ), "ms=%s%c%s%c%i%c" /* "ms"=password\1version\1getContacts\1 */ "%s%c%s%c%i%c" /* capabilities\1dc\1features\1 */ "%s%c%s%c" /* dialingcode\1locale\1 */ "%i%c%i%c%i", /* maxReplyLen\1protocolVer\1lastRosterUpdate */ - session->encpwd, CP_FLD_TERM, MXIT_CP_VERSION, CP_FLD_TERM, 1, CP_FLD_TERM, - MXIT_CP_CAP, CP_FLD_TERM, session->distcode, CP_FLD_TERM, MXIT_CP_FEATURES, CP_FLD_TERM, + session->encpwd, CP_FLD_TERM, clientVersion, CP_FLD_TERM, 1, CP_FLD_TERM, + MXIT_CP_CAP, CP_FLD_TERM, session->distcode, CP_FLD_TERM, features, CP_FLD_TERM, session->dialcode, CP_FLD_TERM, locale, CP_FLD_TERM, CP_MAX_FILESIZE, CP_FLD_TERM, MXIT_CP_PROTO_VESION, CP_FLD_TERM, 0 ); @@ -689,6 +771,8 @@ /* queue packet for transmission */ mxit_queue_packet( session, data, datalen, CP_CMD_LOGIN ); + + g_free( clientVersion ); } @@ -732,7 +816,7 @@ * @param session The MXit session object * @param username Username who's profile is being requested (NULL = our own) * @param nr_attribs Number of attributes being requested - * @param attributes The names of the attributes + * @param attribute The names of the attributes */ void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] ) { @@ -742,7 +826,8 @@ datalen = snprintf( data, sizeof( data ), "ms=%s%c%i", /* "ms="mxitid\1nr_attributes */ - (username ? username : ""), CP_FLD_TERM, nr_attrib); + ( username ? username : "" ), CP_FLD_TERM, nr_attrib + ); /* add attributes */ for ( i = 0; i < nr_attrib; i++ ) @@ -790,6 +875,63 @@ /*------------------------------------------------------------------------ + * Send packet to request list of suggested friends. + * + * @param session The MXit session object + * @param max Maximum number of results to return + * @param nr_attribs Number of attributes being requested + * @param attribute The names of the attributes + */ +void mxit_send_suggest_friends( struct MXitSession* session, int max, unsigned int nr_attrib, const char* attribute[] ) +{ + char data[CP_MAX_PACKET]; + int datalen; + unsigned int i; + + /* convert the packet to a byte stream */ + datalen = snprintf( data, sizeof( data ), + "ms=%i%c%s%c%i%c%i", /* inputType \1 input \ 1 maxSuggestions \1 numAttributes \1 name0 ... \1 nameN */ + CP_SUGGEST_FRIENDS, CP_FLD_TERM, "", CP_FLD_TERM, max, CP_FLD_TERM, nr_attrib ); + + /* add attributes */ + for ( i = 0; i < nr_attrib; i++ ) + datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, attribute[i] ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_SUGGESTCONTACTS ); +} + + +/*------------------------------------------------------------------------ + * Send packet to perform a search for users. + * + * @param session The MXit session object + * @param max Maximum number of results to return + * @param text The search text + * @param nr_attribs Number of attributes being requested + * @param attribute The names of the attributes + */ +void mxit_send_suggest_search( struct MXitSession* session, int max, const char* text, unsigned int nr_attrib, const char* attribute[] ) +{ + char data[CP_MAX_PACKET]; + int datalen; + unsigned int i; + + /* convert the packet to a byte stream */ + datalen = snprintf( data, sizeof( data ), + "ms=%i%c%s%c%i%c%i", /* inputType \1 input \ 1 maxSuggestions \1 numAttributes \1 name0 ... \1 nameN */ + CP_SUGGEST_SEARCH, CP_FLD_TERM, text, CP_FLD_TERM, max, CP_FLD_TERM, nr_attrib ); + + /* add attributes */ + for ( i = 0; i < nr_attrib; i++ ) + datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, attribute[i] ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_SUGGESTCONTACTS ); +} + + +/*------------------------------------------------------------------------ * Send a presence update packet to the MXit server. * * @param session The MXit session object @@ -1039,7 +1181,6 @@ * @param nr_usernames Number of users being invited * @param usernames The usernames of the users being invited */ - void mxit_send_groupchat_invite( struct MXitSession* session, const char* roomid, int nr_usernames, const char* usernames[] ) { char data[CP_MAX_PACKET]; @@ -1271,7 +1412,7 @@ /* map chunk header over data buffer */ chunk = &data[datalen]; - size = mxit_chunk_create_get_avatar( chunk_data(chunk), mxitId, avatarId, MXIT_AVATAR_SIZE ); + size = mxit_chunk_create_get_avatar( chunk_data(chunk), mxitId, avatarId ); if ( size < 0 ) { purple_debug_error( MXIT_PLUGIN_ID, "Error creating get avatar chunk (%i)\n", size ); return; @@ -1322,6 +1463,10 @@ if ( records[1]->fcount >= 9 ) session->uid = g_strdup( records[1]->fields[8]->data ); + /* extract VoIP server (from protocol 6.2) */ + if ( records[1]->fcount >= 11 ) + g_strlcpy( session->voip_server, records[1]->fields[10]->data, sizeof( session->voip_server ) ); + /* display the current splash-screen */ if ( splash_popup_enabled( session ) ) splash_display( session ); @@ -1567,13 +1712,13 @@ */ static void mxit_parse_cmd_presence( struct MXitSession* session, struct record** records, int rcount ) { - struct record* rec; int i; purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_presence (%i recs)\n", rcount ); for ( i = 0; i < rcount; i++ ) { - rec = records[i]; + struct record* rec = records[i]; + int flags = 0; if ( rec->fcount < 6 ) { purple_debug_error( MXIT_PLUGIN_ID, "BAD PRESENCE RECORD! %i fields\n", rec->fcount ); @@ -1582,12 +1727,15 @@ /* * The format of the record is: - * contactAddressN\1presenceN\1moodN\1customMoodN\1statusMsgN\1avatarIdN + * contactAddressN \1 presenceN \1 moodN \1 customMoodN \1 statusMsgN \1 avatarIdN [ \1 flagsN ] */ mxit_strip_domain( rec->fields[0]->data ); /* contactAddress */ + if ( rec->fcount >= 7 ) /* flags field is included */ + flags = atoi( rec->fields[6]->data ); + mxit_update_buddy_presence( session, rec->fields[0]->data, atoi( rec->fields[1]->data ), atoi( rec->fields[2]->data ), - rec->fields[3]->data, rec->fields[4]->data ); + rec->fields[3]->data, rec->fields[4]->data, flags ); mxit_update_buddy_avatar( session, rec->fields[0]->data, rec->fields[5]->data ); } } @@ -1908,7 +2056,7 @@ { /* ignore ping/poll packets */ if ( ( packet->cmd != CP_CMD_PING ) && ( packet->cmd != CP_CMD_POLL ) ) - session->last_rx = time( NULL ); + session->last_rx = mxit_now_milli(); /* * when we pass the packet records to the next level for parsing
--- a/libpurple/protocols/mxit/protocol.h Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/protocol.h Mon Mar 07 06:43:26 2011 +0000 @@ -75,6 +75,8 @@ #define MXIT_CF_NO_AVATARS 0x200000 #define MXIT_CF_GAMING 0x400000 #define MXIT_CF_GAMING_UPDATE 0x800000 +#define MXIT_CF_VOICE 0x1000000 +#define MXIT_CF_VIDEO 0x2000000 /* Client features supported by this implementation */ #define MXIT_CP_FEATURES ( MXIT_CF_FILE_TRANSFER | MXIT_CF_FILE_ACCESS | MXIT_CF_AUDIO | MXIT_CF_MARKUP | MXIT_CF_EXT_MARKUP | MXIT_CF_NO_GATEWAYS | MXIT_CF_IMAGES | MXIT_CF_COMMANDS | MXIT_CF_VIBES | MXIT_CF_MIDP2 ) @@ -82,14 +84,13 @@ #define MXIT_PING_INTERVAL ( 5 * 60 ) /* ping the server after X seconds of being idle (5 minutes) */ #define MXIT_ACK_TIMEOUT ( 30 ) /* timeout after waiting X seconds for an ack from the server (30 seconds) */ +#define MXIT_TX_DELAY ( 100 ) /* delay between sending consecutive packets (100 ms) */ /* MXit client version */ -#define MXIT_CP_DISTCODE "P" /* client distribution code (magic, do not touch!) */ -#define MXIT_CP_RELEASE "5.9.0" /* client version */ +#define MXIT_CP_DISTCODE 'P' /* client distribution code (magic, do not touch!) */ #define MXIT_CP_ARCH "Y" /* client architecture series (Y not for Yoda but for PC-client) */ #define MXIT_CLIENT_ID "LP" /* client ID as specified by MXit */ #define MXIT_CP_PLATFORM "PURPLE" /* client platform */ -#define MXIT_CP_VERSION MXIT_CP_DISTCODE"-"MXIT_CP_RELEASE"-"MXIT_CP_ARCH"-"MXIT_CP_PLATFORM #define MXIT_CP_PROTO_VESION 60 /* client protocol version */ /* set operating system name */ @@ -107,7 +108,7 @@ #define MXIT_CP_CAP "utf8=true;cid="MXIT_CLIENT_ID /* Client settings */ -#define MAX_QUEUE_SIZE ( 1 << 4 ) /* tx queue size (16 packets) */ +#define MAX_QUEUE_SIZE ( 1 << 5 ) /* tx queue size (32 packets) */ #define MXIT_POPUP_WIN_NAME "MXit Notification" /* popup window name */ #define MXIT_MAX_ATTRIBS 10 /* maximum profile attributes supported */ #define MXIT_DEFAULT_LOCALE "en" /* default locale setting */ @@ -125,6 +126,7 @@ #define CP_CMD_TX_MSG 0x000A /* (10) send new message */ #define CP_CMD_REGISTER 0x000B /* (11) register */ //#define CP_CMD_PROFILE_SET 0x000C /* (12) set profile (DEPRECATED see CP_CMD_EXTPROFILE_SET) */ +#define CP_CMD_SUGGESTCONTACTS 0x000D /* (13) suggest contacts */ #define CP_CMD_POLL 0x0011 /* (17) poll the HTTP server for an update */ //#define CP_CMD_PROFILE_GET 0x001A /* (26) get profile (DEPRECATED see CP_CMD_EXTPROFILE_GET) */ #define CP_CMD_MEDIA 0x001B /* (27) get multimedia message */ @@ -202,6 +204,12 @@ /* profile flags */ #define CP_PROF_DOBLOCKED 0x40 /* date-of-birth cannot be changed */ +/* suggestion types */ +#define CP_SUGGEST_ADDRESSBOOK 0 /* address book search */ +#define CP_SUGGEST_FRIENDS 1 /* suggested friends */ +#define CP_SUGGEST_SEARCH 2 /* free-text search */ +#define CP_SUGGEST_MXITID 3 /* MXitId search */ + /* define this to enable protocol debugging (very verbose logging) */ #define DEBUG_PROTOCOL @@ -277,7 +285,8 @@ gboolean find_active_chat( const GList* chats, const char* who ); void mxit_cb_rx( gpointer data, gint source, PurpleInputCondition cond ); -gboolean mxit_manage_queue( gpointer user_data ); +gboolean mxit_manage_queue_slow( gpointer user_data ); +gboolean mxit_manage_queue_fast( gpointer user_data ); gboolean mxit_manage_polling( gpointer user_data ); void mxit_send_register( struct MXitSession* session ); @@ -293,6 +302,9 @@ void mxit_send_extprofile_update( struct MXitSession* session, const char* password, unsigned int nr_attrib, const char* attributes ); void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] ); +void mxit_send_suggest_friends( struct MXitSession* session, int max, unsigned int nr_attrib, const char* attribute[] ); +void mxit_send_suggest_search( struct MXitSession* session, int max, const char* text, unsigned int nr_attrib, const char* attribute[] ); + void mxit_send_invite( struct MXitSession* session, const char* username, const char* alias, const char* groupname ); void mxit_send_remove( struct MXitSession* session, const char* username ); void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias ); @@ -314,6 +326,7 @@ int mxit_parse_packet( struct MXitSession* session ); void dump_bytes( struct MXitSession* session, const char* buf, int len ); void mxit_close_connection( struct MXitSession* session ); +gint64 mxit_now_milli( void ); #endif /* _MXIT_PROTO_H_ */
--- a/libpurple/protocols/mxit/roster.c Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/roster.c Mon Mar 07 06:43:26 2011 +0000 @@ -443,8 +443,9 @@ * @param mood The new mood for the contact * @param customMood The custom mood identifier * @param statusMsg This is the contact's status message + * @param flags The contact's presence flags. */ -void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg ) +void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg, int flags ) { PurpleBuddy* buddy = NULL; struct contact* contact = NULL; @@ -470,6 +471,7 @@ contact->presence = presence; contact->mood = mood; + contact->capabilities = flags; /* validate mood */ if (( contact->mood < MXIT_MOOD_NONE ) || ( contact->mood > MXIT_MOOD_STRESSED ))
--- a/libpurple/protocols/mxit/roster.h Mon Mar 07 06:42:57 2011 +0000 +++ b/libpurple/protocols/mxit/roster.h Mon Mar 07 06:43:26 2011 +0000 @@ -79,6 +79,11 @@ #define MXIT_CFLAG_FOCUS_SEND_BLANK 0x20000 +/* MXit presence flags */ +#define MXIT_PFLAG_VOICE 0x1 +#define MXIT_PFLAG_VIDEO 0x2 + + /* Subscription types */ #define MXIT_SUBTYPE_BOTH 'B' #define MXIT_SUBTYPE_PENDING 'P' @@ -108,6 +113,7 @@ short mood; /* contact current mood */ int flags; /* contact flags */ short presence; /* presence state */ + int capabilities; /* contact capabilities */ short subtype; /* subscription type */ char* msg; /* invite/rejection message */ @@ -129,7 +135,7 @@ /* MXit Protocol callbacks */ void mxit_update_contact( struct MXitSession* session, struct contact* contact ); -void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg ); +void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg, int flags ); void mxit_update_buddy_avatar( struct MXitSession* session, const char* username, const char* avatarId ); void mxit_new_subscription( struct MXitSession* session, struct contact* contact ); void mxit_update_blist( struct MXitSession* session );
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/mxit/voicevideo.c Mon Mar 07 06:43:26 2011 +0000 @@ -0,0 +1,154 @@ +/* + * MXit Protocol libPurple Plugin + * + * -- voice & video -- + * + * Andrew Victor <libpurple@mxit.com> + * + * (C) Copyright 2010 MXit Lifestyle (Pty) Ltd. + * <http://www.mxitlifestyle.com> + * + * 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 "purple.h" +#include "mxit.h" +#include "roster.h" +#include "voicevideo.h" + +#if defined(USE_VV) && defined(MXIT_DEV_VV) + +#warning "MXit VV support enabled." + +/*------------------------------------------------------------------------ + * Does this client support Voice? + */ +gboolean mxit_audio_enabled(void) +{ + PurpleMediaManager *manager = purple_media_manager_get(); + PurpleMediaCaps caps = purple_media_manager_get_ui_caps(manager); + + return (caps & PURPLE_MEDIA_CAPS_AUDIO); +} + +/*------------------------------------------------------------------------ + * Does this client support Voice and Video? + */ +gboolean mxit_video_enabled(void) +{ + PurpleMediaManager *manager = purple_media_manager_get(); + PurpleMediaCaps caps = purple_media_manager_get_ui_caps(manager); + + return (caps & PURPLE_MEDIA_CAPS_VIDEO); +} + +/*------------------------------------------------------------------------ + * Return the list of media capabilities this contact supports. + * + * @param account The MXit account object + * @param who The username of the contact. + * @return The media capabilities supported + */ +PurpleMediaCaps mxit_media_caps(PurpleAccount *account, const char *who) +{ + struct MXitSession* session = purple_account_get_connection(account)->proto_data; + PurpleBuddy* buddy; + struct contact* contact; + PurpleMediaCaps capa = PURPLE_MEDIA_CAPS_NONE; + + purple_debug_info(MXIT_PLUGIN_ID, "mxit_media_caps: buddy '%s'\n", who); + + /* We need to have a voice/video server */ + if (strlen(session->voip_server) == 0) + return PURPLE_MEDIA_CAPS_NONE; + + /* find the buddy information for this contact (reference: "libpurple/blist.h") */ + buddy = purple_find_buddy(account, who); + if (!buddy) { + purple_debug_warning(MXIT_PLUGIN_ID, "mxit_media_caps: unable to find the buddy '%s'\n", who); + return PURPLE_MEDIA_CAPS_NONE; + } + + contact = purple_buddy_get_protocol_data(buddy); + if (!contact) + return PURPLE_MEDIA_CAPS_NONE; + + /* can only communicate with MXit users */ + if (contact->type != MXIT_TYPE_MXIT) + return PURPLE_MEDIA_CAPS_NONE; + + /* and only with contacts in the 'Both' subscription state */ + if (contact->subtype != MXIT_SUBTYPE_BOTH) + return PURPLE_MEDIA_CAPS_NONE; + + /* and only when they're online */ + if (contact->presence == MXIT_PRESENCE_OFFLINE) + return MXIT_PRESENCE_OFFLINE; + + /* they support voice-only */ + if (contact->capabilities & MXIT_PFLAG_VOICE) + capa |= PURPLE_MEDIA_CAPS_AUDIO; + + /* they support voice-and-video */ + if (contact->capabilities & MXIT_PFLAG_VIDEO) + capa |= (PURPLE_MEDIA_CAPS_AUDIO | PURPLE_MEDIA_CAPS_VIDEO | PURPLE_MEDIA_CAPS_AUDIO_VIDEO); + + return capa; +} + + +/*------------------------------------------------------------------------ + * Initiate a voice/video session with a contact. + * + * @param account The MXit account object + * @param who The username of the contact. + * @param type The type of media session to initiate + * @return TRUE if session was initiated + */ +gboolean mxit_media_initiate(PurpleAccount *account, const char *who, PurpleMediaSessionType type) +{ + purple_debug_info(MXIT_PLUGIN_ID, "mxit_media_initiate: buddy '%s'\n", who); + + return FALSE; +} + +#else + +/* + * Voice and Video not supported. + */ + +gboolean mxit_audio_enabled(void) +{ + return FALSE; +} + +gboolean mxit_video_enabled(void) +{ + return FALSE; +} + +PurpleMediaCaps mxit_media_caps(PurpleAccount *account, const char *who) +{ + return PURPLE_MEDIA_CAPS_NONE; +} + +gboolean mxit_media_initiate(PurpleAccount *account, const char *who, PurpleMediaSessionType type) +{ + return FALSE; +} + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/mxit/voicevideo.h Mon Mar 07 06:43:26 2011 +0000 @@ -0,0 +1,41 @@ +/* + * MXit Protocol libPurple Plugin + * + * -- voice & video -- + * + * Andrew Victor <libpurple@mxit.com> + * + * (C) Copyright 2010 MXit Lifestyle (Pty) Ltd. + * <http://www.mxitlifestyle.com> + * + * 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 _MXIT_VOICEVICEO_H_ +#define _MXIT_VOICEVIDEO_H_ + +#include "media.h" + + +#undef MXIT_DEV_VV + + +gboolean mxit_audio_enabled(void); +gboolean mxit_video_enabled(void); +PurpleMediaCaps mxit_media_caps(PurpleAccount* account, const char* who); +gboolean mxit_media_initiate(PurpleAccount* account, const char* who, PurpleMediaSessionType type); + + +#endif /* _MXIT_VOICEVIDEO_H_ */
--- a/pidgin/Makefile.am Mon Mar 07 06:42:57 2011 +0000 +++ b/pidgin/Makefile.am Mon Mar 07 06:43:26 2011 +0000 @@ -156,7 +156,6 @@ $(SM_LIBS) \ $(INTLLIBS) \ $(GTKSPELL_LIBS) \ - $(STARTUP_NOTIFICATION_LIBS) \ $(LIBXML_LIBS) \ $(GTK_LIBS) \ $(top_builddir)/libpurple/libpurple.la @@ -180,7 +179,6 @@ $(GTK_CFLAGS) \ $(DBUS_CFLAGS) \ $(GTKSPELL_CFLAGS) \ - $(STARTUP_NOTIFICATION_CFLAGS) \ $(LIBXML_CFLAGS) \ $(INTGG_CFLAGS) endif # ENABLE_GTK
--- a/pidgin/gtkblist.c Mon Mar 07 06:42:57 2011 +0000 +++ b/pidgin/gtkblist.c Mon Mar 07 06:43:26 2011 +0000 @@ -7743,7 +7743,7 @@ gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); return; } else { - sort_method_none(node, blist, groupiter, cur, iter); + sort_method_alphabetical(node, blist, groupiter, cur, iter); return; }
--- a/pidgin/gtkdialogs.c Mon Mar 07 06:42:57 2011 +0000 +++ b/pidgin/gtkdialogs.c Mon Mar 07 06:43:26 2011 +0000 @@ -650,14 +650,6 @@ else g_string_append(str, " <b>Perl:</b> Disabled<br/>"); -#ifndef _WIN32 -#ifdef HAVE_STARTUP_NOTIFICATION - g_string_append(str, " <b>Startup Notification:</b> Enabled<br/>"); -#else - g_string_append(str, " <b>Startup Notification:</b> Disabled<br/>"); -#endif -#endif - if (purple_plugins_find_with_id("core-tcl") != NULL) { g_string_append(str, " <b>Tcl:</b> Enabled<br/>"); #ifdef HAVE_TK
--- a/pidgin/gtkdocklet-gtk.c Mon Mar 07 06:42:57 2011 +0000 +++ b/pidgin/gtkdocklet-gtk.c Mon Mar 07 06:43:26 2011 +0000 @@ -47,19 +47,37 @@ static gboolean docklet_gtk_embed_timeout_cb(gpointer data) { - /* The docklet was not embedded within the timeout. - * Remove it as a visibility manager, but leave the plugin - * loaded so that it can embed automatically if/when a notification - * area becomes available. - */ - purple_debug_info("docklet", "failed to embed within timeout\n"); - pidgin_docklet_remove(); - purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE); +#if !GTK_CHECK_VERSION(2,12,0) + if (gtk_status_icon_is_embedded(docklet)) { + /* Older GTK+ (<2.12) don't implement the embedded signal, but the + information is still accessable through the above function. */ + purple_debug_info("docklet", "embedded\n"); + pidgin_docklet_embedded(); + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE); + } + else +#endif + { + /* The docklet was not embedded within the timeout. + * Remove it as a visibility manager, but leave the plugin + * loaded so that it can embed automatically if/when a notification + * area becomes available. + */ + purple_debug_info("docklet", "failed to embed within timeout\n"); + pidgin_docklet_remove(); + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE); + } + +#if GTK_CHECK_VERSION(2,12,0) embed_timeout = 0; return FALSE; +#else + return TRUE; +#endif } +#if GTK_CHECK_VERSION(2,12,0) static gboolean docklet_gtk_embedded_cb(GtkWidget *widget, gpointer data) { @@ -82,6 +100,7 @@ return TRUE; } +#endif static void docklet_gtk_destroyed_cb(GtkWidget *widget, gpointer data) @@ -206,7 +225,9 @@ g_signal_connect(G_OBJECT(docklet), "activate", G_CALLBACK(docklet_gtk_status_activated_cb), NULL); g_signal_connect(G_OBJECT(docklet), "popup-menu", G_CALLBACK(docklet_gtk_status_clicked_cb), NULL); +#if GTK_CHECK_VERSION(2,12,0) g_signal_connect(G_OBJECT(docklet), "notify::embedded", G_CALLBACK(docklet_gtk_embedded_cb), NULL); +#endif g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_gtk_destroyed_cb), NULL); gtk_status_icon_set_visible(docklet, TRUE); @@ -226,11 +247,15 @@ */ if (!recreate) { pidgin_docklet_embedded(); +#if GTK_CHECK_VERSION(2,12,0) if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded")) { embed_timeout = purple_timeout_add_seconds(LONG_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL); } else { embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL); } +#else + embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL); +#endif } purple_debug_info("docklet", "GTK+ created\n");
--- a/pidgin/gtkmain.c Mon Mar 07 06:42:57 2011 +0000 +++ b/pidgin/gtkmain.c Mon Mar 07 06:43:26 2011 +0000 @@ -76,18 +76,6 @@ #include <getopt.h> -#ifdef HAVE_STARTUP_NOTIFICATION -# define SN_API_NOT_YET_FROZEN -# include <libsn/sn-launchee.h> -# include <gdk/gdkx.h> -#endif - - - -#ifdef HAVE_STARTUP_NOTIFICATION -static SnLauncheeContext *sn_context = NULL; -static SnDisplay *sn_display = NULL; -#endif #ifdef HAVE_SIGNAL_H @@ -472,42 +460,6 @@ g_free(text); } -#ifdef HAVE_STARTUP_NOTIFICATION -static void -sn_error_trap_push(SnDisplay *display, Display *xdisplay) -{ - gdk_error_trap_push(); -} - -static void -sn_error_trap_pop(SnDisplay *display, Display *xdisplay) -{ - gdk_error_trap_pop(); -} - -static void -startup_notification_complete(void) -{ - Display *xdisplay; - - xdisplay = GDK_DISPLAY(); - sn_display = sn_display_new(xdisplay, - sn_error_trap_push, - sn_error_trap_pop); - sn_context = - sn_launchee_context_new_from_environment(sn_display, - DefaultScreen(xdisplay)); - - if (sn_context != NULL) - { - sn_launchee_context_complete(sn_context); - sn_launchee_context_unref(sn_context); - - sn_display_unref(sn_display); - } -} -#endif /* HAVE_STARTUP_NOTIFICATION */ - /* FUCKING GET ME A TOWEL! */ #ifdef _WIN32 /* suppress gcc "no previous prototype" warning */ @@ -876,6 +828,7 @@ dbus_connection_send_with_reply_and_block(conn, message, -1, NULL); dbus_message_unref(message); #endif + gdk_notify_startup_complete(); purple_core_quit(); g_printerr(_("Exiting because another libpurple client is already running.\n")); #ifdef HAVE_SIGNAL_H @@ -967,9 +920,10 @@ g_list_free(active_accounts); } -#ifdef HAVE_STARTUP_NOTIFICATION - startup_notification_complete(); -#endif + /* GTK clears the notification for us when opening the first window, + * but we may have launched with only a status icon, so clear the it + * just in case. */ + gdk_notify_startup_complete(); #ifdef _WIN32 winpidgin_post_init();
--- a/po/de.po Mon Mar 07 06:42:57 2011 +0000 +++ b/po/de.po Mon Mar 07 06:43:26 2011 +0000 @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-02-06 14:22+0100\n" -"PO-Revision-Date: 2011-02-06 14:22+0100\n" +"POT-Creation-Date: 2011-03-06 12:25+0100\n" +"PO-Revision-Date: 2011-03-06 12:25+0100\n" "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n" "Language-Team: German <de@li.org>\n" "Language: de\n" @@ -3239,9 +3239,7 @@ msgstr "UIN" #. first name -#. purple_notify_user_info_add_pair( info, _( "Hidden Number" ), profile->hidden ? _( "Yes" ) : _( "No" ) ); #. optional information -#. purple_notify_user_info_add_pair( info, _( "Title" ), profile->title ); msgid "First Name" msgstr "Vorname" @@ -4010,7 +4008,6 @@ msgid "Postal Code" msgstr "Postleitzahl" -#. purple_notify_user_info_add_pair( info, _( "Email" ), profile->email ); msgid "Country" msgstr "Land" @@ -6148,6 +6145,9 @@ msgid "Connecting..." msgstr "Verbinde..." +msgid "The Display Name you entered is too short." +msgstr "Der eingegebene Anzeigename ist zu kurz." + msgid "The PIN you entered has an invalid length [7-10]." msgstr "Die eingegebene PIN hat eine ungültige Länge [7-10]." @@ -6227,33 +6227,6 @@ msgid "Retrieving User Information..." msgstr "Abrufen der Benutzerinformationen..." -msgid "Loading menu..." -msgstr "Lade das Menü..." - -msgid "Status Message" -msgstr "Status-Nachricht" - -msgid "Rejection Message" -msgstr "Ablehnungsnachricht" - -#. hidden number -msgid "Hidden Number" -msgstr "Versteckte Nummer" - -msgid "Your MXit ID..." -msgstr "Ihre MXit-ID..." - -#. Configuration options -#. WAP server (reference: "libpurple/accountopt.h") -msgid "WAP Server" -msgstr "WAP-Server" - -msgid "Connect via HTTP" -msgstr "Über HTTP verbinden" - -msgid "Enable splash-screen popup" -msgstr "Startbildschirm-Popup aktivieren" - #. you were kicked msgid "You have been kicked from this MultiMX." msgstr "Sie wurden von MultiMX hinausgeworfen." @@ -6268,6 +6241,43 @@ msgid "You have invited" msgstr "Sie haben eingeladen" +msgid "Loading menu..." +msgstr "Lade das Menü..." + +msgid "Status Message" +msgstr "Status-Nachricht" + +msgid "Rejection Message" +msgstr "Ablehnungsnachricht" + +#. hidden number +msgid "Hidden Number" +msgstr "Versteckte Nummer" + +msgid "No profile available" +msgstr "Kein Profil verfügbar" + +msgid "This contact does not have a profile." +msgstr "Dieser Kontakt hat kein Profil." + +msgid "Your MXit ID..." +msgstr "Ihre MXit-ID..." + +#. contact is in Deleted, Rejected or None state +msgid "Re-Invite" +msgstr "Erneut einladen" + +#. Configuration options +#. WAP server (reference: "libpurple/accountopt.h") +msgid "WAP Server" +msgstr "WAP-Server" + +msgid "Connect via HTTP" +msgstr "Über HTTP verbinden" + +msgid "Enable splash-screen popup" +msgstr "Startbildschirm-Popup aktivieren" + msgid "Last Online" msgstr "Zuletzt online" @@ -7571,6 +7581,14 @@ msgid "You have been disconnected from chat room %s." msgstr "Die Verbindung zum Raum %s wurde unterbrochen." +msgid "The new formatting is invalid." +msgstr "Die neue Formatierung ist ungültig." + +msgid "Username formatting can change only capitalization and whitespace." +msgstr "" +"Benutzernamen-Formatierung kann nur die Groß-/Kleinschreibung und " +"Leerzeichen ändern." + msgid "Pop-Up Message" msgstr "Pop-Up Nachricht" @@ -7846,14 +7864,6 @@ msgid "ICQ Privacy Options" msgstr "ICQ Privatsphärenoptionen" -msgid "The new formatting is invalid." -msgstr "Die neue Formatierung ist ungültig." - -msgid "Username formatting can change only capitalization and whitespace." -msgstr "" -"Benutzernamen-Formatierung kann nur die Groß-/Kleinschreibung und " -"Leerzeichen ändern." - msgid "Change Address To:" msgstr "Ändere die Adresse zu:" @@ -7961,75 +7971,6 @@ "ist notwendig für IM-Bilder. Da Ihre IP-Adresse verwendet wird, kann dies " "eine Verletzung der Privatsphäre bedeuten." -msgid "Invalid SNAC" -msgstr "Ungültiger SNAC" - -msgid "Server rate limit exceeded" -msgstr "Server-Datenrate überschritten" - -msgid "Client rate limit exceeded" -msgstr "Client-Datenrate überschritten" - -msgid "Service unavailable" -msgstr "Dienst nicht verfügbar" - -msgid "Service not defined" -msgstr "Dienst nicht definiert" - -msgid "Obsolete SNAC" -msgstr "Obsoleter SNAC" - -msgid "Not supported by host" -msgstr "Nicht unterstützt vom Host" - -msgid "Not supported by client" -msgstr "Nicht unterstützt vom Client" - -msgid "Refused by client" -msgstr "Abgelehnt vom Client" - -msgid "Reply too big" -msgstr "Antwort zu groß" - -msgid "Responses lost" -msgstr "Antworten verloren" - -msgid "Request denied" -msgstr "Anfrage verweigert" - -msgid "Busted SNAC payload" -msgstr "Ruinierte SNAC-Daten" - -msgid "Insufficient rights" -msgstr "Ungenügende Rechte" - -msgid "In local permit/deny" -msgstr "In lokaler erlaubt/verboten-Liste" - -msgid "Warning level too high (sender)" -msgstr "Warnstufe zu hoch (Absender)" - -msgid "Warning level too high (receiver)" -msgstr "Warnstufe zu hoch (Empfänger)" - -msgid "User temporarily unavailable" -msgstr "Benutzer ist temporär nicht verfügbar" - -msgid "No match" -msgstr "Keine Übereinstimmung" - -msgid "List overflow" -msgstr "Listenüberlauf" - -msgid "Request ambiguous" -msgstr "Anfrage ist nicht eindeutig" - -msgid "Queue full" -msgstr "Warteschlange voll" - -msgid "Not while on AOL" -msgstr "Nicht solange bei AOL angemeldet" - #. Label msgid "Buddy Icon" msgstr "Buddy-Icon" @@ -8148,6 +8089,75 @@ msgid "Capabilities" msgstr "Fähigkeiten" +msgid "Invalid SNAC" +msgstr "Ungültiger SNAC" + +msgid "Server rate limit exceeded" +msgstr "Server-Datenrate überschritten" + +msgid "Client rate limit exceeded" +msgstr "Client-Datenrate überschritten" + +msgid "Service unavailable" +msgstr "Dienst nicht verfügbar" + +msgid "Service not defined" +msgstr "Dienst nicht definiert" + +msgid "Obsolete SNAC" +msgstr "Obsoleter SNAC" + +msgid "Not supported by host" +msgstr "Nicht unterstützt vom Host" + +msgid "Not supported by client" +msgstr "Nicht unterstützt vom Client" + +msgid "Refused by client" +msgstr "Abgelehnt vom Client" + +msgid "Reply too big" +msgstr "Antwort zu groß" + +msgid "Responses lost" +msgstr "Antworten verloren" + +msgid "Request denied" +msgstr "Anfrage verweigert" + +msgid "Busted SNAC payload" +msgstr "Ruinierte SNAC-Daten" + +msgid "Insufficient rights" +msgstr "Ungenügende Rechte" + +msgid "In local permit/deny" +msgstr "In lokaler erlaubt/verboten-Liste" + +msgid "Warning level too high (sender)" +msgstr "Warnstufe zu hoch (Absender)" + +msgid "Warning level too high (receiver)" +msgstr "Warnstufe zu hoch (Empfänger)" + +msgid "User temporarily unavailable" +msgstr "Benutzer ist temporär nicht verfügbar" + +msgid "No match" +msgstr "Keine Übereinstimmung" + +msgid "List overflow" +msgstr "Listenüberlauf" + +msgid "Request ambiguous" +msgstr "Anfrage ist nicht eindeutig" + +msgid "Queue full" +msgstr "Warteschlange voll" + +msgid "Not while on AOL" +msgstr "Nicht solange bei AOL angemeldet" + #. Translators: This string is a menu option that, if selected, will cause #. you to appear online to the chosen user even when your status is set to #. Invisible.