pidgin/pidgin

Merged pidgin/main into default

2017-06-05, Arkadiy Illarionov
826f5da7b56c
Merged pidgin/main into default
  • +0 -11
    .hgignore
  • +1 -0
    ChangeLog.API
  • +0 -7
    Makefile.am
  • +0 -21
    config.h.mingw
  • +0 -9
    doc/pidgin.1.in
  • +1 -0
    doc/reference/finch/Makefile.am
  • +0 -4
    doc/reference/libpurple/libpurple-docs.xml
  • +1 -0
    doc/reference/pidgin/Makefile.am
  • +1 -9
    libpurple/Makefile.am
  • +0 -4
    libpurple/Makefile.mingw
  • +0 -341
    libpurple/ciphers/hmaccipher.c
  • +0 -67
    libpurple/ciphers/hmaccipher.h
  • +0 -191
    libpurple/ciphers/md5hash.c
  • +0 -65
    libpurple/ciphers/md5hash.h
  • +39 -67
    libpurple/ciphers/pbkdf2cipher.c
  • +2 -2
    libpurple/ciphers/pbkdf2cipher.h
  • +0 -191
    libpurple/ciphers/sha1hash.c
  • +0 -65
    libpurple/ciphers/sha1hash.h
  • +0 -191
    libpurple/ciphers/sha256hash.c
  • +0 -65
    libpurple/ciphers/sha256hash.h
  • +1 -1
    libpurple/http.c
  • +1 -1
    libpurple/mime.c
  • +3 -3
    libpurple/ntlm.c
  • +0 -8
    libpurple/plugins/Makefile.mingw
  • +5 -9
    libpurple/plugins/keyrings/internalkeyring.c
  • +6 -6
    libpurple/protocols/bonjour/bonjour_ft.c
  • +1 -1
    libpurple/protocols/gg/avatar.c
  • +7 -14
    libpurple/protocols/gg/oauth/oauth.c
  • +2 -2
    libpurple/protocols/irc/msgs.c
  • +8 -16
    libpurple/protocols/jabber/auth.c
  • +5 -5
    libpurple/protocols/jabber/auth_cyrus.c
  • +13 -28
    libpurple/protocols/jabber/auth_digest_md5.c
  • +1 -1
    libpurple/protocols/jabber/auth_plain.c
  • +39 -45
    libpurple/protocols/jabber/auth_scram.c
  • +1 -4
    libpurple/protocols/jabber/auth_scram.h
  • +11 -10
    libpurple/protocols/jabber/buddy.c
  • +37 -29
    libpurple/protocols/jabber/caps.c
  • +3 -3
    libpurple/protocols/jabber/caps.h
  • +21 -7
    libpurple/protocols/jabber/data.c
  • +2 -2
    libpurple/protocols/jabber/ibb.c
  • +3 -3
    libpurple/protocols/jabber/jabber.c
  • +0 -37
    libpurple/protocols/jabber/jutil.c
  • +0 -2
    libpurple/protocols/jabber/jutil.h
  • +3 -2
    libpurple/protocols/jabber/presence.c
  • +3 -2
    libpurple/protocols/jabber/si.c
  • +3 -5
    libpurple/protocols/jabber/tests/test_jabber_caps.c
  • +1 -2
    libpurple/protocols/jabber/tests/test_jabber_scram.c
  • +5 -4
    libpurple/protocols/jabber/useravatar.c
  • +0 -256
    libpurple/protocols/msn/ft.c
  • +0 -71
    libpurple/protocols/msn/ft.h
  • +0 -83
    libpurple/protocols/mxit/cipher-mxit.c
  • +0 -31
    libpurple/protocols/mxit/cipher-mxit.h
  • +0 -2968
    libpurple/protocols/mxit/client.c
  • +0 -352
    libpurple/protocols/mxit/client.h
  • +8 -14
    libpurple/protocols/oscar/clientlogin.c
  • +19 -19
    libpurple/protocols/oscar/family_auth.c
  • +6 -6
    libpurple/protocols/oscar/oscar.c
  • +1 -1
    libpurple/protocols/sametime/sametime.c
  • +0 -7
    libpurple/protocols/yahoo/tests/.hgignore
  • +0 -23
    libpurple/protocols/yahoo/tests/Makefile.am
  • +0 -250
    libpurple/protocols/yahoo/tests/test_yahoo_util.c
  • +0 -415
    libpurple/protocols/yahoo/yahoo.c
  • +0 -51
    libpurple/protocols/yahoo/yahoo.h
  • +0 -131
    libpurple/protocols/yahoo/yahoojp.c
  • +0 -57
    libpurple/protocols/yahoo/yahoojp.h
  • +0 -4989
    libpurple/protocols/yahoo/ymsg.c
  • +0 -363
    libpurple/protocols/yahoo/ymsg.h
  • +0 -1
    libpurple/proxy.c
  • +0 -15
    libpurple/tag.sh
  • +0 -16
    libpurple/tests/Makefile.am
  • +0 -401
    libpurple/tests/test_hmac.c
  • +0 -107
    libpurple/tests/test_md5.c
  • +0 -101
    libpurple/tests/test_sha1.c
  • +0 -101
    libpurple/tests/test_sha256.c
  • +0 -23
    libpurple/tests/test_util.c
  • +6 -7
    libpurple/tls-certificate-info.c
  • +68 -97
    libpurple/util.c
  • +0 -37
    libpurple/util.h
  • +0 -5
    libpurple/win32/global.mak
  • +0 -3
    libpurple/win32/rules.mak
  • +0 -9
    libpurple/win32/targets.mak
  • +0 -19
    pidgin/gtkdialogs.c
  • +1 -2
    pidgin/gtkwebview.c
  • +0 -4
    pidgin/plugins/Makefile.mingw
  • +1 -1
    pidgin/plugins/imgupload.c
  • +0 -106
    pidgin/plugins/perl/Makefile.am
  • +0 -28
    pidgin/plugins/perl/Makefile.mingw
  • +0 -24
    pidgin/plugins/perl/common/GtkAccount.xs
  • +0 -71
    pidgin/plugins/perl/common/GtkBlist.xs
  • +0 -7
    pidgin/plugins/perl/common/GtkConn.xs
  • +0 -67
    pidgin/plugins/perl/common/GtkConv.xs
  • +0 -139
    pidgin/plugins/perl/common/GtkConvWin.xs
  • +0 -16
    pidgin/plugins/perl/common/GtkDebug.xs
  • +0 -48
    pidgin/plugins/perl/common/GtkDialogs.xs
  • +0 -50
    pidgin/plugins/perl/common/GtkFt.xs
  • +0 -23
    pidgin/plugins/perl/common/GtkLog.xs
  • +0 -33
    pidgin/plugins/perl/common/GtkMenuTray.xs
  • +0 -13
    pidgin/plugins/perl/common/GtkPlugin.xs
  • +0 -4
    pidgin/plugins/perl/common/GtkPluginPref.xs
  • +0 -25
    pidgin/plugins/perl/common/GtkPounce.xs
  • +0 -7
    pidgin/plugins/perl/common/GtkPrefs.xs
  • +0 -23
    pidgin/plugins/perl/common/GtkPrivacy.xs
  • +0 -17
    pidgin/plugins/perl/common/GtkRoomlist.xs
  • +0 -24
    pidgin/plugins/perl/common/GtkSavedStatuses.xs
  • +0 -13
    pidgin/plugins/perl/common/GtkSession.xs
  • +0 -15
    pidgin/plugins/perl/common/GtkSound.xs
  • +0 -41
    pidgin/plugins/perl/common/GtkStatusBox.xs
  • +0 -17
    pidgin/plugins/perl/common/GtkUtils.xs
  • +0 -25
    pidgin/plugins/perl/common/MANIFEST
  • +0 -28
    pidgin/plugins/perl/common/Makefile.PL.in
  • +0 -117
    pidgin/plugins/perl/common/Makefile.mingw
  • +0 -59
    pidgin/plugins/perl/common/Pidgin.pm
  • +0 -60
    pidgin/plugins/perl/common/Pidgin.xs
  • +0 -70
    pidgin/plugins/perl/common/gtkmodule.h
  • +0 -12
    pidgin/plugins/perl/common/typemap
  • +1 -1
    pidgin/plugins/raw.c
  • +0 -14
    pidgin/win32/nsis/pidgin-installer.nsi
  • +0 -132
    po/POTFILES.skip
  • --- a/.hgignore Sun May 28 13:26:27 2017 +0300
    +++ b/.hgignore Mon Jun 05 16:36:29 2017 +0300
    @@ -11,10 +11,6 @@
    .*/?.*\.pc$
    .*/?Makefile(\.in)?$
    .*/?Makefile\.am\.mingw$
    -.*/perl/common/[^/]+\.c$
    -.*/perl/common/blib.*
    -.*/perl/common/pm_to_blib$
    -.*/perl/common/MYMETA\.(json|yml)
    .*~$
    .*\.a$
    .*\.asc$
    @@ -81,11 +77,6 @@
    libpurple/data/purple-url-handler.desktop.in$
    libpurple/marshallers.[ch]
    libpurple/plugins/dbus-example-bindings.c
    -libpurple/plugins/perl/common/Makefile.PL$
    -libpurple/plugins/perl/common/Makefile.old
    -libpurple/plugins/perl/common/const-c.inc
    -libpurple/plugins/perl/common/const-xs.inc
    -libpurple/plugins/perl/common/lib
    libpurple/protocols/facebook/marshal.[ch]
    libpurple/purple-client-bindings.[ch]
    libpurple/purple-client-example
    @@ -126,8 +117,6 @@
    pidgin/pixmaps/emotes/none/theme
    pidgin/pixmaps/emotes/small/16/theme
    pidgin/plugins/musicmessaging/music-messaging-bindings.c
    -pidgin/plugins/perl/common/Makefile.PL$
    -pidgin/plugins/perl/common/Makefile.old
    pidgin/win32/nsis/gtk-runtime-*.*.*.*.zip
    pidgin/win32/nsis/gtk_runtime_stage$
    pidgin/win32/nsis/cacert.pem
    --- a/ChangeLog.API Sun May 28 13:26:27 2017 +0300
    +++ b/ChangeLog.API Mon Jun 05 16:36:29 2017 +0300
    @@ -417,6 +417,7 @@
    * purple_account_add_buddies_with_invite
    * purple_account_add_buddy_with_invite
    * purple_account_set_current_error
    + * purple_base64_*. Use g_base64_* instead
    * purple_blist_load
    * purple_blist_new
    * purple_set_blist
    --- a/Makefile.am Sun May 28 13:26:27 2017 +0300
    +++ b/Makefile.am Mon Jun 05 16:36:29 2017 +0300
    @@ -138,12 +138,5 @@
    SUBDIRS = . m4macros libpurple $(GNT_DIR) $(GTK_DIR) $(PO_DIR) share/ca-certs share/sounds doc
    -# perl's MakeMaker uninstall foo doesn't work well with DESTDIR set, which
    -# breaks "make distcheck" unless we ignore perl things
    -# TODO drop it when we drop perl wrapper
    -
    -distuninstallcheck_listfiles = \
    - find . -type f -print | grep -v perl | grep -v Purple.3pm | grep -v Pidgin.3pm
    -
    DISTCLEANFILES= intltool-extract intltool-merge intltool-update \
    package_revision_raw.txt
    --- a/config.h.mingw Sun May 28 13:26:27 2017 +0300
    +++ b/config.h.mingw Mon Jun 05 16:36:29 2017 +0300
    @@ -180,18 +180,6 @@
    /* Define to 1 if you have the <paths.h> header file. */
    /* #define HAVE_PATHS_H 1 */
    -/* Compile with support for perl */
    -#define HAVE_PERL 1
    -
    -/* Define to 1 if you have the `Perl_eval_pv' function. */
    -/* #undef HAVE_PERL_EVAL_PV */
    -
    -/* Define to 1 if you have the <perl.h> header file. */
    -#define HAVE_PERL_H 1
    -
    -/* Define to 1 if you have the `perl_run' function. */
    -/* #define HAVE_PERL_RUN 1 */
    -
    /* Define to 1 if you have the <prio.h> header file. */
    /* #undef HAVE_PRIO_H */
    @@ -273,9 +261,6 @@
    /* Define to 1 if you have the <sys/wait.h> header file. */
    /* #define HAVE_SYS_WAIT_H 1 */
    -/* Compile with support for the Tcl toolkit */
    -/* #define HAVE_TCL 1 */
    -
    /* Define to 1 if you have the <termios.h> header file. */
    /* #define HAVE_TERMIOS_H 1 */
    @@ -285,9 +270,6 @@
    /* Define if you have the external 'timezone' variable. */
    #define HAVE_TIMEZONE 1
    -/* Compile with support for the Tk toolkit */
    -#define HAVE_TK 1
    -
    /* Define if you have a tm_gmtoff member in struct tm */
    /* #define HAVE_TM_GMTOFF 1 */
    @@ -321,9 +303,6 @@
    /* Define to 1 if you don't have wide-character support. */
    /* #undef NO_WIDECHAR */
    -/* Define if old perl is installed. */
    -/* #undef OLD_PERL */
    -
    /* Name of package */
    #define PACKAGE "pidgin"
    --- a/doc/pidgin.1.in Sun May 28 13:26:27 2017 +0300
    +++ b/doc/pidgin.1.in Mon Jun 05 16:36:29 2017 +0300
    @@ -520,15 +520,6 @@
    configuration options, the \fIConfigure Plugin\fR button will present the
    plugin's preferences dialog.
    -.SH PERL
    -Pidgin allows for plugins to be written in the perl scripting language. See
    -\fIPerl Scripting HOWTO\fR in the Pidgin documentation for more information
    -about perl scripting.
    -
    -.SH TCL
    -Pidgin allows for plugins to be written in the Tcl scripting language. See
    -\fIplugins/tcl/TCL-HOWTO\fR for more information about Tcl scripting.
    -
    .SH D-Bus
    Pidgin allows for interaction via D-Bus. Currently very little documentation
    about this interaction exists.
    --- a/doc/reference/finch/Makefile.am Sun May 28 13:26:27 2017 +0300
    +++ b/doc/reference/finch/Makefile.am Mon Jun 05 16:36:29 2017 +0300
    @@ -103,6 +103,7 @@
    GTKDOC_LIBS = \
    $(top_builddir)/finch/libfinch.la \
    + $(top_builddir)/libpurple/libpurple.la \
    $(DBUS_LIBS) \
    $(INTLLIBS) \
    $(GLIB_LIBS) \
    --- a/doc/reference/libpurple/libpurple-docs.xml Sun May 28 13:26:27 2017 +0300
    +++ b/doc/reference/libpurple/libpurple-docs.xml Mon Jun 05 16:36:29 2017 +0300
    @@ -111,13 +111,9 @@
    <xi:include href="xml/aescipher.xml" />
    <xi:include href="xml/descipher.xml" />
    <xi:include href="xml/des3cipher.xml" />
    - <xi:include href="xml/hmaccipher.xml" />
    <xi:include href="xml/pbkdf2cipher.xml" />
    <xi:include href="xml/rc4cipher.xml" />
    <xi:include href="xml/md4hash.xml" />
    - <xi:include href="xml/md5hash.xml" />
    - <xi:include href="xml/sha1hash.xml" />
    - <xi:include href="xml/sha256hash.xml" />
    </chapter>
    <chapter id="smiley">
    --- a/doc/reference/pidgin/Makefile.am Sun May 28 13:26:27 2017 +0300
    +++ b/doc/reference/pidgin/Makefile.am Mon Jun 05 16:36:29 2017 +0300
    @@ -111,6 +111,7 @@
    GTKDOC_LIBS = \
    $(top_builddir)/pidgin/libpidgin.la \
    + $(top_builddir)/libpurple/libpurple.la \
    $(GLIB_LIBS) \
    $(GPLUGIN_LIBS) \
    $(GCR_LIBS) \
    --- a/libpurple/Makefile.am Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/Makefile.am Mon Jun 05 16:36:29 2017 +0300
    @@ -44,13 +44,9 @@
    ciphers/aescipher.c \
    ciphers/descipher.c \
    ciphers/des3cipher.c \
    - ciphers/hmaccipher.c \
    ciphers/md4hash.c \
    - ciphers/md5hash.c \
    ciphers/pbkdf2cipher.c \
    ciphers/rc4cipher.c \
    - ciphers/sha1hash.c \
    - ciphers/sha256hash.c \
    cipher.c \
    circularbuffer.c \
    cmds.c \
    @@ -245,13 +241,9 @@
    aescipher.h \
    descipher.h \
    des3cipher.h \
    - hmaccipher.h \
    md4hash.h \
    - md5hash.h \
    pbkdf2cipher.h \
    - rc4cipher.h \
    - sha1hash.h \
    - sha256hash.h
    + rc4cipher.h
    purple_builtheaders = purple.h version.h enums.h marshallers.h
    --- a/libpurple/Makefile.mingw Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/Makefile.mingw Mon Jun 05 16:36:29 2017 +0300
    @@ -71,13 +71,9 @@
    ciphers/aescipher.c \
    ciphers/descipher.c \
    ciphers/des3cipher.c \
    - ciphers/hmaccipher.c \
    ciphers/md4hash.c \
    - ciphers/md5hash.c \
    ciphers/pbkdf2cipher.c \
    ciphers/rc4cipher.c \
    - ciphers/sha1hash.c \
    - ciphers/sha256hash.c \
    cipher.c \
    circularbuffer.c \
    cmds.c \
    --- a/libpurple/ciphers/hmaccipher.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,341 +0,0 @@
    -/*
    - * 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 "glibcompat.h"
    -
    -#include "hmaccipher.h"
    -
    -#include <string.h>
    -
    -/*******************************************************************************
    - * Structs
    - ******************************************************************************/
    -#define PURPLE_HMAC_CIPHER_GET_PRIVATE(obj) \
    - (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_HMAC_CIPHER, PurpleHMACCipherPrivate))
    -
    -typedef struct {
    - PurpleHash *hash;
    - guchar *ipad;
    - guchar *opad;
    -} PurpleHMACCipherPrivate;
    -
    -/******************************************************************************
    - * Enums
    - *****************************************************************************/
    -enum {
    - PROP_NONE,
    - PROP_HASH,
    - PROP_LAST,
    -};
    -
    -/*******************************************************************************
    - * Globals
    - ******************************************************************************/
    -static GObjectClass *parent_class = NULL;
    -static GParamSpec *properties[PROP_LAST];
    -
    -/*******************************************************************************
    - * Helpers
    - ******************************************************************************/
    -static void
    -purple_hmac_cipher_set_hash(PurpleCipher *cipher,
    - PurpleHash *hash)
    -{
    - PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
    -
    - priv->hash = g_object_ref(G_OBJECT(hash));
    -
    - g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_HASH]);
    -}
    -
    -/*******************************************************************************
    - * Cipher Stuff
    - ******************************************************************************/
    -static void
    -purple_hmac_cipher_reset(PurpleCipher *cipher) {
    - PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
    -
    - if(PURPLE_IS_HASH(priv->hash))
    - purple_hash_reset(priv->hash);
    -
    - g_free(priv->ipad);
    - priv->ipad = NULL;
    - g_free(priv->opad);
    - priv->opad = NULL;
    -}
    -
    -static void
    -purple_hmac_cipher_reset_state(PurpleCipher *cipher) {
    - PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
    -
    - if(PURPLE_IS_HASH(priv->hash)) {
    - purple_hash_reset_state(priv->hash);
    - purple_hash_append(priv->hash, priv->ipad,
    - purple_hash_get_block_size(priv->hash));
    - }
    -}
    -
    -static void
    -purple_hmac_cipher_append(PurpleCipher *cipher, const guchar *d, size_t l) {
    - PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
    -
    - g_return_if_fail(PURPLE_IS_HASH(priv->hash));
    -
    - purple_hash_append(priv->hash, d, l);
    -}
    -
    -static gboolean
    -purple_hmac_cipher_digest(PurpleCipher *cipher, guchar *out, size_t len)
    -{
    - PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
    - guchar *digest = NULL;
    - size_t hash_len, block_size;
    - gboolean result = FALSE;
    -
    - g_return_val_if_fail(PURPLE_IS_HASH(priv->hash), FALSE);
    -
    - hash_len = purple_hash_get_digest_size(priv->hash);
    - g_return_val_if_fail(hash_len > 0, FALSE);
    -
    - block_size = purple_hash_get_block_size(priv->hash);
    - digest = g_malloc(hash_len);
    -
    - /* get the digest of the data */
    - result = purple_hash_digest(priv->hash, digest, hash_len);
    - purple_hash_reset(priv->hash);
    -
    - if(!result) {
    - g_free(digest);
    -
    - return FALSE;
    - }
    -
    - /* now append the opad and the digest from above */
    - purple_hash_append(priv->hash, priv->opad, block_size);
    - purple_hash_append(priv->hash, digest, hash_len);
    -
    - /* do our last digest */
    - result = purple_hash_digest(priv->hash, out, len);
    -
    - /* cleanup */
    - g_free(digest);
    -
    - return result;
    -}
    -
    -static size_t
    -purple_hmac_cipher_get_digest_size(PurpleCipher *cipher)
    -{
    - PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
    -
    - g_return_val_if_fail(priv->hash != NULL, 0);
    -
    - return purple_hash_get_digest_size(priv->hash);
    -}
    -
    -static void
    -purple_hmac_cipher_set_key(PurpleCipher *cipher, const guchar *key,
    - size_t key_len)
    -{
    - PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
    - gsize block_size, i;
    - guchar *full_key;
    -
    - g_return_if_fail(priv->hash);
    -
    - g_free(priv->ipad);
    - g_free(priv->opad);
    -
    - block_size = purple_hash_get_block_size(priv->hash);
    - priv->ipad = g_malloc(block_size);
    - priv->opad = g_malloc(block_size);
    -
    - if (key_len > block_size) {
    - purple_hash_reset(priv->hash);
    - purple_hash_append(priv->hash, key, key_len);
    -
    - key_len = purple_hash_get_digest_size(priv->hash);
    - full_key = g_malloc(key_len);
    - purple_hash_digest(priv->hash, full_key, key_len);
    - } else {
    - full_key = g_memdup(key, key_len);
    - }
    -
    - if (key_len < block_size) {
    - full_key = g_realloc(full_key, block_size);
    - memset(full_key + key_len, 0, block_size - key_len);
    - }
    -
    - for(i = 0; i < block_size; i++) {
    - priv->ipad[i] = 0x36 ^ full_key[i];
    - priv->opad[i] = 0x5c ^ full_key[i];
    - }
    -
    - g_free(full_key);
    -
    - purple_hash_reset(priv->hash);
    - purple_hash_append(priv->hash, priv->ipad, block_size);
    -}
    -
    -static size_t
    -purple_hmac_cipher_get_block_size(PurpleCipher *cipher)
    -{
    - PurpleHMACCipherPrivate *priv = NULL;
    -
    - g_return_val_if_fail(PURPLE_IS_HMAC_CIPHER(cipher), 0);
    -
    - priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
    -
    - return purple_hash_get_block_size(priv->hash);
    -}
    -
    -/******************************************************************************
    - * Object Stuff
    - *****************************************************************************/
    -static void
    -purple_hmac_cipher_set_property(GObject *obj, guint param_id,
    - const GValue *value,
    - GParamSpec *pspec)
    -{
    - PurpleCipher *cipher = PURPLE_CIPHER(obj);
    -
    - switch(param_id) {
    - case PROP_HASH:
    - purple_hmac_cipher_set_hash(cipher, g_value_get_object(value));
    - break;
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    - break;
    - }
    -}
    -
    -static void
    -purple_hmac_cipher_get_property(GObject *obj, guint param_id, GValue *value,
    - GParamSpec *pspec)
    -{
    - PurpleHMACCipher *cipher = PURPLE_HMAC_CIPHER(obj);
    -
    - switch(param_id) {
    - case PROP_HASH:
    - g_value_set_object(value,
    - purple_hmac_cipher_get_hash(cipher));
    - break;
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    - break;
    - }
    -}
    -
    -static void
    -purple_hmac_cipher_finalize(GObject *obj) {
    - PurpleCipher *cipher = PURPLE_CIPHER(obj);
    - PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
    -
    - purple_hmac_cipher_reset(cipher);
    -
    - if (priv->hash != NULL)
    - g_object_unref(priv->hash);
    -
    - parent_class->finalize(obj);
    -}
    -
    -static void
    -purple_hmac_cipher_class_init(PurpleHMACCipherClass *klass) {
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    - PurpleCipherClass *cipher_class = PURPLE_CIPHER_CLASS(klass);
    -
    - parent_class = g_type_class_peek_parent(klass);
    -
    - g_type_class_add_private(klass, sizeof(PurpleHMACCipherPrivate));
    -
    - obj_class->finalize = purple_hmac_cipher_finalize;
    - obj_class->get_property = purple_hmac_cipher_get_property;
    - obj_class->set_property = purple_hmac_cipher_set_property;
    -
    - cipher_class->reset = purple_hmac_cipher_reset;
    - cipher_class->reset_state = purple_hmac_cipher_reset_state;
    - cipher_class->append = purple_hmac_cipher_append;
    - cipher_class->digest = purple_hmac_cipher_digest;
    - cipher_class->get_digest_size = purple_hmac_cipher_get_digest_size;
    - cipher_class->set_key = purple_hmac_cipher_set_key;
    - cipher_class->get_block_size = purple_hmac_cipher_get_block_size;
    -
    - properties[PROP_HASH] = g_param_spec_object("hash", "hash", "hash",
    - PURPLE_TYPE_HASH,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
    - G_PARAM_STATIC_STRINGS);
    -
    - g_object_class_install_properties(obj_class, PROP_LAST, properties);
    -}
    -
    -/******************************************************************************
    - * PurpleHMACCipher API
    - *****************************************************************************/
    -GType
    -purple_hmac_cipher_get_type(void) {
    - static GType type = 0;
    -
    - if(type == 0) {
    - static const GTypeInfo info = {
    - sizeof(PurpleHMACCipherClass),
    - NULL,
    - NULL,
    - (GClassInitFunc)purple_hmac_cipher_class_init,
    - NULL,
    - NULL,
    - sizeof(PurpleHMACCipher),
    - 0,
    - (GInstanceInitFunc)purple_cipher_reset,
    - NULL,
    - };
    -
    - type = g_type_register_static(PURPLE_TYPE_CIPHER,
    - "PurpleHMACCipher",
    - &info, 0);
    - }
    -
    - return type;
    -}
    -
    -PurpleCipher *
    -purple_hmac_cipher_new(PurpleHash *hash) {
    - g_return_val_if_fail(PURPLE_IS_HASH(hash), NULL);
    -
    - return g_object_new(PURPLE_TYPE_HMAC_CIPHER,
    - "hash", hash,
    - NULL);
    -}
    -
    -PurpleHash *
    -purple_hmac_cipher_get_hash(const PurpleHMACCipher *cipher) {
    - PurpleHMACCipherPrivate *priv = NULL;
    -
    - g_return_val_if_fail(PURPLE_IS_HMAC_CIPHER(cipher), NULL);
    -
    - priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
    -
    - if(priv && priv->hash)
    - return priv->hash;
    -
    - return NULL;
    -}
    -
    --- a/libpurple/ciphers/hmaccipher.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,67 +0,0 @@
    -/* 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 PURPLE_HMAC_CIPHER_H
    -#define PURPLE_HMAC_CIPHER_H
    -/**
    - * SECTION:hmaccipher
    - * @section_id: libpurple-hmaccipher
    - * @short_description: <filename>ciphers/hmaccipher.h</filename>
    - * @title: HMAC Cipher
    - */
    -
    -#include "cipher.h"
    -
    -#define PURPLE_TYPE_HMAC_CIPHER (purple_hmac_cipher_get_type())
    -#define PURPLE_HMAC_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_HMAC_CIPHER, PurpleHMACCipher))
    -#define PURPLE_HMAC_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_HMAC_CIPHER, PurpleHMACCipherClass))
    -#define PURPLE_IS_HMAC_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_HMAC_CIPHER))
    -#define PURPLE_IS_HMAC_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_HMAC_CIPHER))
    -#define PURPLE_HMAC_CIPHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_HMAC_CIPHER, PurpleHMACCipherClass))
    -
    -typedef struct _PurpleHMACCipher PurpleHMACCipher;
    -typedef struct _PurpleHMACCipherClass PurpleHMACCipherClass;
    -
    -struct _PurpleHMACCipher {
    - PurpleCipher gparent;
    -};
    -
    -struct _PurpleHMACCipherClass {
    - PurpleCipherClass gparent;
    -
    - /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    -};
    -
    -G_BEGIN_DECLS
    -
    -GType purple_hmac_cipher_get_type(void);
    -
    -PurpleCipher *purple_hmac_cipher_new(PurpleHash *hash);
    -
    -PurpleHash *purple_hmac_cipher_get_hash(const PurpleHMACCipher *cipher);
    -
    -G_END_DECLS
    -
    -#endif /* PURPLE_HMAC_CIPHER_H */
    --- a/libpurple/ciphers/md5hash.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,191 +0,0 @@
    -/*
    - * 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 "md5hash.h"
    -
    -/*******************************************************************************
    - * Structs
    - ******************************************************************************/
    -#define PURPLE_MD5_HASH_GET_PRIVATE(obj) \
    - (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MD5_HASH, PurpleMD5HashPrivate))
    -
    -typedef struct {
    - GChecksum *checksum;
    -} PurpleMD5HashPrivate;
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -static GObjectClass *parent_class = NULL;
    -
    -/******************************************************************************
    - * Hash Stuff
    - *****************************************************************************/
    -
    -static void
    -purple_md5_hash_reset(PurpleHash *hash)
    -{
    - PurpleMD5Hash *md5_hash = PURPLE_MD5_HASH(hash);
    - PurpleMD5HashPrivate *priv = PURPLE_MD5_HASH_GET_PRIVATE(md5_hash);
    -
    - g_return_if_fail(priv != NULL);
    - g_return_if_fail(priv->checksum != NULL);
    -
    - g_checksum_reset(priv->checksum);
    -}
    -
    -static void
    -purple_md5_hash_append(PurpleHash *hash, const guchar *data,
    - gsize len)
    -{
    - PurpleMD5Hash *md5_hash = PURPLE_MD5_HASH(hash);
    - PurpleMD5HashPrivate *priv = PURPLE_MD5_HASH_GET_PRIVATE(md5_hash);
    -
    - g_return_if_fail(priv != NULL);
    - g_return_if_fail(priv->checksum != NULL);
    -
    - while (len >= G_MAXSSIZE) {
    - g_checksum_update(priv->checksum, data, G_MAXSSIZE);
    - len -= G_MAXSSIZE;
    - data += G_MAXSSIZE;
    - }
    -
    - if (len)
    - g_checksum_update(priv->checksum, data, len);
    -}
    -
    -static gboolean
    -purple_md5_hash_digest(PurpleHash *hash, guchar *digest, size_t buff_len)
    -{
    - PurpleMD5Hash *md5_hash = PURPLE_MD5_HASH(hash);
    - PurpleMD5HashPrivate *priv = PURPLE_MD5_HASH_GET_PRIVATE(md5_hash);
    -
    - const gssize required_len = g_checksum_type_get_length(G_CHECKSUM_MD5);
    - gsize digest_len = buff_len;
    -
    - g_return_val_if_fail(priv != NULL, FALSE);
    - g_return_val_if_fail(priv->checksum != NULL, FALSE);
    -
    - g_return_val_if_fail(required_len >= 0, FALSE);
    - g_return_val_if_fail(buff_len >= (gsize)required_len, FALSE);
    -
    - g_checksum_get_digest(priv->checksum, digest, &digest_len);
    -
    - if (digest_len != (gsize)required_len)
    - return FALSE;
    -
    - purple_md5_hash_reset(hash);
    -
    - return TRUE;
    -}
    -
    -static size_t
    -purple_md5_hash_get_block_size(PurpleHash *hash)
    -{
    - return 64;
    -}
    -
    -static size_t
    -purple_md5_hash_get_digest_size(PurpleHash *hash)
    -{
    - return g_checksum_type_get_length(G_CHECKSUM_MD5);
    -}
    -
    -/******************************************************************************
    - * Object Stuff
    - *****************************************************************************/
    -
    -static void
    -purple_md5_hash_finalize(GObject *obj)
    -{
    - PurpleMD5Hash *md5_hash = PURPLE_MD5_HASH(obj);
    - PurpleMD5HashPrivate *priv = PURPLE_MD5_HASH_GET_PRIVATE(md5_hash);
    -
    - if (priv->checksum)
    - g_checksum_free(priv->checksum);
    -
    - parent_class->finalize(obj);
    -}
    -
    -static void
    -purple_md5_hash_class_init(PurpleMD5HashClass *klass) {
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    - PurpleHashClass *hash_class = PURPLE_HASH_CLASS(klass);
    -
    - parent_class = g_type_class_peek_parent(klass);
    -
    - obj_class->finalize = purple_md5_hash_finalize;
    -
    - hash_class->reset = purple_md5_hash_reset;
    - hash_class->reset_state = purple_md5_hash_reset;
    - hash_class->append = purple_md5_hash_append;
    - hash_class->digest = purple_md5_hash_digest;
    - hash_class->get_digest_size = purple_md5_hash_get_digest_size;
    - hash_class->get_block_size = purple_md5_hash_get_block_size;
    -
    - g_type_class_add_private(klass, sizeof(PurpleMD5HashPrivate));
    -}
    -
    -static void
    -purple_md5_hash_init(PurpleHash *hash)
    -{
    - PurpleMD5Hash *md5_hash = PURPLE_MD5_HASH(hash);
    - PurpleMD5HashPrivate *priv = PURPLE_MD5_HASH_GET_PRIVATE(md5_hash);
    -
    - priv->checksum = g_checksum_new(G_CHECKSUM_MD5);
    -
    - purple_md5_hash_reset(hash);
    -}
    -
    -/******************************************************************************
    - * API
    - *****************************************************************************/
    -GType
    -purple_md5_hash_get_type(void) {
    - static GType type = 0;
    -
    - if(type == 0) {
    - static const GTypeInfo info = {
    - sizeof(PurpleMD5HashClass),
    - NULL,
    - NULL,
    - (GClassInitFunc)purple_md5_hash_class_init,
    - NULL,
    - NULL,
    - sizeof(PurpleMD5Hash),
    - 0,
    - (GInstanceInitFunc)purple_md5_hash_init,
    - NULL,
    - };
    -
    - type = g_type_register_static(PURPLE_TYPE_HASH,
    - "PurpleMD5Hash",
    - &info, 0);
    - }
    -
    - return type;
    -}
    -
    -PurpleHash *
    -purple_md5_hash_new(void) {
    - return g_object_new(PURPLE_TYPE_MD5_HASH, NULL);
    -}
    --- a/libpurple/ciphers/md5hash.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,65 +0,0 @@
    -/* 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 PURPLE_MD5_HASH_H
    -#define PURPLE_MD5_HASH_H
    -/**
    - * SECTION:md5hash
    - * @section_id: libpurple-md5hash
    - * @short_description: <filename>ciphers/md5hash.h</filename>
    - * @title: MD5 Hash
    - */
    -
    -#include "cipher.h"
    -
    -#define PURPLE_TYPE_MD5_HASH (purple_md5_hash_get_type())
    -#define PURPLE_MD5_HASH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MD5_HASH, PurpleMD5Hash))
    -#define PURPLE_MD5_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MD5_HASH, PurpleMD5HashClass))
    -#define PURPLE_IS_MD5_HASH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MD5_HASH))
    -#define PURPLE_IS_MD5_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_MD5_HASH))
    -#define PURPLE_MD5_HASH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MD5_HASH, PurpleMD5HashClass))
    -
    -typedef struct _PurpleMD5Hash PurpleMD5Hash;
    -typedef struct _PurpleMD5HashClass PurpleMD5HashClass;
    -
    -struct _PurpleMD5Hash {
    - PurpleHash gparent;
    -};
    -
    -struct _PurpleMD5HashClass {
    - PurpleHashClass gparent;
    -
    - /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    -};
    -
    -G_BEGIN_DECLS
    -
    -GType purple_md5_hash_get_type(void);
    -
    -PurpleHash *purple_md5_hash_new(void);
    -
    -G_END_DECLS
    -
    -#endif /* PURPLE_MD5_HASH_H */
    --- a/libpurple/ciphers/pbkdf2cipher.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/ciphers/pbkdf2cipher.c Mon Jun 05 16:36:29 2017 +0300
    @@ -25,7 +25,6 @@
    #include "glibcompat.h"
    #include "pbkdf2cipher.h"
    -#include "hmaccipher.h"
    #include "debug.h"
    /* 1024bit */
    @@ -38,7 +37,7 @@
    (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_PBKDF2_CIPHER, PurplePBKDF2CipherPrivate))
    typedef struct {
    - PurpleHash *hash;
    + GChecksumType hash_type;
    guint iter_count;
    size_t out_len;
    @@ -53,7 +52,7 @@
    *****************************************************************************/
    enum {
    PROP_NONE,
    - PROP_HASH,
    + PROP_HASH_TYPE,
    PROP_ITER_COUNT,
    PROP_OUT_LEN,
    PROP_LAST,
    @@ -65,20 +64,6 @@
    static GObjectClass *parent_class = NULL;
    static GParamSpec *properties[PROP_LAST];
    -/*******************************************************************************
    - * Helpers
    - ******************************************************************************/
    -static void
    -purple_pbkdf2_cipher_set_hash(PurpleCipher *cipher,
    - PurpleHash *hash)
    -{
    - PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
    -
    - priv->hash = g_object_ref(G_OBJECT(hash));
    -
    - g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_HASH]);
    -}
    -
    /******************************************************************************
    * Cipher Stuff
    *****************************************************************************/
    @@ -89,8 +74,6 @@
    g_return_if_fail(priv != NULL);
    - if(PURPLE_IS_HASH(priv->hash))
    - purple_hash_reset(priv->hash);
    priv->iter_count = 1;
    priv->out_len = 256;
    @@ -167,17 +150,16 @@
    PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
    guchar halfkey[PBKDF2_HASH_MAX_LEN], halfkey_hash[PBKDF2_HASH_MAX_LEN];
    - guint halfkey_len, halfkey_count, halfkey_pad, halfkey_no;
    + guint halfkey_count, halfkey_pad, halfkey_no;
    + gsize halfkey_len;
    guchar *salt_ext;
    size_t salt_ext_len;
    guint iter_no;
    - PurpleCipher *hash;
    g_return_val_if_fail(priv != NULL, FALSE);
    g_return_val_if_fail(digest != NULL, FALSE);
    g_return_val_if_fail(len >= priv->out_len, FALSE);
    - g_return_val_if_fail(priv->hash != NULL, FALSE);
    g_return_val_if_fail(priv->iter_count > 0, FALSE);
    g_return_val_if_fail(priv->passphrase != NULL ||
    priv->passphrase_len == 0, FALSE);
    @@ -188,19 +170,10 @@
    salt_ext_len = priv->salt_len + 4;
    - hash = purple_hmac_cipher_new(priv->hash);
    - if (hash == NULL) {
    - purple_debug_error("pbkdf2", "Couldn't create new hmac "
    - "cipher\n");
    - return FALSE;
    - }
    - purple_cipher_set_key(hash, (const guchar*)priv->passphrase,
    - priv->passphrase_len);
    -
    - halfkey_len = purple_cipher_get_digest_size(hash);
    + halfkey_len = g_checksum_type_get_length(priv->hash_type);
    if (halfkey_len <= 0 || halfkey_len > PBKDF2_HASH_MAX_LEN) {
    purple_debug_error("pbkdf2", "Unsupported hash function. "
    - "(digest size: %d)\n", halfkey_len);
    + "(digest size: %" G_GSIZE_FORMAT ")\n", halfkey_len);
    return FALSE;
    }
    @@ -215,9 +188,18 @@
    memset(halfkey, 0, halfkey_len);
    for (iter_no = 1; iter_no <= priv->iter_count; iter_no++) {
    + GHmac *hmac;
    guint i;
    - purple_cipher_reset_state(hash);
    + hmac = g_hmac_new(priv->hash_type,
    + (const guchar*)priv->passphrase,
    + priv->passphrase_len);
    + if (hmac == NULL) {
    + purple_debug_error("pbkdf2",
    + "Couldn't create new hmac cipher\n");
    + g_free(salt_ext);
    + return FALSE;
    + }
    if (iter_no == 1) {
    salt_ext[salt_ext_len - 4] =
    @@ -229,21 +211,13 @@
    salt_ext[salt_ext_len - 1] =
    (halfkey_no & 0x000000ff) >> 0;
    - purple_cipher_append(hash, salt_ext,
    - salt_ext_len);
    + g_hmac_update(hmac, salt_ext, salt_ext_len);
    }
    else
    - purple_cipher_append(hash, halfkey_hash,
    - halfkey_len);
    + g_hmac_update(hmac, halfkey_hash, halfkey_len);
    - if (!purple_cipher_digest(hash, halfkey_hash,
    - halfkey_len)) {
    - purple_debug_error("pbkdf2",
    - "Couldn't retrieve a digest\n");
    - g_free(salt_ext);
    - g_object_unref(hash);
    - return FALSE;
    - }
    + g_hmac_get_digest(hmac, halfkey_hash, &halfkey_len);
    + g_hmac_unref(hmac);
    for (i = 0; i < halfkey_len; i++)
    halfkey[i] ^= halfkey_hash[i];
    @@ -255,7 +229,6 @@
    }
    g_free(salt_ext);
    - g_object_unref(hash);
    return TRUE;
    }
    @@ -271,8 +244,8 @@
    PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
    switch(param_id) {
    - case PROP_HASH:
    - g_value_set_object(value, purple_pbkdf2_cipher_get_hash(cipher));
    + case PROP_HASH_TYPE:
    + g_value_set_int(value, priv->hash_type);
    break;
    case PROP_ITER_COUNT:
    g_value_set_uint(value, priv->iter_count);
    @@ -294,8 +267,8 @@
    PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
    switch(param_id) {
    - case PROP_HASH:
    - purple_pbkdf2_cipher_set_hash(cipher, g_value_get_object(value));
    + case PROP_HASH_TYPE:
    + priv->hash_type = g_value_get_int(value);
    break;
    case PROP_ITER_COUNT:
    priv->iter_count = g_value_get_uint(value);
    @@ -313,13 +286,9 @@
    purple_pbkdf2_cipher_finalize(GObject *obj)
    {
    PurpleCipher *cipher = PURPLE_CIPHER(obj);
    - PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
    purple_pbkdf2_cipher_reset(cipher);
    - if (priv->hash != NULL)
    - g_object_unref(priv->hash);
    -
    parent_class->finalize(obj);
    }
    @@ -343,8 +312,8 @@
    g_type_class_add_private(klass, sizeof(PurplePBKDF2CipherPrivate));
    - properties[PROP_HASH] = g_param_spec_object("hash", "hash", "hash",
    - PURPLE_TYPE_HASH,
    + properties[PROP_HASH_TYPE] = g_param_spec_int("hash-type", "hash-type", "hash-type",
    + G_MININT, G_MAXINT, -1,
    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
    G_PARAM_STATIC_STRINGS);
    @@ -364,6 +333,11 @@
    static void
    purple_pbkdf2_cipher_init(PurpleCipher *cipher)
    {
    + PurplePBKDF2CipherPrivate *priv =
    + PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
    +
    + priv->hash_type = -1;
    +
    purple_cipher_reset(cipher);
    }
    @@ -397,24 +371,22 @@
    }
    PurpleCipher *
    -purple_pbkdf2_cipher_new(PurpleHash *hash) {
    - g_return_val_if_fail(PURPLE_IS_HASH(hash), NULL);
    -
    +purple_pbkdf2_cipher_new(GChecksumType hash_type) {
    return g_object_new(PURPLE_TYPE_PBKDF2_CIPHER,
    - "hash", hash,
    + "hash-type", hash_type,
    NULL);
    }
    -PurpleHash *
    -purple_pbkdf2_cipher_get_hash(const PurplePBKDF2Cipher *cipher) {
    +GChecksumType
    +purple_pbkdf2_cipher_get_hash_type(const PurplePBKDF2Cipher *cipher) {
    PurplePBKDF2CipherPrivate *priv = NULL;
    - g_return_val_if_fail(PURPLE_IS_PBKDF2_CIPHER(cipher), NULL);
    + g_return_val_if_fail(PURPLE_IS_PBKDF2_CIPHER(cipher), -1);
    priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
    - if(priv && priv->hash)
    - return priv->hash;
    + if(priv)
    + return priv->hash_type;
    - return NULL;
    + return -1;
    }
    --- a/libpurple/ciphers/pbkdf2cipher.h Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/ciphers/pbkdf2cipher.h Mon Jun 05 16:36:29 2017 +0300
    @@ -58,9 +58,9 @@
    GType purple_pbkdf2_cipher_get_type(void);
    -PurpleCipher *purple_pbkdf2_cipher_new(PurpleHash *hash);
    +PurpleCipher *purple_pbkdf2_cipher_new(GChecksumType hash_type);
    -PurpleHash *purple_pbkdf2_cipher_get_hash(const PurplePBKDF2Cipher *cipher);
    +GChecksumType purple_pbkdf2_cipher_get_hash_type(const PurplePBKDF2Cipher *cipher);
    G_END_DECLS
    --- a/libpurple/ciphers/sha1hash.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,191 +0,0 @@
    -/*
    - * 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 "sha1hash.h"
    -
    -/*******************************************************************************
    - * Structs
    - ******************************************************************************/
    -#define PURPLE_SHA1_HASH_GET_PRIVATE(obj) \
    - (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_SHA1_HASH, PurpleSHA1HashPrivate))
    -
    -typedef struct {
    - GChecksum *checksum;
    -} PurpleSHA1HashPrivate;
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -static GObjectClass *parent_class = NULL;
    -
    -/******************************************************************************
    - * Hash Stuff
    - *****************************************************************************/
    -
    -static void
    -purple_sha1_hash_reset(PurpleHash *hash)
    -{
    - PurpleSHA1Hash *sha1_hash = PURPLE_SHA1_HASH(hash);
    - PurpleSHA1HashPrivate *priv = PURPLE_SHA1_HASH_GET_PRIVATE(sha1_hash);
    -
    - g_return_if_fail(priv != NULL);
    - g_return_if_fail(priv->checksum != NULL);
    -
    - g_checksum_reset(priv->checksum);
    -}
    -
    -static void
    -purple_sha1_hash_append(PurpleHash *hash, const guchar *data,
    - gsize len)
    -{
    - PurpleSHA1Hash *sha1_hash = PURPLE_SHA1_HASH(hash);
    - PurpleSHA1HashPrivate *priv = PURPLE_SHA1_HASH_GET_PRIVATE(sha1_hash);
    -
    - g_return_if_fail(priv != NULL);
    - g_return_if_fail(priv->checksum != NULL);
    -
    - while (len >= G_MAXSSIZE) {
    - g_checksum_update(priv->checksum, data, G_MAXSSIZE);
    - len -= G_MAXSSIZE;
    - data += G_MAXSSIZE;
    - }
    -
    - if (len)
    - g_checksum_update(priv->checksum, data, len);
    -}
    -
    -static gboolean
    -purple_sha1_hash_digest(PurpleHash *hash, guchar *digest, size_t buff_len)
    -{
    - PurpleSHA1Hash *sha1_hash = PURPLE_SHA1_HASH(hash);
    - PurpleSHA1HashPrivate *priv = PURPLE_SHA1_HASH_GET_PRIVATE(sha1_hash);
    -
    - const gssize required_len = g_checksum_type_get_length(G_CHECKSUM_SHA1);
    - gsize digest_len = buff_len;
    -
    - g_return_val_if_fail(priv != NULL, FALSE);
    - g_return_val_if_fail(priv->checksum != NULL, FALSE);
    -
    - g_return_val_if_fail(required_len >= 0, FALSE);
    - g_return_val_if_fail(buff_len >= (gsize)required_len, FALSE);
    -
    - g_checksum_get_digest(priv->checksum, digest, &digest_len);
    -
    - if (digest_len != (gsize)required_len)
    - return FALSE;
    -
    - purple_sha1_hash_reset(hash);
    -
    - return TRUE;
    -}
    -
    -static size_t
    -purple_sha1_hash_get_block_size(PurpleHash *hash)
    -{
    - return 64;
    -}
    -
    -static size_t
    -purple_sha1_hash_get_digest_size(PurpleHash *hash)
    -{
    - return g_checksum_type_get_length(G_CHECKSUM_SHA1);
    -}
    -
    -/******************************************************************************
    - * Object Stuff
    - *****************************************************************************/
    -
    -static void
    -purple_sha1_hash_finalize(GObject *obj)
    -{
    - PurpleSHA1Hash *sha1_hash = PURPLE_SHA1_HASH(obj);
    - PurpleSHA1HashPrivate *priv = PURPLE_SHA1_HASH_GET_PRIVATE(sha1_hash);
    -
    - if (priv->checksum)
    - g_checksum_free(priv->checksum);
    -
    - parent_class->finalize(obj);
    -}
    -
    -static void
    -purple_sha1_hash_class_init(PurpleSHA1HashClass *klass) {
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    - PurpleHashClass *hash_class = PURPLE_HASH_CLASS(klass);
    -
    - parent_class = g_type_class_peek_parent(klass);
    -
    - obj_class->finalize = purple_sha1_hash_finalize;
    -
    - hash_class->reset = purple_sha1_hash_reset;
    - hash_class->reset_state = purple_sha1_hash_reset;
    - hash_class->append = purple_sha1_hash_append;
    - hash_class->digest = purple_sha1_hash_digest;
    - hash_class->get_digest_size = purple_sha1_hash_get_digest_size;
    - hash_class->get_block_size = purple_sha1_hash_get_block_size;
    -
    - g_type_class_add_private(klass, sizeof(PurpleSHA1HashPrivate));
    -}
    -
    -static void
    -purple_sha1_hash_init(PurpleHash *hash)
    -{
    - PurpleSHA1Hash *sha1_hash = PURPLE_SHA1_HASH(hash);
    - PurpleSHA1HashPrivate *priv = PURPLE_SHA1_HASH_GET_PRIVATE(sha1_hash);
    -
    - priv->checksum = g_checksum_new(G_CHECKSUM_SHA1);
    -
    - purple_sha1_hash_reset(hash);
    -}
    -
    -/******************************************************************************
    - * API
    - *****************************************************************************/
    -GType
    -purple_sha1_hash_get_type(void) {
    - static GType type = 0;
    -
    - if(type == 0) {
    - static const GTypeInfo info = {
    - sizeof(PurpleSHA1HashClass),
    - NULL,
    - NULL,
    - (GClassInitFunc)purple_sha1_hash_class_init,
    - NULL,
    - NULL,
    - sizeof(PurpleSHA1Hash),
    - 0,
    - (GInstanceInitFunc)purple_sha1_hash_init,
    - NULL,
    - };
    -
    - type = g_type_register_static(PURPLE_TYPE_HASH,
    - "PurpleSHA1Hash",
    - &info, 0);
    - }
    -
    - return type;
    -}
    -
    -PurpleHash *
    -purple_sha1_hash_new(void) {
    - return g_object_new(PURPLE_TYPE_SHA1_HASH, NULL);
    -}
    --- a/libpurple/ciphers/sha1hash.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,65 +0,0 @@
    -/* 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 PURPLE_SHA1_HASH_H
    -#define PURPLE_SHA1_HASH_H
    -/**
    - * SECTION:sha1hash
    - * @section_id: libpurple-sha1hash
    - * @short_description: <filename>ciphers/sha1hash.h</filename>
    - * @title: SHA1 Hash
    - */
    -
    -#include "cipher.h"
    -
    -#define PURPLE_TYPE_SHA1_HASH (purple_sha1_hash_get_type())
    -#define PURPLE_SHA1_HASH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_SHA1_HASH, PurpleSHA1Hash))
    -#define PURPLE_SHA1_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SHA1_HASH, PurpleSHA1HashClass))
    -#define PURPLE_IS_SHA1_HASH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_SHA1_HASH))
    -#define PURPLE_IS_SHA1_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_SHA1_HASH))
    -#define PURPLE_SHA1_HASH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SHA1_HASH, PurpleSHA1HashClass))
    -
    -typedef struct _PurpleSHA1Hash PurpleSHA1Hash;
    -typedef struct _PurpleSHA1HashClass PurpleSHA1HashClass;
    -
    -struct _PurpleSHA1Hash {
    - PurpleHash gparent;
    -};
    -
    -struct _PurpleSHA1HashClass {
    - PurpleHashClass gparent;
    -
    - /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    -};
    -
    -G_BEGIN_DECLS
    -
    -GType purple_sha1_hash_get_type(void);
    -
    -PurpleHash *purple_sha1_hash_new(void);
    -
    -G_END_DECLS
    -
    -#endif /* PURPLE_SHA1_HASH_H */
    --- a/libpurple/ciphers/sha256hash.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,191 +0,0 @@
    -/*
    - * 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 "sha256hash.h"
    -
    -/*******************************************************************************
    - * Structs
    - ******************************************************************************/
    -#define PURPLE_SHA256_HASH_GET_PRIVATE(obj) \
    - (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_SHA256_HASH, PurpleSHA256HashPrivate))
    -
    -typedef struct {
    - GChecksum *checksum;
    -} PurpleSHA256HashPrivate;
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -static GObjectClass *parent_class = NULL;
    -
    -/******************************************************************************
    - * Hash Stuff
    - *****************************************************************************/
    -
    -static void
    -purple_sha256_hash_reset(PurpleHash *hash)
    -{
    - PurpleSHA256Hash *sha256_hash = PURPLE_SHA256_HASH(hash);
    - PurpleSHA256HashPrivate *priv = PURPLE_SHA256_HASH_GET_PRIVATE(sha256_hash);
    -
    - g_return_if_fail(priv != NULL);
    - g_return_if_fail(priv->checksum != NULL);
    -
    - g_checksum_reset(priv->checksum);
    -}
    -
    -static void
    -purple_sha256_hash_append(PurpleHash *hash, const guchar *data,
    - gsize len)
    -{
    - PurpleSHA256Hash *sha256_hash = PURPLE_SHA256_HASH(hash);
    - PurpleSHA256HashPrivate *priv = PURPLE_SHA256_HASH_GET_PRIVATE(sha256_hash);
    -
    - g_return_if_fail(priv != NULL);
    - g_return_if_fail(priv->checksum != NULL);
    -
    - while (len >= G_MAXSSIZE) {
    - g_checksum_update(priv->checksum, data, G_MAXSSIZE);
    - len -= G_MAXSSIZE;
    - data += G_MAXSSIZE;
    - }
    -
    - if (len)
    - g_checksum_update(priv->checksum, data, len);
    -}
    -
    -static gboolean
    -purple_sha256_hash_digest(PurpleHash *hash, guchar *digest, size_t buff_len)
    -{
    - PurpleSHA256Hash *sha256_hash = PURPLE_SHA256_HASH(hash);
    - PurpleSHA256HashPrivate *priv = PURPLE_SHA256_HASH_GET_PRIVATE(sha256_hash);
    -
    - const gssize required_len = g_checksum_type_get_length(G_CHECKSUM_SHA256);
    - gsize digest_len = buff_len;
    -
    - g_return_val_if_fail(priv != NULL, FALSE);
    - g_return_val_if_fail(priv->checksum != NULL, FALSE);
    -
    - g_return_val_if_fail(required_len >= 0, FALSE);
    - g_return_val_if_fail(buff_len >= (gsize)required_len, FALSE);
    -
    - g_checksum_get_digest(priv->checksum, digest, &digest_len);
    -
    - if (digest_len != (gsize)required_len)
    - return FALSE;
    -
    - purple_sha256_hash_reset(hash);
    -
    - return TRUE;
    -}
    -
    -static size_t
    -purple_sha256_hash_get_block_size(PurpleHash *hash)
    -{
    - return 64;
    -}
    -
    -static size_t
    -purple_sha256_hash_get_digest_size(PurpleHash *hash)
    -{
    - return g_checksum_type_get_length(G_CHECKSUM_SHA256);
    -}
    -
    -/******************************************************************************
    - * Object Stuff
    - *****************************************************************************/
    -
    -static void
    -purple_sha256_hash_finalize(GObject *obj)
    -{
    - PurpleSHA256Hash *sha256_hash = PURPLE_SHA256_HASH(obj);
    - PurpleSHA256HashPrivate *priv = PURPLE_SHA256_HASH_GET_PRIVATE(sha256_hash);
    -
    - if (priv->checksum)
    - g_checksum_free(priv->checksum);
    -
    - parent_class->finalize(obj);
    -}
    -
    -static void
    -purple_sha256_hash_class_init(PurpleSHA256HashClass *klass) {
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    - PurpleHashClass *hash_class = PURPLE_HASH_CLASS(klass);
    -
    - parent_class = g_type_class_peek_parent(klass);
    -
    - obj_class->finalize = purple_sha256_hash_finalize;
    -
    - hash_class->reset = purple_sha256_hash_reset;
    - hash_class->reset_state = purple_sha256_hash_reset;
    - hash_class->append = purple_sha256_hash_append;
    - hash_class->digest = purple_sha256_hash_digest;
    - hash_class->get_digest_size = purple_sha256_hash_get_digest_size;
    - hash_class->get_block_size = purple_sha256_hash_get_block_size;
    -
    - g_type_class_add_private(klass, sizeof(PurpleSHA256HashPrivate));
    -}
    -
    -static void
    -purple_sha256_hash_init(PurpleHash *hash)
    -{
    - PurpleSHA256Hash *sha256_hash = PURPLE_SHA256_HASH(hash);
    - PurpleSHA256HashPrivate *priv = PURPLE_SHA256_HASH_GET_PRIVATE(sha256_hash);
    -
    - priv->checksum = g_checksum_new(G_CHECKSUM_SHA256);
    -
    - purple_sha256_hash_reset(hash);
    -}
    -
    -/******************************************************************************
    - * API
    - *****************************************************************************/
    -GType
    -purple_sha256_hash_get_type(void) {
    - static GType type = 0;
    -
    - if(type == 0) {
    - static const GTypeInfo info = {
    - sizeof(PurpleSHA256HashClass),
    - NULL,
    - NULL,
    - (GClassInitFunc)purple_sha256_hash_class_init,
    - NULL,
    - NULL,
    - sizeof(PurpleSHA256Hash),
    - 0,
    - (GInstanceInitFunc)purple_sha256_hash_init,
    - NULL,
    - };
    -
    - type = g_type_register_static(PURPLE_TYPE_HASH,
    - "PurpleSHA256Hash",
    - &info, 0);
    - }
    -
    - return type;
    -}
    -
    -PurpleHash *
    -purple_sha256_hash_new(void) {
    - return g_object_new(PURPLE_TYPE_SHA256_HASH, NULL);
    -}
    --- a/libpurple/ciphers/sha256hash.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,65 +0,0 @@
    -/* 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 PURPLE_SHA256_HASH_H
    -#define PURPLE_SHA256_HASH_H
    -/**
    - * SECTION:sha256hash
    - * @section_id: libpurple-sha256hash
    - * @short_description: <filename>ciphers/sha256hash.h</filename>
    - * @title: SHA256 Hash
    - */
    -
    -#include "cipher.h"
    -
    -#define PURPLE_TYPE_SHA256_HASH (purple_sha256_hash_get_type())
    -#define PURPLE_SHA256_HASH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_SHA256_HASH, PurpleSHA256Hash))
    -#define PURPLE_SHA256_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SHA256_HASH, PurpleSHA256HashClass))
    -#define PURPLE_IS_SHA256_HASH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_SHA256_HASH))
    -#define PURPLE_IS_SHA256_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_SHA256_HASH))
    -#define PURPLE_SHA256_HASH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SHA256_HASH, PurpleSHA256HashClass))
    -
    -typedef struct _PurpleSHA256Hash PurpleSHA256Hash;
    -typedef struct _PurpleSHA256HashClass PurpleSHA256HashClass;
    -
    -struct _PurpleSHA256Hash {
    - PurpleHash gparent;
    -};
    -
    -struct _PurpleSHA256HashClass {
    - PurpleHashClass gparent;
    -
    - /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    -};
    -
    -G_BEGIN_DECLS
    -
    -GType purple_sha256_hash_get_type(void);
    -
    -PurpleHash *purple_sha256_hash_new(void);
    -
    -G_END_DECLS
    -
    -#endif /* PURPLE_SHA256_HASH_H */
    --- a/libpurple/http.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/http.c Mon Jun 05 16:36:29 2017 +0300
    @@ -859,7 +859,7 @@
    tmp = g_strdup_printf("%s:%s", proxy_username, proxy_password);
    len = strlen(tmp);
    - proxy_auth = purple_base64_encode((const guchar *)tmp, len);
    + proxy_auth = g_base64_encode((const guchar *)tmp, len);
    memset(tmp, 0, len);
    g_free(tmp);
    --- a/libpurple/mime.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/mime.c Mon Jun 05 16:36:29 2017 +0300
    @@ -345,7 +345,7 @@
    *data = purple_base16_decode(part->data->str, len);
    } else if(! g_ascii_strcasecmp(enc, "base64")) {
    - *data = purple_base64_decode(part->data->str, len);
    + *data = g_base64_decode(part->data->str, len);
    } else if(! g_ascii_strcasecmp(enc, "quoted-printable")) {
    *data = purple_quotedp_decode(part->data->str, len);
    --- a/libpurple/ntlm.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/ntlm.c Mon Jun 05 16:36:29 2017 +0300
    @@ -139,7 +139,7 @@
    memcpy(msg + host_off, hostname, hostnamelen);
    memcpy(msg + dom_off, domain, domainlen);
    - tmp = purple_base64_encode(msg, sizeof(struct type1_message) + hostnamelen + domainlen);
    + tmp = g_base64_encode(msg, sizeof(struct type1_message) + hostnamelen + domainlen);
    g_free(msg);
    return tmp;
    @@ -153,7 +153,7 @@
    struct type2_message tmsg;
    static guint8 nonce[8];
    - buff = purple_base64_decode(type2, &retlen);
    + buff = g_base64_decode(type2, &retlen);
    if (buff != NULL && retlen >= (sizeof(struct type2_message) - 1)) {
    memcpy(&tmsg, buff, MIN(retlen, sizeof(tmsg)));
    @@ -397,7 +397,7 @@
    /*tmsg->flags2 = 0x0a280105;
    tmsg->flags3 = 0x0f000000;*/
    - tmp = purple_base64_encode((guchar *)tmsg, msglen);
    + tmp = g_base64_encode((guchar *)tmsg, msglen);
    g_free(tmsg);
    return tmp;
    --- a/libpurple/plugins/Makefile.mingw Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/plugins/Makefile.mingw Mon Jun 05 16:36:29 2017 +0300
    @@ -7,8 +7,6 @@
    PIDGIN_TREE_TOP := ../..
    include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
    -PERL_PLUGIN := ./perl
    -SSL_PLUGIN := ./ssl
    KEYRING_PLUGIN := ./keyrings
    .SUFFIXES:
    @@ -45,13 +43,9 @@
    .PHONY: all clean plugins install
    all: $(PURPLE_DLL).a plugins
    - $(MAKE_at) $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE)
    - $(MAKE_at) $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE)
    $(MAKE_at) $(MAKE) -C $(KEYRING_PLUGIN) -f $(MINGW_MAKEFILE)
    install: all $(PURPLE_INSTALL_PLUGINS_DIR)
    - $(MAKE_at) $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE) install
    - $(MAKE_at) $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) install
    $(MAKE_at) $(MAKE) -C $(KEYRING_PLUGIN) -f $(MINGW_MAKEFILE) install
    cp *.dll $(PURPLE_INSTALL_PLUGINS_DIR)
    @@ -74,8 +68,6 @@
    ##
    clean:
    rm -f *.o *.dll
    - $(MAKE_at) $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE) clean
    - $(MAKE_at) $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) clean
    $(MAKE_at) $(MAKE) -C $(KEYRING_PLUGIN) -f $(MINGW_MAKEFILE) clean
    include $(PIDGIN_COMMON_TARGETS)
    --- a/libpurple/plugins/keyrings/internalkeyring.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/plugins/keyrings/internalkeyring.c Mon Jun 05 16:36:29 2017 +0300
    @@ -33,7 +33,6 @@
    #include "ciphers/aescipher.h"
    #include "ciphers/pbkdf2cipher.h"
    -#include "ciphers/sha256hash.h"
    #define INTKEYRING_NAME N_("Internal keyring")
    #define INTKEYRING_DESCRIPTION N_("This plugin provides the default password " \
    @@ -139,7 +138,7 @@
    guchar *data;
    gsize len;
    - data = purple_base64_decode(base64, &len);
    + data = g_base64_decode(base64, &len);
    return intkeyring_buff_new(data, len);
    }
    @@ -152,14 +151,12 @@
    intkeyring_derive_key(const gchar *passphrase, intkeyring_buff_t *salt)
    {
    PurpleCipher *cipher;
    - PurpleHash *hash;
    gboolean succ;
    intkeyring_buff_t *ret;
    g_return_val_if_fail(passphrase != NULL, NULL);
    - hash = purple_sha256_hash_new();
    - cipher = purple_pbkdf2_cipher_new(hash);
    + cipher = purple_pbkdf2_cipher_new(G_CHECKSUM_SHA256);
    g_object_set(G_OBJECT(cipher), "iter_count",
    GUINT_TO_POINTER(purple_prefs_get_int(INTKEYRING_PREFS
    @@ -175,7 +172,6 @@
    succ = purple_cipher_digest(cipher, ret->data, ret->len);
    g_object_unref(cipher);
    - g_object_unref(hash);
    if (!succ) {
    intkeyring_buff_free(ret);
    @@ -282,7 +278,7 @@
    if (encrypted_size < 0)
    return NULL;
    - return purple_base64_encode(encrypted_raw, encrypted_size);
    + return g_base64_encode(encrypted_raw, encrypted_size);
    }
    @@ -304,7 +300,7 @@
    cipher = purple_aes_cipher_new();
    g_return_val_if_fail(cipher != NULL, NULL);
    - encrypted_raw = purple_base64_decode(str, &encrypted_size);
    + encrypted_raw = g_base64_decode(str, &encrypted_size);
    g_return_val_if_fail(encrypted_raw != NULL, NULL);
    iv_len = purple_cipher_get_block_size(cipher);
    @@ -378,7 +374,7 @@
    * but it's not a problem.
    */
    verifier = intkeyring_encrypt(key, INTKEYRING_VERIFY_STR);
    - salt_b64 = purple_base64_encode(salt->data, salt->len);
    + salt_b64 = g_base64_encode(salt->data, salt->len);
    }
    if (!verifier || !salt_b64) {
    --- a/libpurple/protocols/bonjour/bonjour_ft.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/bonjour/bonjour_ft.c Mon Jun 05 16:36:29 2017 +0300
    @@ -28,7 +28,6 @@
    #include "buddy.h"
    #include "bonjour.h"
    #include "bonjour_ft.h"
    -#include "ciphers/sha1hash.h"
    static void
    bonjour_bytestreams_init(PurpleXfer *xfer);
    @@ -1014,11 +1013,12 @@
    {
    PurpleBuddy *pb;
    PurpleAccount *account = NULL;
    - PurpleHash *hash;
    + GChecksum *hash;
    XepXfer *xf;
    char dstaddr[41];
    const gchar *name = NULL;
    unsigned char hashval[20];
    + gsize digest_len = 20;
    char *p;
    int i;
    @@ -1037,10 +1037,10 @@
    p = g_strdup_printf("%s%s%s", xf->sid, name, bonjour_get_jid(account));
    - hash = purple_sha1_hash_new();
    - purple_hash_append(hash, (guchar *)p, strlen(p));
    - purple_hash_digest(hash, hashval, sizeof(hashval));
    - g_object_unref(G_OBJECT(hash));
    + hash = g_checksum_new(G_CHECKSUM_SHA1);
    + g_checksum_update(hash, (guchar *)p, -1);
    + g_checksum_get_digest(hash, hashval, &digest_len);
    + g_checksum_free(hash);
    g_free(p);
    --- a/libpurple/protocols/gg/avatar.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/gg/avatar.c Mon Jun 05 16:36:29 2017 +0300
    @@ -353,7 +353,7 @@
    }
    own_data->img = NULL;
    - img_data = purple_base64_encode(purple_image_get_data(img),
    + img_data = g_base64_encode(purple_image_get_data(img),
    purple_image_get_data_size(img));
    img_data_e = g_uri_escape_string(img_data, NULL, FALSE);
    g_free(img_data);
    --- a/libpurple/protocols/gg/oauth/oauth.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/gg/oauth/oauth.c Mon Jun 05 16:36:29 2017 +0300
    @@ -26,8 +26,6 @@
    #include "oauth.h"
    #include "oauth-parameter.h"
    -#include "ciphers/hmaccipher.h"
    -#include "ciphers/sha1hash.h"
    char *gg_oauth_static_nonce; /* dla unit testów */
    char *gg_oauth_static_timestamp; /* dla unit testów */
    @@ -49,21 +47,16 @@
    static gchar *gg_hmac_sha1(const char *key, const char *message)
    {
    - PurpleCipher *cipher;
    - PurpleHash *hash;
    + GHmac *hmac;
    guchar digest[20];
    -
    - hash = purple_sha1_hash_new();
    - cipher = purple_hmac_cipher_new(hash);
    + gsize digest_len = 20;
    - purple_cipher_set_key(cipher, (guchar *)key, strlen(key));
    - purple_cipher_append(cipher, (guchar *)message, strlen(message));
    - purple_cipher_digest(cipher, digest, sizeof(digest));
    + hmac = g_hmac_new(G_CHECKSUM_SHA1, (guchar *)key, strlen(key));
    + g_hmac_update(hmac, (guchar *)message, -1);
    + g_hmac_get_digest(hmac, digest, &digest_len);
    + g_hmac_unref(hmac);
    - g_object_unref(cipher);
    - g_object_unref(hash);
    -
    - return purple_base64_encode(digest, sizeof(digest));
    + return g_base64_encode(digest, sizeof(digest));
    }
    static char *
    --- a/libpurple/protocols/irc/msgs.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/irc/msgs.c Mon Jun 05 16:36:29 2017 +0300
    @@ -1660,7 +1660,7 @@
    return;
    if (arg[0] != '+')
    - serverin = (char *)purple_base64_decode(arg, &serverinlen);
    + serverin = (char *)g_base64_decode(arg, &serverinlen);
    ret = sasl_client_step(irc->sasl_conn, serverin, serverinlen,
    NULL, &c_out, &clen);
    @@ -1678,7 +1678,7 @@
    }
    if (clen > 0)
    - authinfo = purple_base64_encode((const guchar*)c_out, clen);
    + authinfo = g_base64_encode((const guchar*)c_out, clen);
    else
    authinfo = g_strdup("+");
    --- a/libpurple/protocols/jabber/auth.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/auth.c Mon Jun 05 16:36:29 2017 +0300
    @@ -38,9 +38,6 @@
    #include "iq.h"
    #include "notify.h"
    -#include "ciphers/hmaccipher.h"
    -#include "ciphers/md5hash.h"
    -
    static GSList *auth_mechs = NULL;
    static void auth_old_result_cb(JabberStream *js, const char *from,
    @@ -266,7 +263,8 @@
    x = purple_xmlnode_new_child(query, "digest");
    s = g_strdup_printf("%s%s", js->stream_id, pw);
    - hash = jabber_calculate_data_hash(s, strlen(s), "sha1");
    + hash = g_compute_checksum_for_string(G_CHECKSUM_SHA1,
    + s, -1);
    purple_xmlnode_insert_data(x, hash, -1);
    g_free(hash);
    g_free(s);
    @@ -277,22 +275,15 @@
    * to non-SASL authentication.
    */
    const char *challenge;
    - gchar digest[33];
    - PurpleCipher *hmac;
    - PurpleHash *md5;
    - gssize diglen;
    + gchar *digest;
    /* Calculate the MHAC-MD5 digest */
    - md5 = purple_md5_hash_new();
    - hmac = purple_hmac_cipher_new(md5);
    challenge = purple_xmlnode_get_attrib(x, "challenge");
    - purple_cipher_set_key(hmac, (guchar *)pw, strlen(pw));
    - purple_cipher_append(hmac, (guchar *)challenge, strlen(challenge));
    - diglen = purple_cipher_digest_to_str(hmac, digest, 33);
    - g_object_unref(hmac);
    - g_object_unref(md5);
    + digest = g_compute_hmac_for_string(G_CHECKSUM_MD5,
    + (guchar *)pw, strlen(pw),
    + challenge, -1);
    - g_return_if_fail(diglen > 0);
    + g_return_if_fail(digest != NULL);
    /* Create the response query */
    iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
    @@ -306,6 +297,7 @@
    x = purple_xmlnode_new_child(query, "crammd5");
    purple_xmlnode_insert_data(x, digest, 32);
    + g_free(digest);
    jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
    jabber_iq_send(iq);
    --- a/libpurple/protocols/jabber/auth_cyrus.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/auth_cyrus.c Mon Jun 05 16:36:29 2017 +0300
    @@ -336,7 +336,7 @@
    if (coutlen == 0) {
    purple_xmlnode_insert_data(auth, "=", -1);
    } else {
    - enc_out = purple_base64_encode((unsigned char*)clientout, coutlen);
    + enc_out = g_base64_encode((unsigned char*)clientout, coutlen);
    purple_xmlnode_insert_data(auth, enc_out, -1);
    g_free(enc_out);
    }
    @@ -458,7 +458,7 @@
    unsigned int clen;
    gsize declen;
    - dec_in = purple_base64_decode(enc_in, &declen);
    + dec_in = g_base64_decode(enc_in, &declen);
    js->sasl_state = sasl_client_step(js->sasl, (char*)dec_in, declen,
    NULL, &c_out, &clen);
    @@ -486,10 +486,10 @@
    if (!purple_strequal(js->current_mech, "DIGEST-MD5") ||
    strstr(c_out, ",charset="))
    /* If we're not using DIGEST-MD5 or Cyrus SASL is fixed */
    - enc_out = purple_base64_encode((unsigned char*)c_out, clen);
    + enc_out = g_base64_encode((unsigned char*)c_out, clen);
    else {
    char *tmp = g_strdup_printf("%s,charset=utf-8", c_out);
    - enc_out = purple_base64_encode((unsigned char*)tmp, clen + 14);
    + enc_out = g_base64_encode((unsigned char*)tmp, clen + 14);
    g_free(tmp);
    }
    @@ -519,7 +519,7 @@
    gsize declen = 0;
    if(enc_in != NULL)
    - dec_in = purple_base64_decode(enc_in, &declen);
    + dec_in = g_base64_decode(enc_in, &declen);
    js->sasl_state = sasl_client_step(js->sasl, (char*)dec_in, declen, NULL, &c_out, &clen);
    --- a/libpurple/protocols/jabber/auth_digest_md5.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/auth_digest_md5.c Mon Jun 05 16:36:29 2017 +0300
    @@ -23,7 +23,6 @@
    #include "internal.h"
    #include "debug.h"
    -#include "ciphers/md5hash.h"
    #include "util.h"
    #include "xmlnode.h"
    @@ -106,9 +105,8 @@
    generate_response_value(JabberID *jid, const char *passwd, const char *nonce,
    const char *cnonce, const char *a2, const char *realm)
    {
    - PurpleHash *hash;
    - guchar result[16];
    - size_t a1len;
    + GChecksum *hash;
    + gsize digest_len = 16;
    gchar *a1, *convnode=NULL, *convpasswd = NULL, *ha1, *ha2, *kd, *x, *z;
    @@ -121,36 +119,22 @@
    convpasswd = g_strdup(passwd);
    }
    - hash = purple_md5_hash_new();
    + hash = g_checksum_new(G_CHECKSUM_MD5);
    x = g_strdup_printf("%s:%s:%s", convnode, realm, convpasswd ? convpasswd : "");
    - purple_hash_append(hash, (const guchar *)x, strlen(x));
    - purple_hash_digest(hash, result, sizeof(result));
    + g_checksum_update(hash, (const guchar *)x, -1);
    a1 = g_strdup_printf("xxxxxxxxxxxxxxxx:%s:%s", nonce, cnonce);
    - a1len = strlen(a1);
    - g_memmove(a1, result, 16);
    -
    - purple_hash_reset(hash);
    - purple_hash_append(hash, (const guchar *)a1, a1len);
    - purple_hash_digest(hash, result, sizeof(result));
    - ha1 = purple_base16_encode(result, 16);
    + g_checksum_get_digest(hash, (guint8 *)a1, &digest_len);
    + g_checksum_free(hash);
    - purple_hash_reset(hash);
    - purple_hash_append(hash, (const guchar *)a2, strlen(a2));
    - purple_hash_digest(hash, result, sizeof(result));
    -
    - ha2 = purple_base16_encode(result, 16);
    + ha1 = g_compute_checksum_for_string(G_CHECKSUM_MD5, a1, -1);
    + ha2 = g_compute_checksum_for_string(G_CHECKSUM_MD5, a2, -1);
    kd = g_strdup_printf("%s:%s:00000001:%s:auth:%s", ha1, nonce, cnonce, ha2);
    - purple_hash_reset(hash);
    - purple_hash_append(hash, (const guchar *)kd, strlen(kd));
    - purple_hash_digest(hash, result, sizeof(result));
    - g_object_unref(hash);
    -
    - z = purple_base16_encode(result, 16);
    + z = g_compute_checksum_for_string(G_CHECKSUM_MD5, kd, -1);
    g_free(convnode);
    g_free(convpasswd);
    @@ -171,6 +155,7 @@
    char *enc_in = purple_xmlnode_get_data(packet);
    char *dec_in;
    char *enc_out;
    + gsize size = 0;
    GHashTable *parts;
    JabberSaslState state = JABBER_SASL_STATE_CONTINUE;
    @@ -179,10 +164,10 @@
    return JABBER_SASL_STATE_FAIL;
    }
    - dec_in = (char *)purple_base64_decode(enc_in, NULL);
    + dec_in = (char *)g_base64_decode(enc_in, &size);
    purple_debug_misc("jabber", "decoded challenge (%"
    G_GSIZE_FORMAT "): %s\n",
    - strlen(dec_in),
    + size,
    dec_in);
    parts = jabber_auth_digest_md5_parse(dec_in);
    @@ -254,7 +239,7 @@
    g_free(auth_resp);
    g_free(cnonce);
    - enc_out = purple_base64_encode((guchar *)response->str, response->len);
    + enc_out = g_base64_encode((guchar *)response->str, response->len);
    purple_debug_misc("jabber", "decoded response (%"
    G_GSIZE_FORMAT "): %s\n",
    --- a/libpurple/protocols/jabber/auth_plain.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/auth_plain.c Mon Jun 05 16:36:29 2017 +0300
    @@ -50,7 +50,7 @@
    response = g_string_append(response,
    purple_connection_get_password(js->gc));
    - enc_out = purple_base64_encode((guchar *)response->str, response->len);
    + enc_out = g_base64_encode((guchar *)response->str, response->len);
    purple_xmlnode_set_attrib(auth, "mechanism", "PLAIN");
    purple_xmlnode_insert_data(auth, enc_out, -1);
    --- a/libpurple/protocols/jabber/auth_scram.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/auth_scram.c Mon Jun 05 16:36:29 2017 +0300
    @@ -25,12 +25,10 @@
    #include "auth.h"
    #include "auth_scram.h"
    -#include "ciphers/hmaccipher.h"
    -#include "ciphers/sha1hash.h"
    #include "debug.h"
    static const JabberScramHash hashes[] = {
    - { "-SHA-1", purple_sha1_hash_new, 20 },
    + { "-SHA-1", G_CHECKSUM_SHA1 },
    };
    static const JabberScramHash *mech_to_hash(const char *mech)
    @@ -80,8 +78,8 @@
    guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str,
    GString *salt, guint iterations)
    {
    - PurpleHash *hasher;
    - PurpleCipher *cipher;
    + GHmac *hmac;
    + gsize digest_len;
    guchar *result;
    guint i;
    guchar *prev, *tmp;
    @@ -91,40 +89,38 @@
    g_return_val_if_fail(salt != NULL && salt->len > 0, NULL);
    g_return_val_if_fail(iterations > 0, NULL);
    - prev = g_new0(guint8, hash->size);
    - tmp = g_new0(guint8, hash->size);
    - result = g_new0(guint8, hash->size);
    + digest_len = g_checksum_type_get_length(hash->type);
    + prev = g_new0(guchar, digest_len);
    + tmp = g_new0(guchar, digest_len);
    + result = g_new0(guchar, digest_len);
    - hasher = hash->new_cipher();
    - cipher = purple_hmac_cipher_new(hasher);
    - g_object_unref(G_OBJECT(hasher));
    + hmac = g_hmac_new(hash->type, (guchar *)str->str, str->len);
    /* Append INT(1), a four-octet encoding of the integer 1, most significant
    * octet first. */
    g_string_append_len(salt, "\0\0\0\1", 4);
    /* Compute U0 */
    - purple_cipher_set_key(cipher, (guchar *)str->str, str->len);
    - purple_cipher_append(cipher, (guchar *)salt->str, salt->len);
    - purple_cipher_digest(cipher, result, hash->size);
    + g_hmac_update(hmac, (guchar *)salt->str, salt->len);
    + g_hmac_get_digest(hmac, result, &digest_len);
    + g_hmac_unref(hmac);
    - memcpy(prev, result, hash->size);
    + memcpy(prev, result, digest_len);
    /* Compute U1...Ui */
    for (i = 1; i < iterations; ++i) {
    guint j;
    - purple_cipher_reset(cipher);
    - purple_cipher_set_key(cipher, (guchar *)str->str, str->len);
    - purple_cipher_append(cipher, prev, hash->size);
    - purple_cipher_digest(cipher, tmp, hash->size);
    + hmac = g_hmac_new(hash->type, (guchar *)str->str, str->len);
    + g_hmac_update(hmac, prev, digest_len);
    + g_hmac_get_digest(hmac, tmp, &digest_len);
    + g_hmac_unref(hmac);
    - for (j = 0; j < hash->size; ++j)
    + for (j = 0; j < digest_len; ++j)
    result[j] ^= tmp[j];
    - memcpy(prev, tmp, hash->size);
    + memcpy(prev, tmp, digest_len);
    }
    - g_object_unref(G_OBJECT(cipher));
    g_free(tmp);
    g_free(prev);
    return result;
    @@ -142,33 +138,31 @@
    static void
    hmac(const JabberScramHash *hash, guchar *out, const guchar *key, const gchar *str)
    {
    - PurpleHash *hasher;
    - PurpleCipher *cipher;
    + GHmac *hmac;
    + gsize digest_len = g_checksum_type_get_length(hash->type);
    - hasher = hash->new_cipher();
    - cipher = purple_hmac_cipher_new(hasher);
    - g_object_unref(G_OBJECT(hasher));
    - purple_cipher_set_key(cipher, key, hash->size);
    - purple_cipher_append(cipher, (guchar *)str, strlen(str));
    - purple_cipher_digest(cipher, out, hash->size);
    - g_object_unref(G_OBJECT(cipher));
    + hmac = g_hmac_new(hash->type, key, digest_len);
    + g_hmac_update(hmac, (guchar *)str, -1);
    + g_hmac_get_digest(hmac, out, &digest_len);
    + g_hmac_unref(hmac);
    }
    static void
    hash(const JabberScramHash *hash, guchar *out, const guchar *data)
    {
    - PurpleHash *hasher;
    + GChecksum *checksum;
    + gsize digest_len = g_checksum_type_get_length(hash->type);
    - hasher = hash->new_cipher();
    - purple_hash_append(hasher, data, hash->size);
    - purple_hash_digest(hasher, out, hash->size);
    - g_object_unref(G_OBJECT(hasher));
    + checksum = g_checksum_new(hash->type);
    + g_checksum_update(checksum, data, digest_len);
    + g_checksum_get_digest(checksum, out, &digest_len);
    + g_checksum_free(checksum);
    }
    gboolean
    jabber_scram_calc_proofs(JabberScramData *data, GString *salt, guint iterations)
    {
    - guint hash_len = data->hash->size;
    + guint hash_len = g_checksum_type_get_length(data->hash->type);
    guint i;
    GString *pass = g_string_new(data->password);
    @@ -252,7 +246,7 @@
    if (token[0] != 's' || token[1] != '=')
    goto err;
    - decoded = (gchar *)purple_base64_decode(token + 2, &len);
    + decoded = (gchar *)g_base64_decode(token + 2, &len);
    if (!decoded || *decoded == '\0') {
    g_free(decoded);
    goto err;
    @@ -343,7 +337,7 @@
    return FALSE;
    }
    - proof = purple_base64_encode((guchar *)data->client_proof->str, data->client_proof->len);
    + proof = g_base64_encode((guchar *)data->client_proof->str, data->client_proof->len);
    *out = g_strdup_printf("c=%s,r=%s,p=%s", "biws", nonce, proof);
    g_free(nonce);
    g_free(proof);
    @@ -355,7 +349,7 @@
    if (!ret)
    return FALSE;
    - server_sig = (gchar *)purple_base64_decode(enc_server_sig, &len);
    + server_sig = (gchar *)g_base64_decode(enc_server_sig, &len);
    g_free(enc_server_sig);
    if (server_sig == NULL || len != data->server_signature->len) {
    @@ -427,7 +421,7 @@
    data->channel_binding = TRUE;
    #endif
    cnonce = ((guint64)g_random_int() << 32) | g_random_int();
    - data->cnonce = purple_base64_encode((guchar *)&cnonce, sizeof(cnonce));
    + data->cnonce = g_base64_encode((guchar *)&cnonce, sizeof(cnonce));
    data->auth_message = g_string_new(NULL);
    g_string_printf(data->auth_message, "n=%s,r=%s",
    @@ -442,7 +436,7 @@
    /* TODO: Channel binding */
    dec_out = g_strdup_printf("%c,,%s", 'n', data->auth_message->str);
    - enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out));
    + enc_out = g_base64_encode((guchar *)dec_out, strlen(dec_out));
    purple_debug_misc("jabber", "initial SCRAM message '%s'\n", dec_out);
    purple_xmlnode_insert_data(reply, enc_out, -1);
    @@ -473,7 +467,7 @@
    goto out;
    }
    - dec_in = (gchar *)purple_base64_decode(enc_in, &len);
    + dec_in = (gchar *)g_base64_decode(enc_in, &len);
    if (!dec_in || len != strlen(dec_in)) {
    /* Danger afoot; SCRAM shouldn't contain NUL bytes */
    reply = purple_xmlnode_new("abort");
    @@ -500,7 +494,7 @@
    purple_debug_misc("jabber", "decoded response: %s\n", dec_out ? dec_out : "(null)");
    if (dec_out) {
    - enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out));
    + enc_out = g_base64_encode((guchar *)dec_out, strlen(dec_out));
    purple_xmlnode_insert_data(reply, enc_out, -1);
    }
    @@ -546,7 +540,7 @@
    return JABBER_SASL_STATE_FAIL;
    }
    - dec_in = (gchar *)purple_base64_decode(enc_in, &len);
    + dec_in = (gchar *)g_base64_decode(enc_in, &len);
    g_free(enc_in);
    if (!dec_in || len != strlen(dec_in)) {
    /* Danger afoot; SCRAM shouldn't contain NUL bytes */
    --- a/libpurple/protocols/jabber/auth_scram.h Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/auth_scram.h Mon Jun 05 16:36:29 2017 +0300
    @@ -29,15 +29,12 @@
    * DO NOT USE ANYTHING HERE OR YOU WILL BE SENT TO THE PIT OF DESPAIR.
    */
    -#include "cipher.h"
    -
    /* Per-connection state stored between messages.
    * This is stored in js->auth_data_mech.
    */
    typedef struct {
    const char *mech_substr;
    - PurpleHash *(*new_cipher)(void);
    - guint size;
    + GChecksumType type;
    } JabberScramHash;
    typedef struct {
    --- a/libpurple/protocols/jabber/buddy.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/buddy.c Mon Jun 05 16:36:29 2017 +0300
    @@ -472,10 +472,10 @@
    g_free(js->initial_avatar_hash);
    image = purple_buddy_icons_find_account_icon(purple_connection_get_account(gc));
    if (image != NULL) {
    - js->initial_avatar_hash = jabber_calculate_data_hash(
    + js->initial_avatar_hash = g_compute_checksum_for_data(
    + G_CHECKSUM_SHA1,
    purple_image_get_data(image),
    - purple_image_get_data_size(image),
    - "sha1"
    + purple_image_get_data_size(image)
    );
    g_object_unref(image);
    } else {
    @@ -528,10 +528,10 @@
    type = purple_xmlnode_new_child(photo, "TYPE");
    purple_xmlnode_insert_data(type, "image/png", -1);
    binval = purple_xmlnode_new_child(photo, "BINVAL");
    - enc = purple_base64_encode(avatar_data, avatar_len);
    + enc = g_base64_encode(avatar_data, avatar_len);
    - js->avatar_hash =
    - jabber_calculate_data_hash(avatar_data, avatar_len, "sha1");
    + js->avatar_hash = g_compute_checksum_for_data(G_CHECKSUM_SHA1,
    + avatar_data, avatar_len);
    purple_xmlnode_insert_data(binval, enc, -1);
    g_free(enc);
    @@ -957,11 +957,12 @@
    gsize size;
    char *bintext = purple_xmlnode_get_data(binval);
    if (bintext) {
    - guchar *data = purple_base64_decode(bintext, &size);
    + guchar *data = g_base64_decode(bintext, &size);
    g_free(bintext);
    if (data) {
    - vcard_hash = jabber_calculate_data_hash(data, size, "sha1");
    + vcard_hash = g_compute_checksum_for_data(
    + G_CHECKSUM_SHA1, data, size);
    g_free(data);
    }
    }
    @@ -1200,7 +1201,7 @@
    guchar *data;
    gboolean photo = (strcmp(child->name, "PHOTO") == 0);
    - data = purple_base64_decode(bintext, &size);
    + data = g_base64_decode(bintext, &size);
    if (data) {
    PurpleImage *img;
    guint img_id;
    @@ -1216,7 +1217,7 @@
    purple_notify_user_info_add_pair_html(user_info, (photo ? _("Photo") : _("Logo")), img_text);
    - hash = jabber_calculate_data_hash(data, size, "sha1");
    + hash = g_compute_checksum_for_data(G_CHECKSUM_SHA1, data, size);
    purple_buddy_icons_set_for_user(account, bare_jid, data, size, hash);
    g_free(hash);
    g_free(img_text);
    --- a/libpurple/protocols/jabber/caps.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/caps.c Mon Jun 05 16:36:29 2017 +0300
    @@ -30,9 +30,6 @@
    #include "util.h"
    #include "xdata.h"
    -#include "ciphers/md5hash.h"
    -#include "ciphers/sha1hash.h"
    -
    #define JABBER_CAPS_FILENAME "xmpp-caps.xml"
    typedef struct _JabberDataFormField {
    @@ -457,19 +454,20 @@
    /* Only validate if these are v1.5 capabilities */
    if (userdata->hash) {
    gchar *hash = NULL;
    - PurpleHash *hasher = NULL;
    - /*
    - * TODO: If you add *any* hash here, make sure the checksum buffer
    - * size in jabber_caps_calculate_hash is large enough. The cipher API
    - * doesn't seem to offer a "Get the hash size" function(?).
    - */
    + GChecksumType hash_type;
    + gboolean supported_hash = TRUE;
    +
    if (g_str_equal(userdata->hash, "sha-1")) {
    - hasher = purple_sha1_hash_new();
    + hash_type = G_CHECKSUM_SHA1;
    } else if (g_str_equal(userdata->hash, "md5")) {
    - hasher = purple_md5_hash_new();
    + hash_type = G_CHECKSUM_MD5;
    + } else {
    + supported_hash = FALSE;
    }
    - hash = jabber_caps_calculate_hash(info, hasher);
    - g_object_unref(hasher);
    +
    + if (supported_hash) {
    + hash = jabber_caps_calculate_hash(info, hash_type);
    + }
    if (!hash || !g_str_equal(hash, userdata->ver)) {
    purple_debug_warning("jabber", "Could not validate caps info from "
    @@ -818,27 +816,29 @@
    }
    static void
    -append_escaped_string(PurpleHash *hash, const gchar *str)
    +append_escaped_string(GChecksum *hash, const gchar *str)
    {
    g_return_if_fail(hash != NULL);
    if (str && *str) {
    char *tmp = g_markup_escape_text(str, -1);
    - purple_hash_append(hash, (const guchar *)tmp, strlen(tmp));
    + g_checksum_update(hash, (const guchar *)tmp, -1);
    g_free(tmp);
    }
    - purple_hash_append(hash, (const guchar *)"<", 1);
    + g_checksum_update(hash, (const guchar *)"<", -1);
    }
    -gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, PurpleHash *hash)
    +gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info,
    + GChecksumType hash_type)
    {
    + GChecksum *hash;
    GList *node;
    - guint8 checksum[20];
    - gsize checksum_size = 20;
    - gboolean success;
    + guint8 *checksum;
    + gsize checksum_size;
    + gchar *ret;
    - if (!info || !hash)
    + if (!info)
    return NULL;
    /* sort identities, features and x-data forms */
    @@ -846,6 +846,12 @@
    info->features = g_list_sort(info->features, (GCompareFunc)strcmp);
    info->forms = g_list_sort(info->forms, jabber_xdata_compare);
    + hash = g_checksum_new(hash_type);
    +
    + if (hash == NULL) {
    + return NULL;
    + }
    +
    /* Add identities to the hash data */
    for (node = info->identities; node; node = node->next) {
    JabberIdentity *id = (JabberIdentity*)node->data;
    @@ -863,7 +869,7 @@
    tmp = g_strconcat(category, "/", type, "/", lang ? lang : "",
    "/", name ? name : "", "<", NULL);
    - purple_hash_append(hash, (const guchar *)tmp, strlen(tmp));
    + g_checksum_update(hash, (const guchar *)tmp, -1);
    g_free(tmp);
    g_free(category);
    @@ -912,16 +918,20 @@
    }
    }
    + checksum_size = g_checksum_type_get_length(hash_type);
    + checksum = g_new(guint8, checksum_size);
    +
    /* generate hash */
    - success = purple_hash_digest(hash, checksum, checksum_size);
    - checksum_size = purple_hash_get_digest_size(hash);
    + g_checksum_get_digest(hash, checksum, &checksum_size);
    - return (success ? purple_base64_encode(checksum, checksum_size) : NULL);
    + ret = g_base64_encode(checksum, checksum_size);
    + g_free(checksum);
    +
    + return ret;
    }
    void jabber_caps_calculate_own_hash(JabberStream *js) {
    JabberCapsClientInfo info;
    - PurpleHash *hasher;
    GList *iter = NULL;
    GList *features = NULL;
    @@ -952,9 +962,7 @@
    info.forms = NULL;
    g_free(js->caps_hash);
    - hasher = purple_sha1_hash_new();
    - js->caps_hash = jabber_caps_calculate_hash(&info, hasher);
    - g_object_unref(hasher);
    + js->caps_hash = jabber_caps_calculate_hash(&info, G_CHECKSUM_SHA1);
    g_list_free(info.identities);
    g_list_free(info.features);
    }
    --- a/libpurple/protocols/jabber/caps.h Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/caps.h Mon Jun 05 16:36:29 2017 +0300
    @@ -27,7 +27,6 @@
    typedef struct _JabberCapsClientInfo JabberCapsClientInfo;
    #include "jabber.h"
    -#include "cipher.h"
    /* Implementation of XEP-0115 - Entity Capabilities */
    @@ -96,10 +95,11 @@
    * XEP-0115 Version 1.5.
    *
    * @param info A JabberCapsClientInfo pointer.
    - * @param hash Hash cipher to be used. Either sha-1 or md5.
    + * @param hash_type GChecksumType to be used. Either sha-1 or md5.
    * @return The base64 encoded SHA-1 hash; must be freed by caller
    */
    -gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, PurpleHash *hash);
    +gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info,
    + GChecksumType hash_type);
    /**
    * Calculate SHA1 hash for own featureset.
    --- a/libpurple/protocols/jabber/data.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/data.c Mon Jun 05 16:36:29 2017 +0300
    @@ -50,7 +50,7 @@
    g_return_val_if_fail(type != NULL, NULL);
    data = g_new0(JabberData, 1);
    - checksum = jabber_calculate_data_hash(rawdata, size, "sha1");
    + checksum = g_compute_checksum_for_data(G_CHECKSUM_SHA1, rawdata, size);
    g_snprintf(cid, sizeof(cid), "sha1+%s@bob.xmpp.org", checksum);
    g_free(checksum);
    @@ -109,7 +109,7 @@
    }
    data = g_new0(JabberData, 1);
    - data->data = purple_base64_decode(raw_data, &data->size);
    + data->data = g_base64_decode(raw_data, &data->size);
    g_free(raw_data);
    if (data->data == NULL) {
    @@ -174,7 +174,7 @@
    g_return_val_if_fail(data != NULL, NULL);
    tag = purple_xmlnode_new("data");
    - base64data = purple_base64_encode(data->data, data->size);
    + base64data = g_base64_encode(data->data, data->size);
    purple_xmlnode_set_namespace(tag, NS_BOB);
    purple_xmlnode_set_attrib(tag, "cid", data->cid);
    @@ -238,11 +238,25 @@
    if (num_sub_parts == 2) {
    const gchar *hash_algo = sub_parts[0];
    const gchar *hash_value = sub_parts[1];
    - gchar *digest =
    - jabber_calculate_data_hash(jabber_data_get_data(data),
    - jabber_data_get_size(data), hash_algo);
    + GChecksumType hash_type;
    + gboolean valid_hash_type = TRUE;
    - if (digest) {
    + if (purple_strequal(hash_algo, "sha1"))
    + hash_type = G_CHECKSUM_SHA1;
    + else if (purple_strequal(hash_algo, "sha256"))
    + hash_type = G_CHECKSUM_SHA256;
    + else if (purple_strequal(hash_algo, "sha512"))
    + hash_type = G_CHECKSUM_SHA512;
    + else if (purple_strequal(hash_algo, "md5"))
    + hash_type = G_CHECKSUM_MD5;
    + else
    + valid_hash_type = FALSE;
    +
    + if (valid_hash_type) {
    + gchar *digest = g_compute_checksum_for_data(
    + hash_type, jabber_data_get_data(data),
    + jabber_data_get_size(data));
    +
    ret = purple_strequal(digest, hash_value);
    if (!ret)
    --- a/libpurple/protocols/jabber/ibb.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/ibb.c Mon Jun 05 16:36:29 2017 +0300
    @@ -335,7 +335,7 @@
    JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess),
    JABBER_IQ_SET);
    PurpleXmlNode *data_element = purple_xmlnode_new("data");
    - char *base64 = purple_base64_encode(data, size);
    + char *base64 = g_base64_encode(data, size);
    char seq[10];
    g_snprintf(seq, sizeof(seq), "%u", jabber_ibb_session_get_send_seq(sess));
    @@ -415,7 +415,7 @@
    if (sess->data_received_cb) {
    gchar *base64 = purple_xmlnode_get_data(child);
    gsize size;
    - gpointer rawdata = purple_base64_decode(base64, &size);
    + gpointer rawdata = g_base64_decode(base64, &size);
    g_free(base64);
    --- a/libpurple/protocols/jabber/jabber.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/jabber.c Mon Jun 05 16:36:29 2017 +0300
    @@ -1135,10 +1135,10 @@
    */
    image = purple_buddy_icons_find_account_icon(account);
    if (image != NULL) {
    - js->initial_avatar_hash = jabber_calculate_data_hash(
    + js->initial_avatar_hash = g_compute_checksum_for_data(
    + G_CHECKSUM_SHA1,
    purple_image_get_data(image),
    - purple_image_get_data_size(image),
    - "sha1"
    + purple_image_get_data_size(image)
    );
    g_object_unref(image);
    }
    --- a/libpurple/protocols/jabber/jutil.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/jutil.c Mon Jun 05 16:36:29 2017 +0300
    @@ -32,10 +32,6 @@
    #include "presence.h"
    #include "jutil.h"
    -#include "ciphers/sha1hash.h"
    -#include "ciphers/sha256hash.h"
    -#include "ciphers/md5hash.h"
    -
    #ifdef USE_IDN
    #include <idna.h>
    #include <stringprep.h>
    @@ -780,36 +776,3 @@
    return NULL;
    }
    -char *
    -jabber_calculate_data_hash(gconstpointer data, size_t len,
    - const gchar *hash_algo)
    -{
    - PurpleHash *hash = NULL;
    - static gchar digest[129]; /* 512 bits hex + \0 */
    -
    - if (g_str_equal(hash_algo, "sha1"))
    - hash = purple_sha1_hash_new();
    - else if (g_str_equal(hash_algo, "sha256"))
    - hash = purple_sha256_hash_new();
    - else if (g_str_equal(hash_algo, "md5"))
    - hash = purple_md5_hash_new();
    -
    - if (hash == NULL)
    - {
    - purple_debug_error("jabber", "Unexpected hashing algorithm %s requested\n", hash_algo);
    - g_return_val_if_reached(NULL);
    - }
    -
    - /* Hash the data */
    - purple_hash_append(hash, data, len);
    - if (!purple_hash_digest_to_str(hash, digest, sizeof(digest)))
    - {
    - purple_debug_error("jabber", "Failed to get digest for %s cipher.\n",
    - hash_algo);
    - g_return_val_if_reached(NULL);
    - }
    - g_object_unref(G_OBJECT(hash));
    -
    - return g_strdup(digest);
    -}
    -
    --- a/libpurple/protocols/jabber/jutil.h Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/jutil.h Mon Jun 05 16:36:29 2017 +0300
    @@ -94,6 +94,4 @@
    /* show attr (presence stanza) -> state */
    JabberBuddyState jabber_buddy_show_get_state(const char *id);
    -char *jabber_calculate_data_hash(gconstpointer data, size_t len,
    - const gchar *hash_algo);
    #endif /* PURPLE_JABBER_JUTIL_H_ */
    --- a/libpurple/protocols/jabber/presence.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/presence.c Mon Jun 05 16:36:29 2017 +0300
    @@ -458,11 +458,12 @@
    if ((binval = purple_xmlnode_get_child(photo, "BINVAL")) &&
    (text = purple_xmlnode_get_data(binval))) {
    - data = purple_base64_decode(text, &size);
    + data = g_base64_decode(text, &size);
    g_free(text);
    if (data)
    - hash = jabber_calculate_data_hash(data, size, "sha1");
    + g_compute_checksum_for_data(
    + G_CHECKSUM_SHA1, data, size);
    }
    purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, data, size, hash);
    --- a/libpurple/protocols/jabber/si.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/si.c Mon Jun 05 16:36:29 2017 +0300
    @@ -301,7 +301,8 @@
    jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource);
    /* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
    - hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
    + hash = g_compute_checksum_for_string(G_CHECKSUM_SHA1,
    + dstaddr, -1);
    account = purple_connection_get_account(jsx->js->gc);
    jsx->connect_data = purple_proxy_connect_socks5_account(NULL, account,
    @@ -478,7 +479,7 @@
    jsx->js->user->resource, purple_xfer_get_remote_user(xfer));
    /* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
    - hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
    + hash = g_compute_checksum_for_string(G_CHECKSUM_SHA1, dstaddr, -1);
    if(strncmp(hash, jsx->rxqueue + 5, 40) ||
    jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) {
    --- a/libpurple/protocols/jabber/tests/test_jabber_caps.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/tests/test_jabber_caps.c Mon Jun 05 16:36:29 2017 +0300
    @@ -1,7 +1,6 @@
    #include <glib.h>
    #include "xmlnode.h"
    -#include "ciphers/sha1hash.h"
    #include "protocols/jabber/caps.h"
    static void
    @@ -24,13 +23,12 @@
    }
    static void
    -_test_jabber_caps_match(PurpleHash *hash, const gchar *in, const gchar *expected) {
    +_test_jabber_caps_match(GChecksumType hash_type, const gchar *in, const gchar *expected) {
    PurpleXmlNode *query = purple_xmlnode_from_str(in, -1);
    JabberCapsClientInfo *info = jabber_caps_parse_client_info(query);
    gchar *got = NULL;
    - got = jabber_caps_calculate_hash(info, hash);
    - g_object_unref(G_OBJECT(hash));
    + got = jabber_caps_calculate_hash(info, hash_type);
    g_assert_cmpstr(expected, ==, got);
    g_free(got);
    @@ -39,7 +37,7 @@
    static void
    test_jabber_caps_calculate_from_xmlnode(void) {
    _test_jabber_caps_match(
    - purple_sha1_hash_new(),
    + G_CHECKSUM_SHA1,
    "<query xmlns='http://jabber.org/protocol/disco#info' node='http://tkabber.jabber.ru/#GNjxthSckUNvAIoCCJFttjl6VL8='><identity category='client' type='pc' name='Tkabber'/><x xmlns='jabber:x:data' type='result'><field var='FORM_TYPE' type='hidden'><value>urn:xmpp:dataforms:softwareinfo</value></field><field var='software'><value>Tkabber</value></field><field var='software_version'><value> ( 8.5.5 )</value></field><field var='os'><value>ATmega640-16AU</value></field><field var='os_version'><value/></field></x><feature var='games:board'/><feature var='google:mail:notify'/><feature var='http://jabber.org/protocol/activity'/><feature var='http://jabber.org/protocol/bytestreams'/><feature var='http://jabber.org/protocol/chatstates'/><feature var='http://jabber.org/protocol/commands'/><feature var='http://jabber.org/protocol/commands'/><feature var='http://jabber.org/protocol/disco#info'/><feature var='http://jabber.org/protocol/disco#items'/><feature var='http://jabber.org/protocol/feature-neg'/><feature var='http://jabber.org/protocol/geoloc'/><feature var='http://jabber.org/protocol/ibb'/><feature var='http://jabber.org/protocol/iqibb'/><feature var='http://jabber.org/protocol/mood'/><feature var='http://jabber.org/protocol/muc'/><feature var='http://jabber.org/protocol/mute#ancestor'/><feature var='http://jabber.org/protocol/mute#editor'/><feature var='http://jabber.org/protocol/rosterx'/><feature var='http://jabber.org/protocol/si'/><feature var='http://jabber.org/protocol/si/profile/file-transfer'/><feature var='http://jabber.org/protocol/tune'/><feature var='jabber:iq:avatar'/><feature var='jabber:iq:browse'/><feature var='jabber:iq:dtcp'/><feature var='jabber:iq:filexfer'/><feature var='jabber:iq:ibb'/><feature var='jabber:iq:inband'/><feature var='jabber:iq:jidlink'/><feature var='jabber:iq:last'/><feature var='jabber:iq:oob'/><feature var='jabber:iq:privacy'/><feature var='jabber:iq:time'/><feature var='jabber:iq:version'/><feature var='jabber:x:data'/><feature var='jabber:x:event'/><feature var='jabber:x:oob'/><feature var='urn:xmpp:ping'/><feature var='urn:xmpp:receipts'/><feature var='urn:xmpp:time'/></query>",
    "GNjxthSckUNvAIoCCJFttjl6VL8="
    );
    --- a/libpurple/protocols/jabber/tests/test_jabber_scram.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/tests/test_jabber_scram.c Mon Jun 05 16:36:29 2017 +0300
    @@ -4,9 +4,8 @@
    #include "util.h"
    #include "protocols/jabber/auth_scram.h"
    #include "protocols/jabber/jutil.h"
    -#include "ciphers/sha1hash.h"
    -static JabberScramHash sha1_mech = { "-SHA-1", purple_sha1_hash_new, 20 };
    +static JabberScramHash sha1_mech = { "-SHA-1", G_CHECKSUM_SHA1 };
    #define assert_pbkdf2_equal(password, salt, count, expected) { \
    GString *p = g_string_new(password); \
    --- a/libpurple/protocols/jabber/useravatar.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/jabber/useravatar.c Mon Jun 05 16:36:29 2017 +0300
    @@ -154,10 +154,11 @@
    char *lengthstring, *widthstring, *heightstring;
    /* compute the sha1 hash */
    - char *hash = jabber_calculate_data_hash(
    + char *hash = g_compute_checksum_for_data(
    + G_CHECKSUM_SHA1,
    purple_image_get_data(img),
    - purple_image_get_data_size(img), "sha1");
    - char *base64avatar = purple_base64_encode(
    + purple_image_get_data_size(img));
    + char *base64avatar = g_base64_encode(
    purple_image_get_data(img),
    purple_image_get_data_size(img));
    @@ -314,7 +315,7 @@
    if(!b64data)
    return;
    - img = purple_base64_decode(b64data, &size);
    + img = g_base64_decode(b64data, &size);
    if(!img) {
    g_free(b64data);
    return;
    --- a/libpurple/protocols/msn/ft.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,256 +0,0 @@
    -/**
    - * @file ft.c MSN File Transfer functions
    - *
    - * 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 "msnutils.h"
    -#include "sbconn.h"
    -#include "ft.h"
    -
    -/**************************************************************************
    - * Xfer
    - **************************************************************************/
    -
    -void
    -msn_xfer_init(PurpleXfer *xfer)
    -{
    - MsnSlpCall *slpcall;
    - /* MsnSlpLink *slplink; */
    - char *content;
    -
    - purple_debug_info("msn", "xfer_init\n");
    -
    - slpcall = purple_xfer_get_protocol_data(xfer);
    -
    - /* Send Ok */
    - content = g_strdup_printf("SessionID: %lu\r\n\r\n",
    - slpcall->session_id);
    -
    - msn_slp_send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
    - content);
    -
    - g_free(content);
    - msn_slplink_send_queued_slpmsgs(slpcall->slplink);
    -}
    -
    -void
    -msn_xfer_cancel(PurpleXfer *xfer)
    -{
    - MsnSlpCall *slpcall;
    - char *content;
    -
    - g_return_if_fail(xfer != NULL);
    -
    - slpcall = purple_xfer_get_protocol_data(xfer);
    - g_return_if_fail(slpcall != NULL);
    -
    - if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
    - {
    - if (slpcall->started)
    - {
    - msn_slpcall_close(slpcall);
    - }
    - else
    - {
    - content = g_strdup_printf("SessionID: %lu\r\n\r\n",
    - slpcall->session_id);
    -
    - msn_slp_send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
    - content);
    -
    - g_free(content);
    - msn_slplink_send_queued_slpmsgs(slpcall->slplink);
    -
    - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND)
    - slpcall->wasted = TRUE;
    - else
    - msn_slpcall_destroy(slpcall);
    - }
    - }
    -}
    -
    -gssize
    -msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer)
    -{
    - MsnSlpCall *slpcall;
    -
    - g_return_val_if_fail(xfer != NULL, -1);
    - g_return_val_if_fail(data != NULL, -1);
    - g_return_val_if_fail(len > 0, -1);
    -
    - g_return_val_if_fail(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND, -1);
    -
    - slpcall = purple_xfer_get_protocol_data(xfer);
    - /* Not sure I trust it'll be there */
    - g_return_val_if_fail(slpcall != NULL, -1);
    -
    - g_return_val_if_fail(slpcall->xfer_msg != NULL, -1);
    -
    - slpcall->u.outgoing.len = len;
    - slpcall->u.outgoing.data = data;
    - msn_slplink_send_msgpart(slpcall->slplink, slpcall->xfer_msg);
    -
    - return MIN(MSN_SBCONN_MAX_SIZE, len);
    -}
    -
    -gssize
    -msn_xfer_read(guchar **data, gsize size, PurpleXfer *xfer)
    -{
    - MsnSlpCall *slpcall;
    - gsize len;
    -
    - g_return_val_if_fail(xfer != NULL, -1);
    - g_return_val_if_fail(data != NULL, -1);
    -
    - g_return_val_if_fail(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE, -1);
    -
    - slpcall = purple_xfer_get_protocol_data(xfer);
    - /* Not sure I trust it'll be there */
    - g_return_val_if_fail(slpcall != NULL, -1);
    -
    - /* Just pass up the whole GByteArray. We'll make another. */
    - *data = slpcall->u.incoming_data->data;
    - len = slpcall->u.incoming_data->len;
    -
    - g_byte_array_free(slpcall->u.incoming_data, FALSE);
    - slpcall->u.incoming_data = g_byte_array_new();
    -
    - return len;
    -}
    -
    -void
    -msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session)
    -{
    - if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) &&
    - (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) &&
    - (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL))
    - {
    - purple_xfer_cancel_remote(slpcall->xfer);
    - }
    -}
    -
    -void
    -msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
    - gsize size)
    -{
    - PurpleXfer *xfer = slpcall->xfer;
    -
    - purple_xfer_set_completed(xfer, TRUE);
    - purple_xfer_end(xfer);
    -}
    -
    -gchar *
    -msn_file_context_to_wire(MsnFileContext *context)
    -{
    - gchar *ret, *tmp;
    -
    - tmp = ret = g_new(gchar, MSN_FILE_CONTEXT_SIZE_V2 + context->preview_len + 1);
    -
    - msn_push32le(tmp, context->length);
    - msn_push32le(tmp, context->version);
    - msn_push64le(tmp, context->file_size);
    - msn_push32le(tmp, context->type);
    - memcpy(tmp, context->file_name, MAX_FILE_NAME_LEN * 2);
    - tmp += MAX_FILE_NAME_LEN * 2;
    -#if 0
    - memcpy(tmp, context->unknown1, sizeof(context->unknown1));
    - tmp += sizeof(context->unknown1);
    - msn_push32le(tmp, context->unknown2);
    -#else
    - memset(tmp, 0, sizeof(gchar[30]));
    - tmp += sizeof(gchar[30]);
    - msn_push32le(tmp, 0xffffffff);
    -#endif
    - if (context->preview) {
    - memcpy(tmp, context->preview, context->preview_len);
    - }
    - tmp[context->preview_len] = '\0';
    -
    - return ret;
    -}
    -
    -MsnFileContext *
    -msn_file_context_from_wire(const char *buf, gsize len)
    -{
    - MsnFileContext *context;
    -
    - if (!buf || len < MSN_FILE_CONTEXT_SIZE_V0)
    - return NULL;
    -
    - context = g_new(MsnFileContext, 1);
    -
    - context->length = msn_pop32le(buf);
    - context->version = msn_pop32le(buf);
    - if (context->version == 0) {
    - if (context->length != MSN_FILE_CONTEXT_SIZE_V0) {
    - g_free(context);
    - return NULL;
    - }
    - } else if (context->version == 2) {
    - /* The length field is broken for this version. No check. */
    - context->length = MSN_FILE_CONTEXT_SIZE_V2;
    - if (len < MSN_FILE_CONTEXT_SIZE_V2) {
    - g_free(context);
    - return NULL;
    - }
    - } else if (context->version == 3) {
    - if (context->length != MSN_FILE_CONTEXT_SIZE_V3) {
    - g_free(context);
    - return NULL;
    - } else if (len < MSN_FILE_CONTEXT_SIZE_V3) {
    - g_free(context);
    - return NULL;
    - }
    - } else {
    - purple_debug_warning("msn", "Received MsnFileContext with unknown version: %d\n", context->version);
    - g_free(context);
    - return NULL;
    - }
    -
    - context->file_size = msn_pop64le(buf);
    - context->type = msn_pop32le(buf);
    - memcpy(context->file_name, buf, MAX_FILE_NAME_LEN * 2);
    - buf += MAX_FILE_NAME_LEN * 2;
    - if (context->version > 0) {
    -#if 0
    - memcpy(context->unknown1, buf, sizeof(context->unknown1));
    - buf += sizeof(context->unknown1);
    - context->unknown2 = msn_pop32le(buf);
    -#else
    - buf += sizeof(gchar[30]) + sizeof(guint32);
    -#endif
    - }
    -
    - if (context->type == 0 && len > context->length) {
    - context->preview_len = len - context->length;
    - context->preview = g_memdup(buf, context->preview_len);
    - } else {
    - context->preview_len = 0;
    - context->preview = NULL;
    - }
    -
    - return context;
    -}
    -
    --- a/libpurple/protocols/msn/ft.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,71 +0,0 @@
    -/**
    - * @file ft.h MSN File Transfer functions
    - *
    - * 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 MSN_FT_H
    -#define MSN_FT_H
    -
    -#include "slpcall.h"
    -
    -#define MAX_FILE_NAME_LEN 260 /* MAX_PATH in Windows */
    -
    -/**
    - * The context data for a file transfer request
    - */
    -typedef struct
    -{
    - guint32 length; /*< Length of header */
    - guint32 version; /*< MSN version */
    - guint64 file_size; /*< Size of file */
    - guint32 type; /*< Transfer type */
    - gunichar2 file_name[MAX_FILE_NAME_LEN]; /*< Self-explanatory */
    -#if 0
    - gchar unknown1[30]; /*< Used somehow for background sharing */
    - guint32 unknown2; /*< Possibly for background sharing as well */
    -#endif
    - gchar *preview; /*< File preview data, 96x96 PNG */
    - gsize preview_len;
    -} MsnFileContext;
    -
    -#define MSN_FILE_CONTEXT_SIZE_V0 (4*3 + 1*8 + 2*MAX_FILE_NAME_LEN)
    -#define MSN_FILE_CONTEXT_SIZE_V2 (MSN_FILE_CONTEXT_SIZE_V0 + 4*1 + 30)
    -#define MSN_FILE_CONTEXT_SIZE_V3 (MSN_FILE_CONTEXT_SIZE_V2 + 63)
    -
    -void msn_xfer_init(PurpleXfer *xfer);
    -void msn_xfer_cancel(PurpleXfer *xfer);
    -
    -gssize msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer);
    -gssize msn_xfer_read(guchar **data, gsize size, PurpleXfer *xfer);
    -
    -void msn_xfer_completed_cb(MsnSlpCall *slpcall,
    - const guchar *body, gsize size);
    -void msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session);
    -
    -gchar *
    -msn_file_context_to_wire(MsnFileContext *context);
    -
    -MsnFileContext *
    -msn_file_context_from_wire(const char *buf, gsize len);
    -
    -#endif /* MSN_FT_H */
    -
    --- a/libpurple/protocols/mxit/cipher-mxit.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,83 +0,0 @@
    -/*
    - * purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#include "cipher-mxit.h"
    -
    -#include "cipher.h"
    -#include "ciphers/aescipher.h"
    -#include "debug.h"
    -#include "internal.h"
    -
    -#define INITIAL_KEY "6170383452343567"
    -#define SECRET_HEADER "<mxit/>"
    -
    -/**
    - * Encrypt the user's cleartext password using the AES 128-bit (ECB)
    - * encryption algorithm.
    - *
    - * @param session The MXit session object
    - *
    - * @return The encrypted & encoded password. Must be g_free'd when
    - * no longer needed.
    - */
    -gchar *
    -mxit_encrypt_password(struct MXitSession* session)
    -{
    - guchar key[16];
    - size_t clientkey_len, header_len, pass_len, plaintext_len;
    - const gchar *plaintext_passwd;
    - guchar *plaintext;
    - guchar encrypted[64]; /* shouldn't be longer than 17 */
    - PurpleCipher *cipher;
    - ssize_t encrypted_size;
    -
    - purple_debug_info(MXIT_PLUGIN_ID, "mxit_encrypt_password");
    -
    - /* build the AES encryption key */
    - g_assert(strlen(INITIAL_KEY) == sizeof(key));
    - memcpy(key, INITIAL_KEY, sizeof(key));
    - clientkey_len = strlen(session->clientkey);
    - if (clientkey_len > sizeof(key))
    - clientkey_len = sizeof(key);
    - memcpy(key, session->clientkey, clientkey_len);
    -
    - /* build the secret data to be encrypted: SECRET_HEADER + password */
    - plaintext_passwd = purple_connection_get_password(session->con);
    - g_return_val_if_fail(plaintext_passwd, NULL);
    - pass_len = strlen(plaintext_passwd);
    - header_len = strlen(SECRET_HEADER);
    - /* Trailing NUL, just to be safe. But PKCS#7 seems to be enough. */
    - plaintext_len = header_len + pass_len + 1;
    - plaintext = g_new0(guchar, plaintext_len);
    - memcpy(plaintext, SECRET_HEADER, header_len);
    - memcpy(plaintext + header_len, plaintext_passwd, pass_len);
    -
    - /* encrypt */
    - cipher = purple_aes_cipher_new();
    - purple_cipher_set_key(cipher, key, sizeof(key));
    - purple_cipher_set_batch_mode(cipher, PURPLE_CIPHER_BATCH_MODE_ECB);
    - encrypted_size = purple_cipher_encrypt(cipher,
    - plaintext, plaintext_len, encrypted, sizeof(encrypted));
    - g_return_val_if_fail(encrypted_size > 0, NULL);
    -
    - return purple_base64_encode(encrypted, encrypted_size);
    -}
    --- a/libpurple/protocols/mxit/cipher-mxit.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,31 +0,0 @@
    -/*
    - * 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 _MXIT_CIPHER_H_
    -#define _MXIT_CIPHER_H_
    -
    -#include "mxit.h"
    -
    -gchar *
    -mxit_encrypt_password(struct MXitSession* session);
    -
    -#endif /* _MXIT_CIPHER_H_ */
    --- a/libpurple/protocols/mxit/client.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2968 +0,0 @@
    -/*
    - * MXit Protocol libPurple Plugin
    - *
    - * -- MXit client protocol implementation --
    - *
    - * Pieter Loubser <libpurple@mxit.com>
    - *
    - * (C) Copyright 2009 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 "internal.h"
    -#include "debug.h"
    -#include "version.h"
    -
    -#include "client.h"
    -#include "mxit.h"
    -#include "roster.h"
    -#include "chunk.h"
    -#include "filexfer.h"
    -#include "markup.h"
    -#include "multimx.h"
    -#include "splashscreen.h"
    -#include "login.h"
    -#include "formcmds.h"
    -#include "http.h"
    -#include "cipher.h"
    -
    -
    -#define MXIT_MS_OFFSET 3
    -
    -/* configure the right record terminator char to use */
    -#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.
    - *
    - * @param type The type of notification:
    - * - info: PURPLE_NOTIFY_MSG_INFO
    - * - warning: PURPLE_NOTIFY_MSG_WARNING
    - * - error: PURPLE_NOTIFY_MSG_ERROR
    - * @param heading Heading text
    - * @param message Message text
    - */
    -void mxit_popup( int type, const char* heading, const char* message )
    -{
    - /* (reference: "libpurple/notify.h") */
    - purple_notify_message( NULL, type, _( MXIT_POPUP_WIN_NAME ), heading, message, NULL, NULL, NULL );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * For compatibility with legacy clients, all usernames are sent from MXit with a domain
    - * appended. For MXit contacts, this domain is set to "@m". This function strips
    - * those fake domains.
    - *
    - * @param username The username of the contact
    - */
    -void mxit_strip_domain( char* username )
    -{
    - if ( g_str_has_suffix( username, "@m" ) )
    - username[ strlen( username ) - 2 ] = '\0';
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Dump a byte buffer to the console for debugging purposes.
    - *
    - * @param buf The data
    - * @param len The data length
    - */
    -void dump_bytes( struct MXitSession* session, const char* buf, int len )
    -{
    - char* msg = g_malloc0( len + 1 );
    - int i;
    -
    - for ( i = 0; i < len; i++ ) {
    - char ch = buf[i];
    -
    - if ( ch == CP_REC_TERM ) /* record terminator */
    - msg[i] = '!';
    - else if ( ch == CP_FLD_TERM ) /* field terminator */
    - msg[i] = '^';
    - else if ( ch == CP_PKT_TERM ) /* packet terminator */
    - msg[i] = '@';
    - else if ( ( ch < 0x20 ) || ( ch > 0x7E ) ) /* non-printable character */
    - msg[i] = '_';
    - else
    - msg[i] = ch;
    - }
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "DUMP: '%s'\n", msg );
    -
    - g_free( msg );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Determine if we have an active chat with a specific contact
    - *
    - * @param session The MXit session object
    - * @param who The contact name
    - * @return Return true if we have an active chat with the contact
    - */
    -gboolean find_active_chat( const GList* chats, const char* who )
    -{
    - const GList* list = chats;
    - const char* chat = NULL;
    -
    - while ( list ) {
    - chat = (const char*) list->data;
    -
    - if ( strcmp( chat, who ) == 0 )
    - return TRUE;
    -
    - list = g_list_next( list );
    - }
    -
    - return FALSE;
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * scnprintf
    - *
    - * @param string The destination buffer.
    - * @param size The maximum size of the destination buffer.
    - * @param format The format string
    - * @param ... The parameters to the format string.
    - * @return The number of characters actually stored in the buffer.
    - */
    -static int scnprintf( gchar* string, size_t size, const char *format, ... )
    -{
    - va_list args;
    - guint i;
    -
    - va_start( args, format );
    - i = g_vsnprintf( string, size, format, args );
    - va_end( args );
    -
    - if ( i < size )
    - return i;
    - else if ( size > 0 ) /* destination buffer too short - return number of characters actually inserted */
    - return size - 1;
    - else
    - return 0;
    -}
    -
    -
    -
    -/*========================================================================================================================
    - * Low-level Packet transmission
    - */
    -
    -/*------------------------------------------------------------------------
    - * Remove next packet from transmission queue.
    - *
    - * @param session The MXit session object
    - * @return The next packet for transmission (or NULL)
    - */
    -static struct tx_packet* pop_tx_packet( struct MXitSession* session )
    -{
    - struct tx_packet* packet = NULL;
    -
    - if ( session->queue.count > 0 ) {
    - /* dequeue the next packet */
    - packet = session->queue.packets[session->queue.rd_i];
    - session->queue.packets[session->queue.rd_i] = NULL;
    - session->queue.rd_i = ( session->queue.rd_i + 1 ) % MAX_QUEUE_SIZE;
    - session->queue.count--;
    - }
    -
    - return packet;
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Add packet to transmission queue.
    - *
    - * @param session The MXit session object
    - * @param packet The packet to transmit
    - * @return Return TRUE if packet was enqueue, or FALSE if queue is full.
    - */
    -static gboolean push_tx_packet( struct MXitSession* session, struct tx_packet* packet )
    -{
    - if ( session->queue.count < MAX_QUEUE_SIZE ) {
    - /* enqueue packet */
    - session->queue.packets[session->queue.wr_i] = packet;
    - session->queue.wr_i = ( session->queue.wr_i + 1 ) % MAX_QUEUE_SIZE;
    - session->queue.count++;
    - return TRUE;
    - }
    - else
    - return FALSE; /* queue is full */
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Deallocate transmission packet.
    - *
    - * @param packet The packet to deallocate.
    - */
    -static void free_tx_packet( struct tx_packet* packet )
    -{
    - g_free( packet->data );
    - g_free( packet );
    - packet = NULL;
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Flush all the packets from the tx queue and release the resources.
    - *
    - * @param session The MXit session object
    - */
    -static void flush_queue( struct MXitSession* session )
    -{
    - struct tx_packet* packet;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "flushing the tx queue\n" );
    -
    - while ( (packet = pop_tx_packet( session ) ) != NULL )
    - free_tx_packet( packet );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * TX Step 3: Write the packet data to the TCP connection.
    - *
    - * @param fd The file descriptor
    - * @param pktdata The packet data
    - * @param pktlen The length of the packet data
    - * @return Return -1 on error, otherwise 0
    - */
    -static int mxit_write_sock_packet( int fd, const char* pktdata, int pktlen )
    -{
    - int written;
    - int res;
    -
    - written = 0;
    - while ( written < pktlen ) {
    - res = write( fd, &pktdata[written], pktlen - written );
    - if ( res <= 0 ) {
    - /* error on socket */
    - if ( errno == EAGAIN )
    - continue;
    -
    - purple_debug_error( MXIT_PLUGIN_ID, "Error while writing packet to MXit server (%i)\n", res );
    - return -1;
    - }
    - written += res;
    - }
    -
    - return 0;
    -}
    -
    -
    -/**
    - * Callback called for handling a HTTP GET response
    - *
    - * @param http_conn http api object (see http.h)
    - * @param response http api object (see http.h)
    - * @param _session The MXit session object
    - */
    -static void
    -mxit_cb_http_rx(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
    - gpointer _session)
    -{
    - struct MXitSession *session = _session;
    - const gchar *got_data;
    - size_t got_len;
    -
    - if (!purple_http_response_is_successful(response)) {
    - purple_debug_error(MXIT_PLUGIN_ID, "HTTP response error (%s)\n",
    - purple_http_response_get_error(response));
    - return;
    - }
    -
    - /* convert the HTTP result */
    - got_data = purple_http_response_get_data(response, &got_len);
    - memcpy(session->rx_dbuf, got_data, got_len);
    - session->rx_i = got_len;
    -
    - mxit_parse_packet(session);
    -}
    -
    -
    -/**
    - * TX Step 3: Write the packet data to the HTTP connection (GET style).
    - *
    - * @param session The MXit session object
    - * @param packet The packet data
    - */
    -static void
    -mxit_write_http_get(struct MXitSession* session, struct tx_packet* packet)
    -{
    - PurpleHttpRequest *req;
    - char *part = NULL;
    -
    - if (packet->datalen > 0) {
    - char *tmp;
    -
    - tmp = g_strndup(packet->data, packet->datalen);
    - part = g_strdup(purple_url_encode(tmp));
    - g_free(tmp);
    - }
    -
    - req = purple_http_request_new(NULL);
    - purple_http_request_set_url_printf(req, "%s?%s%s", session->http_server,
    - purple_url_encode(packet->header), part ? part : "");
    - purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT);
    - purple_http_connection_set_add(session->async_http_reqs,
    - purple_http_request(session->con, req, mxit_cb_http_rx,
    - session));
    - purple_http_request_unref(req);
    -
    - g_free(part);
    -}
    -
    -
    -/**
    - * TX Step 3: Write the packet data to the HTTP connection (POST style).
    - *
    - * @param session The MXit session object
    - * @param packet The packet data
    - */
    -static void
    -mxit_write_http_post(struct MXitSession* session, struct tx_packet* packet)
    -{
    - PurpleHttpRequest *req;
    -
    - /* strip off the last '&' from the header */
    - packet->header[packet->headerlen - 1] = '\0';
    - packet->headerlen--;
    -
    - req = purple_http_request_new(NULL);
    - purple_http_request_set_url_printf(req, "%s?%s", session->http_server,
    - purple_url_encode(packet->header));
    - purple_http_request_set_method(req, "POST");
    - purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT);
    - purple_http_request_header_set(req, "Content-Type",
    - "application/octet-stream");
    - purple_http_request_set_contents(req, packet->data + MXIT_MS_OFFSET,
    - packet->datalen - MXIT_MS_OFFSET);
    - purple_http_connection_set_add(session->async_http_reqs,
    - purple_http_request(session->con, req, mxit_cb_http_rx,
    - session));
    - purple_http_request_unref(req);
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * TX Step 2: Handle the transmission of the packet to the MXit server.
    - *
    - * @param session The MXit session object
    - * @param packet The packet to transmit
    - */
    -static void mxit_send_packet( struct MXitSession* session, struct tx_packet* packet )
    -{
    - int res;
    -
    - if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) {
    - /* we are not connected so ignore all packets to be send */
    - purple_debug_error( MXIT_PLUGIN_ID, "Dropping TX packet (we are not connected)\n" );
    - return;
    - }
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "Packet send CMD:%i (%i)\n", packet->cmd, packet->headerlen + packet->datalen );
    -#ifdef DEBUG_PROTOCOL
    - dump_bytes( session, packet->header, packet->headerlen );
    - dump_bytes( session, packet->data, packet->datalen );
    -#endif
    -
    - if ( !session->http ) {
    - /* socket connection */
    - char data[packet->datalen + packet->headerlen];
    - int datalen;
    -
    - /* create raw data buffer */
    - memcpy( data, packet->header, packet->headerlen );
    - memcpy( data + packet->headerlen, packet->data, packet->datalen );
    - datalen = packet->headerlen + packet->datalen;
    -
    - res = mxit_write_sock_packet( session->fd, data, datalen );
    - if ( res < 0 ) {
    - /* we must have lost the connection, so terminate it so that we can reconnect */
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "We have lost the connection to MXit. Please reconnect." ) );
    - }
    - }
    - else {
    - /* http connection */
    -
    - if ( packet->cmd == CP_CMD_MEDIA ) {
    - /* multimedia packets must be send with a HTTP POST */
    - mxit_write_http_post( session, packet );
    - }
    - else {
    - mxit_write_http_get( session, packet );
    - }
    - }
    -
    - /* update the timestamp of the last-transmitted packet */
    - session->last_tx = mxit_now_milli();
    -
    - /*
    - * we need to remember that we are still waiting for the ACK from
    - * the server on this request
    - */
    - session->outack = packet->cmd;
    -
    - /* free up the packet resources */
    - free_tx_packet( packet );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * TX Step 1: Create a new Tx packet and queue it for sending.
    - *
    - * @param session The MXit session object
    - * @param data The packet data (payload)
    - * @param datalen The length of the packet data
    - * @param cmd The MXit command for this packet
    - */
    -static void mxit_queue_packet( struct MXitSession* session, const char* data, int datalen, int cmd )
    -{
    - struct tx_packet* packet;
    - char header[256];
    - int hlen;
    -
    - /* create a packet for sending */
    - packet = g_new0( struct tx_packet, 1 );
    - packet->data = g_malloc0( datalen );
    - packet->cmd = cmd;
    - packet->headerlen = 0;
    -
    - /* create generic packet header */
    - hlen = scnprintf( header, sizeof( header ), "id=%s%c", purple_account_get_username( session->acc ), CP_REC_TERM ); /* client mxitid */
    -
    - if ( session->http ) {
    - /* http connection only */
    - hlen += scnprintf( header + hlen, sizeof( header ) - hlen, "s=" );
    - if ( session->http_sesid > 0 ) {
    - hlen += scnprintf( header + hlen, sizeof( header ) - hlen, "%u%c", session->http_sesid, CP_FLD_TERM ); /* http session id */
    - }
    - session->http_seqno++;
    - hlen += scnprintf( header + hlen, sizeof( header ) - hlen, "%u%c", session->http_seqno, CP_REC_TERM ); /* http request sequence id */
    - }
    -
    - hlen += scnprintf( header + hlen, sizeof( header ) - hlen, "cm=%i%c", cmd, CP_REC_TERM ); /* packet command */
    -
    - if ( !session->http ) {
    - /* socket connection only */
    - packet->headerlen = scnprintf( packet->header, sizeof( packet->header ), "ln=%i%c", ( datalen + hlen ), CP_REC_TERM ); /* packet length */
    - }
    -
    - /* copy the header to packet */
    - memcpy( packet->header + packet->headerlen, header, hlen );
    - packet->headerlen += hlen;
    -
    - /* copy payload to packet */
    - if ( datalen > 0 )
    - memcpy( packet->data, data, datalen );
    - packet->datalen = datalen;
    -
    -
    - /* 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 {
    - /* 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 */
    - free_tx_packet( packet );
    - return;
    - }
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "queueing packet for later sending cmd=%i\n", cmd );
    - if ( !push_tx_packet( session, packet ) ) {
    - /* packet could not be queued for transmission */
    - mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Message Send Error" ), _( "Unable to process your request at this time" ) );
    - free_tx_packet( packet );
    - }
    - }
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Manage the packet send queue (send next packet, timeout's, etc).
    - *
    - * @param session The MXit session object
    - */
    -static void mxit_manage_queue( struct MXitSession* session )
    -{
    - 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;
    - }
    - else if ( session->outack > 0 ) {
    - /* we are still waiting for an outstanding ACK from the MXit server */
    - 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, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Timeout while waiting for a response from the MXit server." ) );
    - }
    - 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->q_fast_timer_id == 0 ) {
    - /* the fast timer has not been set yet */
    - 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;
    - session->q_fast_timer_id = 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 );
    - }
    - }
    - }
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * 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;
    -
    - session->q_fast_timer_id = 0;
    - mxit_manage_queue( session );
    -
    - /* stop running */
    - return FALSE;
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Callback to manage HTTP server polling (HTTP connections ONLY)
    - *
    - * @param session The MXit session object
    - */
    -gboolean mxit_manage_polling( gpointer user_data )
    -{
    - struct MXitSession* session = (struct MXitSession*) user_data;
    - gboolean poll = FALSE;
    - gint64 now = mxit_now_milli();
    - gint64 rxdiff;
    -
    - if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) {
    - /* we only poll if we are actually logged in */
    - return TRUE;
    - }
    -
    - /* calculate the time differences */
    - rxdiff = now - session->last_rx;
    -
    - if ( rxdiff < MXIT_HTTP_POLL_MIN ) {
    - /* we received some reply a few moments ago, so reset the poll interval */
    - session->http_interval = MXIT_HTTP_POLL_MIN;
    - }
    - else if ( session->http_last_poll < ( now - session->http_interval ) ) {
    - /* time to poll again */
    - poll = TRUE;
    -
    - /* back-off some more with the polling */
    - session->http_interval = session->http_interval + ( session->http_interval / 2 );
    - if ( session->http_interval > MXIT_HTTP_POLL_MAX )
    - session->http_interval = MXIT_HTTP_POLL_MAX;
    - }
    -
    - /* debugging */
    - //purple_debug_info( MXIT_PLUGIN_ID, "POLL TIMER: %i (%i)\n", session->http_interval, rxdiff );
    -
    - if ( poll ) {
    - /* send poll request */
    - session->http_last_poll = mxit_now_milli();
    - mxit_send_poll( session );
    - }
    -
    - return TRUE;
    -}
    -
    -
    -/*========================================================================================================================
    - * Send MXit operations.
    - */
    -
    -/*------------------------------------------------------------------------
    - * Send a ping/keepalive packet to MXit server.
    - *
    - * @param session The MXit session object
    - */
    -void mxit_send_ping( struct MXitSession* session )
    -{
    - /* queue packet for transmission */
    - mxit_queue_packet( session, NULL, 0, CP_CMD_PING );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a poll request to the HTTP server (HTTP connections ONLY).
    - *
    - * @param session The MXit session object
    - */
    -void mxit_send_poll( struct MXitSession* session )
    -{
    - /* queue packet for transmission */
    - mxit_queue_packet( session, NULL, 0, CP_CMD_POLL );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a logout packet to the MXit server.
    - *
    - * @param session The MXit session object
    - */
    -void mxit_send_logout( struct MXitSession* session )
    -{
    - /* queue packet for transmission */
    - mxit_queue_packet( session, NULL, 0, CP_CMD_LOGOUT );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a register packet to the MXit server.
    - *
    - * @param session The MXit session object
    - */
    -void mxit_send_register( struct MXitSession* session )
    -{
    - struct MXitProfile* profile = session->profile;
    - 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 );
    -
    - /* 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 = scnprintf( 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 */
    - "%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, 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 );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a login packet to the MXit server.
    - *
    - * @param session The MXit session object
    - */
    -void mxit_send_login( struct MXitSession* session )
    -{
    - 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 );
    -
    - /* 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 = scnprintf( 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, 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
    - );
    -
    - /* include "custom resource" information */
    - splashId = splash_current( session );
    - if ( splashId != NULL )
    - datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%ccr=%s", CP_REC_TERM, splashId );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_LOGIN );
    -
    - g_free( clientVersion );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a chat message packet to the MXit server.
    - *
    - * @param session The MXit session object
    - * @param to The username of the recipient
    - * @param msg The message text
    - */
    -void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup, gboolean is_command )
    -{
    - char data[CP_MAX_PACKET];
    - char* markuped_msg;
    - int datalen;
    - int msgtype = ( is_command ? CP_MSGTYPE_COMMAND : CP_MSGTYPE_NORMAL );
    -
    - /* first we need to convert the markup from libPurple to MXit format */
    - if ( parse_markup )
    - markuped_msg = mxit_convert_markup_tx( msg, &msgtype );
    - else
    - markuped_msg = g_strdup( msg );
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s%c%s%c%i%c%i", /* "ms"=jid\1msg\1type\1flags */
    - to, CP_FLD_TERM, markuped_msg, CP_FLD_TERM, msgtype, CP_FLD_TERM, CP_MSG_MARKUP | CP_MSG_EMOTICON
    - );
    -
    - /* free the resources */
    - g_free( markuped_msg );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_TX_MSG );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a extended profile request packet to the MXit server.
    - *
    - * @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 attribute The names of the attributes
    - */
    -void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    - unsigned int i;
    -
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s%c%i", /* "ms="mxitid\1nr_attributes */
    - ( username ? username : "" ), CP_FLD_TERM, nr_attrib
    - );
    -
    - /* add attributes */
    - for ( i = 0; i < nr_attrib; i++ )
    - datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, attribute[i] );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_GET );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send an update profile packet to the MXit server.
    - *
    - * @param session The MXit session object
    - * @param password The new password to be used for logging in (optional)
    - * @param nr_attrib The number of attributes
    - * @param attributes String containing the attribute-name, attribute-type and value (seperated by '\01')
    - */
    -void mxit_send_extprofile_update( struct MXitSession* session, const char* password, unsigned int nr_attrib, const char* attributes )
    -{
    - char data[CP_MAX_PACKET];
    - gchar** parts = NULL;
    - int datalen;
    - unsigned int i;
    -
    - if ( attributes )
    - parts = g_strsplit( attributes, "\01", 1 + ( nr_attrib * 3 ) );
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s%c%i", /* "ms"=password\1nr_attibutes */
    - ( password ) ? password : "", CP_FLD_TERM, nr_attrib
    - );
    -
    - /* add attributes */
    - for ( i = 1; i < nr_attrib * 3; i+=3 ) {
    - if ( parts == NULL || parts[i] == NULL || parts[i + 1] == NULL || parts[i + 2] == NULL ) {
    - purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile update attributes = '%s' - nbr=%u\n", attributes, nr_attrib );
    - g_strfreev( parts );
    - return;
    - }
    - datalen += scnprintf( data + datalen, sizeof( data ) - datalen,
    - "%c%s%c%s%c%s", /* \1name\1type\1value */
    - CP_FLD_TERM, parts[i], CP_FLD_TERM, parts[i + 1], CP_FLD_TERM, parts[i + 2] );
    - }
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_SET );
    -
    - /* freeup the memory */
    - g_strfreev( parts );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * 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 = scnprintf( data, sizeof( data ),
    - "ms=%i%c%s%c%i%c%i%c%i", /* inputType \1 input \1 maxSuggestions \1 startIndex \1 numAttributes \1 name0 \1 name1 ... \1 nameN */
    - CP_SUGGEST_FRIENDS, CP_FLD_TERM, "", CP_FLD_TERM, max, CP_FLD_TERM, 0, CP_FLD_TERM, nr_attrib );
    -
    - /* add attributes */
    - for ( i = 0; i < nr_attrib; i++ )
    - datalen += scnprintf( data + datalen, sizeof( 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 = scnprintf( data, sizeof( data ),
    - "ms=%i%c%s%c%i%c%i%c%i", /* inputType \1 input \1 maxSuggestions \1 startIndex \1 numAttributes \1 name0 \1 name1 ... \1 nameN */
    - CP_SUGGEST_SEARCH, CP_FLD_TERM, text, CP_FLD_TERM, max, CP_FLD_TERM, 0, CP_FLD_TERM, nr_attrib );
    -
    - /* add attributes */
    - for ( i = 0; i < nr_attrib; i++ )
    - datalen += scnprintf( data + datalen, sizeof( 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
    - * @param presence The presence (as per MXit types)
    - * @param statusmsg The status message (can be NULL)
    - */
    -void mxit_send_presence( struct MXitSession* session, int presence, const char* statusmsg )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%i%c", /* "ms"=show\1status */
    - presence, CP_FLD_TERM
    - );
    -
    - /* append status message (if one is set) */
    - if ( statusmsg )
    - datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%s", statusmsg );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_STATUS );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a mood update packet to the MXit server.
    - *
    - * @param session The MXit session object
    - * @param mood The mood (as per MXit types)
    - */
    -void mxit_send_mood( struct MXitSession* session, int mood )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%i", /* "ms"=mood */
    - mood
    - );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_MOOD );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send an invite contact packet to the MXit server.
    - *
    - * @param session The MXit session object
    - * @param username The username of the contact being invited
    - * @param mxitid Indicates the username is a MXitId.
    - * @param alias Our alias for the contact
    - * @param groupname Group in which contact should be stored.
    - * @param message Invite message
    - */
    -void mxit_send_invite( struct MXitSession* session, const char* username, gboolean mxitid, const char* alias, const char* groupname, const char* message )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s%c%s%c%s%c%i%c%s%c%i", /* "ms"=group \1 username \1 alias \1 type \1 msg \1 isuserid */
    - groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias,
    - CP_FLD_TERM, MXIT_TYPE_MXIT, CP_FLD_TERM,
    - ( message ? message : "" ), CP_FLD_TERM,
    - ( mxitid ? 0 : 1 )
    - );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_INVITE );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a remove contact packet to the MXit server.
    - *
    - * @param session The MXit session object
    - * @param username The username of the contact being removed
    - */
    -void mxit_send_remove( struct MXitSession* session, const char* username )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s", /* "ms"=username */
    - username
    - );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_REMOVE );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send an accept subscription (invite) packet to the MXit server.
    - *
    - * @param session The MXit session object
    - * @param username The username of the contact being accepted
    - * @param alias Our alias for the contact
    - */
    -void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s%c%s%c%s", /* "ms"=username\1group\1alias */
    - username, CP_FLD_TERM, "", CP_FLD_TERM, alias
    - );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_ALLOW );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send an deny subscription (invite) packet to the MXit server.
    - *
    - * @param session The MXit session object
    - * @param username The username of the contact being denied
    - * @param reason The message describing the reason for the rejection (can be NULL).
    - */
    -void mxit_send_deny_sub( struct MXitSession* session, const char* username, const char* reason )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s", /* "ms"=username */
    - username
    - );
    -
    - /* append reason (if one is set) */
    - if ( reason )
    - datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, reason );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_DENY );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send an update contact packet to the MXit server.
    - *
    - * @param session The MXit session object
    - * @param username The username of the contact being denied
    - * @param alias Our alias for the contact
    - * @param groupname Group in which contact should be stored.
    - */
    -void mxit_send_update_contact( struct MXitSession* session, const char* username, const char* alias, const char* groupname )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s%c%s%c%s", /* "ms"=groupname\1username\1alias */
    - groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias
    - );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_UPDATE );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a splash-screen click event packet.
    - *
    - * @param session The MXit session object
    - * @param splashid The identifier of the splash-screen
    - */
    -void mxit_send_splashclick( struct MXitSession* session, const char* splashid )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s", /* "ms"=splashId */
    - splashid
    - );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_SPLASHCLICK );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a message event packet.
    - *
    - * @param session The MXit session object
    - * @param to The username of the original sender (ie, recipient of the event)
    - * @param id The identifier of the event (received in message)
    - * @param event Identified the type of event
    - */
    -void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_msgevent: to=%s id=%s event=%i\n", to, id, event );
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s%c%s%c%i", /* "ms"=contactAddress \1 id \1 event */
    - to, CP_FLD_TERM, id, CP_FLD_TERM, event
    - );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_MSGEVENT );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send packet to create a MultiMX room.
    - *
    - * @param session The MXit session object
    - * @param groupname Name of the room to create
    - * @param nr_usernames Number of users in initial invite
    - * @param usernames The usernames of the users in the initial invite
    - */
    -void mxit_send_groupchat_create( struct MXitSession* session, const char* groupname, int nr_usernames, const char* usernames[] )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen;
    - int i;
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s%c%i", /* "ms"=roomname\1nr_jids\1jid0\1..\1jidN */
    - groupname, CP_FLD_TERM, nr_usernames
    - );
    -
    - /* add usernames */
    - for ( i = 0; i < nr_usernames; i++ )
    - datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, usernames[i] );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_CREATE );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send packet to invite users to existing MultiMX room.
    - *
    - * @param session The MXit session object
    - * @param roomid The unique RoomID for the MultiMx room.
    - * @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];
    - int datalen;
    - int i;
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ),
    - "ms=%s%c%i", /* "ms"=roomid\1nr_jids\1jid0\1..\1jidN */
    - roomid, CP_FLD_TERM, nr_usernames
    - );
    -
    - /* add usernames */
    - for ( i = 0; i < nr_usernames; i++ )
    - datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, usernames[i] );
    -
    - /* queue packet for transmission */
    - mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_INVITE );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a "send file direct" multimedia packet.
    - *
    - * @param session The MXit session object
    - * @param username The username of the recipient
    - * @param filename The name of the file being sent
    - * @param buf The content of the file
    - * @param buflen The length of the file contents
    - */
    -void mxit_send_file( struct MXitSession* session, const char* username, const char* filename, const unsigned char* buf, size_t buflen )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen = 0;
    - gchar* chunk;
    - size_t chunksize;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "SENDING FILE '%s' of %zu bytes to user '%s'\n", filename, buflen, username );
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ), "ms=" );
    -
    - /* map chunk header over data buffer */
    - chunk = &data[datalen];
    -
    - /* encode chunk */
    - chunksize = mxit_chunk_create_senddirect( chunk_data( chunk ), username, filename, buf, buflen );
    - set_chunk_type( chunk, CP_CHUNK_DIRECT_SND );
    - set_chunk_length( chunk, chunksize );
    - datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
    -
    - /* send the byte stream to the mxit server */
    - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a "reject file" multimedia packet.
    - *
    - * @param session The MXit session object
    - * @param fileid A unique ID that identifies this file
    - */
    -void mxit_send_file_reject( struct MXitSession* session, const char* fileid )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen = 0;
    - gchar* chunk;
    - size_t chunksize;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_reject\n" );
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ), "ms=" );
    -
    - /* map chunk header over data buffer */
    - chunk = &data[datalen];
    -
    - /* encode chunk */
    - chunksize = mxit_chunk_create_reject( chunk_data( chunk ), fileid );
    - set_chunk_type( chunk, CP_CHUNK_REJECT );
    - set_chunk_length( chunk, chunksize );
    - datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
    -
    - /* send the byte stream to the mxit server */
    - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a "get file" multimedia packet.
    - *
    - * @param session The MXit session object
    - * @param fileid A unique ID that identifies this file
    - * @param filesize The number of bytes to retrieve
    - * @param offset Offset in file at which to start retrieving
    - */
    -void mxit_send_file_accept( struct MXitSession* session, const char* fileid, size_t filesize, size_t offset )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen = 0;
    - gchar* chunk;
    - size_t chunksize;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_accept\n" );
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ), "ms=" );
    -
    - /* map chunk header over data buffer */
    - chunk = &data[datalen];
    -
    - /* encode chunk */
    - chunksize = mxit_chunk_create_get( chunk_data(chunk), fileid, filesize, offset );
    - set_chunk_type( chunk, CP_CHUNK_GET );
    - set_chunk_length( chunk, chunksize );
    - datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
    -
    - /* send the byte stream to the mxit server */
    - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a "received file" multimedia packet.
    - *
    - * @param session The MXit session object
    - * @param status The status of the file-transfer
    - */
    -void mxit_send_file_received( struct MXitSession* session, const char* fileid, short status )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen = 0;
    - gchar* chunk;
    - size_t chunksize;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_received\n" );
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ), "ms=" );
    -
    - /* map chunk header over data buffer */
    - chunk = &data[datalen];
    -
    - /* encode chunk */
    - chunksize = mxit_chunk_create_received( chunk_data(chunk), fileid, status );
    - set_chunk_type( chunk, CP_CHUNK_RECEIVED );
    - set_chunk_length( chunk, chunksize );
    - datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
    -
    - /* send the byte stream to the mxit server */
    - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a "set avatar" multimedia packet.
    - *
    - * @param session The MXit session object
    - * @param data The avatar data
    - * @param buflen The length of the avatar data
    - */
    -void mxit_set_avatar( struct MXitSession* session, const unsigned char* avatar, size_t avatarlen )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen = 0;
    - gchar* chunk;
    - size_t chunksize;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_set_avatar: %zu bytes\n", avatarlen );
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ), "ms=" );
    -
    - /* map chunk header over data buffer */
    - chunk = &data[datalen];
    -
    - /* encode chunk */
    - chunksize = mxit_chunk_create_set_avatar( chunk_data(chunk), avatar, avatarlen );
    - set_chunk_type( chunk, CP_CHUNK_SET_AVATAR );
    - set_chunk_length( chunk, chunksize );
    - datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
    -
    - /* send the byte stream to the mxit server */
    - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Send a "get avatar" multimedia packet.
    - *
    - * @param session The MXit session object
    - * @param mxitId The username who's avatar to request
    - * @param avatarId The id of the avatar image (as string)
    - * @param data The avatar data
    - * @param buflen The length of the avatar data
    - */
    -void mxit_get_avatar( struct MXitSession* session, const char* mxitId, const char* avatarId )
    -{
    - char data[CP_MAX_PACKET];
    - int datalen = 0;
    - gchar* chunk;
    - size_t chunksize;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_get_avatar: %s\n", mxitId );
    -
    - /* convert the packet to a byte stream */
    - datalen = scnprintf( data, sizeof( data ), "ms=" );
    -
    - /* map chunk header over data buffer */
    - chunk = &data[datalen];
    -
    - /* encode chunk */
    - chunksize = mxit_chunk_create_get_avatar( chunk_data(chunk), mxitId, avatarId );
    - set_chunk_type( chunk, CP_CHUNK_GET_AVATAR );
    - set_chunk_length( chunk, chunksize );
    - datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
    -
    - /* send the byte stream to the mxit server */
    - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Process a login message packet.
    - *
    - * @param session The MXit session object
    - * @param records The packet's data records
    - * @param rcount The number of data records
    - */
    -static void mxit_parse_cmd_login( struct MXitSession* session, struct record** records, int rcount )
    -{
    - PurpleStatus* status;
    - int presence;
    - const char* statusmsg;
    - const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_FULLNAME,
    - CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL,
    - CP_PROFILE_MOBILENR, CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME, CP_PROFILE_RELATIONSHIP, CP_PROFILE_FLAGS };
    -
    - purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
    -
    - /* we were not yet logged in so we need to complete the login sequence here */
    - session->flags |= MXIT_FLAG_LOGGEDIN;
    - purple_connection_update_progress( session->con, _( "Successfully Logged In..." ), 3, 4 );
    - purple_connection_set_state( session->con, PURPLE_CONNECTION_CONNECTED );
    -
    - /* save extra info if this is a HTTP connection */
    - if ( session->http ) {
    - /* save the http server to use for this session */
    - g_strlcpy( session->http_server, records[1]->fields[3]->data, sizeof( session->http_server ) );
    -
    - /* save the session id */
    - session->http_sesid = atoi( records[0]->fields[0]->data );
    - }
    -
    - /* extract UserId (from protocol 5.9) */
    - if ( records[1]->fcount >= 9 )
    - session->uid = g_strdup( records[1]->fields[8]->data );
    -
    - /* display the current splash-screen */
    - if ( splash_popup_enabled( session ) )
    - splash_display( session );
    -
    - /* update presence status */
    - status = purple_account_get_active_status( session->acc );
    - presence = mxit_convert_presence( purple_status_get_id( status ) );
    - statusmsg = purple_status_get_attr_string( status, "message" );
    -
    - if ( ( presence != MXIT_PRESENCE_ONLINE ) || ( statusmsg ) ) {
    - /* when logging into MXit, your default presence is online. but with the UI, one can change
    - * the presence to whatever. in the case where its changed to a different presence setting
    - * we need to send an update to the server, otherwise the user's presence will be out of
    - * sync between the UI and MXit.
    - */
    - char* statusmsg1 = purple_markup_strip_html( statusmsg );
    - char* statusmsg2 = g_strndup( statusmsg1, CP_MAX_STATUS_MSG );
    -
    - mxit_send_presence( session, presence, statusmsg2 );
    -
    - g_free( statusmsg1 );
    - g_free( statusmsg2 );
    - }
    -
    - /* retrieve our MXit profile */
    - mxit_send_extprofile_request( session, NULL, ARRAY_SIZE( profilelist ), profilelist );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Process a received message packet.
    - *
    - * @param session The MXit session object
    - * @param records The packet's data records
    - * @param rcount The number of data records
    - */
    -static void mxit_parse_cmd_message( struct MXitSession* session, struct record** records, int rcount )
    -{
    - struct RXMsgData* mx = NULL;
    - char* message = NULL;
    - char* sender = NULL;
    - int msglen = 0;
    - int msgflags = 0;
    - int msgtype = 0;
    -
    - if ( ( rcount == 1 ) || ( records[0]->fcount < 2 ) || ( records[1]->fcount == 0 ) || ( records[1]->fields[0]->len == 0 ) ) {
    - /* packet contains no message or an empty message */
    - return;
    - }
    -
    - message = records[1]->fields[0]->data;
    - msglen = strlen( message );
    -
    - /* strip off dummy domain */
    - sender = records[0]->fields[0]->data;
    - mxit_strip_domain( sender );
    -
    -#ifdef DEBUG_PROTOCOL
    - purple_debug_info( MXIT_PLUGIN_ID, "Message received from '%s'\n", sender );
    -#endif
    -
    - /* decode message flags (if any) */
    - if ( records[0]->fcount >= 5 )
    - msgflags = atoi( records[0]->fields[4]->data );
    - msgtype = atoi( records[0]->fields[2]->data );
    -
    - if ( msgflags & CP_MSG_PWD_ENCRYPTED ) {
    - /* this is a password encrypted message. we do not currently support those so ignore it */
    - PurpleBuddy* buddy;
    - const char* name;
    - char msg[128];
    -
    - buddy = purple_blist_find_buddy( session->acc, sender );
    - if ( buddy )
    - name = purple_buddy_get_alias( buddy );
    - else
    - name = sender;
    - g_snprintf( msg, sizeof( msg ), _( "%s sent you an encrypted message, but it is not supported on this client." ), name );
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Error" ), msg );
    - return;
    - }
    - else if ( msgflags & CP_MSG_TL_ENCRYPTED ) {
    - /* This is a transport-layer encrypted message. We don't support
    - * it anymore, because original client doesn't look like it was. */
    - purple_serv_got_im(session->con, sender,
    - _("An encrypted message was received which could not be decrypted."),
    - PURPLE_MESSAGE_ERROR, time(NULL));
    - return;
    - }
    -
    - if ( msgflags & CP_MSG_NOTIFY_DELIVERY ) {
    - /* delivery notification is requested */
    - if ( records[0]->fcount >= 4 )
    - mxit_send_msgevent( session, sender, records[0]->fields[3]->data, CP_MSGEVENT_DELIVERED );
    - }
    -
    - /* create and initialise new markup struct */
    - mx = g_new0( struct RXMsgData, 1 );
    - mx->msg = g_string_sized_new( msglen );
    - mx->session = session;
    - mx->from = g_strdup( sender );
    - mx->timestamp = atoi( records[0]->fields[1]->data );
    - mx->got_img = FALSE;
    - mx->chatid = -1;
    - mx->img_count = 0;
    -
    - /* update list of active chats */
    - if ( !find_active_chat( session->active_chats, mx->from ) ) {
    - session->active_chats = g_list_append( session->active_chats, g_strdup( mx->from ) );
    - }
    -
    - if ( is_multimx_contact( session, mx->from ) ) {
    - /* this is a MultiMx chatroom message */
    - multimx_message_received( mx, message, msglen, msgtype, msgflags );
    - }
    - else {
    - mxit_parse_markup( mx, message, msglen, msgtype, msgflags );
    - }
    -
    - /* we are now done parsing the message */
    - mx->converted = TRUE;
    - if ( mx->img_count == 0 ) {
    - /* we have all the data we need for this message to be displayed now. */
    - mxit_show_message( mx );
    - }
    - else {
    - /* this means there are still images outstanding for this message and
    - * still need to wait for them before we can display the message.
    - * so the image received callback function will eventually display
    - * the message. */
    - }
    -
    - /* cleanup */
    - if ( msgflags & CP_MSG_TL_ENCRYPTED )
    - g_free( message );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Process a received subscription request packet.
    - *
    - * @param session The MXit session object
    - * @param records The packet's data records
    - * @param rcount The number of data records
    - */
    -static void mxit_parse_cmd_new_sub( struct MXitSession* session, struct record** records, int rcount )
    -{
    - struct contact* contact;
    - struct record* rec;
    - int i;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_new_sub (%i recs)\n", rcount );
    -
    - for ( i = 0; i < rcount; i++ ) {
    - rec = records[i];
    -
    - if ( rec->fcount < 4 ) {
    - purple_debug_error( MXIT_PLUGIN_ID, "BAD SUBSCRIPTION RECORD! %i fields\n", rec->fcount );
    - break;
    - }
    -
    - /* build up a new contact info struct */
    - contact = g_new0( struct contact, 1 );
    -
    - g_strlcpy( contact->username, rec->fields[0]->data, sizeof( contact->username ) );
    - mxit_strip_domain( contact->username ); /* remove dummy domain */
    - g_strlcpy( contact->alias, rec->fields[1]->data, sizeof( contact->alias ) );
    - contact->type = atoi( rec->fields[2]->data );
    -
    - if ( rec->fcount >= 5 ) {
    - /* there is a personal invite message attached */
    - if ( ( rec->fields[4]->data ) && ( *rec->fields[4]->data ) )
    - contact->msg = strdup( rec->fields[4]->data );
    - }
    -
    - /* handle the subscription */
    - if ( contact-> type == MXIT_TYPE_MULTIMX ) { /* subscription to a MultiMX room */
    - char* creator = NULL;
    -
    - if ( rec->fcount >= 6 )
    - creator = rec->fields[5]->data;
    -
    - multimx_invite( session, contact, creator );
    - }
    - else
    - mxit_new_subscription( session, contact );
    - }
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Parse the received presence value, and ensure that it is supported.
    - *
    - * @param value The received presence value.
    - * @return A valid presence value.
    - */
    -static short mxit_parse_presence( const char* value )
    -{
    - short presence = atoi( value );
    -
    - /* ensure that the presence value is valid */
    - switch ( presence ) {
    - case MXIT_PRESENCE_OFFLINE :
    - case MXIT_PRESENCE_ONLINE :
    - case MXIT_PRESENCE_AWAY :
    - case MXIT_PRESENCE_DND :
    - return presence;
    -
    - default :
    - return MXIT_PRESENCE_ONLINE;
    - }
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Parse the received mood value, and ensure that it is supported.
    - *
    - * @param value The received mood value.
    - * @return A valid mood value.
    - */
    -static short mxit_parse_mood( const char* value )
    -{
    - short mood = atoi( value );
    -
    - /* ensure that the mood value is valid */
    - if ( ( mood >= MXIT_MOOD_NONE ) && ( mood <= MXIT_MOOD_STRESSED ) )
    - return mood;
    -
    - return MXIT_MOOD_NONE;
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Process a received contact update packet.
    - *
    - * @param session The MXit session object
    - * @param records The packet's data records
    - * @param rcount The number of data records
    - */
    -static void mxit_parse_cmd_contact( struct MXitSession* session, struct record** records, int rcount )
    -{
    - struct contact* contact = NULL;
    - struct record* rec;
    - int i;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_contact (%i recs)\n", rcount );
    -
    - for ( i = 0; i < rcount; i++ ) {
    - rec = records[i];
    -
    - if ( rec->fcount < 6 ) {
    - purple_debug_error( MXIT_PLUGIN_ID, "BAD CONTACT RECORD! %i fields\n", rec->fcount );
    - break;
    - }
    -
    - /* build up a new contact info struct */
    - contact = g_new0( struct contact, 1 );
    -
    - g_strlcpy( contact->groupname, rec->fields[0]->data, sizeof( contact->groupname ) );
    - g_strlcpy( contact->username, rec->fields[1]->data, sizeof( contact->username ) );
    - mxit_strip_domain( contact->username ); /* remove dummy domain */
    - g_strlcpy( contact->alias, rec->fields[2]->data, sizeof( contact->alias ) );
    -
    - contact->presence = mxit_parse_presence( rec->fields[3]->data );
    - contact->type = atoi( rec->fields[4]->data );
    - contact->mood = mxit_parse_mood( rec->fields[5]->data );
    -
    - if ( rec->fcount > 6 ) {
    - /* added in protocol 5.9 - flags & subtype */
    - contact->flags = atoi( rec->fields[6]->data );
    - contact->subtype = rec->fields[7]->data[0];
    - }
    - if ( rec->fcount > 8 ) {
    - /* added in protocol 6.0 - reject message */
    - contact->msg = g_strdup( rec->fields[8]->data );
    - }
    -
    - /* add the contact to the buddy list */
    - if ( contact-> type == MXIT_TYPE_MULTIMX ) /* contact is a MultiMX room */
    - multimx_created( session, contact );
    - else
    - mxit_update_contact( session, contact );
    - }
    -
    - if ( !( session->flags & MXIT_FLAG_FIRSTROSTER ) ) {
    - session->flags |= MXIT_FLAG_FIRSTROSTER;
    - mxit_update_blist( session );
    - }
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Process a received presence update packet.
    - *
    - * @param session The MXit session object
    - * @param records The packet's data records
    - * @param rcount The number of data records
    - */
    -static void mxit_parse_cmd_presence( struct MXitSession* session, struct record** records, int rcount )
    -{
    - int i;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_presence (%i recs)\n", rcount );
    -
    - for ( i = 0; i < rcount; 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 );
    - break;
    - }
    -
    - /*
    - * The format of the record is:
    - * 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, mxit_parse_presence( rec->fields[1]->data ), mxit_parse_mood( rec->fields[2]->data ),
    - rec->fields[3]->data, rec->fields[4]->data, flags );
    - mxit_update_buddy_avatar( session, rec->fields[0]->data, rec->fields[5]->data );
    - }
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Process a received extended profile packet.
    - *
    - * @param session The MXit session object
    - * @param records The packet's data records
    - * @param rcount The number of data records
    - */
    -static void mxit_parse_cmd_extprofile( struct MXitSession* session, struct record** records, int rcount )
    -{
    - const char* mxitId = records[0]->fields[0]->data;
    - struct MXitProfile* profile = NULL;
    - int count;
    - int i;
    - const char* avatarId = NULL;
    - char* statusMsg = NULL;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_extprofile: profile for '%s'\n", mxitId );
    -
    - if ( ( records[0]->fields[0]->len == 0 ) || ( session->uid && ( strcmp( session->uid, records[0]->fields[0]->data ) == 0 ) ) ) {
    - /* No UserId or Our UserId provided, so this must be our own profile information */
    - if ( session->profile == NULL )
    - session->profile = g_new0( struct MXitProfile, 1 );
    - profile = session->profile;
    - }
    - else {
    - /* is a buddy's profile */
    - profile = g_new0( struct MXitProfile, 1 );
    - }
    -
    - /* set the count for attributes */
    - count = atoi( records[0]->fields[1]->data );
    -
    - /* ensure the packet has the correct number of fields */
    - if ( records[0]->fcount < ( 2 + ( count * 3 ) ) ) {
    - purple_debug_error( MXIT_PLUGIN_ID, "Insufficient number of fields in extprofile response. fields=%i records=%i", records[0]->fcount, count );
    - return;
    - }
    -
    - for ( i = 0; i < count; i++ ) {
    - char* fname;
    - char* fvalue;
    - char* fstatus;
    - int f = ( i * 3 ) + 2;
    -
    - fname = records[0]->fields[f]->data; /* field name */
    - fvalue = records[0]->fields[f + 1]->data; /* field value */
    - fstatus = records[0]->fields[f + 2]->data; /* field status */
    -
    - /* first check the status on the returned attribute */
    - if ( fstatus[0] != '0' ) {
    - /* error: attribute requested was NOT found */
    - purple_debug_error( MXIT_PLUGIN_ID, "Bad profile status on attribute '%s' \n", fname );
    - continue;
    - }
    -
    - if ( strcmp( CP_PROFILE_BIRTHDATE, fname ) == 0 ) {
    - /* birthdate */
    - if ( records[0]->fields[f + 1]->len > 10 ) {
    - fvalue[10] = '\0';
    - records[0]->fields[f + 1]->len = 10;
    - }
    - memcpy( profile->birthday, fvalue, records[0]->fields[f + 1]->len );
    - }
    - else if ( strcmp( CP_PROFILE_GENDER, fname ) == 0 ) {
    - /* gender */
    - profile->male = ( fvalue[0] == '1' );
    - }
    - else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) {
    - /* nickname */
    - g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) );
    - }
    - else if ( strcmp( CP_PROFILE_STATUS, fname ) == 0 ) {
    - /* status message - just keep a reference to the value */
    - statusMsg = g_markup_escape_text( fvalue, -1 );
    - }
    - else if ( strcmp( CP_PROFILE_AVATAR, fname ) == 0 ) {
    - /* avatar id - just keep a reference to the value */
    - avatarId = fvalue;
    - }
    - else if ( strcmp( CP_PROFILE_TITLE, fname ) == 0 ) {
    - /* title */
    - g_strlcpy( profile->title, fvalue, sizeof( profile->title ) );
    - }
    - else if ( strcmp( CP_PROFILE_FIRSTNAME, fname ) == 0 ) {
    - /* first name */
    - g_strlcpy( profile->firstname, fvalue, sizeof( profile->firstname ) );
    - }
    - else if ( strcmp( CP_PROFILE_LASTNAME, fname ) == 0 ) {
    - /* last name */
    - g_strlcpy( profile->lastname, fvalue, sizeof( profile->lastname ) );
    - }
    - else if ( strcmp( CP_PROFILE_EMAIL, fname ) == 0 ) {
    - /* email address */
    - g_strlcpy( profile->email, fvalue, sizeof( profile->email ) );
    - }
    - else if ( strcmp( CP_PROFILE_MOBILENR, fname ) == 0 ) {
    - /* mobile number */
    - g_strlcpy( profile->mobilenr, fvalue, sizeof( profile->mobilenr ) );
    - }
    - else if ( strcmp( CP_PROFILE_REGCOUNTRY, fname ) == 0 ) {
    - /* registered country */
    - g_strlcpy( profile->regcountry, fvalue, sizeof( profile->regcountry ) );
    - }
    - else if ( strcmp( CP_PROFILE_FLAGS, fname ) == 0 ) {
    - /* profile flags */
    - profile->flags = g_ascii_strtoll( fvalue, NULL, 10 );
    - }
    - else if ( strcmp( CP_PROFILE_LASTSEEN, fname ) == 0 ) {
    - /* last seen online */
    - profile->lastonline = g_ascii_strtoll( fvalue, NULL, 10 );
    - }
    - else if ( strcmp( CP_PROFILE_WHEREAMI, fname ) == 0 ) {
    - /* where am I */
    - g_strlcpy( profile->whereami, fvalue, sizeof( profile->whereami ) );
    - }
    - else if ( strcmp( CP_PROFILE_ABOUTME, fname ) == 0) {
    - /* about me */
    - g_strlcpy( profile->aboutme, fvalue, sizeof( profile->aboutme ) );
    - }
    - else if ( strcmp( CP_PROFILE_RELATIONSHIP, fname ) == 0) {
    - /* relatinship status */
    - profile->relationship = strtol( fvalue, NULL, 10 );
    - }
    - else {
    - /* invalid profile attribute */
    - purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile attribute received '%s' \n", fname );
    - }
    - }
    -
    - if ( profile != session->profile ) {
    - /* not our own profile */
    - struct contact* contact = NULL;
    -
    - contact = get_mxit_invite_contact( session, mxitId );
    - if ( contact ) {
    - /* this is an invite, so update its profile info */
    - if ( ( statusMsg ) && ( *statusMsg ) ) {
    - /* update the status message */
    - g_free(contact->statusMsg);
    - contact->statusMsg = strdup( statusMsg );
    - }
    - else
    - contact->statusMsg = NULL;
    - g_free(contact->profile);
    - contact->profile = profile;
    - if ( ( avatarId ) && ( *avatarId ) ) {
    - /* avatar must be requested for this invite before we can display it */
    - mxit_get_avatar( session, mxitId, avatarId );
    - g_free(contact->avatarId);
    - contact->avatarId = strdup( avatarId );
    - }
    - else {
    - /* display what we have */
    - contact->avatarId = NULL;
    - mxit_show_profile( session, mxitId, profile );
    - }
    - }
    - else {
    - /* this is a contact */
    - if ( avatarId )
    - mxit_update_buddy_avatar( session, mxitId, avatarId );
    -
    - if ( ( statusMsg ) && ( *statusMsg ) ) {
    - /* update the status message */
    - PurpleBuddy* buddy = NULL;
    -
    - buddy = purple_blist_find_buddy( session->acc, mxitId );
    - if ( buddy ) {
    - contact = purple_buddy_get_protocol_data( buddy );
    - if ( contact ) {
    - g_free(contact->statusMsg);
    - contact->statusMsg = strdup( statusMsg );
    - }
    - }
    - }
    -
    - /* show the profile */
    - mxit_show_profile( session, mxitId, profile );
    - g_free( profile );
    - }
    - }
    -
    - g_free( statusMsg );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Process a received suggest-contacts packet.
    - *
    - * @param session The MXit session object
    - * @param records The packet's data records
    - * @param rcount The number of data records
    - */
    -static void mxit_parse_cmd_suggestcontacts( struct MXitSession* session, struct record** records, int rcount )
    -{
    - GList* entries = NULL;
    - int searchType;
    - int maxResults;
    - int count;
    - int i;
    -
    - /*
    - * searchType \1 numSuggestions \1 total \1 numAttributes \1 name0 \1 name1 \1 ... \1 nameN \0
    - * userid \1 contactType \1 value0 \1 value1 ... valueN \0
    - * ...
    - * userid \1 contactType \1 value0 \1 value1 ... valueN
    - */
    -
    - /* ensure that record[0] contacts the minumum number of fields */
    - if ( records[0]->fcount < 4 ) {
    - purple_debug_error( MXIT_PLUGIN_ID, "Insufficient number of fields in suggest contacts response. fields=%i", records[0]->fcount );
    - return;
    - }
    -
    - /* the type of results */
    - searchType = atoi( records[0]->fields[0]->data );
    -
    - /* the maximum number of results */
    - maxResults = atoi( records[0]->fields[2]->data );
    -
    - /* set the count for attributes */
    - count = atoi( records[0]->fields[3]->data );
    -
    - /* ensure that record[0] contains the specified number of attributes */
    - if ( records[0]->fcount < ( 4 + count ) ) {
    - purple_debug_error( MXIT_PLUGIN_ID, "Insufficient number of fields in suggest contacts response. fields=%i attributes=%i", records[0]->fcount, count );
    - return;
    - }
    -
    - for ( i = 1; i < rcount; i ++ ) {
    - struct record* rec = records[i];
    - struct MXitProfile* profile = g_new0( struct MXitProfile, 1 );
    - int j;
    -
    - /* ensure that each result contains the specified number of attributes */
    - if ( rec->fcount != ( 2 + count ) ) {
    - purple_debug_error( MXIT_PLUGIN_ID, "Insufficient number of fields in suggest contacts response. fields=%i attributes=%i", rec->fcount, count );
    - g_free( profile );
    - continue;
    - }
    -
    - g_strlcpy( profile->userid, rec->fields[0]->data, sizeof( profile->userid ) );
    - // TODO: ContactType - User or Service
    -
    - for ( j = 0; j < count; j++ ) {
    - char* fname;
    - char* fvalue = "";
    -
    - fname = records[0]->fields[4 + j]->data; /* field name */
    - if ( records[i]->fcount > ( 2 + j ) )
    - fvalue = records[i]->fields[2 + j]->data; /* field value */
    -
    - purple_debug_info( MXIT_PLUGIN_ID, " %s: field='%s' value='%s'\n", profile->userid, fname, fvalue );
    -
    - if ( strcmp( CP_PROFILE_BIRTHDATE, fname ) == 0 ) {
    - /* birthdate */
    - g_strlcpy( profile->birthday, fvalue, sizeof( profile->birthday ) );
    - }
    - else if ( strcmp( CP_PROFILE_FIRSTNAME, fname ) == 0 ) {
    - /* first name */
    - g_strlcpy( profile->firstname, fvalue, sizeof( profile->firstname ) );
    - }
    - else if ( strcmp( CP_PROFILE_LASTNAME, fname ) == 0 ) {
    - /* last name */
    - g_strlcpy( profile->lastname, fvalue, sizeof( profile->lastname ) );
    - }
    - else if ( strcmp( CP_PROFILE_GENDER, fname ) == 0 ) {
    - /* gender */
    - profile->male = ( fvalue[0] == '1' );
    - }
    - else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) {
    - /* nickname */
    - g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) );
    - }
    - else if ( strcmp( CP_PROFILE_WHEREAMI, fname ) == 0 ) {
    - /* where am I */
    - g_strlcpy( profile->whereami, fvalue, sizeof( profile->whereami ) );
    - }
    - /* ignore other attibutes */
    - }
    -
    - entries = g_list_append( entries, profile );
    - }
    -
    - /* display */
    - mxit_show_search_results( session, searchType, maxResults, entries );
    -
    - /* cleanup */
    - g_list_foreach( entries, (GFunc)g_free, NULL );
    -}
    -
    -/*------------------------------------------------------------------------
    - * Process a received message event packet.
    - *
    - * @param session The MXit session object
    - * @param records The packet's data records
    - * @param rcount The number of data records
    - */
    -static void mxit_parse_cmd_msgevent( struct MXitSession* session, struct record** records, int rcount )
    -{
    - int event;
    -
    - /*
    - * contactAddress \1 dateTime \1 id \1 event
    - */
    -
    - /* strip off dummy domain */
    - mxit_strip_domain( records[0]->fields[0]->data );
    -
    - event = atoi( records[0]->fields[3]->data );
    -
    - switch ( event ) {
    - case CP_MSGEVENT_TYPING : /* user is typing */
    - case CP_MSGEVENT_ANGRY : /* user is typing angrily */
    - purple_serv_got_typing( session->con, records[0]->fields[0]->data, 0, PURPLE_IM_TYPING );
    - break;
    -
    - case CP_MSGEVENT_STOPPED : /* user has stopped typing */
    - purple_serv_got_typing_stopped( session->con, records[0]->fields[0]->data );
    - break;
    -
    - case CP_MSGEVENT_ERASING : /* user is erasing text */
    - case CP_MSGEVENT_DELIVERED : /* message was delivered */
    - case CP_MSGEVENT_DISPLAYED : /* message was viewed */
    - /* these are currently not supported by libPurple */
    - break;
    -
    - default:
    - purple_debug_error( MXIT_PLUGIN_ID, "Unknown message event received (%i)\n", event );
    - }
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Process a received multimedia packet.
    - *
    - * @param session The MXit session object
    - * @param records The packet's data records
    - * @param rcount The number of data records
    - */
    -static void mxit_parse_cmd_media( struct MXitSession* session, struct record** records, int rcount )
    -{
    - guint chunktype;
    - guint32 chunksize;
    - gchar* chunkdata;
    -
    - /* received packet is too short to even contain a chunk header */
    - if ( records[0]->fields[0]->len < MXIT_CHUNK_HEADER_SIZE )
    - return;
    -
    - /* decode the chunk header */
    - chunktype = chunk_type( records[0]->fields[0]->data );
    - chunksize = chunk_length( records[0]->fields[0]->data );
    - chunkdata = chunk_data( records[0]->fields[0]->data );
    -
    - /* check chunk size against length of received data */
    - if ( MXIT_CHUNK_HEADER_SIZE + chunksize > records[0]->fields[0]->len )
    - return;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_media (%i records) (%i type) (%i bytes)\n", rcount, chunktype, chunksize );
    -
    - /* supported chunked data types */
    - switch ( chunktype ) {
    - case CP_CHUNK_CUSTOM : /* custom resource */
    - {
    - struct cr_chunk chunk;
    -
    - /* decode the chunked data */
    - if ( mxit_chunk_parse_cr( chunkdata, chunksize, &chunk ) ) {
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "chunk info id=%s handle=%s op=%i\n", chunk.id, chunk.handle, chunk.operation );
    -
    - /* this is a splash-screen operation */
    - if ( strcmp( chunk.handle, HANDLE_SPLASH2 ) == 0 ) {
    - if ( chunk.operation == CR_OP_UPDATE ) { /* update the splash-screen */
    - struct splash_chunk *splash = chunk.resources->data; // TODO: Fix - assuming 1st resource is splash
    - gboolean clickable = ( g_list_length( chunk.resources ) > 1 ); // TODO: Fix - if 2 resources, then is clickable
    -
    - if ( splash != NULL )
    - splash_update( session, chunk.id, splash->data, splash->datalen, clickable );
    - }
    - else if ( chunk.operation == CR_OP_REMOVE ) /* remove the splash-screen */
    - splash_remove( session );
    - }
    -
    - /* cleanup custom resources */
    - g_list_foreach( chunk.resources, (GFunc)g_free, NULL );
    - }
    - }
    - break;
    -
    - case CP_CHUNK_OFFER : /* file offer */
    - {
    - struct offerfile_chunk chunk;
    -
    - /* decode the chunked data */
    - if ( mxit_chunk_parse_offer( chunkdata, chunksize, &chunk ) ) {
    - /* process the offer */
    - mxit_xfer_rx_offer( session, chunk.username, chunk.filename, chunk.filesize, chunk.fileid );
    - }
    - }
    - break;
    -
    - case CP_CHUNK_GET : /* get file response */
    - {
    - struct getfile_chunk chunk;
    -
    - /* decode the chunked data */
    - if ( mxit_chunk_parse_get( chunkdata, chunksize, &chunk ) ) {
    - /* process the getfile */
    - mxit_xfer_rx_file( session, chunk.fileid, chunk.data, chunk.length );
    - }
    - }
    - break;
    -
    - case CP_CHUNK_GET_AVATAR : /* get avatars */
    - {
    - struct getavatar_chunk chunk;
    - struct contact* contact = NULL;
    -
    - /* decode the chunked data */
    - if ( mxit_chunk_parse_get_avatar( chunkdata, chunksize, &chunk ) ) {
    - /* update avatar image */
    - purple_debug_info( MXIT_PLUGIN_ID, "updating avatar for contact '%s'\n", chunk.mxitid );
    -
    - contact = get_mxit_invite_contact( session, chunk.mxitid );
    - if ( contact ) {
    - /* this is an invite (add image to the internal image store) */
    - if (contact->image)
    - g_object_unref(contact->image);
    - contact->image = purple_image_new_from_data(
    - g_memdup(chunk.data, chunk.length), chunk.length);
    - /* show the profile */
    - mxit_show_profile( session, chunk.mxitid, contact->profile );
    - }
    - else {
    - /* this is a contact's avatar, so update it */
    - purple_buddy_icons_set_for_user( session->acc, chunk.mxitid, g_memdup( chunk.data, chunk.length ), chunk.length, chunk.avatarid );
    - }
    - }
    - }
    - break;
    -
    - case CP_CHUNK_SET_AVATAR :
    - /* this is a reply packet to a set avatar request. no action is required */
    - break;
    -
    - case CP_CHUNK_REJECT :
    - /* this is a reply packet to a reject file request. no action is required */
    - break;
    -
    - case CP_CHUNK_DIRECT_SND :
    - /* this is a ack for a file send. */
    - {
    - struct sendfile_chunk chunk;
    -
    - if ( mxit_chunk_parse_sendfile( chunkdata, chunksize, &chunk ) ) {
    - purple_debug_info( MXIT_PLUGIN_ID, "file-send send to '%s' [status=%i message='%s']\n", chunk.username, chunk.status, chunk.statusmsg );
    -
    - if ( chunk.status != 0 ) /* not success */
    - mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "File Send Failed" ), chunk.statusmsg );
    - }
    - }
    - break;
    -
    - case CP_CHUNK_RECEIVED :
    - /* this is a ack for a file received. no action is required */
    - break;
    -
    - default :
    - purple_debug_error( MXIT_PLUGIN_ID, "Unsupported chunked data packet type received (%i)\n", chunktype );
    - break;
    - }
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Handle a redirect sent from the MXit server.
    - *
    - * @param session The MXit session object
    - * @param url The redirect information
    - */
    -static void mxit_perform_redirect( struct MXitSession* session, const char* url )
    -{
    - gchar** parts;
    - gchar** host;
    - int type;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_perform_redirect: %s\n", url );
    -
    - /* tokenize the URL string */
    - parts = g_strsplit( url, ";", 0 );
    -
    - /* Part 1: protocol://host:port */
    - host = g_strsplit( parts[0], ":", 4 );
    - if ( strcmp( host[0], "socket" ) == 0 ) {
    - /* redirect to a MXit socket proxy */
    - g_strlcpy( session->server, &host[1][2], sizeof( session->server ) );
    - session->port = atoi( host[2] );
    - }
    - else {
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Cannot perform redirect using the specified protocol" ) );
    - goto redirect_fail;
    - }
    -
    - /* Part 2: type of redirect */
    - type = atoi( parts[1] );
    - if ( type == CP_REDIRECT_PERMANENT ) {
    - /* permanent redirect, so save new MXit server and port */
    - purple_account_set_string( session->acc, MXIT_CONFIG_SERVER_ADDR, session->server );
    - purple_account_set_int( session->acc, MXIT_CONFIG_SERVER_PORT, session->port );
    - }
    -
    - /* Part 3: message (optional) */
    - if ( parts[2] != NULL )
    - purple_connection_notice( session->con, parts[2] );
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_perform_redirect: %s redirect to %s:%i\n",
    - ( type == CP_REDIRECT_PERMANENT ) ? "Permanent" : "Temporary", session->server, session->port );
    -
    - /* perform the re-connect to the new MXit server */
    - mxit_reconnect( session );
    -
    -redirect_fail:
    - g_strfreev( parts );
    - g_strfreev( host );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Process a success response received from the MXit server.
    - *
    - * @param session The MXit session object
    - * @param packet The received packet
    - */
    -static int process_success_response( struct MXitSession* session, struct rx_packet* packet )
    -{
    - /* ignore ping/poll packets */
    - if ( ( packet->cmd != CP_CMD_PING ) && ( packet->cmd != CP_CMD_POLL ) )
    - session->last_rx = mxit_now_milli();
    -
    - /*
    - * when we pass the packet records to the next level for parsing
    - * we minus 3 records because 1) the first record is the packet
    - * type 2) packet reply status 3) the last record is bogus
    - */
    -
    - /* packet command */
    - switch ( packet->cmd ) {
    -
    - case CP_CMD_REGISTER :
    - /* fall through, when registeration successful, MXit will auto login */
    - case CP_CMD_LOGIN :
    - /* login response */
    - if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) {
    - mxit_parse_cmd_login( session, &packet->records[2], packet->rcount - 3 );
    - }
    - break;
    -
    - case CP_CMD_LOGOUT :
    - /* logout response */
    - session->flags &= ~MXIT_FLAG_LOGGEDIN;
    - purple_account_disconnect( session->acc );
    -
    - /* note:
    - * we do not prompt the user here for a reconnect, because this could be the user
    - * logging in with his phone. so we just disconnect the account otherwise
    - * mxit will start to bounce between the phone and pidgin. also could be a valid
    - * disconnect selected by the user.
    - */
    - return -1;
    -
    - case CP_CMD_CONTACT :
    - /* contact update */
    - mxit_parse_cmd_contact( session, &packet->records[2], packet->rcount - 3 );
    - break;
    -
    - case CP_CMD_PRESENCE :
    - /* presence update */
    - mxit_parse_cmd_presence( session, &packet->records[2], packet->rcount - 3 );
    - break;
    -
    - case CP_CMD_RX_MSG :
    - /* incoming message (no bogus record) */
    - mxit_parse_cmd_message( session, &packet->records[2], packet->rcount - 2 );
    - break;
    -
    - case CP_CMD_NEW_SUB :
    - /* new subscription request */
    - mxit_parse_cmd_new_sub( session, &packet->records[2], packet->rcount - 3 );
    - break;
    -
    - case CP_CMD_MEDIA :
    - /* multi-media message */
    - mxit_parse_cmd_media( session, &packet->records[2], packet->rcount - 2 );
    - break;
    -
    - case CP_CMD_EXTPROFILE_GET :
    - /* profile update */
    - mxit_parse_cmd_extprofile( session, &packet->records[2], packet->rcount - 2 );
    - break;
    -
    - case CP_CMD_SUGGESTCONTACTS :
    - /* suggest contacts */
    - mxit_parse_cmd_suggestcontacts( session, &packet->records[2], packet->rcount - 2 );
    - break;
    -
    - case CP_CMD_GOT_MSGEVENT :
    - /* received message event */
    - mxit_parse_cmd_msgevent( session, &packet->records[2], packet->rcount - 2 );
    - break;
    -
    - case CP_CMD_MOOD :
    - /* mood update */
    - case CP_CMD_UPDATE :
    - /* update contact information */
    - case CP_CMD_ALLOW :
    - /* allow subscription ack */
    - case CP_CMD_DENY :
    - /* deny subscription ack */
    - case CP_CMD_INVITE :
    - /* invite contact ack */
    - case CP_CMD_REMOVE :
    - /* remove contact ack */
    - case CP_CMD_TX_MSG :
    - /* outgoing message ack */
    - case CP_CMD_STATUS :
    - /* presence update ack */
    - case CP_CMD_GRPCHAT_CREATE :
    - /* create groupchat */
    - case CP_CMD_GRPCHAT_INVITE :
    - /* groupchat invite */
    - case CP_CMD_PING :
    - /* ping reply */
    - case CP_CMD_POLL :
    - /* HTTP poll reply */
    - case CP_CMD_EXTPROFILE_SET :
    - /* profile update */
    - // TODO: Protocol 6.2 indicates status for each attribute, and current value.
    - case CP_CMD_SPLASHCLICK :
    - /* splash-screen clickthrough */
    - case CP_CMD_MSGEVENT :
    - /* event message */
    - break;
    -
    - default :
    - /* unknown packet */
    - purple_debug_error( MXIT_PLUGIN_ID, "Received unknown client packet (cmd = %i)\n", packet->cmd );
    - }
    -
    - return 0;
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Process an error response received from the MXit server.
    - *
    - * @param session The MXit session object
    - * @param packet The received packet
    - */
    -static int process_error_response( struct MXitSession* session, struct rx_packet* packet )
    -{
    - char errmsg[256];
    - const char* errdesc;
    -
    - /* set the error description to be shown to the user */
    - if ( packet->errmsg )
    - errdesc = packet->errmsg;
    - else
    - errdesc = _( "An internal MXit server error occurred." );
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "Error Reply %i:%s\n", packet->errcode, errdesc );
    -
    - if ( packet->errcode == MXIT_ERRCODE_LOGGEDOUT ) {
    - /* we are not currently logged in, so we need to reconnect */
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( errdesc ) );
    - }
    -
    - /* packet command */
    - switch ( packet->cmd ) {
    -
    - case CP_CMD_REGISTER :
    - case CP_CMD_LOGIN :
    - if ( packet->errcode == MXIT_ERRCODE_REDIRECT ) {
    - mxit_perform_redirect( session, packet->errmsg );
    - return 0;
    - }
    - else {
    - g_snprintf( errmsg, sizeof( errmsg ), _( "Login error: %s (%i)" ), errdesc, packet->errcode );
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errmsg );
    - return -1;
    - }
    - case CP_CMD_LOGOUT :
    - g_snprintf( errmsg, sizeof( errmsg ), _( "Logout error: %s (%i)" ), errdesc, packet->errcode );
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _( errmsg ) );
    - return -1;
    - case CP_CMD_CONTACT :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_RX_MSG :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_TX_MSG :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Sending Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_STATUS :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Status Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_MOOD :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Mood Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_KICK :
    - /*
    - * the MXit server sends this packet if we were idle for too long.
    - * to stop the server from closing this connection we need to resend
    - * the login packet.
    - */
    - mxit_send_login( session );
    - break;
    - case CP_CMD_INVITE :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Invitation Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_REMOVE :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Removal Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_ALLOW :
    - case CP_CMD_DENY :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Subscription Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_UPDATE :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Update Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_MEDIA :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "File Transfer Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_GRPCHAT_CREATE :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Cannot create MultiMx room" ), _( errdesc ) );
    - break;
    - case CP_CMD_GRPCHAT_INVITE :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "MultiMx Invitation Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_EXTPROFILE_GET :
    - case CP_CMD_EXTPROFILE_SET :
    - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Profile Error" ), _( errdesc ) );
    - break;
    - case CP_CMD_SPLASHCLICK :
    - case CP_CMD_MSGEVENT :
    - /* ignore error */
    - break;
    - case CP_CMD_PING :
    - case CP_CMD_POLL :
    - break;
    - default :
    - mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Error" ), _( errdesc ) );
    - break;
    - }
    -
    - return 0;
    -}
    -
    -
    -/*========================================================================================================================
    - * Low-level Packet receive
    - */
    -
    -#ifdef DEBUG_PROTOCOL
    -/*------------------------------------------------------------------------
    - * Dump a received packet structure.
    - *
    - * @param p The received packet
    - */
    -static void dump_packet( struct rx_packet* p )
    -{
    - struct record* r = NULL;
    - struct field* f = NULL;
    - int i;
    - int j;
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "PACKET DUMP: (%i records)\n", p->rcount );
    -
    - for ( i = 0; i < p->rcount; i++ ) {
    - r = p->records[i];
    - purple_debug_info( MXIT_PLUGIN_ID, "RECORD: (%i fields)\n", r->fcount );
    -
    - for ( j = 0; j < r->fcount; j++ ) {
    - f = r->fields[j];
    - purple_debug_info( MXIT_PLUGIN_ID, "\tFIELD: (len=%zu) '%s' \n", f->len, f->data );
    - }
    - }
    -}
    -#endif
    -
    -
    -/*------------------------------------------------------------------------
    - * Free up memory used by a packet structure.
    - *
    - * @param p The received packet
    - */
    -static void free_rx_packet( struct rx_packet* p )
    -{
    - struct record* r = NULL;
    - struct field* f = NULL;
    - int i;
    - int j;
    -
    - for ( i = 0; i < p->rcount; i++ ) {
    - r = p->records[i];
    -
    - for ( j = 0; j < r->fcount; j++ ) {
    - g_free( f );
    - }
    - g_free( r->fields );
    - g_free( r );
    - }
    - g_free( p->records );
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Add a new field to a record.
    - *
    - * @param r Parent record object
    - * @return The newly created field
    - */
    -static struct field* add_field( struct record* r )
    -{
    - struct field* field;
    -
    - field = g_new0( struct field, 1 );
    -
    - r->fields = g_realloc( r->fields, sizeof( struct field* ) * ( r->fcount + 1 ) );
    - r->fields[r->fcount] = field;
    - r->fcount++;
    -
    - return field;
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Add a new record to a packet.
    - *
    - * @param p The packet object
    - * @return The newly created record
    - */
    -static struct record* add_record( struct rx_packet* p )
    -{
    - struct record* rec;
    -
    - rec = g_new0( struct record, 1 );
    -
    - p->records = g_realloc( p->records, sizeof( struct record* ) * ( p->rcount + 1 ) );
    - p->records[p->rcount] = rec;
    - p->rcount++;
    -
    - return rec;
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Parse the received byte stream into a proper client protocol packet.
    - *
    - * @param session The MXit session object
    - * @return Success (0) or Failure (!0)
    - */
    -int mxit_parse_packet( struct MXitSession* session )
    -{
    - struct rx_packet packet;
    - struct record* rec;
    - struct field* field;
    - gboolean pbreak;
    - unsigned int i;
    - int res = 0;
    -
    -#ifdef DEBUG_PROTOCOL
    - purple_debug_info( MXIT_PLUGIN_ID, "Received packet (%i bytes)\n", session->rx_i );
    - dump_bytes( session, session->rx_dbuf, session->rx_i );
    -#endif
    -
    - i = 0;
    - while ( i < session->rx_i ) {
    -
    - /* create first record and field */
    - rec = NULL;
    - field = NULL;
    - memset( &packet, 0x00, sizeof( struct rx_packet ) );
    - rec = add_record( &packet );
    - pbreak = FALSE;
    -
    - /* break up the received packet into fields and records for easy parsing */
    - while ( ( i < session->rx_i ) && ( !pbreak ) ) {
    -
    - switch ( session->rx_dbuf[i] ) {
    - case CP_SOCK_REC_TERM :
    - /* new record */
    - if ( packet.rcount == 1 ) {
    - /* packet command */
    - if ( packet.records[0]->fcount > 0 )
    - packet.cmd = atoi( packet.records[0]->fields[0]->data );
    - }
    - else if ( packet.rcount == 2 ) {
    - /* special case: binary multimedia packets should not be parsed here */
    - if ( packet.cmd == CP_CMD_MEDIA ) {
    - /* add the chunked to new record */
    - rec = add_record( &packet );
    - field = add_field( rec );
    - field->data = &session->rx_dbuf[i + 1];
    - field->len = session->rx_i - i;
    - /* now skip the binary data */
    - res = chunk_length( field->data );
    - /* determine if we have more packets */
    - if ( res + 6 + i < session->rx_i ) {
    - /* we have more than one packet in this stream */
    - i += res + 6;
    - pbreak = TRUE;
    - }
    - else {
    - i = session->rx_i;
    - }
    - }
    - }
    - else if ( !field ) {
    - field = add_field( rec );
    - field->data = &session->rx_dbuf[i];
    - }
    - session->rx_dbuf[i] = '\0';
    - rec = add_record( &packet );
    - field = NULL;
    -
    - break;
    - case CP_FLD_TERM :
    - /* new field */
    - session->rx_dbuf[i] = '\0';
    - if ( !field ) {
    - field = add_field( rec );
    - field->data = &session->rx_dbuf[i];
    - }
    - field = NULL;
    - break;
    - case CP_PKT_TERM :
    - /* packet is done! */
    - session->rx_dbuf[i] = '\0';
    - pbreak = TRUE;
    - break;
    - default :
    - /* skip non special characters */
    - if ( !field ) {
    - field = add_field( rec );
    - field->data = &session->rx_dbuf[i];
    - }
    - field->len++;
    - break;
    - }
    -
    - i++;
    - }
    -
    - if ( packet.rcount < 2 ) {
    - /* bad packet */
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Invalid packet received from MXit." ) );
    - free_rx_packet( &packet );
    - continue;
    - }
    -
    - session->rx_dbuf[session->rx_i] = '\0';
    - packet.errcode = atoi( packet.records[1]->fields[0]->data );
    -
    - purple_debug_info( MXIT_PLUGIN_ID, "Packet received CMD:%i (%i)\n", packet.cmd, packet.errcode );
    -#ifdef DEBUG_PROTOCOL
    - /* debug */
    - dump_packet( &packet );
    -#endif
    -
    - /* reset the out ack */
    - if ( session->outack == packet.cmd ) {
    - /* outstanding ack received from mxit server */
    - session->outack = 0;
    - }
    -
    - /* check packet status */
    - if ( packet.errcode != MXIT_ERRCODE_SUCCESS ) {
    - /* error reply! */
    - if ( ( packet.records[1]->fcount > 1 ) && ( packet.records[1]->fields[1]->data ) )
    - packet.errmsg = packet.records[1]->fields[1]->data;
    - else
    - packet.errmsg = NULL;
    -
    - res = process_error_response( session, &packet );
    - }
    - else {
    - /* success reply! */
    - res = process_success_response( session, &packet );
    - }
    -
    - /* free up the packet resources */
    - free_rx_packet( &packet );
    - }
    -
    - if ( session->outack == 0 )
    - mxit_manage_queue( session );
    -
    - return res;
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Callback when data is received from the MXit server.
    - *
    - * @param user_data The MXit session object
    - * @param source The file-descriptor on which data was received
    - * @param cond Condition which caused the callback (PURPLE_INPUT_READ)
    - */
    -void mxit_cb_rx( gpointer user_data, gint source, PurpleInputCondition cond )
    -{
    - struct MXitSession* session = (struct MXitSession*) user_data;
    - char ch;
    - int res;
    - int len;
    -
    - if ( session->rx_state == RX_STATE_RLEN ) {
    - /* we are reading in the packet length */
    - len = read( session->fd, &ch, 1 );
    - if ( len < 0 ) {
    - /* connection error */
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x01)" ) );
    - return;
    - }
    - else if ( len == 0 ) {
    - /* connection closed */
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x02)" ) );
    - return;
    - }
    - else {
    - /* byte read */
    - if ( ch == CP_REC_TERM ) {
    - /* the end of the length record found */
    - session->rx_lbuf[session->rx_i] = '\0';
    - session->rx_res = atoi( &session->rx_lbuf[3] );
    - if ( ( session->rx_res <= 0 ) || ( session->rx_res > CP_MAX_PACKET ) ) {
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x03)" ) );
    - return;
    - }
    - session->rx_state = RX_STATE_DATA;
    - session->rx_i = 0;
    - }
    - else {
    - /* still part of the packet length record */
    - session->rx_lbuf[session->rx_i] = ch;
    - session->rx_i++;
    - if ( session->rx_i >= sizeof( session->rx_lbuf ) ) {
    - /* malformed packet length record (too long) */
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x04)" ) );
    - return;
    - }
    - }
    - }
    - }
    - else if ( session->rx_state == RX_STATE_DATA ) {
    - /* we are reading in the packet data */
    - len = read( session->fd, &session->rx_dbuf[session->rx_i], session->rx_res );
    - if ( len < 0 ) {
    - /* connection error */
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x05)" ) );
    - return;
    - }
    - else if ( len == 0 ) {
    - /* connection closed */
    - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x06)" ) );
    - return;
    - }
    - else {
    - /* data read */
    - session->rx_i += len;
    - session->rx_res -= len;
    -
    - if ( session->rx_res == 0 ) {
    - /* ok, so now we have read in the whole packet */
    - session->rx_state = RX_STATE_PROC;
    - }
    - }
    - }
    -
    - if ( session->rx_state == RX_STATE_PROC ) {
    - /* we have a full packet, which we now need to process */
    - res = mxit_parse_packet( session );
    -
    - if ( res == 0 ) {
    - /* we are still logged in */
    - session->rx_state = RX_STATE_RLEN;
    - session->rx_res = 0;
    - session->rx_i = 0;
    - }
    - }
    -}
    -
    -
    -/*------------------------------------------------------------------------
    - * Log the user off MXit and close the connection
    - *
    - * @param session The MXit session object
    - */
    -void mxit_close_connection( struct MXitSession* session )
    -{
    - purple_debug_info( MXIT_PLUGIN_ID, "mxit_close_connection\n" );
    -
    - if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) {
    - /* we are already closed */
    - return;
    - }
    - else if ( session->flags & MXIT_FLAG_LOGGEDIN ) {
    - /* we are currently logged in so we need to send a logout packet */
    - if ( !session->http ) {
    - mxit_send_logout( session );
    - }
    - session->flags &= ~MXIT_FLAG_LOGGEDIN;
    - }
    - session->flags &= ~MXIT_FLAG_CONNECTED;
    -
    - /* cancel all outstanding async calls */
    - purple_http_connection_set_destroy(session->async_http_reqs);
    - session->async_http_reqs = NULL;
    -
    - /* remove the input cb function */
    - if ( session->inpa ) {
    - purple_input_remove( session->inpa );
    - session->inpa = 0;
    - }
    -
    - /* remove HTTP poll timer */
    - if ( session->http_timer_id > 0 )
    - purple_timeout_remove( session->http_timer_id );
    -
    - /* remove slow queue manager timer */
    - if ( session->q_slow_timer_id > 0 )
    - purple_timeout_remove( session->q_slow_timer_id );
    -
    - /* remove fast queue manager timer */
    - if ( session->q_fast_timer_id > 0 )
    - purple_timeout_remove( session->q_fast_timer_id );
    -
    - /* remove all groupchat rooms */
    - while ( session->rooms != NULL ) {
    - struct multimx* multimx = (struct multimx *) session->rooms->data;
    -
    - session->rooms = g_list_remove( session->rooms, multimx );
    -
    - free( multimx );
    - }
    - g_list_free( session->rooms );
    - session->rooms = NULL;
    -
    - /* remove all rx chats names */
    - while ( session->active_chats != NULL ) {
    - char* chat = (char*) session->active_chats->data;
    -
    - session->active_chats = g_list_remove( session->active_chats, chat );
    -
    - g_free( chat );
    - }
    - g_list_free( session->active_chats );
    - session->active_chats = NULL;
    -
    - /* clear the internal invites */
    - while ( session->invites != NULL ) {
    - struct contact* contact = (struct contact*) session->invites->data;
    -
    - session->invites = g_list_remove( session->invites, contact );
    -
    - g_free(contact->msg);
    - g_free(contact->statusMsg);
    - g_free(contact->profile);
    - if (contact->image)
    - g_object_unref(contact->image);
    - g_free( contact );
    - }
    - g_list_free( session->invites );
    - session->invites = NULL;
    -
    - free( session->profile );
    - mxit_free_emoticon_cache( session );
    - g_free( session->uid );
    - g_free( session->encpwd );
    - session->encpwd = NULL;
    -
    - /* flush all the commands still in the queue */
    - flush_queue( session );
    -}
    -
    --- a/libpurple/protocols/mxit/client.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,352 +0,0 @@
    -/*
    - * MXit Protocol libPurple Plugin
    - *
    - * -- MXit client protocol implementation --
    - *
    - * Pieter Loubser <libpurple@mxit.com>
    - *
    - * (C) Copyright 2009 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_PROTO_H_
    -#define _MXIT_PROTO_H_
    -
    -
    -/* Client protocol constants */
    -#define CP_SOCK_REC_TERM '\x00' /* socket record terminator */
    -#define CP_HTTP_REC_TERM '\x26' /* http record terminator '&' */
    -#define CP_FLD_TERM '\x01' /* field terminator */
    -#define CP_PKT_TERM '\x02' /* packet terminator */
    -
    -
    -#define CP_MAX_PACKET ( 1 * 1000 * 1000 ) /* maximum client protocol packet size (1 MB) */
    -#define CP_MAX_FILESIZE ( CP_MAX_PACKET - 1000 ) /* maximum file size (reserve some space for packet headers) */
    -#define MXIT_EMOTICON_SIZE 18 /* icon size for custom emoticons */
    -#define CP_MAX_STATUS_MSG 250 /* maximum status message length (in characters) */
    -
    -/* Avatars */
    -#define MXIT_AVATAR_SIZE 96 /* default avatar image size 96x96 */
    -#define MXIT_AVATAR_TYPE "PNG" /* request avatars in this file type (only a suggestion) */
    -#define MXIT_AVATAR_BITDEPT 24 /* request avatars with this bit depth (only a suggestion) */
    -
    -/* Protocol error codes */
    -#define MXIT_ERRCODE_SUCCESS 0
    -#define MXIT_ERRCODE_REDIRECT 16
    -#define MXIT_ERRCODE_LOGGEDOUT 42
    -
    -/* MXit client features */
    -#define MXIT_CF_NONE 0x000000
    -#define MXIT_CF_FORMS 0x000001
    -#define MXIT_CF_FILE_TRANSFER 0x000002
    -#define MXIT_CF_CAMERA 0x000004
    -#define MXIT_CF_COMMANDS 0x000008
    -#define MXIT_CF_SMS 0x000010
    -#define MXIT_CF_FILE_ACCESS 0x000020
    -#define MXIT_CF_MIDP2 0x000040
    -#define MXIT_CF_SKINS 0x000080
    -#define MXIT_CF_AUDIO 0x000100
    -#define MXIT_CF_ENCRYPTION 0x000200
    -#define MXIT_CF_VOICE_REC 0x000400
    -#define MXIT_CF_VECTOR_GFX 0x000800
    -#define MXIT_CF_IMAGES 0x001000
    -#define MXIT_CF_MARKUP 0x002000
    -#define MXIT_CF_VIBES 0x004000
    -#define MXIT_CF_SELECT_CONTACT 0x008000
    -#define MXIT_CF_CUSTOM_EMO 0x010000
    -#define MXIT_CF_ALERT_PROFILES 0x020000
    -#define MXIT_CF_EXT_MARKUP 0x040000
    -#define MXIT_CF_PLAIN_PWD 0x080000
    -#define MXIT_CF_NO_GATEWAYS 0x100000
    -#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
    -#define MXIT_CF_TOUCHSCREEN 0x4000000
    -#define MXIT_CF_SVC_CONNECTION 0x8000000
    -#define MXIT_CF_MXML 0x10000000
    -#define MXIT_CF_TYPING_NOTIFY 0x20000000
    -
    -/* 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 | MXIT_CF_TYPING_NOTIFY )
    -
    -
    -#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_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_PROTO_VESION 63 /* client protocol version */
    -
    -/* set operating system name */
    -#if defined( __APPLE__ )
    -#define MXIT_CP_OS "apple"
    -#elif defined( _WIN32 )
    -#define MXIT_CP_OS "windows"
    -#elif defined( __linux__ )
    -#define MXIT_CP_OS "linux"
    -#else
    -#define MXIT_CP_OS "unknown"
    -#endif
    -
    -/* Client capabilities */
    -#define MXIT_CP_CAP "utf8=true;cid="MXIT_CLIENT_ID
    -
    -/* Client settings */
    -#define MAX_QUEUE_SIZE ( 1 << 5 ) /* tx queue size (32 packets) */
    -#define MXIT_POPUP_WIN_NAME "MXit Notification" /* popup window name */
    -#define MXIT_DEFAULT_LOCALE "en" /* default locale setting */
    -#define MXIT_DEFAULT_LOC "planetpurple" /* the default location for registration */
    -
    -/* Client protocol commands */
    -#define CP_CMD_LOGIN 0x0001 /* (1) login */
    -#define CP_CMD_LOGOUT 0x0002 /* (2) logout */
    -#define CP_CMD_CONTACT 0x0003 /* (3) get contacts */
    -#define CP_CMD_UPDATE 0x0005 /* (5) update contact information */
    -#define CP_CMD_INVITE 0x0006 /* (6) subscribe to new contact */
    -#define CP_CMD_PRESENCE 0x0007 /* (7) get presence */
    -#define CP_CMD_REMOVE 0x0008 /* (8) remove contact */
    -#define CP_CMD_RX_MSG 0x0009 /* (9) get new messages */
    -#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 */
    -#define CP_CMD_SPLASHCLICK 0x001F /* (31) splash-screen clickthrough */
    -#define CP_CMD_STATUS 0x0020 /* (32) set shown presence & status */
    -#define CP_CMD_MSGEVENT 0x0023 /* (35) Raise message event */
    -#define CP_CMD_GOT_MSGEVENT 0x0024 /* (36) Get message event */
    -#define CP_CMD_MOOD 0x0029 /* (41) set mood */
    -#define CP_CMD_KICK 0x002B /* (43) login kick */
    -#define CP_CMD_GRPCHAT_CREATE 0x002C /* (44) create new groupchat */
    -#define CP_CMD_GRPCHAT_INVITE 0x002D /* (45) add new groupchat member */
    -#define CP_CMD_NEW_SUB 0x0033 /* (51) get new subscription */
    -#define CP_CMD_ALLOW 0x0034 /* (52) allow subscription */
    -#define CP_CMD_DENY 0x0037 /* (55) deny subscription */
    -#define CP_CMD_EXTPROFILE_GET 0x0039 /* (57) get extended profile */
    -#define CP_CMD_EXTPROFILE_SET 0x003A /* (58) set extended profile */
    -#define CP_CMD_PING 0x03E8 /* (1000) ping (keepalive) */
    -
    -/* HTTP connection */
    -#define MXIT_HTTP_POLL_MIN 7 /* minimum time between HTTP polls (seconds) */
    -#define MXIT_HTTP_POLL_MAX ( 10 * 60 ) /* maximum time between HTTP polls (seconds) */
    -
    -/* receiver states */
    -#define RX_STATE_RLEN 0x01 /* reading packet length section */
    -#define RX_STATE_DATA 0x02 /* reading packet data section */
    -#define RX_STATE_PROC 0x03 /* process read data */
    -
    -/* message flags */
    -#define CP_MSG_NOTIFY_DELIVERY 0x0002 /* request delivery notification */
    -#define CP_MSG_NOTIFY_READ 0x0004 /* request read notification */
    -#define CP_MSG_PWD_ENCRYPTED 0x0010 /* message is password encrypted */
    -#define CP_MSG_TL_ENCRYPTED 0x0020 /* message is transport encrypted */
    -#define CP_MSG_RPLY_PWD_ENCRYPT 0x0040 /* reply should be password encrypted */
    -#define CP_MSG_RPLY_TL_ENCRYPT 0x0080 /* reply should be transport encrypted */
    -#define CP_MSG_MARKUP 0x0200 /* message may contain markup */
    -#define CP_MSG_EMOTICON 0x0400 /* message may contain custom emoticons */
    -#define CP_MSG_FAREWELL 0x0800 /* this is a farewell message */
    -
    -/* redirect types */
    -#define CP_REDIRECT_PERMANENT 1 /* permanent redirect */
    -#define CP_REDIRECT_TEMPORARY 2 /* temporary redirect */
    -
    -/* message tx types */
    -#define CP_MSGTYPE_NORMAL 0x01 /* normal message */
    -#define CP_MSGTYPE_CHAT 0x02 /* chat message */
    -#define CP_MSGTYPE_HEADLINE 0x03 /* headline message */
    -#define CP_MSGTYPE_ERROR 0x04 /* error message */
    -#define CP_MSGTYPE_GROUPCHAT 0x05 /* groupchat message */
    -#define CP_MSGTYPE_FORM 0x06 /* mxit custom form */
    -#define CP_MSGTYPE_COMMAND 0x07 /* mxit command */
    -
    -/* message event types */
    -#define CP_MSGEVENT_DELIVERED 0x02 /* message was delivered */
    -#define CP_MSGEVENT_DISPLAYED 0x04 /* message was viewed */
    -#define CP_MSGEVENT_TYPING 0x10 /* user is typing */
    -#define CP_MSGEVENT_STOPPED 0x20 /* user has stopped typing */
    -#define CP_MSGEVENT_ANGRY 0x40 /* user is typing angrily */
    -#define CP_MSGEVENT_ERASING 0x80 /* user is erasing text */
    -
    -/* extended profile attribute fields */
    -#define CP_PROFILE_BIRTHDATE "birthdate" /* Birthdate (String - ISO 8601 format) */
    -#define CP_PROFILE_GENDER "gender" /* Gender (Boolean - 0=female, 1=male) */
    -// #define CP_PROFILE_HIDENUMBER "hidenumber" /* Hide Number (Boolean - 0=false, 1=true) (DEPRECATED) */
    -#define CP_PROFILE_FULLNAME "fullname" /* Fullname (UTF8 String) */
    -#define CP_PROFILE_STATUS "statusmsg" /* Status Message (UTF8 String) */
    -#define CP_PROFILE_PREVSTATUS "prevstatusmsgs" /* Previous Status Messages (UTF8 String) */
    -#define CP_PROFILE_AVATAR "avatarid" /* Avatar ID (String) */
    -#define CP_PROFILE_MODIFIED "lastmodified" /* Last-Modified timestamp */
    -#define CP_PROFILE_TITLE "title" /* Title (UTF8 String) */
    -#define CP_PROFILE_FIRSTNAME "firstname" /* First name (UTF8 String) */
    -#define CP_PROFILE_LASTNAME "lastname" /* Last name (UTF8 String) */
    -#define CP_PROFILE_EMAIL "email" /* Email address (UTF8 String) */
    -#define CP_PROFILE_MOBILENR "mobilenumber" /* Mobile Number (UTF8 String) */
    -#define CP_PROFILE_REGCOUNTRY "registeredcountry" /* Registered Country Code (UTF8 String) */
    -#define CP_PROFILE_FLAGS "flags" /* Profile flags (Bitset) */
    -#define CP_PROFILE_LASTSEEN "lastseen" /* Last-Online timestamp */
    -#define CP_PROFILE_WHEREAMI "whereami" /* Where am I / Where I live */
    -#define CP_PROFILE_ABOUTME "aboutme" /* About me */
    -#define CP_PROFILE_RELATIONSHIP "relationship" /* Relationship Status */
    -
    -/* extended profile field types */
    -#define CP_PROFILE_TYPE_BOOL 0x02 /* boolean (0 or 1) */
    -#define CP_PROFILE_TYPE_SHORT 0x04 /* short (16-bit) */
    -#define CP_PROFILE_TYPE_INT 0x05 /* integer (32-bit) */
    -#define CP_PROFILE_TYPE_LONG 0x06 /* long (64-bit) */
    -#define CP_PROFILE_TYPE_UTF8 0x0A /* UTF8 string */
    -#define CP_PROFILE_TYPE_DATE 0x0B /* date-time (ISO 8601 format) */
    -
    -/* profile flags */
    -#define CP_PROF_NOT_SEARCHABLE 0x02 /* user cannot be searched for */
    -#define CP_PROF_NOT_SUGGESTABLE 0x08 /* user cannot be suggested as friend */
    -#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
    -
    -
    -/* ======================================================================================= */
    -
    -struct MXitSession;
    -
    -/*------------------------------------------*/
    -
    -struct field {
    - char* data;
    - size_t len;
    -};
    -
    -struct record {
    - struct field** fields;
    - int fcount;
    -};
    -
    -struct rx_packet {
    - int cmd;
    - int errcode;
    - char* errmsg;
    - struct record** records;
    - int rcount;
    -};
    -
    -struct tx_packet {
    - int cmd;
    - char header[256];
    - int headerlen;
    - char* data;
    - int datalen;
    -};
    -
    -/*------------------------------------------*/
    -
    -
    -/*
    - * A received message data object
    - */
    -struct RXMsgData {
    - struct MXitSession* session; /* MXit session object */
    - char* from; /* the sender's name */
    - time_t timestamp; /* time at which the message was sent */
    - GString* msg; /* newly created message converted to libPurple formatting */
    - gboolean got_img; /* flag to say if this message got any images/emoticons embedded */
    - short img_count; /* the amount of images/emoticons still outstanding for the message */
    - int chatid; /* multimx chatroom id */
    - int flags; /* libPurple conversation flags */
    - gboolean converted; /* true if the message has been completely parsed and converted to libPurple markup */
    - gboolean processed; /* the message has been processed completely and should be freed up */
    -};
    -
    -
    -
    -/*
    - * The packet transmission queue.
    - */
    -struct tx_queue {
    - struct tx_packet* packets[MAX_QUEUE_SIZE]; /* array of packet pointers */
    - int count; /* number of packets queued */
    - int rd_i; /* queue current read index (queue offset for reading a packet) */
    - int wr_i; /* queue current write index (queue offset for adding new packet) */
    -};
    -
    -
    -/* ======================================================================================= */
    -
    -void mxit_popup( int type, const char* heading, const char* message );
    -void mxit_strip_domain( char* username );
    -gboolean find_active_chat( const GList* chats, const char* who );
    -
    -void mxit_cb_rx( gpointer data, gint source, PurpleInputCondition cond );
    -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 );
    -void mxit_send_login( struct MXitSession* session );
    -void mxit_send_logout( struct MXitSession* session );
    -void mxit_send_ping( struct MXitSession* session );
    -void mxit_send_poll( struct MXitSession* session );
    -
    -void mxit_send_presence( struct MXitSession* session, int presence, const char* statusmsg );
    -void mxit_send_mood( struct MXitSession* session, int mood );
    -void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup, gboolean is_command );
    -
    -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, gboolean mxitid, const char* alias, const char* groupname, const char* message );
    -void mxit_send_remove( struct MXitSession* session, const char* username );
    -void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias );
    -void mxit_send_deny_sub( struct MXitSession* session, const char* username, const char* reason );
    -void mxit_send_update_contact( struct MXitSession* session, const char* username, const char* alias, const char* groupname );
    -void mxit_send_splashclick( struct MXitSession* session, const char* splashid );
    -void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event);
    -
    -void mxit_send_file( struct MXitSession* session, const char* username, const char* filename, const unsigned char* buf, size_t buflen );
    -void mxit_send_file_reject( struct MXitSession* session, const char* fileid );
    -void mxit_send_file_accept( struct MXitSession* session, const char* fileid, size_t filesize, size_t offset );
    -void mxit_send_file_received( struct MXitSession* session, const char* fileid, short status );
    -void mxit_set_avatar( struct MXitSession* session, const unsigned char* avatar, size_t avatarlen );
    -void mxit_get_avatar( struct MXitSession* session, const char* mxitId, const char* avatarId );
    -
    -void mxit_send_groupchat_create( struct MXitSession* session, const char* groupname, int nr_usernames, const char* usernames[] );
    -void mxit_send_groupchat_invite( struct MXitSession* session, const char* roomid, int nr_usernames, const char* usernames[] );
    -
    -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/oscar/clientlogin.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/oscar/clientlogin.c Mon Jun 05 16:36:29 2017 +0300
    @@ -40,9 +40,6 @@
    #include "oscarcommon.h"
    #include "core.h"
    -#include "ciphers/hmaccipher.h"
    -#include "ciphers/sha256hash.h"
    -
    #define AIM_LOGIN_HOST "api.screenname.aol.com"
    #define ICQ_LOGIN_HOST "api.login.icq.net"
    @@ -122,19 +119,16 @@
    */
    static gchar *hmac_sha256(const char *key, const char *message)
    {
    - PurpleCipher *cipher;
    - PurpleHash *hash;
    + GHmac *hmac;
    guchar digest[32];
    + gsize digest_len = 32;
    - hash = purple_sha256_hash_new();
    - cipher = purple_hmac_cipher_new(hash);
    - purple_cipher_set_key(cipher, (guchar *)key, strlen(key));
    - purple_cipher_append(cipher, (guchar *)message, strlen(message));
    - purple_cipher_digest(cipher, digest, sizeof(digest));
    - g_object_unref(cipher);
    - g_object_unref(hash);
    + hmac = g_hmac_new(G_CHECKSUM_SHA256, (guchar *)key, strlen(key));
    + g_hmac_update(hmac, (guchar *)message, -1);
    + g_hmac_get_digest(hmac, digest, &digest_len);
    + g_hmac_unref(hmac);
    - return purple_base64_encode(digest, sizeof(digest));
    + return g_base64_encode(digest, sizeof(digest));
    }
    /**
    @@ -345,7 +339,7 @@
    if (!parse_start_oscar_session_response(gc, got_data, got_len, &host, &port, &cookie, &tls_certname))
    return;
    - cookiedata = purple_base64_decode(cookie, &cookiedata_len);
    + cookiedata = g_base64_decode(cookie, &cookiedata_len);
    oscar_connect_to_bos(gc, od, host, port, cookiedata, cookiedata_len, tls_certname);
    g_free(cookiedata);
    --- a/libpurple/protocols/oscar/family_auth.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/oscar/family_auth.c Mon Jun 05 16:36:29 2017 +0300
    @@ -31,8 +31,6 @@
    #include <ctype.h>
    -#include "ciphers/md5hash.h"
    -
    /* #define USE_XOR_FOR_ICQ */
    #ifdef USE_XOR_FOR_ICQ
    @@ -75,14 +73,15 @@
    static int
    aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
    {
    - PurpleHash *hash;
    + GChecksum *hash;
    + gsize digest_len = 16;
    - hash = purple_md5_hash_new();
    - purple_hash_append(hash, (const guchar *)key, strlen(key));
    - purple_hash_append(hash, (const guchar *)password, password_len);
    - purple_hash_append(hash, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
    - purple_hash_digest(hash, 16, digest, NULL);
    - g_object_unref(hash);
    + hash = g_checksum_new(G_CHECKSUM_MD5);
    + g_checksum_update(hash, (const guchar *)key, -1);
    + g_checksum_update(hash, (const guchar *)password, password_len);
    + g_checksum_update(hash, (const guchar *)AIM_MD5_STRING, -1);
    + g_checksum_get_digest(hash, digest, &digest_len);
    + g_checksum_free(hash);
    return 0;
    }
    @@ -90,19 +89,20 @@
    static int
    aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
    {
    - PurpleHash *hash;
    + GChecksum *hash;
    guchar passdigest[16];
    + gsize digest_len = 16;
    - hash = purple_md5_hash_new();
    - purple_hash_append(hash, (const guchar *)password, password_len);
    - purple_hash_digest(hash, passdigest, sizeof(passdigest));
    - purple_hash_reset(hash);
    + hash = g_checksum_new(G_CHECKSUM_MD5);
    + g_checksum_update(hash, (const guchar *)password, password_len);
    + g_checksum_get_digest(hash, passdigest, &digest_len);
    + g_checksum_reset(hash);
    - purple_hash_append(hash, (const guchar *)key, strlen(key));
    - purple_hash_append(hash, passdigest, 16);
    - purple_hash_append(hash, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
    - purple_hash_digest(hash, digest, 16);
    - g_object_unref(hash);
    + g_checksum_update(hash, (const guchar *)key, -1);
    + g_checksum_update(hash, passdigest, digest_len);
    + g_checksum_update(hash, (const guchar *)AIM_MD5_STRING, -1);
    + g_checksum_get_digest(hash, digest, &digest_len);
    + g_checksum_free(hash);
    return 0;
    }
    --- a/libpurple/protocols/oscar/oscar.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/oscar/oscar.c Mon Jun 05 16:36:29 2017 +0300
    @@ -33,7 +33,6 @@
    #include "account.h"
    #include "accountopt.h"
    #include "buddyicon.h"
    -#include "ciphers/md5hash.h"
    #include "conversation.h"
    #include "core.h"
    #include "debug.h"
    @@ -5238,15 +5237,16 @@
    if (img == NULL) {
    aim_ssi_delicon(od);
    } else {
    - PurpleHash *hash;
    + GChecksum *hash;
    guchar md5[16];
    + gsize digest_len = 16;
    gconstpointer data = purple_image_get_data(img);
    size_t len = purple_image_get_data_size(img);
    - hash = purple_md5_hash_new();
    - purple_hash_append(hash, data, len);
    - purple_hash_digest(hash, md5, sizeof(md5));
    - g_object_unref(hash);
    + hash = g_checksum_new(G_CHECKSUM_MD5);
    + g_checksum_update(hash, data, len);
    + g_checksum_get_digest(hash, md5, &digest_len);
    + g_checksum_free(hash);
    aim_ssi_seticon(od, md5, 16);
    }
    --- a/libpurple/protocols/sametime/sametime.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/protocols/sametime/sametime.c Mon Jun 05 16:36:29 2017 +0300
    @@ -3879,7 +3879,7 @@
    /* obtain and base64 encode the image data, and put it in the
    mime part */
    size = purple_image_get_data_size(img);
    - data = purple_base64_encode(purple_image_get_data(img), size);
    + data = g_base64_encode(purple_image_get_data(img), size);
    purple_mime_part_set_data(part, data);
    g_free(data);
    --- a/libpurple/protocols/yahoo/tests/.hgignore Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,7 +0,0 @@
    -syntax: regexp
    -^test_yahoo_util$
    -
    -syntax: glob
    -*.log
    -*.trs
    -
    --- a/libpurple/protocols/yahoo/tests/Makefile.am Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,23 +0,0 @@
    -include $(top_srcdir)/glib-tap.mk
    -
    -COMMON_LIBS=\
    - $(top_builddir)/libpurple/libpurple.la \
    - $(top_builddir)/libpurple/protocols/yahoo/libyahoo.la \
    - $(GLIB_LIBS) \
    - $(GPLUGIN_LIBS)
    -
    -test_programs=\
    - test_yahoo_util
    -
    -test_yahoo_util_SOURCES=test_yahoo_util.c
    -test_yahoo_util_LDADD=$(COMMON_LIBS)
    -
    -AM_CPPFLAGS = \
    - -I$(top_srcdir)/libpurple \
    - -I$(top_builddir)/libpurple \
    - $(DEBUG_CFLAGS) \
    - $(GLIB_CFLAGS) \
    - $(GPLUGIN_CFLAGS) \
    - $(PLUGIN_CFLAGS) \
    - $(DBUS_CFLAGS) \
    - $(NSS_CFLAGS)
    --- a/libpurple/protocols/yahoo/tests/test_yahoo_util.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,250 +0,0 @@
    -#include <glib.h>
    -
    -#include "../ymsg.h"
    -
    -typedef struct {
    - gchar *input;
    - gchar *output;
    -} YahooStringTestData;
    -
    -static void
    -test_codes_to_html(void) {
    - YahooStringTestData data[] = {
    - {
    - "",
    - "",
    - }, {
    - "\x1B[12345m",
    - "",
    - }, {
    - "plain",
    - "plain",
    - }, {
    - "unknown \x1B[12345m ansi code",
    - "unknown ansi code",
    - }, {
    - "plain <peanut>",
    - "plain &lt;peanut&gt;",
    - }, {
    - "plain <peanut",
    - "plain &lt;peanut",
    - }, {
    - "plain> peanut",
    - "plain&gt; peanut",
    - }, {
    - "<font face='inva>lid'>test",
    - "<font face='inva&gt;lid'>test</font>",
    - }, {
    - "<font face='inva>lid",
    - "&lt;font face=&apos;inva&gt;lid",
    - }, {
    - "\x1B[1mbold",
    - "<b>bold</b>",
    - }, {
    - "\x1B[2mitalic",
    - "<i>italic</i>",
    - }, {
    - "\x1B[4munderline",
    - "<u>underline</u>",
    - }, {
    - "no\x1B[x4m markup",
    - "no markup",
    - }, {
    - /* bold italic underline */
    - "\x1B[1mbold\x1B[x1m \x1B[2mitalic\x1B[x2m \x1B[4munderline",
    - "<b>bold</b> <i>italic</i> <u>underline</u>",
    - }, {
    - "\x1B[1mbold \x1B[2mbolditalic\x1B[x1m italic",
    - "<b>bold <i>bolditalic</i></b><i> italic</i>",
    - }, {
    - "\x1B[1mbold \x1B[2mbolditalic\x1B[x1m \x1B[4mitalicunderline",
    - "<b>bold <i>bolditalic</i></b><i> <u>italicunderline</u></i>",
    - }, {
    - "\x1B[1mbold \x1B[2mbolditalic \x1B[4mbolditalicunderline\x1B[x2m boldunderline",
    - "<b>bold <i>bolditalic <u>bolditalicunderline</u></i><u> boldunderline</u></b>",
    - }, {
    - "\x1B[1mbold \x1B[2mbolditalic \x1B[4mbolditalicunderline\x1B[x1m italicunderline",
    - "<b>bold <i>bolditalic <u>bolditalicunderline</u></i></b><i><u> italicunderline</u></i>",
    - }, {
    - /* links */
    - "\x1B[lmhttps://pidgin.im/\x1B[xlm",
    - "https://pidgin.im/",
    - }, {
    - /* font color */
    - "\x1B[31mblue",
    - "<font color='#0000FF'>blue</font>",
    - }, {
    - "\x1B[#70ea15mcustom color",
    - "<font color='#70ea15'>custom color</font>",
    - }, {
    - "<ALT #ff0000,#00ff00,#0000ff>test</ALT>",
    - "test",
    - }, {
    - /* font face */
    - "<font face='Georgia'>test",
    - "<font face='Georgia'>test</font>",
    - }, {
    - /* font size */
    - "<font size='15'>test",
    - "<font size='4' absz='15'>test</font>",
    - }, {
    - "<font size='32'>size 32",
    - "<font size='6' absz='32'>size 32</font>",
    - }, {
    - /* combinations */
    - "<font face='Georgia' size='32'>test",
    - "<font face='Georgia' size='6' absz='32'>test</font>",
    - }, {
    - "\x1B[35m<font size='15'>test",
    - "<font color='#FF0080'><font size='4' absz='15'>test</font></font>",
    - }, {
    - "<FADE #ff0000,#00ff00,#0000ff>:<</FADE>",
    - ":&lt;",
    - }, {
    - NULL,
    - NULL,
    - }
    - };
    - gint i;
    -
    - yahoo_init_colorht();
    -
    - for(i = 0; data[i].input; i++) {
    - gchar *result = yahoo_codes_to_html(data[i].input);
    -
    - g_assert_cmpstr(result, ==, data[i].output);
    -
    - g_free(result);
    - }
    -
    - yahoo_dest_colorht();
    -}
    -
    -static void
    -test_html_to_codes(void) {
    - YahooStringTestData data[] = {
    - {
    - "plain",
    - "plain",
    - }, {
    - "plain &lt;peanut&gt;",
    - "plain <peanut>",
    - }, {
    - "plain &lt;peanut",
    - "plain <peanut",
    - }, {
    - "plain&gt; peanut",
    - "plain> peanut",
    - }, {
    - "plain &gt;",
    - "plain >",
    - }, {
    - "plain &gt; ",
    - "plain > ",
    - }, {
    - "plain &lt;",
    - "plain <",
    - }, {
    - "plain &lt; ",
    - "plain < ",
    - }, {
    - "plain &lt",
    - "plain &lt",
    - }, {
    - "plain &amp;",
    - "plain &",
    - }, {
    - /* bold/italic/underline */
    - "<b>bold</b>",
    - "\x1B[1mbold\x1B[x1m",
    - }, {
    - "<i>italic</i>",
    - "\x1B[2mitalic\x1B[x2m",
    - }, {
    - "<u>underline</u>",
    - "\x1B[4munderline\x1B[x4m",
    - }, {
    - "no</u> markup",
    - "no markup",
    - }, {
    - "<b>bold</b> <i>italic</i> <u>underline</u>",
    - "\x1B[1mbold\x1B[x1m \x1B[2mitalic\x1B[x2m \x1B[4munderline\x1B[x4m",
    - }, {
    - "<b>bold <i>bolditalic</i></b><i> italic</i>",
    - "\x1B[1mbold \x1B[2mbolditalic\x1B[x2m\x1B[x1m\x1B[2m italic\x1B[x2m",
    - }, {
    - "<b>bold <i>bolditalic</i></b><i> <u>italicunderline</u></i>",
    - "\x1B[1mbold \x1B[2mbolditalic\x1B[x2m\x1B[x1m\x1B[2m \x1B[4mitalicunderline\x1B[x4m\x1B[x2m",
    - }, {
    - /* link */
    - "<A HREF=\"https://pidgin.im/\">https://pidgin.im/</A>",
    - "https://pidgin.im/",
    - }, {
    - "<A HREF=\"mailto:mark@example.com\">mark@example.com</A>",
    - "mark@example.com",
    - }, {
    - /* font nothing */
    - "<font>nothing</font>",
    - "nothing",
    - }, {
    - /* font color */
    - "<font color=\"#E71414\">red</font>",
    - "\x1B[#E71414mred\x1B[#000000m",
    - }, {
    - "<font color=\"#FF0000\">red</font> <font color=\"#0000FF\">blue</font> black",
    - "\x1B[#FF0000mred\x1B[#000000m \x1B[#0000FFmblue\x1B[#000000m black",
    - }, {
    - /* font size */
    - "<font size=\"2\">test</font>",
    - "<font size=\"10\">test</font>",
    - }, {
    - "<font size=\"6\">test</font>",
    - "<font size=\"30\">test</font>",
    - }, {
    - /* combinations */
    - "<font color=\"#FF0000\"><font size=\"1\">redsmall</font> rednormal</font>",
    - "\x1B[#FF0000m<font size=\"8\">redsmall</font> rednormal\x1B[#000000m",
    - }, {
    - "<font color=\"#FF0000\"><font size=\"1\">redsmall</font> <font color=\"#00FF00\">greennormal</font> rednormal</font>",
    - "\x1B[#FF0000m<font size=\"8\">redsmall</font> \x1B[#00FF00mgreennormal\x1B[#FF0000m rednormal\x1B[#000000m",
    - }, {
    - "<b>bold <font color=\"#FF0000\">red <font face=\"Comic Sans MS\" size=\"5\">larger <font color=\"#000000\">backtoblack <font size=\"3\">normalsize</font></font></font></font></b>",
    - "\x1B[1mbold \x1B[#FF0000mred <font face=\"Comic Sans MS\" size=\"20\">larger \x1B[#000000mbacktoblack <font size=\"12\">normalsize</font>\x1B[#FF0000m</font>\x1B[#000000m\x1B[x1m",
    - }, {
    - /* buzz/unknown tags */
    - "<ding>",
    - "<ding>",
    - }, {
    - "Unknown <tags>",
    - "Unknown <tags>",
    - }, {
    - NULL,
    - NULL,
    - }
    - };
    - gint i;
    -
    - yahoo_init_colorht();
    -
    - for(i = 0; data[i].input; i++) {
    - gchar *result = yahoo_html_to_codes(data[i].input);
    -
    - g_assert_cmpstr(result, ==, data[i].output);
    -
    - g_free(result);
    - }
    -
    - yahoo_dest_colorht();
    -}
    -
    -gint
    -main(gint argc, gchar **argv) {
    - g_test_init(&argc, &argv, NULL);
    -
    - g_test_add_func("/yahoo/format/network to html",
    - test_codes_to_html);
    - g_test_add_func("/yahoo/format/html to network",
    - test_html_to_codes);
    -
    - return g_test_run();
    -}
    --- a/libpurple/protocols/yahoo/yahoo.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,415 +0,0 @@
    -/*
    - * 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 <account.h>
    -#include <core.h>
    -#include <plugins.h>
    -
    -#include "ymsg.h"
    -#include "yahoo.h"
    -#include "yahoochat.h"
    -#include "yahoo_aliases.h"
    -#include "yahoo_doodle.h"
    -#include "yahoo_filexfer.h"
    -#include "yahoo_picture.h"
    -
    -static PurpleProtocol *yahoo_protocol = NULL;
    -
    -static GSList *cmds = NULL;
    -
    -static void yahoo_register_commands(void)
    -{
    - PurpleCmdId id;
    -
    - id = purple_cmd_register("join", "s", PURPLE_CMD_P_PROTOCOL,
    - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
    - PURPLE_CMD_FLAG_PROTOCOL_ONLY,
    - "prpl-yahoo", yahoopurple_cmd_chat_join,
    - _("join &lt;room&gt;: Join a chat room on the Yahoo network"), NULL);
    - cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
    -
    - id = purple_cmd_register("list", "", PURPLE_CMD_P_PROTOCOL,
    - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
    - PURPLE_CMD_FLAG_PROTOCOL_ONLY,
    - "prpl-yahoo", yahoopurple_cmd_chat_list,
    - _("list: List rooms on the Yahoo network"), NULL);
    - cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
    -
    - id = purple_cmd_register("buzz", "", PURPLE_CMD_P_PROTOCOL,
    - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
    - "prpl-yahoo", yahoopurple_cmd_buzz,
    - _("buzz: Buzz a user to get their attention"), NULL);
    - cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
    -
    - id = purple_cmd_register("doodle", "", PURPLE_CMD_P_PROTOCOL,
    - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
    - "prpl-yahoo", yahoo_doodle_purple_cmd_start,
    - _("doodle: Request user to start a Doodle session"), NULL);
    - cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
    -}
    -
    -static void yahoo_unregister_commands(void)
    -{
    - while (cmds) {
    - PurpleCmdId id = GPOINTER_TO_UINT(cmds->data);
    - purple_cmd_unregister(id);
    - cmds = g_slist_delete_link(cmds, cmds);
    - }
    -}
    -
    -static PurpleAccount *find_acct(const char *protocol, const char *acct_id)
    -{
    - PurpleAccount *acct = NULL;
    -
    - /* If we have a specific acct, use it */
    - if (acct_id) {
    - acct = purple_accounts_find(acct_id, protocol);
    - if (acct && !purple_account_is_connected(acct))
    - acct = NULL;
    - } else { /* Otherwise find an active account for the protocol */
    - GList *l = purple_accounts_get_all();
    - while (l) {
    - if (!strcmp(protocol, purple_account_get_protocol_id(l->data))
    - && purple_account_is_connected(l->data)) {
    - acct = l->data;
    - break;
    - }
    - l = l->next;
    - }
    - }
    -
    - return acct;
    -}
    -
    -/* This may not be the best way to do this, but we find the first key w/o a value
    - * and assume it is the buddy name */
    -static void yahoo_find_uri_novalue_param(gpointer key, gpointer value, gpointer user_data)
    -{
    - char **retval = user_data;
    -
    - if (value == NULL && *retval == NULL) {
    - *retval = key;
    - }
    -}
    -
    -static gboolean yahoo_uri_handler(const char *proto, const char *cmd, GHashTable *params)
    -{
    - char *acct_id = g_hash_table_lookup(params, "account");
    - PurpleAccount *acct;
    -
    - if (g_ascii_strcasecmp(proto, "ymsgr"))
    - return FALSE;
    -
    - acct = find_acct(purple_protocol_get_id(yahoo_protocol), acct_id);
    -
    - if (!acct)
    - return FALSE;
    -
    - /* ymsgr:SendIM?screename&m=The+Message */
    - if (!g_ascii_strcasecmp(cmd, "SendIM")) {
    - char *sname = NULL;
    - g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &sname);
    - if (sname) {
    - char *message = g_hash_table_lookup(params, "m");
    -
    - PurpleIMConversation *im = purple_conversations_find_im_with_account(
    - sname, acct);
    - if (im == NULL)
    - im = purple_im_conversation_new(acct, sname);
    - purple_conversation_present(PURPLE_CONVERSATION(im));
    -
    - if (message) {
    - /* Spaces are encoded as '+' */
    - g_strdelimit(message, "+", ' ');
    - purple_conversation_send_confirm(PURPLE_CONVERSATION(im), message);
    - }
    - }
    - /* else
    - **If pidgindialogs_im() was in the core, we could use it here.
    - * It is all purple_request_* based, but I'm not sure it really belongs in the core
    - pidgindialogs_im(); */
    -
    - return TRUE;
    - }
    - /* ymsgr:Chat?roomname */
    - else if (!g_ascii_strcasecmp(cmd, "Chat")) {
    - char *rname = NULL;
    - g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &rname);
    - if (rname) {
    - /* This is somewhat hacky, but the params aren't useful after this command */
    - g_hash_table_insert(params, g_strdup("room"), g_strdup(rname));
    - g_hash_table_insert(params, g_strdup("type"), g_strdup("Chat"));
    - purple_serv_join_chat(purple_account_get_connection(acct), params);
    - }
    - /* else
    - ** Same as above (except that this would have to be re-written using purple_request_*)
    - pidgin_blist_joinchat_show(); */
    -
    - return TRUE;
    - }
    - /* ymsgr:AddFriend?name */
    - else if (!g_ascii_strcasecmp(cmd, "AddFriend")) {
    - char *name = NULL;
    - g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &name);
    - purple_blist_request_add_buddy(acct, name, NULL, NULL);
    - return TRUE;
    - }
    -
    - return FALSE;
    -}
    -
    -static GHashTable *
    -yahoo_get_account_text_table(PurpleAccount *account)
    -{
    - GHashTable *table;
    - table = g_hash_table_new(g_str_hash, g_str_equal);
    - g_hash_table_insert(table, "login_label", (gpointer)_("Yahoo ID..."));
    - return table;
    -}
    -
    -static PurpleWhiteboardOps yahoo_whiteboard_ops =
    -{
    - yahoo_doodle_start,
    - yahoo_doodle_end,
    - yahoo_doodle_get_dimensions,
    - NULL,
    - yahoo_doodle_get_brush,
    - yahoo_doodle_set_brush,
    - yahoo_doodle_send_draw_list,
    - yahoo_doodle_clear,
    -
    - /* padding */
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void
    -yahoo_protocol_init(PurpleProtocol *protocol)
    -{
    - PurpleAccountOption *option;
    -
    - protocol->id = "prpl-yahoo";
    - protocol->name = "Yahoo";
    - protocol->options = OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC |
    - OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE;
    - protocol->icon_spec = purple_buddy_icon_spec_new("png,gif,jpeg",
    - 96, 96, 96, 96, 0,
    - PURPLE_ICON_SCALE_SEND);
    -
    - protocol->whiteboard_ops = &yahoo_whiteboard_ops;
    -
    - option = purple_account_option_int_new(_("Pager port"), "port", YAHOO_PAGER_PORT);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOO_XFER_HOST);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOO_ROOMLIST_LOCALE);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_string_new(_("Encoding"), "local_charset", "UTF-8");
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    -#if 0
    - option = purple_account_option_bool_new(_("Use account proxy for HTTP and HTTPS connections"), "proxy_ssl", FALSE);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -#endif
    -}
    -
    -static void
    -yahoo_protocol_class_init(PurpleProtocolClass *klass)
    -{
    - klass->login = yahoo_login;
    - klass->close = yahoo_close;
    - klass->status_types = yahoo_status_types;
    - klass->list_icon = yahoo_list_icon;
    -}
    -
    -static void
    -yahoo_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    -{
    - client_iface->get_actions = yahoo_get_actions;
    - client_iface->list_emblem = yahoo_list_emblem;
    - client_iface->status_text = yahoo_status_text;
    - client_iface->tooltip_text = yahoo_tooltip_text;
    - client_iface->blist_node_menu = yahoo_blist_node_menu;
    - client_iface->normalize = purple_normalize_nocase;
    - client_iface->offline_message = yahoo_offline_message;
    - client_iface->get_account_text_table = yahoo_get_account_text_table;
    - client_iface->get_max_message_size = yahoo_get_max_message_size;
    -}
    -
    -static void
    -yahoo_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    -{
    - server_iface->get_info = yahoo_get_info;
    - server_iface->set_status = yahoo_set_status;
    - server_iface->set_idle = yahoo_set_idle;
    - server_iface->add_buddy = yahoo_add_buddy;
    - server_iface->remove_buddy = yahoo_remove_buddy;
    - server_iface->keepalive = yahoo_keepalive;
    - server_iface->alias_buddy = yahoo_update_alias;
    - server_iface->group_buddy = yahoo_change_buddys_group;
    - server_iface->rename_group = yahoo_rename_group;
    - server_iface->set_buddy_icon = yahoo_set_buddy_icon;
    -}
    -
    -static void
    -yahoo_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    -{
    - im_iface->send = yahoo_send_im;
    - im_iface->send_typing = yahoo_send_typing;
    -}
    -
    -static void
    -yahoo_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
    -{
    - chat_iface->info = yahoo_c_info;
    - chat_iface->info_defaults = yahoo_c_info_defaults;
    - chat_iface->join = yahoo_c_join;
    - chat_iface->get_name = yahoo_get_chat_name;
    - chat_iface->invite = yahoo_c_invite;
    - chat_iface->leave = yahoo_c_leave;
    - chat_iface->send = yahoo_c_send;
    -}
    -
    -static void
    -yahoo_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface)
    -{
    - privacy_iface->add_deny = yahoo_add_deny;
    - privacy_iface->rem_deny = yahoo_rem_deny;
    - privacy_iface->set_permit_deny = yahoo_set_permit_deny;
    -}
    -
    -static void
    -yahoo_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface)
    -{
    - roomlist_iface->get_list = yahoo_roomlist_get_list;
    - roomlist_iface->cancel = yahoo_roomlist_cancel;
    - roomlist_iface->expand_category = yahoo_roomlist_expand_category;
    -}
    -
    -static void
    -yahoo_protocol_attention_iface_init(PurpleProtocolAttentionIface *attention_iface)
    -{
    - attention_iface->send = yahoo_send_attention;
    - attention_iface->get_types = yahoo_attention_types;
    -}
    -
    -static void
    -yahoo_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface)
    -{
    - xfer_iface->can_receive = yahoo_can_receive_file;
    - xfer_iface->send = yahoo_send_file;
    - xfer_iface->new_xfer = yahoo_new_xfer;
    -}
    -
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - YahooProtocol, yahoo_protocol, PURPLE_TYPE_PROTOCOL, 0,
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - yahoo_protocol_client_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - yahoo_protocol_server_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - yahoo_protocol_im_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - yahoo_protocol_chat_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
    - yahoo_protocol_privacy_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE,
    - yahoo_protocol_roomlist_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE,
    - yahoo_protocol_attention_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE,
    - yahoo_protocol_xfer_iface_init)
    -);
    -
    -static PurplePluginInfo *
    -plugin_query(GError **error)
    -{
    - return purple_plugin_info_new(
    - "id", "prpl-yahoo",
    - "name", "Yahoo Protocols",
    - "version", DISPLAY_VERSION,
    - "category", N_("Protocol"),
    - "summary", N_("Yahoo! and Yahoo! JAPAN Protocols Plugin"),
    - "description", N_("Yahoo! and Yahoo! JAPAN Protocols Plugin"),
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
    - PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
    - NULL
    - );
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin *plugin, GError **error)
    -{
    - yahoo_protocol_register_type(plugin);
    -
    - yahoo_protocol = purple_protocols_add(YAHOO_TYPE_PROTOCOL, error);
    - if (!yahoo_protocol)
    - return FALSE;
    -
    - yahoo_init_colorht();
    -
    - yahoo_register_commands();
    -
    - purple_signal_connect(purple_get_core(), "uri-handler", yahoo_protocol,
    - PURPLE_CALLBACK(yahoo_uri_handler), NULL);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin, GError **error)
    -{
    - yahoo_unregister_commands();
    -
    - yahoo_dest_colorht();
    -
    - if (!purple_protocols_remove(yahoo_protocol, error))
    - return FALSE;
    -
    - return TRUE;
    -}
    -
    -PURPLE_PLUGIN_INIT(yahoo, plugin_query, plugin_load, plugin_unload);
    --- a/libpurple/protocols/yahoo/yahoo.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,51 +0,0 @@
    -/* 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 _YAHOO_H_
    -#define _YAHOO_H_
    -
    -#include <gmodule.h>
    -
    -#include "protocol.h"
    -
    -#define YAHOO_TYPE_PROTOCOL (yahoo_protocol_get_type())
    -#define YAHOO_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), YAHOO_TYPE_PROTOCOL, YahooProtocol))
    -#define YAHOO_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), YAHOO_TYPE_PROTOCOL, YahooProtocolClass))
    -#define YAHOO_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), YAHOO_TYPE_PROTOCOL))
    -#define YAHOO_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), YAHOO_TYPE_PROTOCOL))
    -#define YAHOO_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), YAHOO_TYPE_PROTOCOL, YahooProtocolClass))
    -
    -typedef struct _YahooProtocol
    -{
    - PurpleProtocol parent;
    -} YahooProtocol;
    -
    -typedef struct _YahooProtocolClass
    -{
    - PurpleProtocolClass parent_class;
    -} YahooProtocolClass;
    -
    -/**
    - * Returns the GType for the YahooProtocol object.
    - */
    -G_MODULE_EXPORT GType yahoo_protocol_get_type(void);
    -
    -#endif /* _YAHOO_H_ */
    --- a/libpurple/protocols/yahoo/yahoojp.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,131 +0,0 @@
    -/*
    - * 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 <account.h>
    -#include <plugins.h>
    -
    -#include "yahoojp.h"
    -#include "ymsg.h"
    -#include "yahoochat.h"
    -#include "yahoo_aliases.h"
    -#include "yahoo_doodle.h"
    -#include "yahoo_filexfer.h"
    -#include "yahoo_picture.h"
    -
    -static GSList *cmds = NULL;
    -
    -void yahoojp_register_commands(void)
    -{
    - PurpleCmdId id;
    -
    - id = purple_cmd_register("join", "s", PURPLE_CMD_P_PROTOCOL,
    - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
    - PURPLE_CMD_FLAG_PROTOCOL_ONLY,
    - "prpl-yahoojp", yahoopurple_cmd_chat_join,
    - _("join &lt;room&gt;: Join a chat room on the Yahoo network"), NULL);
    - cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
    -
    - id = purple_cmd_register("list", "", PURPLE_CMD_P_PROTOCOL,
    - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
    - PURPLE_CMD_FLAG_PROTOCOL_ONLY,
    - "prpl-yahoojp", yahoopurple_cmd_chat_list,
    - _("list: List rooms on the Yahoo network"), NULL);
    - cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
    -
    - id = purple_cmd_register("buzz", "", PURPLE_CMD_P_PROTOCOL,
    - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
    - "prpl-yahoojp", yahoopurple_cmd_buzz,
    - _("buzz: Buzz a user to get their attention"), NULL);
    - cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
    -
    - id = purple_cmd_register("doodle", "", PURPLE_CMD_P_PROTOCOL,
    - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
    - "prpl-yahoojp", yahoo_doodle_purple_cmd_start,
    - _("doodle: Request user to start a Doodle session"), NULL);
    - cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
    -}
    -
    -void yahoojp_unregister_commands(void)
    -{
    - while (cmds) {
    - PurpleCmdId id = GPOINTER_TO_UINT(cmds->data);
    - purple_cmd_unregister(id);
    - cmds = g_slist_delete_link(cmds, cmds);
    - }
    -}
    -
    -static GHashTable *
    -yahoojp_get_account_text_table(PurpleAccount *account)
    -{
    - GHashTable *table;
    - table = g_hash_table_new(g_str_hash, g_str_equal);
    - g_hash_table_insert(table, "login_label", (gpointer)_("Yahoo JAPAN ID..."));
    - return table;
    -}
    -
    -static void
    -yahoojp_protocol_init(PurpleProtocol *protocol)
    -{
    - PurpleAccountOption *option;
    -
    - protocol->id = "prpl-yahoojp";
    - protocol->name = "Yahoo JAPAN";
    -
    - /* delete yahoo's protocol options */
    - purple_protocol_override(protocol, PURPLE_PROTOCOL_OVERRIDE_PROTOCOL_OPTIONS);
    -
    - option = purple_account_option_int_new(_("Pager port"), "port", YAHOO_PAGER_PORT);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOOJP_XFER_HOST);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOOJP_ROOMLIST_LOCALE);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_string_new(_("Encoding"), "local_charset", "UTF-8");
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -}
    -
    -static void
    -yahoojp_protocol_class_init(PurpleProtocolClass *klass)
    -{
    -}
    -
    -static void
    -yahoojp_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    -{
    - client_iface->get_account_text_table = yahoojp_get_account_text_table;
    -}
    -
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - YahooJPProtocol, yahoojp_protocol, YAHOO_TYPE_PROTOCOL, 0,
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - yahoojp_protocol_client_iface_init)
    -);
    --- a/libpurple/protocols/yahoo/yahoojp.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,57 +0,0 @@
    -/* 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 _YAHOOJP_H_
    -#define _YAHOOJP_H_
    -
    -#include "yahoo.h"
    -
    -#define YAHOOJP_TYPE_PROTOCOL (yahoojp_protocol_get_type())
    -#define YAHOOJP_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), YAHOOJP_TYPE_PROTOCOL, YahooJPProtocol))
    -#define YAHOOJP_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), YAHOOJP_TYPE_PROTOCOL, YahooJPProtocolClass))
    -#define YAHOOJP_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), YAHOOJP_TYPE_PROTOCOL))
    -#define YAHOOJP_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), YAHOOJP_TYPE_PROTOCOL))
    -#define YAHOOJP_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), YAHOOJP_TYPE_PROTOCOL, YahooJPProtocolClass))
    -
    -typedef struct _YahooJPProtocol
    -{
    - YahooProtocol parent;
    -} YahooJPProtocol;
    -
    -typedef struct _YahooJPProtocolClass
    -{
    - YahooProtocolClass parent_class;
    -} YahooJPProtocolClass;
    -
    -/**
    - * Registers the YahooJPProtocol type in the type system.
    - */
    -void yahoojp_protocol_register_type(PurplePlugin *plugin);
    -
    -/**
    - * Returns the GType for the YahooJPProtocol object.
    - */
    -G_MODULE_EXPORT GType yahoojp_protocol_get_type(void);
    -
    -void yahoojp_register_commands(void);
    -void yahoojp_unregister_commands(void);
    -
    -#endif /* _YAHOOJP_H_ */
    --- a/libpurple/protocols/yahoo/ymsg.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,4989 +0,0 @@
    -/*
    - * 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
    - *
    - */
    -
    -/*
    - * Note: When handling the list of struct yahoo_pair's from an incoming
    - * packet the value might not be UTF-8. You should either validate that
    - * it is UTF-8 using g_utf8_validate() or use yahoo_string_decode().
    - */
    -
    -#include "internal.h"
    -
    -#include "account.h"
    -#include "accountopt.h"
    -#include "buddylist.h"
    -#include "ciphers/md5hash.h"
    -#include "cmds.h"
    -#include "core.h"
    -#include "debug.h"
    -#include "http.h"
    -#include "network.h"
    -#include "notify.h"
    -#include "protocol.h"
    -#include "proxy.h"
    -#include "request.h"
    -#include "server.h"
    -#include "util.h"
    -#include "version.h"
    -#include "xmlnode.h"
    -
    -#include "ymsg.h"
    -#include "yahoochat.h"
    -#include "yahoo_aliases.h"
    -#include "yahoo_doodle.h"
    -#include "yahoo_filexfer.h"
    -#include "yahoo_friend.h"
    -#include "yahoo_packet.h"
    -#include "yahoo_picture.h"
    -#include "ycht.h"
    -
    -/* #define YAHOO_DEBUG */
    -
    -/* It doesn't look like it is working (the previously used host is down, another
    - * one doesn't send us back cookies).
    - */
    -#define TRY_WEBMESSENGER_LOGIN 0
    -
    -/* One hour */
    -#define PING_TIMEOUT 3600
    -
    -/* One minute */
    -#define KEEPALIVE_TIMEOUT 60
    -
    -#if TRY_WEBMESSENGER_LOGIN
    -static void
    -yahoo_login_page_cb(PurpleHttpConnection *http_conn,
    - PurpleHttpResponse *response, gpointer _unused);
    -#endif /* TRY_WEBMESSENGER_LOGIN */
    -
    -static void yahoo_update_status(PurpleConnection *gc, const char *name, YahooFriend *f)
    -{
    - char *status = NULL;
    -
    - if (!gc || !name || !f || !purple_blist_find_buddy(purple_connection_get_account(gc), name))
    - return;
    -
    - switch (f->status) {
    - case YAHOO_STATUS_OFFLINE:
    - status = YAHOO_STATUS_TYPE_OFFLINE;
    - break;
    - case YAHOO_STATUS_AVAILABLE:
    - status = YAHOO_STATUS_TYPE_AVAILABLE;
    - break;
    - case YAHOO_STATUS_BRB:
    - status = YAHOO_STATUS_TYPE_BRB;
    - break;
    - case YAHOO_STATUS_BUSY:
    - status = YAHOO_STATUS_TYPE_BUSY;
    - break;
    - case YAHOO_STATUS_NOTATHOME:
    - status = YAHOO_STATUS_TYPE_NOTATHOME;
    - break;
    - case YAHOO_STATUS_NOTATDESK:
    - status = YAHOO_STATUS_TYPE_NOTATDESK;
    - break;
    - case YAHOO_STATUS_NOTINOFFICE:
    - status = YAHOO_STATUS_TYPE_NOTINOFFICE;
    - break;
    - case YAHOO_STATUS_ONPHONE:
    - status = YAHOO_STATUS_TYPE_ONPHONE;
    - break;
    - case YAHOO_STATUS_ONVACATION:
    - status = YAHOO_STATUS_TYPE_ONVACATION;
    - break;
    - case YAHOO_STATUS_OUTTOLUNCH:
    - status = YAHOO_STATUS_TYPE_OUTTOLUNCH;
    - break;
    - case YAHOO_STATUS_STEPPEDOUT:
    - status = YAHOO_STATUS_TYPE_STEPPEDOUT;
    - break;
    - case YAHOO_STATUS_INVISIBLE: /* this should never happen? */
    - status = YAHOO_STATUS_TYPE_INVISIBLE;
    - break;
    - case YAHOO_STATUS_CUSTOM:
    - case YAHOO_STATUS_IDLE:
    - if (!f->away)
    - status = YAHOO_STATUS_TYPE_AVAILABLE;
    - else
    - status = YAHOO_STATUS_TYPE_AWAY;
    - break;
    - default:
    - purple_debug_warning("yahoo", "Warning, unknown status %d\n", f->status);
    - break;
    - }
    -
    - if (status) {
    - if (f->status == YAHOO_STATUS_CUSTOM)
    - purple_protocol_got_user_status(purple_connection_get_account(gc), name, status, "message",
    - yahoo_friend_get_status_message(f), NULL);
    - else
    - purple_protocol_got_user_status(purple_connection_get_account(gc), name, status, NULL);
    - }
    -
    - if (f->idle != 0)
    - purple_protocol_got_user_idle(purple_connection_get_account(gc), name, TRUE, f->idle);
    - else
    - purple_protocol_got_user_idle(purple_connection_get_account(gc), name, FALSE, 0);
    -
    - if (f->sms)
    - purple_protocol_got_user_status(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE, NULL);
    - else
    - purple_protocol_got_user_status_deactive(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE);
    -}
    -
    -static void yahoo_process_status(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - GSList *l = pkt->hash;
    - YahooFriend *f = NULL;
    - char *name = NULL;
    - gboolean unicode = FALSE;
    - char *message = NULL;
    -
    - if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
    - if (!purple_account_get_remember_password(account))
    - purple_account_set_password(account, NULL, NULL, NULL);
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE,
    - _("You have signed on from another location"));
    - return;
    - }
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - switch (pair->key) {
    - case 0: /* we won't actually do anything with this */
    - case 1: /* we won't actually do anything with this */
    - break;
    - case 8: /* how many online buddies we have */
    - break;
    - case 7: /* the current buddy */
    - /* update the previous buddy before changing the variables */
    - if (f) {
    - if (message)
    - yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
    - if (name)
    - yahoo_update_status(gc, name, f);
    - }
    - name = message = NULL;
    - f = NULL;
    - if (pair->value && g_utf8_validate(pair->value, -1, NULL)) {
    - name = pair->value;
    - f = yahoo_friend_find_or_new(gc, name);
    - }
    - break;
    - case 10: /* state */
    - if (!f)
    - break;
    -
    - f->status = strtol(pair->value, NULL, 10);
    - if ((f->status >= YAHOO_STATUS_BRB) && (f->status <= YAHOO_STATUS_STEPPEDOUT))
    - f->away = 1;
    - else
    - f->away = 0;
    -
    - if (f->status == YAHOO_STATUS_IDLE) {
    - /* Idle may have already been set in a more precise way in case 137 */
    - if (f->idle == 0)
    - {
    - if(pkt->service == YAHOO_SERVICE_STATUS_15)
    - f->idle = -1;
    - else
    - f->idle = time(NULL);
    - }
    - } else
    - f->idle = 0;
    -
    - if (f->status != YAHOO_STATUS_CUSTOM)
    - yahoo_friend_set_status_message(f, NULL);
    -
    - f->sms = 0;
    - break;
    - case 19: /* custom message */
    - if (f)
    - message = pair->value;
    - break;
    - case 11: /* this is the buddy's session id */
    - if (f)
    - f->session_id = strtol(pair->value, NULL, 10);
    - break;
    - case 17: /* in chat? */
    - break;
    - case 47: /* is custom status away or not? 2=idle*/
    - if (!f)
    - break;
    -
    - /* I have no idea what it means when this is
    - * set when someone's available, but it doesn't
    - * mean idle. */
    - if (f->status == YAHOO_STATUS_AVAILABLE)
    - break;
    -
    - f->away = strtol(pair->value, NULL, 10);
    - if (f->away == 2) {
    - /* Idle may have already been set in a more precise way in case 137 */
    - if (f->idle == 0)
    - {
    - if(pkt->service == YAHOO_SERVICE_STATUS_15)
    - f->idle = -1;
    - else
    - f->idle = time(NULL);
    - }
    - }
    -
    - break;
    - case 138: /* when value is 1, either we're not idle, or we are but won't say how long */
    - if (!f)
    - break;
    -
    - if( (strtol(pair->value, NULL, 10) == 1) && (f->idle) )
    - f->idle = -1;
    - break;
    - case 137: /* usually idle time in seconds, sometimes login time */
    - if (!f)
    - break;
    -
    - if (f->status != YAHOO_STATUS_AVAILABLE)
    - f->idle = time(NULL) - strtol(pair->value, NULL, 10);
    - break;
    - case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
    - if (strtol(pair->value, NULL, 10) == 0) {
    - if (f)
    - f->status = YAHOO_STATUS_OFFLINE;
    - if (name) {
    - purple_protocol_got_user_status(account, name, "offline", NULL);
    - purple_protocol_got_user_status_deactive(account, name, YAHOO_STATUS_TYPE_MOBILE);
    - }
    - break;
    - }
    - break;
    - case 60: /* SMS */
    - if (f) {
    - f->sms = strtol(pair->value, NULL, 10);
    - yahoo_update_status(gc, name, f);
    - }
    - break;
    - case 197: /* Avatars */
    - {
    - guchar *decoded;
    - char *tmp;
    - gsize len;
    -
    - if (pair->value) {
    - decoded = purple_base64_decode(pair->value, &len);
    - if (decoded && len > 0) {
    - tmp = purple_str_binary_to_ascii(decoded, len);
    - purple_debug_info("yahoo", "Got key 197, value = %s\n", tmp);
    - g_free(tmp);
    - }
    - g_free(decoded);
    - }
    - break;
    - }
    - case 192: /* Pictures, aka Buddy Icons, checksum */
    - {
    - /* FIXME: Please, if you know this protocol,
    - * FIXME: fix up the strtol() stuff if possible. */
    - int cksum = strtol(pair->value, NULL, 10);
    - const char *locksum = NULL;
    - PurpleBuddy *b;
    -
    - if (!name)
    - break;
    -
    - b = purple_blist_find_buddy(purple_connection_get_account(gc), name);
    -
    - if (!cksum || (cksum == -1)) {
    - if (f)
    - yahoo_friend_set_buddy_icon_need_request(f, TRUE);
    - purple_buddy_icons_set_for_user(purple_connection_get_account(gc), name, NULL, 0, NULL);
    - break;
    - }
    -
    - if (!f)
    - break;
    -
    - yahoo_friend_set_buddy_icon_need_request(f, FALSE);
    - if (b) {
    - locksum = purple_buddy_icons_get_checksum_for_user(b);
    - if (!locksum || (cksum != strtol(locksum, NULL, 10)))
    - yahoo_send_picture_request(gc, name);
    - }
    -
    - break;
    - }
    - case 16: /* Custom error message */
    - {
    - char *tmp = yahoo_string_decode(gc, pair->value, TRUE);
    - purple_notify_error(gc, NULL, tmp, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(tmp);
    - }
    - break;
    - case 97: /* Unicode status message */
    - unicode = !strcmp(pair->value, "1");
    - break;
    - case 244: /* client version number. Yahoo Client Detection */
    - if(f && strtol(pair->value, NULL, 10))
    - f->version_id = strtol(pair->value, NULL, 10);
    - break;
    - default:
    - purple_debug_warning("yahoo",
    - "Unknown status key %d\n", pair->key);
    - break;
    - }
    -
    - l = l->next;
    - }
    -
    - if (f) {
    - if (pkt->service == YAHOO_SERVICE_LOGOFF)
    - f->status = YAHOO_STATUS_OFFLINE;
    - if (message)
    - yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
    -
    - if (name) /* update the last buddy */
    - yahoo_update_status(gc, name, f);
    - }
    -}
    -
    -static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const char *name, const char *group)
    -{
    - PurpleBuddy *b;
    - PurpleGroup *g;
    - GSList *list, *i;
    - gboolean onlist = FALSE;
    - char *oname = NULL;
    -
    - if (g_hash_table_lookup_extended(ht, name, (gpointer *)&oname, (gpointer *)&list))
    - g_hash_table_steal(ht, oname);
    - else
    - list = purple_blist_find_buddies(account, name);
    -
    - for (i = list; i; i = i->next) {
    - b = i->data;
    - g = purple_buddy_get_group(b);
    - if (!purple_utf8_strcasecmp(group, purple_group_get_name(g))) {
    - purple_debug_misc("yahoo",
    - "Oh good, %s is in the right group (%s).\n", name, group);
    - list = g_slist_delete_link(list, i);
    - onlist = TRUE;
    - break;
    - }
    - }
    -
    - if (!onlist) {
    - purple_debug_misc("yahoo",
    - "Uhoh, %s isn't on the list (or not in this group), adding him to group %s.\n", name, group);
    - if (!(g = purple_blist_find_group(group))) {
    - g = purple_group_new(group);
    - purple_blist_add_group(g, NULL);
    - }
    - b = purple_buddy_new(account, name, NULL);
    - purple_blist_add_buddy(b, NULL, g, NULL);
    - }
    -
    - if (list) {
    - if (!oname)
    - oname = g_strdup(name);
    - g_hash_table_insert(ht, oname, list);
    - } else
    - g_free(oname);
    -}
    -
    -static void yahoo_do_group_cleanup(gpointer key, gpointer value, gpointer user_data)
    -{
    - char *name = key;
    - GSList *list = value, *i;
    - PurpleBuddy *b;
    - PurpleGroup *g;
    -
    - for (i = list; i; i = i->next) {
    - b = i->data;
    - g = purple_buddy_get_group(b);
    - purple_debug_misc("yahoo", "Deleting Buddy %s from group %s.\n", name,
    - purple_group_get_name(g));
    - purple_blist_remove_buddy(b);
    - }
    -}
    -
    -static char *_getcookie(char *rawcookie)
    -{
    - char *cookie = NULL;
    - char *tmpcookie;
    - char *cookieend;
    -
    - if (strlen(rawcookie) < 2)
    - return NULL;
    - tmpcookie = g_strdup(rawcookie+2);
    - cookieend = strchr(tmpcookie, ';');
    -
    - if (cookieend)
    - *cookieend = '\0';
    -
    - cookie = g_strdup(tmpcookie);
    - g_free(tmpcookie);
    -
    - return cookie;
    -}
    -
    -static void yahoo_process_cookie(YahooData *yd, char *c)
    -{
    - if (c[0] == 'Y') {
    - g_free(yd->cookie_y);
    - yd->cookie_y = _getcookie(c);
    - } else if (c[0] == 'T') {
    - g_free(yd->cookie_t);
    - yd->cookie_t = _getcookie(c);
    - } else
    - purple_debug_info("yahoo", "Unrecognized cookie '%c'\n", c[0]);
    - yd->cookies = g_slist_prepend(yd->cookies, g_strdup(c));
    -}
    -
    -static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - GSList *l = pkt->hash;
    -
    - PurpleAccount *account = purple_connection_get_account(gc);
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - GHashTable *ht;
    - char *norm_bud = NULL;
    - char *temp = NULL;
    - YahooFriend *f = NULL; /* It's your friends. They're going to want you to share your StarBursts. */
    - /* But what if you had no friends? */
    - int stealth = 0;
    -
    - ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    - l = l->next;
    -
    - switch (pair->key) {
    - case 302:
    - /* This is always 318 before a group, 319 before the first s/n in a group, 320 before any ignored s/n.
    - * It is not sent for s/n's in a group after the first.
    - * All ignored s/n's are listed last, so when we see a 320 we clear the group and begin marking the
    - * s/n's as ignored. It is always followed by an identical 300 key.
    - */
    - if (pair->value && !strcmp(pair->value, "320")) {
    - /* No longer in any group; this indicates the start of the ignore list. */
    - g_free(yd->current_list15_grp);
    - yd->current_list15_grp = NULL;
    - }
    -
    - break;
    - case 301: /* This is 319 before all s/n's in a group after the first. It is followed by an identical 300. */
    - if(temp != NULL) {
    - norm_bud = g_strdup(temp);
    -
    - if (yd->current_list15_grp) {
    - /* This buddy is in a group */
    - f = yahoo_friend_find_or_new(gc, norm_bud);
    - if (!purple_blist_find_buddy(account, norm_bud)) {
    - PurpleBuddy *b;
    - PurpleGroup *g;
    - if (!(g = purple_blist_find_group(yd->current_list15_grp))) {
    - g = purple_group_new(yd->current_list15_grp);
    - purple_blist_add_group(g, NULL);
    - }
    - b = purple_buddy_new(account, norm_bud, NULL);
    - purple_blist_add_buddy(b, NULL, g, NULL);
    - }
    - yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp);
    -
    - if(stealth == 2)
    - f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
    -
    - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
    - f->p2p_packet_sent = 0;
    - } else {
    - /* This buddy is on the ignore list (and therefore in no group) */
    - purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n", purple_account_get_username(account), norm_bud);
    - purple_account_privacy_deny_add(account, norm_bud, 1);
    - }
    -
    - g_free(norm_bud);
    - norm_bud=NULL;
    - stealth = 0;
    - g_free(temp);
    - temp = NULL;
    - }
    - break;
    - case 300: /* This is 318 before a group, 319 before any s/n in a group, and 320 before any ignored s/n. */
    - break;
    - case 65: /* This is the group */
    - g_free(yd->current_list15_grp);
    - yd->current_list15_grp = yahoo_string_decode(gc, pair->value, FALSE);
    - break;
    - case 7: /* buddy's s/n */
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - g_free(temp);
    - temp = g_strdup(purple_normalize(account, pair->value));
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_list_15 "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 59: /* somebody told cookies come here too, but im not sure */
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - yahoo_process_cookie(yd, pair->value);
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_list_15 "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 317: /* Stealth Setting */
    - stealth = strtol(pair->value, NULL, 10);
    - break;
    - }
    - }
    -
    - g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
    -
    - /* The reporter of ticket #9745 determined that we weren't retrieving the
    - * aliases during buddy list retrieval, so we never updated aliases that
    - * changed while we were signed off. */
    - yahoo_fetch_aliases(gc);
    -
    - /* Now that we have processed the buddy list, we can say yahoo has connected */
    - purple_connection_set_display_name(gc, purple_normalize(account, purple_account_get_username(account)));
    - yd->logged_in = TRUE;
    - purple_debug_info("yahoo","Authentication: Connection established\n");
    - purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
    - if (yd->picture_upload_todo) {
    - yahoo_buddy_icon_upload(gc, yd->picture_upload_todo);
    - yd->picture_upload_todo = NULL;
    - }
    - yahoo_set_status(account, purple_account_get_active_status(account));
    -
    - g_hash_table_destroy(ht);
    - g_free(temp);
    -}
    -
    -static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - GSList *l = pkt->hash;
    - gboolean got_serv_list = FALSE;
    - YahooFriend *f = NULL;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - GHashTable *ht;
    -
    - char **lines;
    - char **split;
    - char **buddies;
    - char **tmp, **bud, *norm_bud;
    - char *grp = NULL;
    -
    - if (pkt->id)
    - yd->session_id = pkt->id;
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    - l = l->next;
    -
    - switch (pair->key) {
    - case 87:
    - if (!yd->tmp_serv_blist)
    - yd->tmp_serv_blist = g_string_new(pair->value);
    - else
    - g_string_append(yd->tmp_serv_blist, pair->value);
    - break;
    - case 88:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - if (!yd->tmp_serv_ilist)
    - yd->tmp_serv_ilist = g_string_new(pair->value);
    - else
    - g_string_append(yd->tmp_serv_ilist, pair->value);
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_list "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 89:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - yd->profiles = g_strsplit(pair->value, ",", -1);
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_list "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 59: /* cookies, yum */
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - yahoo_process_cookie(yd, pair->value);
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_list "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case YAHOO_SERVICE_PRESENCE_PERM:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - if (!yd->tmp_serv_plist)
    - yd->tmp_serv_plist = g_string_new(pair->value);
    - else
    - g_string_append(yd->tmp_serv_plist, pair->value);
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_list "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - }
    - }
    -
    - if (pkt->status != 0)
    - return;
    -
    - if (yd->tmp_serv_blist) {
    - ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
    -
    - lines = g_strsplit(yd->tmp_serv_blist->str, "\n", -1);
    - for (tmp = lines; *tmp; tmp++) {
    - split = g_strsplit(*tmp, ":", 2);
    - if (!split)
    - continue;
    - if (!split[0] || !split[1]) {
    - g_strfreev(split);
    - continue;
    - }
    - grp = yahoo_string_decode(gc, split[0], FALSE);
    - buddies = g_strsplit(split[1], ",", -1);
    - for (bud = buddies; bud && *bud; bud++) {
    - if (!g_utf8_validate(*bud, -1, NULL)) {
    - purple_debug_warning("yahoo", "yahoo_process_list "
    - "got non-UTF-8 string for bud\n");
    - continue;
    - }
    -
    - norm_bud = g_strdup(purple_normalize(account, *bud));
    - f = yahoo_friend_find_or_new(gc, norm_bud);
    -
    - if (!purple_blist_find_buddy(account, norm_bud)) {
    - PurpleBuddy *b;
    - PurpleGroup *g;
    - if (!(g = purple_blist_find_group(grp))) {
    - g = purple_group_new(grp);
    - purple_blist_add_group(g, NULL);
    - }
    - b = purple_buddy_new(account, norm_bud, NULL);
    - purple_blist_add_buddy(b, NULL, g, NULL);
    - }
    -
    - yahoo_do_group_check(account, ht, norm_bud, grp);
    - /* set p2p status not connected and no p2p packet sent */
    - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
    - f->p2p_packet_sent = 0;
    -
    - g_free(norm_bud);
    - }
    - g_strfreev(buddies);
    - g_strfreev(split);
    - g_free(grp);
    - }
    - g_strfreev(lines);
    -
    - g_string_free(yd->tmp_serv_blist, TRUE);
    - yd->tmp_serv_blist = NULL;
    - g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
    - g_hash_table_destroy(ht);
    - }
    -
    - if (yd->tmp_serv_ilist) {
    - buddies = g_strsplit(yd->tmp_serv_ilist->str, ",", -1);
    - for (bud = buddies; bud && *bud; bud++) {
    - /* The server is already ignoring the user */
    - got_serv_list = TRUE;
    - purple_account_privacy_deny_add(account, *bud, 1);
    - }
    - g_strfreev(buddies);
    -
    - g_string_free(yd->tmp_serv_ilist, TRUE);
    - yd->tmp_serv_ilist = NULL;
    - }
    -
    - if (got_serv_list &&
    - ((purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST) &&
    - (purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_DENY_ALL) &&
    - (purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS)))
    - {
    - purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
    - purple_debug_info("yahoo", "%s privacy defaulting to PURPLE_ACCOUNT_PRIVACY_DENY_USERS.\n",
    - purple_account_get_username(account));
    - }
    -
    - if (yd->tmp_serv_plist) {
    - buddies = g_strsplit(yd->tmp_serv_plist->str, ",", -1);
    - for (bud = buddies; bud && *bud; bud++) {
    - f = yahoo_friend_find(gc, *bud);
    - if (f) {
    - purple_debug_info("yahoo", "%s setting presence for %s to PERM_OFFLINE\n",
    - purple_account_get_username(account), *bud);
    - f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
    - }
    - }
    - g_strfreev(buddies);
    - g_string_free(yd->tmp_serv_plist, TRUE);
    - yd->tmp_serv_plist = NULL;
    -
    - }
    - /* Now that we've got the list, request aliases */
    - yahoo_fetch_aliases(gc);
    -}
    -
    -/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */
    -static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type)
    -{
    - PurpleAccount *account;
    - char *msg = NULL;
    - char *from = NULL;
    - char *stat = NULL;
    - char *game = NULL;
    - YahooFriend *f = NULL;
    - GSList *l = pkt->hash;
    - gint val_11 = 0;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    -
    - account = purple_connection_get_account(gc);
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    - if (pair->key == 4 || pair->key == 1) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - from = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_notify "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - } else if (pair->key == 49) {
    - msg = pair->value;
    - } else if (pair->key == 13) {
    - stat = pair->value;
    - } else if (pair->key == 14) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - game = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_notify "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - } else if (pair->key == 11) {
    - val_11 = strtol(pair->value, NULL, 10);
    - }
    - l = l->next;
    - }
    -
    - if (!from || !msg)
    - return;
    -
    - /* disconnect the peer if connected through p2p and sends wrong value for session id */
    - if ((pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id)) {
    - purple_debug_warning("yahoo","p2p: %s sent us notify with wrong session id. Disconnecting p2p connection to peer\n", from);
    - /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
    - g_hash_table_remove(yd->peers, from);
    - return;
    - }
    -
    - if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING"))
    - && (purple_account_privacy_check(account, from)))
    - {
    -
    - if (stat && *stat == '1')
    - purple_serv_got_typing(gc, from, 0, PURPLE_IM_TYPING);
    - else
    - purple_serv_got_typing_stopped(gc, from);
    -
    - } else if (!g_ascii_strncasecmp(msg, "GAME", strlen("GAME"))) {
    - PurpleBuddy *bud = purple_blist_find_buddy(account, from);
    -
    - if (!bud) {
    - purple_debug_warning("yahoo",
    - "%s is playing a game, and doesn't want you to know.\n", from);
    - }
    -
    - f = yahoo_friend_find(gc, from);
    - if (!f)
    - return; /* if they're not on the list, don't bother */
    -
    - yahoo_friend_set_game(f, NULL);
    -
    - if (stat && *stat == '1') {
    - yahoo_friend_set_game(f, game);
    - if (bud)
    - yahoo_update_status(gc, from, f);
    - }
    - } else if (!g_ascii_strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {
    - PurpleIMConversation *im = purple_conversations_find_im_with_account(from, account);
    - char *buf = g_strdup_printf(_("%s has sent you a webcam invite, which is not yet supported."), from);
    - purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
    - buf, PURPLE_MESSAGE_NOTIFY);
    - g_free(buf);
    - }
    -}
    -
    -
    -struct _yahoo_im {
    - char *from;
    - char *active_id;
    - int time;
    - int utf8;
    - int buddy_icon;
    - char *id;
    - char *msg;
    -};
    -
    -static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - PurpleAccount *account;
    - GSList *l = pkt->hash;
    - struct _yahoo_im *sms = NULL;
    - YahooData *yd;
    - char *server_msg = NULL;
    - char *m;
    -
    - yd = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - while (l != NULL) {
    - struct yahoo_pair *pair = l->data;
    - if (pair->key == 4) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - sms = g_new0(struct _yahoo_im, 1);
    - sms->from = g_strdup_printf("+%s", pair->value);
    - sms->time = time(NULL);
    - sms->utf8 = TRUE;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_sms_message "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - } else if (pair->key == 14) {
    - if (sms)
    - sms->msg = pair->value;
    - } else if (pair->key == 68) {
    - if(sms)
    - g_hash_table_insert(yd->sms_carrier, g_strdup(sms->from), g_strdup(pair->value));
    - } else if (pair->key == 16) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - server_msg = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_sms_message "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - }
    - l = l->next;
    - }
    -
    - if(!sms) {
    - purple_debug_info("yahoo", "Received a malformed SMS packet!\n");
    - return;
    - }
    -
    - if ((int)pkt->status < 0)
    - pkt->status = YAHOO_STATUS_DISCONNECTED;
    - if (pkt->status == YAHOO_STATUS_DISCONNECTED) {
    - if (server_msg) {
    - PurpleIMConversation *im;
    - im = purple_conversations_find_im_with_account(sms->from, account);
    - if (im == NULL)
    - im = purple_im_conversation_new(account, sms->from);
    - purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
    - server_msg, 0);
    - }
    - else {
    - purple_notify_error(gc, NULL,
    - _("Your SMS was not delivered"), NULL,
    - purple_request_cpar_from_connection(gc));
    - }
    -
    - g_free(sms->from);
    - g_free(sms);
    - return ;
    - }
    -
    - if (!sms->from || !sms->msg) {
    - g_free(sms);
    - return;
    - }
    -
    - m = yahoo_string_decode(gc, sms->msg, sms->utf8);
    - purple_serv_got_im(gc, sms->from, m, 0, sms->time);
    -
    - g_free(m);
    - g_free(sms->from);
    - g_free(sms);
    -}
    -
    -/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */
    -static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type)
    -{
    - PurpleAccount *account;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - GSList *l = pkt->hash;
    - GSList *list = NULL;
    - struct _yahoo_im *im = NULL;
    -
    - account = purple_connection_get_account(gc);
    -
    - if (pkt->status <= 1 || pkt->status == 5 || pkt->status == YAHOO_STATUS_OFFLINE) {
    - /* messages are received with status YAHOO_STATUS_OFFLINE in case of p2p */
    - while (l != NULL) {
    - struct yahoo_pair *pair = l->data;
    - if (pair->key == 4 || pair->key == 1) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - im = g_new0(struct _yahoo_im, 1);
    - list = g_slist_append(list, im);
    - im->from = pair->value;
    - im->time = time(NULL);
    - im->utf8 = TRUE;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_message "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - } else if (im && pair->key == 5) {
    - im->active_id = pair->value;
    - } else if (pair->key == 97) {
    - if (im)
    - im->utf8 = strtol(pair->value, NULL, 10);
    - } else if (pair->key == 15) {
    - if (im)
    - im->time = strtol(pair->value, NULL, 10);
    - } else if (pair->key == 206) {
    - if (im)
    - im->buddy_icon = strtol(pair->value, NULL, 10);
    - } else if (pair->key == 14) {
    - if (im)
    - im->msg = pair->value;
    - } else if (im && (pair->key == 11)) {
    - /* peer session id */
    - /* disconnect the peer if connected through p2p and sends wrong value for session id */
    - if( (pkt_type == YAHOO_PKT_TYPE_P2P)
    - && (yd->session_id != strtol(pair->value, NULL, 10)) )
    - {
    - purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->from);
    - /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
    - g_hash_table_remove(yd->peers, im->from);
    - g_free(im);
    - return; /* Not sure whether we should process remaining IMs in this packet */
    - }
    -
    - } else if (im && pair->key == 63 && g_utf8_validate(pair->value, -1, NULL)) {
    - /* IMV key */
    - /* Check for the Doodle IMV */
    - if (im->from != NULL)
    - {
    - g_hash_table_replace(yd->imvironments, g_strdup(im->from), g_strdup(pair->value));
    -
    - if (strstr(pair->value, "doodle;") != NULL)
    - {
    - PurpleWhiteboard *wb;
    -
    - if (!purple_account_privacy_check(account, im->from)) {
    - purple_debug_info("yahoo", "Doodle request from %s dropped.\n",
    - im->from);
    - g_free(im);
    - return;
    - }
    - /* I'm not sure the following ever happens -DAA */
    - wb = purple_whiteboard_get_session(account, im->from);
    -
    - /* If a Doodle session doesn't exist between this user */
    - if(wb == NULL)
    - {
    - doodle_session *ds;
    - wb = purple_whiteboard_new(account, im->from,
    - DOODLE_STATE_REQUESTED);
    - ds = purple_whiteboard_get_protocol_data(wb);
    - ds->imv_key = g_strdup(pair->value);
    -
    - yahoo_doodle_command_send_request(gc, im->from, pair->value);
    - yahoo_doodle_command_send_ready(gc, im->from, pair->value);
    - }
    - }
    - }
    - } else if (pair->key == 429) {
    - if (im)
    - im->id = pair->value;
    - }
    - l = l->next;
    - }
    - } else if (pkt->status == 2) {
    - purple_notify_error(gc, NULL,
    - _("Your Yahoo! message did not get sent."), NULL,
    - purple_request_cpar_from_connection(gc));
    - }
    -
    - for (l = list; l; l = l->next) {
    - char *m, *m2;
    - im = l->data;
    -
    - if (!im->msg) {
    - g_free(im);
    - continue;
    - }
    -
    - if (!purple_account_privacy_check(account, im->from)) {
    - purple_debug_info("yahoo", "Message from %s dropped.\n", im->from);
    - return;
    - }
    -
    - /*
    - * TODO: Is there anything else we should check when determining whether
    - * we should send an acknowledgement?
    - */
    - if (im->id != NULL) {
    - /* Send acknowledgement. If we don't do this then the official
    - * Yahoo Messenger client for Windows will send us the same
    - * message 7 seconds later as an offline message. This is true
    - * for at least version 9.0.0.2162 on Windows XP. */
    - struct yahoo_packet *pkt2;
    - pkt2 = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_ACK,
    - YAHOO_STATUS_AVAILABLE, pkt->id);
    - yahoo_packet_hash(pkt2, "ssisii",
    - 1, im->active_id, /* May not always be the connection's display name */
    - 5, im->from,
    - 302, 430,
    - 430, im->id,
    - 303, 430,
    - 450, 0);
    - yahoo_packet_send_and_free(pkt2, yd);
    - }
    -
    - m = yahoo_string_decode(gc, im->msg, im->utf8);
    - /* This may actually not be necessary, but it appears
    - * that at least at one point some clients were sending
    - * "\r\n" as line delimiters, so we want to avoid double
    - * lines. */
    - m2 = purple_strreplace(m, "\r\n", "\n");
    - g_free(m);
    - m = m2;
    - purple_util_chrreplace(m, '\r', '\n');
    - if (!strcmp(m, "<ding>")) {
    - char *username;
    -
    - username = g_markup_escape_text(im->from, -1);
    - purple_protocol_got_attention(gc, username, YAHOO_BUZZ);
    - g_free(username);
    - g_free(m);
    - g_free(im);
    - continue;
    - }
    -
    - m2 = yahoo_codes_to_html(m);
    - g_free(m);
    -
    - purple_serv_got_im(gc, im->from, m2, 0, im->time);
    - g_free(m2);
    - g_free(im);
    - }
    -
    - g_slist_free(list);
    -}
    -
    -static void yahoo_process_sysmessage(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - GSList *l = pkt->hash;
    - char *prim, *me = NULL, *msg = NULL;
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - if (pair->key == 5) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - me = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_sysmessage "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - } else if (pair->key == 14) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - msg = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_sysmessage "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - }
    -
    - l = l->next;
    - }
    -
    - if (!msg || !g_utf8_validate(msg, -1, NULL))
    - return;
    -
    - prim = g_strdup_printf(_("Yahoo! system message for %s:"),
    - me?me:purple_connection_get_display_name(gc));
    - purple_notify_info(NULL, NULL, prim, msg,
    - purple_request_cpar_from_connection(gc));
    - g_free(prim);
    -}
    -
    -struct yahoo_add_request {
    - PurpleConnection *gc;
    - char *id;
    - char *who;
    -};
    -
    -static void
    -yahoo_buddy_add_authorize_cb(const char *message, gpointer data)
    -{
    - struct yahoo_add_request *add_req = data;
    - struct yahoo_packet *pkt;
    - YahooData *yd = purple_connection_get_protocol_data(add_req->gc);
    - const char *who = add_req->who;
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash(pkt, "ssii",
    - 1, add_req->id,
    - 5, who,
    - 13, 1,
    - 334, 0);
    -
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - g_free(add_req->id);
    - g_free(add_req->who);
    - g_free(add_req);
    -}
    -
    -static void
    -yahoo_buddy_add_deny_cb(const char *msg, gpointer data)
    -{
    - struct yahoo_add_request *add_req = data;
    - YahooData *yd = purple_connection_get_protocol_data(add_req->gc);
    - struct yahoo_packet *pkt;
    - char *encoded_msg = NULL;
    - const char *who = add_req->who;
    -
    - if (msg && *msg)
    - encoded_msg = yahoo_string_encode(add_req->gc, msg, FALSE);
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15,
    - YAHOO_STATUS_AVAILABLE, yd->session_id);
    -
    - yahoo_packet_hash(pkt, "ssiiis",
    - 1, add_req->id,
    - 5, who,
    - 13, 2,
    - 334, 0,
    - 97, 1, /* UTF-8 */
    - 14, encoded_msg ? encoded_msg : "");
    -
    -
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - g_free(encoded_msg);
    -
    - g_free(add_req->id);
    - g_free(add_req->who);
    - g_free(add_req);
    -}
    -
    -static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason)
    -{
    - char *notify_msg;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    -
    - if (who == NULL)
    - return;
    -
    - if (reason != NULL) {
    - char *msg2 = yahoo_string_decode(gc, reason, FALSE);
    - notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2);
    - g_free(msg2);
    - } else
    - notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list."), who);
    -
    - purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg,
    - purple_request_cpar_from_connection(gc));
    - g_free(notify_msg);
    -
    - g_hash_table_remove(yd->friends, who);
    - purple_protocol_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */
    - /* TODO: Shouldn't we remove the buddy from our local list? */
    -}
    -
    -static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *pkt) {
    - PurpleAccount *account;
    - GSList *l = pkt->hash;
    - const char *msg = NULL;
    -
    - account = purple_connection_get_account(gc);
    -
    - /* Buddy authorized/declined our addition */
    - if (pkt->status == 1) {
    - char *who = NULL;
    - int response = 0;
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - switch (pair->key) {
    - case 4:
    - break;
    - case 13:
    - response = strtol(pair->value, NULL, 10);
    - break;
    - case 14:
    - msg = pair->value;
    - break;
    - }
    - l = l->next;
    - }
    -
    - if (response == 1) /* Authorized */
    - purple_debug_info("yahoo", "Received authorization from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
    - else if (response == 2) { /* Declined */
    - purple_debug_info("yahoo", "Received authorization decline from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
    - yahoo_buddy_denied_our_add(gc, who, msg);
    - } else
    - purple_debug_error("yahoo", "Received unknown authorization response of %d from buddy '%s'.\n", response, who ? who : "(Unknown Buddy)");
    - g_free(who);
    - }
    - /* Buddy requested authorization to add us. */
    - else if (pkt->status == 3) {
    - struct yahoo_add_request *add_req;
    - const char *firstname = NULL, *lastname = NULL;
    -
    - add_req = g_new0(struct yahoo_add_request, 1);
    - add_req->gc = gc;
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - switch (pair->key) {
    - case 4:
    - break;
    - case 5:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - add_req->id = g_strdup(pair->value);
    - } else {
    - purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 14:
    - msg = pair->value;
    - break;
    - case 216:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - firstname = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 254:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - lastname = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    -
    - }
    - l = l->next;
    - }
    -
    - if (add_req->id && add_req->who) {
    - char *alias = NULL, *dec_msg = NULL;
    -
    - if (!purple_account_privacy_check(account, add_req->who))
    - {
    - purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
    - add_req->who);
    - yahoo_buddy_add_deny_cb(NULL, add_req);
    - return;
    - }
    -
    - if (msg)
    - dec_msg = yahoo_string_decode(gc, msg, FALSE);
    -
    - if (firstname && lastname)
    - alias = g_strdup_printf("%s %s", firstname, lastname);
    - else if (firstname)
    - alias = g_strdup(firstname);
    - else if (lastname)
    - alias = g_strdup(lastname);
    -
    - /* DONE! this is almost exactly the same as what MSN does,
    - * this should probably be moved to the core.
    - */
    - purple_account_request_authorization(account, add_req->who, add_req->id,
    - alias, dec_msg,
    - purple_blist_find_buddy(account, add_req->who) != NULL,
    - yahoo_buddy_add_authorize_cb,
    - yahoo_buddy_add_deny_cb,
    - add_req);
    - g_free(alias);
    - g_free(dec_msg);
    - } else {
    - g_free(add_req->id);
    - g_free(add_req->who);
    - g_free(add_req);
    - }
    - } else {
    - purple_debug_error("yahoo", "Received authorization of unknown status (%d).\n", pkt->status);
    - }
    -}
    -
    -/* I don't think this happens anymore in Version 15 */
    -static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt) {
    - PurpleAccount *account;
    - struct yahoo_add_request *add_req;
    - char *msg = NULL;
    - GSList *l = pkt->hash;
    -
    - account = purple_connection_get_account(gc);
    -
    - add_req = g_new0(struct yahoo_add_request, 1);
    - add_req->gc = gc;
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - switch (pair->key) {
    - case 1:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - add_req->id = g_strdup(pair->value);
    - } else {
    - purple_debug_warning("yahoo", "yahoo_buddy_added_us "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 3:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - add_req->who = g_strdup(pair->value);
    - } else {
    - purple_debug_warning("yahoo", "yahoo_buddy_added_us "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 15: /* time, for when they add us and we're offline */
    - break;
    - case 14:
    - msg = pair->value;
    - break;
    - }
    - l = l->next;
    - }
    -
    - if (add_req->id && add_req->who) {
    - char *dec_msg = NULL;
    -
    - if (!purple_account_privacy_check(account, add_req->who)) {
    - purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
    - add_req->who);
    - yahoo_buddy_add_deny_cb(NULL, add_req);
    - return;
    - }
    -
    - if (msg)
    - dec_msg = yahoo_string_decode(gc, msg, FALSE);
    -
    - /* DONE! this is almost exactly the same as what MSN does,
    - * this should probably be moved to the core.
    - */
    - purple_account_request_authorization(account, add_req->who, add_req->id,
    - NULL, dec_msg,
    - purple_blist_find_buddy(account,add_req->who) != NULL,
    - yahoo_buddy_add_authorize_cb,
    - yahoo_buddy_add_deny_cb, add_req);
    - g_free(dec_msg);
    - } else {
    - g_free(add_req->id);
    - g_free(add_req->who);
    - g_free(add_req);
    - }
    -}
    -
    -/* I have no idea if this every gets called in version 15 */
    -static void yahoo_buddy_denied_our_add_old(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - char *who = NULL;
    - char *msg = NULL;
    - GSList *l = pkt->hash;
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - switch (pair->key) {
    - case 3:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - who = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_buddy_denied_our_add_old "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 14:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - msg = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_buddy_denied_our_add_old "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - }
    - l = l->next;
    - }
    -
    - yahoo_buddy_denied_our_add(gc, who, msg);
    -}
    -
    -static void yahoo_process_contact(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - switch (pkt->status) {
    - case 1:
    - yahoo_process_status(gc, pkt);
    - return;
    - case 3:
    - yahoo_buddy_added_us(gc, pkt);
    - break;
    - case 7:
    - yahoo_buddy_denied_our_add_old(gc, pkt);
    - break;
    - default:
    - break;
    - }
    -}
    -
    -#define OUT_CHARSET "utf-8"
    -
    -static char *yahoo_decode(const char *text)
    -{
    - char *converted = NULL;
    - char *n, *new;
    - const char *end, *p;
    - int i, k;
    -
    - n = new = g_malloc(strlen (text) + 1);
    - end = text + strlen(text);
    -
    - for (p = text; p < end; p++, n++) {
    - if (*p == '\\') {
    - if (p[1] >= '0' && p[1] <= '7') {
    - p += 1;
    - for (i = 0, k = 0; k < 3; k += 1) {
    - char c = p[k];
    - if (c < '0' || c > '7') break;
    - i *= 8;
    - i += c - '0';
    - }
    - *n = i;
    - p += k - 1;
    - } else { /* bug 959248 */
    - /* If we see a \ not followed by an octal number,
    - * it means that it is actually a \\ with one \
    - * already eaten by some unknown function.
    - * This is arguably broken.
    - *
    - * I think wing is wrong here, there is no function
    - * called that I see that could have done it. I guess
    - * it is just really sending single \'s. That's yahoo
    - * for you.
    - */
    - *n = *p;
    - }
    - }
    - else
    - *n = *p;
    - }
    -
    - *n = '\0';
    -
    - /* XXX: Is this related to Yahoo! Japan? If so, it should be removed. -mmcco */
    - if (strstr(text, "\033$B"))
    - converted = g_convert(new, n - new, OUT_CHARSET, "iso-2022-jp", NULL, NULL, NULL);
    - if (!converted)
    - converted = g_convert(new, n - new, OUT_CHARSET, "iso-8859-1", NULL, NULL, NULL);
    - g_free(new);
    -
    - return converted;
    -}
    -
    -static void yahoo_process_mail(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - const char *who = NULL;
    - const char *email = NULL;
    - const char *subj = NULL;
    - const char *yahoo_mail_url = YAHOO_MAIL_URL;
    - int count = 0;
    - GSList *l = pkt->hash;
    -
    - if (!purple_account_get_check_mail(account))
    - return;
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    - if (pair->key == 9) {
    - count = strtol(pair->value, NULL, 10);
    - } else if (pair->key == 43) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - who = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_mail "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - } else if (pair->key == 42) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - email = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_mail "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - } else if (pair->key == 18) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - subj = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_mail "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - }
    - l = l->next;
    - }
    -
    - if (who && subj && email && *email) {
    - char *dec_who = yahoo_decode(who);
    - char *dec_subj = yahoo_decode(subj);
    - char *from = g_strdup_printf("%s (%s)", dec_who, email);
    -
    - purple_notify_email(gc, dec_subj, from, purple_account_get_username(account),
    - yahoo_mail_url, NULL, NULL);
    -
    - g_free(dec_who);
    - g_free(dec_subj);
    - g_free(from);
    - } else if (count > 0) {
    - const char *tos[2] = { purple_account_get_username(account) };
    - const char *urls[2] = { yahoo_mail_url };
    -
    - purple_notify_emails(gc, count, FALSE, NULL, NULL, tos, urls,
    - NULL, NULL);
    - }
    -}
    -
    -/* We use this structure once while we authenticate */
    -struct yahoo_auth_data
    -{
    - PurpleConnection *gc;
    - char *seed;
    -};
    -
    -/* This is the y64 alphabet... it's like base64, but has a . and a _ */
    -static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
    -
    -/* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function
    - * in util.c, but it is different from the one yahoo uses */
    -static void to_y64(char *out, const unsigned char *in, gsize inlen)
    - /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
    -{
    - for (; inlen >= 3; inlen -= 3)
    - {
    - *out++ = base64digits[in[0] >> 2];
    - *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
    - *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
    - *out++ = base64digits[in[2] & 0x3f];
    - in += 3;
    - }
    - if (inlen > 0)
    - {
    - unsigned char fragment;
    -
    - *out++ = base64digits[in[0] >> 2];
    - fragment = (in[0] << 4) & 0x30;
    - if (inlen > 1)
    - fragment |= in[1] >> 4;
    - *out++ = base64digits[fragment];
    - *out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c];
    - *out++ = '-';
    - }
    - *out = '\0';
    -}
    -
    -static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - const char *name = purple_normalize(account, purple_account_get_username(account));
    - PurpleHash *md5_hash;
    - guchar md5_digest[16];
    - gchar base64_string[25];
    - struct yahoo_packet *pkt;
    -
    - purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage3\n");
    -
    - g_return_if_fail(crypt != NULL);
    -
    - md5_hash = purple_md5_hash_new();
    - purple_hash_append(md5_hash, (guchar *)crypt, strlen(crypt));
    - purple_hash_digest(md5_hash, md5_digest, sizeof(md5_digest));
    -
    - to_y64(base64_string, md5_digest, 16);
    -
    - purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status);
    - pkt = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->current_status, yd->session_id);
    -
    - if(yd->cookie_b) { /* send B cookie if we have it */
    - yahoo_packet_hash(pkt, "ssssssssss",
    - 1, name,
    - 0, name,
    - 277, yd->cookie_y,
    - 278, yd->cookie_t,
    - 307, base64_string,
    - 244, YAHOO_CLIENT_VERSION_ID,
    - 2, name,
    - 2, "1",
    - 59, yd->cookie_b,
    - 98, purple_account_get_string(account, "room_list_locale", "us"),
    - 135, YAHOO_CLIENT_VERSION);
    - } else { /* don't try to send an empty B cookie - the server will be mad */
    - yahoo_packet_hash(pkt, "sssssssss",
    - 1, name,
    - 0, name,
    - 277, yd->cookie_y,
    - 278, yd->cookie_t,
    - 307, base64_string,
    - 244, YAHOO_CLIENT_VERSION_ID,
    - 2, name,
    - 2, "1",
    - 98, purple_account_get_string(account, "room_list_locale", "us"),
    - 135, YAHOO_CLIENT_VERSION);
    - }
    -
    - if (yd->picture_checksum)
    - yahoo_packet_hash_int(pkt, 192, yd->picture_checksum);
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - g_object_unref(md5_hash);
    -}
    -
    -static void yahoo_auth16_stage2(PurpleHttpConnection *http_conn,
    - PurpleHttpResponse *response, gpointer _auth_data)
    -{
    - struct yahoo_auth_data *auth_data = _auth_data;
    - PurpleConnection *gc = auth_data->gc;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    -
    - int i;
    - gchar **splits;
    - int response_no = -1;
    - char *crumb = NULL;
    - char *crypt = NULL;
    - PurpleHttpCookieJar *cookiejar;
    -
    - purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n");
    -
    - if (!purple_http_response_is_successful(response)) {
    - const gchar *error_message = purple_http_response_get_error(response);
    - purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message);
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
    - g_free(auth_data->seed);
    - g_free(auth_data);
    - return;
    - }
    -
    - splits = g_strsplit(purple_http_response_get_data(response, NULL),
    - "\r\n", -1);
    -
    - cookiejar = purple_http_conn_get_cookie_jar(http_conn);
    - yd->cookie_b = purple_http_cookie_jar_get(cookiejar, "B");
    - yd->cookie_t = purple_http_cookie_jar_get(cookiejar, "T");
    - yd->cookie_y = purple_http_cookie_jar_get(cookiejar, "Y");
    -
    - i = 0;
    - while (splits[i]) {
    - /* I'm not exactly a fan of the magic numbers, but it's obvious,
    - * so no sense in wasting a bajillion vars or calls to strlen */
    -
    - if (i == 0 && g_ascii_isdigit(splits[i][0])) {
    - response_no = strtol(splits[i], NULL, 10);
    - purple_debug_info("yahoo", "Got auth16 stage 2 response code: %d\n",
    - response_no);
    - } else if (strncmp(splits[i], "crumb=", 6) == 0) {
    - crumb = g_strdup(&splits[i][6]);
    -
    - if (purple_debug_is_unsafe())
    - purple_debug_info("yahoo", "Got crumb: %s\n", crumb);
    - }
    - i++;
    - }
    -
    - g_strfreev(splits);
    -
    - if (crumb == NULL)
    - response_no = -1;
    -
    - if(response_no != 0) {
    - /* Some error in the login process */
    - PurpleConnectionError error;
    - char *error_reason = NULL;
    -
    - switch (response_no) {
    - case -1:
    - /* Some error in the received stream */
    - error_reason = g_strdup(_("Received invalid data"));
    - error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
    - break;
    - case 100:
    - /* Unknown error */
    - error_reason = g_strdup(_("Unknown error"));
    - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
    - break;
    - default:
    - /* if we have everything we need, why not try to login irrespective of response */
    - if ((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) {
    -#if 0
    - try_login_on_error = TRUE;
    -#endif
    - break;
    - }
    - error_reason = g_strdup(_("Unknown error"));
    - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
    - break;
    - }
    - if (error_reason) {
    - purple_debug_error("yahoo", "Authentication error: %s. "
    - "Code %d\n", error_reason, response_no);
    - purple_connection_error(gc, error, error_reason);
    - g_free(error_reason);
    - g_free(crumb);
    - g_free(auth_data->seed);
    - g_free(auth_data);
    - return;
    - }
    - }
    -
    - crypt = g_strconcat(crumb, auth_data->seed, NULL);
    - yahoo_auth16_stage3(gc, crypt);
    - g_free(crypt);
    - g_free(crumb);
    - g_free(auth_data->seed);
    - g_free(auth_data);
    -}
    -
    -static void yahoo_auth16_stage1_cb(PurpleHttpConnection *http_conn,
    - PurpleHttpResponse *response, gpointer _auth_data)
    -{
    - struct yahoo_auth_data *auth_data = _auth_data;
    - PurpleConnection *gc = auth_data->gc;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    -
    - purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n");
    -
    - if (!purple_http_response_is_successful(response)) {
    - const gchar *error_message = purple_http_response_get_error(response);
    - purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message);
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
    - g_free(auth_data->seed);
    - g_free(auth_data);
    - return;
    - } else {
    - PurpleAccount *account = purple_connection_get_account(gc);
    - gchar **split_data = g_strsplit(purple_http_response_get_data(
    - response, NULL), "\r\n", -1);
    - int totalelements = 0;
    - int response_no = -1;
    - char *token = NULL;
    -
    - totalelements = g_strv_length(split_data);
    -
    - if(totalelements == 1) { /* Received an error code */
    - response_no = strtol(split_data[0], NULL, 10);
    - } else if(totalelements == 2 || totalelements == 3 ) { /* received valid data */
    - response_no = strtol(split_data[0], NULL, 10);
    - token = g_strdup(split_data[1] + strlen("ymsgr="));
    - } else { /* It looks like a transparent proxy has returned a document we don't want */
    - response_no = -1;
    - }
    -
    - g_strfreev(split_data);
    -
    - if(response_no != 0) {
    - /* Some error in the login process */
    - PurpleConnectionError error;
    - char *error_reason;
    -
    - switch(response_no) {
    - case -1:
    - /* Some error in the received stream */
    - error_reason = g_strdup(_("Received invalid data"));
    - error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
    - break;
    - case 1212:
    - /* Password incorrect */
    - /* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */
    - if (!purple_account_get_remember_password(account))
    - purple_account_set_password(account, NULL, NULL, NULL);
    - error_reason = g_strdup(_("Incorrect password"));
    - error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
    - break;
    - case 1213:
    - /* security lock from too many failed login attempts */
    - error_reason = g_strdup(_("Account locked: Too many failed login "
    - "attempts. Logging into the Yahoo! website may fix this."));
    - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
    - break;
    - case 1235:
    - /* the username does not exist */
    - error_reason = g_strdup(_("Username does not exist"));
    - error = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
    - break;
    - case 1214:
    - /* indicates a lock of some description */
    - error_reason = g_strdup(_("Account locked: Unknown reason. Logging "
    - "into the Yahoo! website may fix this."));
    - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
    - break;
    - case 1236:
    - /* indicates a lock due to logging in too frequently */
    - error_reason = g_strdup(_("Account locked: You have been logging in too "
    - "frequently. Wait a few minutes before trying to connect "
    - "again. Logging into the Yahoo! website may help."));
    - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
    - break;
    - case 100:
    - /* username or password missing */
    - error_reason = g_strdup(_("Username or password missing"));
    - error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
    - break;
    - default:
    - /* Unknown error! */
    - error_reason = g_strdup_printf(_("Unknown error (%d)"), response_no);
    - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
    - break;
    - }
    - purple_debug_error("yahoo", "Authentication error: %s. Code %d\n",
    - error_reason, response_no);
    - purple_connection_error(gc, error, error_reason);
    - g_free(error_reason);
    - g_free(auth_data->seed);
    - g_free(auth_data);
    - g_free(token);
    - }
    - else {
    - PurpleHttpRequest *req;
    -
    - req = purple_http_request_new(NULL);
    - purple_http_request_set_url_printf(req, YAHOO_LOGIN_URL, token);
    - purple_http_request_header_set(req, "User-Agent",
    - YAHOO_CLIENT_USERAGENT);
    - purple_http_connection_set_add(yd->http_reqs,
    - purple_http_request(gc, req,
    - yahoo_auth16_stage2, auth_data));
    - purple_http_request_unref(req);
    -
    - g_free(token);
    - }
    - }
    -}
    -
    -static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - PurpleHttpRequest *req;
    - struct yahoo_auth_data *auth_data = NULL;
    - char *encoded_username;
    - char *encoded_password;
    -
    - purple_debug_info("yahoo", "Authentication: In yahoo_auth16_stage1\n");
    -
    - auth_data = g_new0(struct yahoo_auth_data, 1);
    - auth_data->gc = gc;
    - auth_data->seed = g_strdup(seed);
    -
    - encoded_username = g_strdup(purple_url_encode(purple_account_get_username(purple_connection_get_account(gc))));
    - encoded_password = g_strdup(purple_url_encode(purple_connection_get_password(gc)));
    -
    - req = purple_http_request_new(NULL);
    - purple_http_request_set_url_printf(req, YAHOO_TOKEN_URL,
    - encoded_username, encoded_password, purple_url_encode(seed));
    - purple_http_request_header_set(req, "User-Agent", YAHOO_CLIENT_USERAGENT);
    - purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
    - req, yahoo_auth16_stage1_cb, auth_data));
    - purple_http_request_unref(req);
    -
    - purple_str_wipe(encoded_password);
    - g_free(encoded_username);
    -}
    -
    -static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - char *seed = NULL;
    - GSList *l = pkt->hash;
    - int m = 0;
    - gchar *buf;
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    - /* (pair->key == 1) -> sn */
    - if (pair->key == 94) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - seed = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_auth "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - } else if (pair->key == 13) {
    - m = atoi(pair->value);
    - }
    - l = l->next;
    - }
    -
    - if (seed) {
    - switch (m) {
    - case 0:
    - /* used to be for really old auth routine, dont support now */
    - case 1:
    - case 2: /* Yahoo ver 16 authentication */
    - yahoo_auth16_stage1(gc, seed);
    - break;
    - default:
    - {
    - GHashTable *ui_info = purple_core_get_ui_info();
    -
    - buf = g_strdup_printf(_("The Yahoo server has requested the use of an unrecognized "
    - "authentication method. You will probably not be able "
    - "to successfully sign on to Yahoo. Check %s for updates."),
    - ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE));
    - purple_notify_error(gc, "",
    - _("Failed Yahoo! Authentication"), buf,
    - purple_request_cpar_from_connection(gc));
    - g_free(buf);
    - yahoo_auth16_stage1(gc, seed); /* Can't hurt to try it anyway. */
    - break;
    - }
    - }
    - }
    -}
    -
    -static void ignore_buddy(PurpleBuddy *buddy) {
    - PurpleGroup *group;
    - PurpleAccount *account;
    - gchar *name;
    -
    - if (!buddy)
    - return;
    -
    - group = purple_buddy_get_group(buddy);
    - name = g_strdup(purple_buddy_get_name(buddy));
    - account = purple_buddy_get_account(buddy);
    -
    - purple_debug_info("yahoo", "blist: Removing '%s' from buddy list.\n", name);
    - purple_account_remove_buddy(account, buddy, group);
    - purple_blist_remove_buddy(buddy);
    -
    - purple_serv_add_deny(purple_account_get_connection(account), name);
    -
    - g_free(name);
    -}
    -
    -static void keep_buddy(PurpleBuddy *b)
    -{
    - purple_account_privacy_deny_remove(purple_buddy_get_account(b),
    - purple_buddy_get_name(b), 1);
    -}
    -
    -static void yahoo_process_ignore(PurpleConnection *gc, struct yahoo_packet *pkt) {
    - PurpleBuddy *b;
    - GSList *l;
    - gchar *who = NULL;
    - gchar buf[BUF_LONG];
    - gboolean ignore = TRUE;
    - gint status = 0;
    -
    - for (l = pkt->hash; l; l = l->next) {
    - struct yahoo_pair *pair = l->data;
    - switch (pair->key) {
    - case 0:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - who = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_ignore "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - /* 1 -> me */
    - case 13:
    - /* 1 == ignore, 2 == unignore */
    - ignore = (strtol(pair->value, NULL, 10) == 1);
    - break;
    - case 66:
    - status = strtol(pair->value, NULL, 10);
    - break;
    - default:
    - break;
    - }
    - }
    -
    - /*
    - * status
    - * 0 - ok
    - * 2 - already in ignore list, could not add
    - * 3 - not in ignore list, could not delete
    - * 12 - is a buddy, could not add (and possibly also a not-in-ignore list condition?)
    - */
    - switch (status) {
    - case 12:
    - purple_debug_info("yahoo", "Server reported \"is a buddy\" for %s while %s",
    - who, (ignore ? "ignoring" : "unignoring"));
    -
    - if (ignore) {
    - b = purple_blist_find_buddy(purple_connection_get_account(gc), who);
    - g_snprintf(buf, sizeof(buf), _("You have tried to ignore %s, but the "
    - "user is on your buddy list. Clicking \"Yes\" "
    - "will remove and ignore the buddy."), who);
    - purple_request_yes_no(gc, NULL,
    - _("Ignore buddy?"), buf, 0,
    - purple_request_cpar_from_connection(gc),
    - b, G_CALLBACK(ignore_buddy),
    - G_CALLBACK(keep_buddy));
    - break;
    - }
    - case 2:
    - purple_debug_info("yahoo", "Server reported that %s is already in the ignore list.\n",
    - who);
    - break;
    - case 3:
    - purple_debug_info("yahoo", "Server reported that %s is not in the ignore list; could not delete\n",
    - who);
    - case 0:
    - default:
    - break;
    - }
    -}
    -
    -#if TRY_WEBMESSENGER_LOGIN
    -
    -static gboolean
    -yahoo_try_webmessenger_login(PurpleConnection *gc)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - PurpleHttpRequest *req;
    -
    - if (yd->wm)
    - return FALSE;
    -
    - yd->wm = TRUE;
    - if (yd->fd >= 0)
    - close(yd->fd);
    - if (yd->inpa) {
    - purple_input_remove(yd->inpa);
    - yd->inpa = 0;
    - }
    -
    - req = purple_http_request_new(WEBMESSENGER_URL);
    - purple_http_request_header_set(req, "User-Agent", "Purple/" VERSION);
    - purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
    - req, yahoo_login_page_cb, NULL));
    - purple_http_request_unref(req);
    -
    - return TRUE;
    -}
    -
    -#endif /* TRY_WEBMESSENGER_LOGIN */
    -
    -static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - GSList *l = pkt->hash;
    - int err = 0;
    - char *msg;
    - char *url = NULL;
    - char *fullmsg;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - if (pair->key == 66) {
    - err = strtol(pair->value, NULL, 10);
    - } else if (pair->key == 20) {
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - url = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_authresp "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - }
    -
    - l = l->next;
    - }
    -
    - switch (err) {
    - case 0:
    - msg = g_strdup(_("Unknown error"));
    - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
    - break;
    - case 3:
    - msg = g_strdup(_("Username does not exist"));
    - reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
    - break;
    - case 13:
    -#if TRY_WEBMESSENGER_LOGIN
    - if (yahoo_try_webmessenger_login(gc))
    - return;
    -#else
    - purple_debug_info("yahoo", "Web messenger login is disabled\n");
    -#endif /* TRY_WEBMESSENGER_LOGIN */
    - if (!purple_account_get_remember_password(account))
    - purple_account_set_password(account, NULL, NULL, NULL);
    -
    - msg = g_strdup(_("Invalid username or password"));
    - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
    - break;
    - case 14:
    - msg = g_strdup(_("Your account has been locked due to too many failed login attempts."
    - " Please try logging into the Yahoo! website."));
    - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
    - break;
    - case 52:
    - /* See #9660. As much as we know, reconnecting shouldn't hurt */
    - purple_debug_info("yahoo", "Got error 52, Set to autoreconnect\n");
    - msg = g_strdup(_("Unknown error 52. Reconnecting should fix this."));
    - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
    - break;
    - case 1013:
    - msg = g_strdup(_("Error 1013: The username you have entered is invalid."
    - " The most common cause of this error is entering your email"
    - " address instead of your Yahoo! ID."));
    - reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
    - break;
    - default:
    - msg = g_strdup_printf(_("Unknown error number %d. Logging into the Yahoo! website may fix this."), err);
    - }
    -
    - if (url)
    - fullmsg = g_strdup_printf("%s\n%s", msg, url);
    - else
    - fullmsg = g_strdup(msg);
    -
    - purple_connection_error(gc, reason, fullmsg);
    - g_free(msg);
    - g_free(fullmsg);
    -}
    -
    -static void yahoo_process_addbuddy(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - int err = 0;
    - char *who = NULL;
    - char *temp = NULL;
    - char *group = NULL;
    - char *decoded_group;
    - char *buf;
    - YahooFriend *f;
    - GSList *l = pkt->hash;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - switch (pair->key) {
    - case 66:
    - err = strtol(pair->value, NULL, 10);
    - break;
    - case 7:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - temp = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_addbuddy "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 65:
    - group = pair->value;
    - break;
    - }
    -
    - l = l->next;
    - }
    -
    - if (!temp)
    - return;
    - if (!group)
    - group = "";
    -
    - who = g_strdup(temp);
    -
    - if (!err || (err == 2)) { /* 0 = ok, 2 = already on serv list */
    - f = yahoo_friend_find_or_new(gc, who);
    - yahoo_update_status(gc, who, f);
    -
    - if( !g_hash_table_lookup(yd->peers, who) ) {
    - /* we are not connected as client, so set friend to not connected */
    - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
    - f->p2p_packet_sent = 0;
    - }
    - else /* we are already connected. set friend to YAHOO_P2PSTATUS_WE_ARE_CLIENT */
    - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT);
    - g_free(who);
    - return;
    - }
    -
    - decoded_group = yahoo_string_decode(gc, group, FALSE);
    - buf = g_strdup_printf(_("Unable to add buddy %s to group %s to the server list on account %s."),
    - who, decoded_group, purple_connection_get_display_name(gc));
    - if (!purple_conversation_present_error(who, purple_connection_get_account(gc), buf))
    - purple_notify_error(gc, NULL, _("Unable to add buddy to server list"), buf,
    - purple_request_cpar_from_connection(gc));
    - g_free(buf);
    - g_free(decoded_group);
    - g_free(who);
    -}
    -
    -/* write pkt to the source */
    -static void yahoo_p2p_write_pkt(gint source, struct yahoo_packet *pkt)
    -{
    - size_t pkt_len;
    - gssize written;
    - guchar *raw_packet;
    -
    - /*build the raw packet and send it to the host*/
    - pkt_len = yahoo_packet_build(pkt, 0, 0, &raw_packet);
    - written = write(source, raw_packet, pkt_len);
    - if (written < 0 || (gsize)written != pkt_len)
    - purple_debug_warning("yahoo","p2p: couldn't write to the source\n");
    - g_free(raw_packet);
    -}
    -
    -static void yahoo_p2p_keepalive_cb(gpointer key, gpointer value, gpointer user_data)
    -{
    - struct yahoo_p2p_data *p2p_data = value;
    - PurpleConnection *gc = user_data;
    - struct yahoo_packet *pkt_to_send;
    - PurpleAccount *account;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    -
    - account = purple_connection_get_account(gc);
    -
    - pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash(pkt_to_send, "ssisi",
    - 4, purple_normalize(account, purple_account_get_username(account)),
    - 5, p2p_data->host_username,
    - 241, 0, /* Protocol identifier */
    - 49, "PEERTOPEER",
    - 13, 7);
    - yahoo_p2p_write_pkt(p2p_data->source, pkt_to_send);
    -
    - yahoo_packet_free(pkt_to_send);
    -}
    -
    -static gboolean yahoo_p2p_keepalive(gpointer data)
    -{
    - PurpleConnection *gc = data;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    -
    - g_hash_table_foreach(yd->peers, yahoo_p2p_keepalive_cb, gc);
    -
    - return TRUE;
    -}
    -
    -/* destroy p2p_data associated with a peer and close p2p connection.
    - * g_hash_table_remove() calls this function to destroy p2p_data associated with the peer,
    - * call g_hash_table_remove() instead of this fucntion if peer has an entry in the table */
    -static void yahoo_p2p_disconnect_destroy_data(gpointer data)
    -{
    - struct yahoo_p2p_data *p2p_data;
    - YahooFriend *f;
    -
    - if(!(p2p_data = data))
    - return ;
    -
    - /* If friend, set him not connected */
    - f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username);
    - if (f)
    - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
    -
    - if(p2p_data->source >= 0)
    - close(p2p_data->source);
    - if (p2p_data->input_event > 0)
    - purple_input_remove(p2p_data->input_event);
    - g_free(p2p_data->host_ip);
    - g_free(p2p_data->host_username);
    - g_free(p2p_data);
    -}
    -
    -/* exchange of initial p2pfilexfer packets, service type YAHOO_SERVICE_P2PFILEXFER */
    -static void yahoo_p2p_process_p2pfilexfer(gpointer data, gint source, struct yahoo_packet *pkt)
    -{
    - struct yahoo_p2p_data *p2p_data;
    - char *who = NULL;
    - GSList *l = pkt->hash;
    - struct yahoo_packet *pkt_to_send;
    - PurpleAccount *account;
    - int val_13_to_send = 0;
    - YahooData *yd;
    - YahooFriend *f;
    -
    - if(!(p2p_data = data))
    - return ;
    -
    - yd = purple_connection_get_protocol_data(p2p_data->gc);
    -
    - /* lets see whats in the packet */
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - switch (pair->key) {
    - case 4:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - who = pair->value;
    - if(strncmp(who, p2p_data->host_username, strlen(p2p_data->host_username)) != 0) {
    - /* from whom are we receiving the packets ?? */
    - purple_debug_warning("yahoo","p2p: received data from wrong user\n");
    - return;
    - }
    - } else {
    - purple_debug_warning("yahoo", "yahoo_p2p_process_p2pfilexfer "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 13:
    - p2p_data->val_13 = strtol(pair->value, NULL, 10); /* Value should be 5-7 */
    - break;
    - /* case 5, 49 look laters, no use right now */
    - }
    - l = l->next;
    - }
    -
    - account = purple_connection_get_account(p2p_data->gc);
    -
    - /* key_13: sort of a counter.
    - * WHEN WE ARE CLIENT: yahoo server sends val_13 = 0, we send to peer val_13 = 1, receive back val_13 = 5,
    - * we send val_13=6, receive val_13=7, we send val_13=7, HALT. Keep sending val_13 = 7 as keep alive.
    - * WHEN WE ARE SERVER: we send val_13 = 0 to yahoo server, peer sends us val_13 = 1, we send val_13 = 5,
    - * receive val_13 = 6, send val_13 = 7, receive val_13 = 7. HALT. Keep sending val_13 = 7 as keep alive. */
    -
    - switch(p2p_data->val_13) {
    - case 1 : val_13_to_send = 5; break;
    - case 5 : val_13_to_send = 6; break;
    - case 6 : val_13_to_send = 7; break;
    - case 7 : if( g_hash_table_lookup(yd->peers, p2p_data->host_username) )
    - return;
    - val_13_to_send = 7; break;
    - default: purple_debug_warning("yahoo","p2p:Unknown value for key 13\n");
    - return;
    - }
    -
    - /* Build the yahoo packet */
    - pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash(pkt_to_send, "ssisi",
    - 4, purple_normalize(account, purple_account_get_username(account)),
    - 5, p2p_data->host_username,
    - 241, 0, /* Protocol identifier */
    - 49, "PEERTOPEER",
    - 13, val_13_to_send);
    -
    - /* build the raw packet and send it to the host */
    - yahoo_p2p_write_pkt(source, pkt_to_send);
    - yahoo_packet_free(pkt_to_send);
    -
    - if( val_13_to_send == 7 )
    - if( !g_hash_table_lookup(yd->peers, p2p_data->host_username) ) {
    - g_hash_table_insert(yd->peers, g_strdup(p2p_data->host_username), p2p_data);
    - /* If the peer is a friend, set him connected */
    - f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username);
    - if (f) {
    - if(p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) {
    - p2p_data->session_id = f->session_id;
    - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_SERVER);
    - }
    - else
    - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT);
    - }
    - }
    -}
    -
    -/* callback function associated with receiving of data, not considering receipt of multiple YMSG packets in a single TCP packet */
    -static void yahoo_p2p_read_pkt_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - guchar buf[1024]; /* is it safe to assume a fixed array length of 1024 ?? */
    - int len;
    - int pos = 0;
    - int pktlen;
    - struct yahoo_packet *pkt;
    - guchar *start;
    - struct yahoo_p2p_data *p2p_data;
    - YahooData *yd;
    -
    - if(!(p2p_data = data))
    - return ;
    - yd = purple_connection_get_protocol_data(p2p_data->gc);
    -
    - len = read(source, buf, sizeof(buf));
    - if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
    - return ; /* No Worries*/
    - else if (len <= 0)
    - {
    - purple_debug_warning("yahoo","p2p: Error in connection, or host disconnected\n");
    - /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
    - if( g_hash_table_lookup(yd->peers, p2p_data->host_username) )
    - g_hash_table_remove(yd->peers,p2p_data->host_username);
    - else
    - yahoo_p2p_disconnect_destroy_data(data);
    - return;
    - }
    -
    - /* TODO: It looks like there's a bug here (and above) where an incorrect
    - * assumtion is being made that the buffer will be added to when this
    - * is next called, but that's not really the case! */
    - if(len < YAHOO_PACKET_HDRLEN)
    - return;
    -
    - if(strncmp((char *)buf, "YMSG", 4) != 0) {
    - /* Not a YMSG packet */
    - purple_debug_warning("yahoo", "p2p: Got something other than YMSG packet\n");
    -
    - start = (guchar *) g_strstr_len((char *) buf + 1, len - 1 ,"YMSG");
    - if (start == NULL) {
    - /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
    - if (g_hash_table_lookup(yd->peers, p2p_data->host_username))
    - g_hash_table_remove(yd->peers, p2p_data->host_username);
    - else
    - yahoo_p2p_disconnect_destroy_data(data);
    - return;
    - }
    - purple_debug_warning("yahoo","p2p: Got something other than YMSG packet\n");
    -
    - len -= (start - buf);
    - g_memmove(buf, start, len);
    - }
    -
    - pos += 4; /* YMSG */
    - pos += 2;
    - pos += 2;
    -
    - pktlen = yahoo_get16(buf + pos); pos += 2;
    - if (len < (YAHOO_PACKET_HDRLEN + pktlen)) {
    - purple_debug_error("yahoo", "p2p: packet length(%d) > buffer length(%d)\n",
    - pktlen, (len - pos));
    - /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
    - if (g_hash_table_lookup(yd->peers, p2p_data->host_username))
    - g_hash_table_remove(yd->peers, p2p_data->host_username);
    - else
    - yahoo_p2p_disconnect_destroy_data(data);
    - return;
    - } else
    - purple_debug_misc("yahoo", "p2p: %d bytes to read\n", pktlen);
    -
    - pkt = yahoo_packet_new(0, 0, 0);
    - pkt->service = yahoo_get16(buf + pos); pos += 2;
    - pkt->status = yahoo_get32(buf + pos); pos += 4;
    - pkt->id = yahoo_get32(buf + pos); pos += 4;
    -
    - purple_debug_misc("yahoo", "p2p: Yahoo Service: 0x%02x Status: %d\n",pkt->service, pkt->status);
    - yahoo_packet_read(pkt, buf + pos, pktlen);
    -
    - /* packet processing */
    - switch(pkt->service) {
    - case YAHOO_SERVICE_P2PFILEXFER:
    - yahoo_p2p_process_p2pfilexfer(data, source, pkt);
    - break;
    - case YAHOO_SERVICE_MESSAGE:
    - yahoo_process_message(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P);
    - break;
    - case YAHOO_SERVICE_NOTIFY:
    - yahoo_process_notify(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P);
    - break;
    - default:
    - purple_debug_warning("yahoo","p2p: p2p service %d Unhandled\n",pkt->service);
    - }
    -
    - yahoo_packet_free(pkt);
    -}
    -
    -static void yahoo_p2p_server_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - int acceptfd;
    - struct yahoo_p2p_data *p2p_data;
    - YahooData *yd;
    -
    - if(!(p2p_data = data))
    - return ;
    - yd = purple_connection_get_protocol_data(p2p_data->gc);
    -
    - acceptfd = accept(source, NULL, 0);
    - if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
    - return;
    - else if(acceptfd == -1) {
    - purple_debug_warning("yahoo","yahoo_p2p_server_send_connected_cb: accept: %s\n", g_strerror(errno));
    - yahoo_p2p_disconnect_destroy_data(data);
    - return;
    - }
    -
    - /* remove timeout */
    - if (yd->yahoo_p2p_server_timeout_handle) {
    - purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle);
    - yd->yahoo_p2p_server_timeout_handle = 0;
    - }
    -
    - /* remove watcher and close p2p server */
    - if (yd->yahoo_p2p_server_watcher) {
    - purple_input_remove(yd->yahoo_p2p_server_watcher);
    - yd->yahoo_p2p_server_watcher = 0;
    - }
    - if (yd->yahoo_local_p2p_server_fd >= 0) {
    - close(yd->yahoo_local_p2p_server_fd);
    - yd->yahoo_local_p2p_server_fd = -1;
    - }
    -
    - /* Add an Input Read event to the file descriptor */
    - p2p_data->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data);
    - p2p_data->source = acceptfd;
    -}
    -
    -static gboolean yahoo_cancel_p2p_server_listen_cb(gpointer data)
    -{
    - struct yahoo_p2p_data *p2p_data;
    - YahooData *yd;
    -
    - if(!(p2p_data = data))
    - return FALSE;
    -
    - yd = purple_connection_get_protocol_data(p2p_data->gc);
    -
    - purple_debug_warning("yahoo","yahoo p2p server timeout, peer failed to connect\n");
    - yahoo_p2p_disconnect_destroy_data(data);
    - purple_input_remove(yd->yahoo_p2p_server_watcher);
    - yd->yahoo_p2p_server_watcher = 0;
    - close(yd->yahoo_local_p2p_server_fd);
    - yd->yahoo_local_p2p_server_fd = -1;
    - yd->yahoo_p2p_server_timeout_handle = 0;
    -
    - return FALSE;
    -}
    -
    -static void yahoo_p2p_server_listen_cb(int listenfd, gpointer data)
    -{
    - struct yahoo_p2p_data *p2p_data;
    - YahooData *yd;
    -
    - if(!(p2p_data = data))
    - return ;
    -
    - yd = purple_connection_get_protocol_data(p2p_data->gc);
    - yd->listen_data = NULL;
    -
    - if(listenfd == -1) {
    - purple_debug_warning("yahoo","p2p: error starting p2p server\n");
    - yahoo_p2p_disconnect_destroy_data(data);
    - return;
    - }
    -
    - /* Add an Input Read event to the file descriptor */
    - yd->yahoo_local_p2p_server_fd = listenfd;
    - yd->yahoo_p2p_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_server_send_connected_cb,data);
    -
    - /* add timeout */
    - yd->yahoo_p2p_server_timeout_handle = purple_timeout_add_seconds(YAHOO_P2P_SERVER_TIMEOUT, yahoo_cancel_p2p_server_listen_cb, data);
    -}
    -
    -/* send p2p pkt containing our encoded ip, asking peer to connect to us */
    -void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13)
    -{
    - const char *public_ip;
    - guint32 temp[4];
    - guint32 ip;
    - char temp_str[100];
    - gchar *base64_ip = NULL;
    - YahooFriend *f;
    - struct yahoo_packet *pkt;
    - PurpleAccount *account;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - struct yahoo_p2p_data *p2p_data;
    - const char *norm_username;
    -
    - f = yahoo_friend_find(gc, who);
    - account = purple_connection_get_account(gc);
    -
    - /* Do not send invitation if already listening for other connection */
    - if(yd->yahoo_local_p2p_server_fd >= 0)
    - return;
    -
    - /* One shouldn't try to connect to self */
    - if( strcmp(purple_normalize(account, purple_account_get_username(account)), who) == 0)
    - return;
    -
    - /* send packet to only those friends who arent p2p connected and to whom we havent already sent. Do not send if this condition doesn't hold good */
    - if( !( f && (yahoo_friend_get_p2p_status(f) == YAHOO_P2PSTATUS_NOT_CONNECTED) && (f->p2p_packet_sent == 0)) )
    - return;
    -
    - /* Finally, don't try to connect to buddies not online or on sms */
    - if( (f->status == YAHOO_STATUS_OFFLINE) || f->sms )
    - return;
    -
    - public_ip = purple_network_get_public_ip();
    - if( (sscanf(public_ip, "%u.%u.%u.%u", &temp[0], &temp[1], &temp[2], &temp[3])) !=4 )
    - return ;
    -
    - ip = (temp[3] << 24) | (temp[2] <<16) | (temp[1] << 8) | temp[0];
    - sprintf(temp_str, "%d", ip);
    - base64_ip = purple_base64_encode( (guchar *)temp_str, strlen(temp_str) );
    -
    - norm_username = purple_normalize(account, purple_account_get_username(account));
    - pkt = yahoo_packet_new(YAHOO_SERVICE_PEERTOPEER, YAHOO_STATUS_AVAILABLE, 0);
    - yahoo_packet_hash(pkt, "sssissis",
    - 1, norm_username,
    - 4, norm_username,
    - 12, base64_ip, /* base64 encode ip */
    - 61, 0, /* To-do : figure out what is 61 for?? */
    - 2, "",
    - 5, who,
    - 13, val_13,
    - 49, "PEERTOPEER");
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - f->p2p_packet_sent = 1; /* set p2p_packet_sent to sent */
    -
    - p2p_data = g_new0(struct yahoo_p2p_data, 1);
    -
    - p2p_data->gc = gc;
    - p2p_data->host_ip = NULL;
    - p2p_data->host_username = g_strdup(who);
    - p2p_data->val_13 = val_13;
    - p2p_data->connection_type = YAHOO_P2P_WE_ARE_SERVER;
    - p2p_data->source = -1;
    -
    - /* FIXME: If the port is already used, purple_network_listener returns NULL and old listener won't be canceled
    - * in yahoo_close function. */
    - if (yd->listen_data)
    - purple_debug_warning("yahoo","p2p: Failed to create p2p server - server already exists\n");
    - else {
    - yd->listen_data = purple_network_listen(YAHOO_PAGER_PORT_P2P, AF_UNSPEC, SOCK_STREAM, TRUE, yahoo_p2p_server_listen_cb, p2p_data);
    - if (yd->listen_data == NULL)
    - purple_debug_warning("yahoo","p2p: Failed to created p2p server\n");
    - }
    -
    - g_free(base64_ip);
    -}
    -
    -/* function called when connection to p2p host is setup */
    -static void yahoo_p2p_init_cb(gpointer data, gint source, const gchar *error_message)
    -{
    - struct yahoo_p2p_data *p2p_data;
    - struct yahoo_packet *pkt_to_send;
    - PurpleAccount *account;
    - YahooData *yd;
    -
    - p2p_data = data;
    - yd = purple_connection_get_protocol_data(p2p_data->gc);
    -
    - if(error_message != NULL) {
    - purple_debug_warning("yahoo","p2p: %s\n",error_message);
    - yahoo_send_p2p_pkt(p2p_data->gc, p2p_data->host_username, 2);/* send p2p init packet with val_13=2 */
    -
    - yahoo_p2p_disconnect_destroy_data(p2p_data);
    - return;
    - }
    -
    - /* Add an Input Read event to the file descriptor */
    - p2p_data->input_event = purple_input_add(source, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data);
    - p2p_data->source = source;
    -
    - account = purple_connection_get_account(p2p_data->gc);
    -
    - /* Build the yahoo packet */
    - pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash(pkt_to_send, "ssisi",
    - 4, purple_normalize(account, purple_account_get_username(account)),
    - 5, p2p_data->host_username,
    - 241, 0, /* Protocol identifier */
    - 49, "PEERTOPEER",
    - 13, 1); /* we receive key13= 0 or 2, we send key13=1 */
    -
    - yahoo_p2p_write_pkt(source, pkt_to_send); /* build raw packet and send */
    - yahoo_packet_free(pkt_to_send);
    -}
    -
    -static void yahoo_process_p2p(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - GSList *l = pkt->hash;
    - char *who = NULL;
    - char *base64 = NULL;
    - guchar *decoded;
    - gsize len;
    - gint val_13 = 0;
    - gint val_11 = 0;
    - PurpleAccount *account;
    - YahooFriend *f;
    -
    - /* if status is not YAHOO_STATUS_BRB or YAHOO_STATUS_P2P, the packet bounced back,
    - * so it contains our own ip */
    - if(pkt->status != YAHOO_STATUS_BRB && pkt->status != YAHOO_STATUS_P2P)
    - return ;
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - switch (pair->key) {
    - case 5:
    - /* our identity */
    - break;
    - case 4:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - who = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_p2p "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 1:
    - /* who again, the master identity this time? */
    - break;
    - case 12:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - base64 = pair->value;
    - /* so, this is an ip address. in base64. decoded it's in ascii.
    - after strtol, it's in reversed byte order. Who thought this up?*/
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_p2p "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 13:
    - val_13 = strtol(pair->value, NULL, 10);
    - break;
    - case 11:
    - val_11 = strtol(pair->value, NULL, 10); /* session id of peer */
    - if( (f = yahoo_friend_find(gc, who)) )
    - f->session_id = val_11;
    - break;
    - /*
    - TODO: figure these out
    - yahoo: Key: 61 Value: 0
    - yahoo: Key: 2 Value:
    - yahoo: Key: 13 Value: 0 packet count ??
    - yahoo: Key: 49 Value: PEERTOPEER
    - yahoo: Key: 140 Value: 1
    - */
    -
    - }
    -
    - l = l->next;
    - }
    -
    - if (base64) {
    - guint32 ip;
    - YahooFriend *f;
    - char *host_ip, *tmp;
    - struct yahoo_p2p_data *p2p_data;
    -
    - decoded = purple_base64_decode(base64, &len);
    - if (decoded == NULL) {
    - purple_debug_info("yahoo","p2p: Unable to decode base64 IP (%s) \n", base64);
    - return;
    - }
    - tmp = purple_str_binary_to_ascii(decoded, len);
    - purple_debug_info("yahoo", "Got P2P service packet (from server): who = %s, ip = %s\n", who, tmp);
    - g_free(tmp);
    -
    - ip = strtol((gchar *)decoded, NULL, 10);
    - g_free(decoded);
    - host_ip = g_strdup_printf("%u.%u.%u.%u", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff,
    - (ip >> 24) & 0xff);
    - f = yahoo_friend_find(gc, who);
    - if (f)
    - yahoo_friend_set_ip(f, host_ip);
    - purple_debug_info("yahoo", "IP : %s\n", host_ip);
    -
    - account = purple_connection_get_account(gc);
    -
    - if(val_11==0) {
    - if(!f)
    - return;
    - else
    - val_11 = f->session_id;
    - }
    -
    - p2p_data = g_new0(struct yahoo_p2p_data, 1);
    - p2p_data->host_username = g_strdup(who);
    - p2p_data->val_13 = val_13;
    - p2p_data->session_id = val_11;
    - p2p_data->host_ip = host_ip;
    - p2p_data->gc = gc;
    - p2p_data->connection_type = YAHOO_P2P_WE_ARE_CLIENT;
    - p2p_data->source = -1;
    -
    - /* connect to host */
    - if((purple_proxy_connect(gc, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) {
    - purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip);
    - g_free(p2p_data->host_ip);
    - g_free(p2p_data->host_username);
    - g_free(p2p_data);
    - }
    - }
    -}
    -
    -static void yahoo_process_audible(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - PurpleAccount *account;
    - char *who = NULL, *msg = NULL, *id = NULL;
    - GSList *l = pkt->hash;
    -
    - account = purple_connection_get_account(gc);
    -
    - while (l) {
    - struct yahoo_pair *pair = l->data;
    -
    - switch (pair->key) {
    - case 4:
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - who = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_audible "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 5:
    - /* us */
    - break;
    - case 230:
    - /* the audible, in foo.locale.bar.baz format
    - eg: base.tw.smiley.smiley43 */
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - id = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_audible "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 231:
    - /* the text of the audible */
    - if (g_utf8_validate(pair->value, -1, NULL)) {
    - msg = pair->value;
    - } else {
    - purple_debug_warning("yahoo", "yahoo_process_audible "
    - "got non-UTF-8 string for key %d\n", pair->key);
    - }
    - break;
    - case 232:
    - /* SHA-1 hash of audible SWF file (eg: 4e8691499d9c0fb8374478ff9720f4a9ea4a4915) */
    - break;
    - }
    -
    - l = l->next;
    - }
    -
    - if (!msg)
    - msg = id;
    - if (!who || !msg)
    - return;
    - if (!g_utf8_validate(msg, -1, NULL)) {
    - purple_debug_misc("yahoo", "Warning, nonutf8 audible, ignoring!\n");
    - return;
    - }
    - if (!purple_account_privacy_check(account, who)) {
    - purple_debug_misc("yahoo", "Audible message from %s for %s dropped!\n",
    - purple_account_get_username(account), who);
    - return;
    - }
    - if (id) {
    - /* "http://l.yimg.com/pu/dl/aud/"+locale+"/"+id+".swf" */
    - char **audible_locale = g_strsplit(id, ".", 0);
    - char *buf = g_strdup_printf(_("[ Audible %s/%s/%s.swf ] %s"), YAHOO_AUDIBLE_URL, audible_locale[1], id, msg);
    - g_strfreev(audible_locale);
    -
    - purple_serv_got_im(gc, who, buf, 0, time(NULL));
    - g_free(buf);
    - } else
    - purple_serv_got_im(gc, who, msg, 0, time(NULL));
    -}
    -
    -static void yahoo_packet_process(PurpleConnection *gc, struct yahoo_packet *pkt)
    -{
    - switch (pkt->service) {
    - case YAHOO_SERVICE_LOGON:
    - case YAHOO_SERVICE_LOGOFF:
    - case YAHOO_SERVICE_ISAWAY:
    - case YAHOO_SERVICE_ISBACK:
    - case YAHOO_SERVICE_GAMELOGON:
    - case YAHOO_SERVICE_GAMELOGOFF:
    - case YAHOO_SERVICE_CHATLOGON:
    - case YAHOO_SERVICE_CHATLOGOFF:
    - case YAHOO_SERVICE_Y6_STATUS_UPDATE:
    - case YAHOO_SERVICE_STATUS_15:
    - yahoo_process_status(gc, pkt);
    - break;
    - case YAHOO_SERVICE_NOTIFY:
    - yahoo_process_notify(gc, pkt, YAHOO_PKT_TYPE_SERVER);
    - break;
    - case YAHOO_SERVICE_MESSAGE:
    - case YAHOO_SERVICE_GAMEMSG:
    - case YAHOO_SERVICE_CHATMSG:
    - yahoo_process_message(gc, pkt, YAHOO_PKT_TYPE_SERVER);
    - break;
    - case YAHOO_SERVICE_SYSMESSAGE:
    - yahoo_process_sysmessage(gc, pkt);
    - break;
    - case YAHOO_SERVICE_NEWMAIL:
    - yahoo_process_mail(gc, pkt);
    - break;
    - case YAHOO_SERVICE_NEWCONTACT:
    - yahoo_process_contact(gc, pkt);
    - break;
    - case YAHOO_SERVICE_AUTHRESP:
    - yahoo_process_authresp(gc, pkt);
    - break;
    - case YAHOO_SERVICE_LIST:
    - yahoo_process_list(gc, pkt);
    - break;
    - case YAHOO_SERVICE_LIST_15:
    - yahoo_process_list_15(gc, pkt);
    - break;
    - case YAHOO_SERVICE_AUTH:
    - yahoo_process_auth(gc, pkt);
    - break;
    - case YAHOO_SERVICE_AUTH_REQ_15:
    - yahoo_buddy_auth_req_15(gc, pkt);
    - break;
    - case YAHOO_SERVICE_ADDBUDDY:
    - yahoo_process_addbuddy(gc, pkt);
    - break;
    - case YAHOO_SERVICE_IGNORECONTACT:
    - yahoo_process_ignore(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CONFINVITE:
    - case YAHOO_SERVICE_CONFADDINVITE:
    - yahoo_process_conference_invite(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CONFDECLINE:
    - yahoo_process_conference_decline(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CONFLOGON:
    - yahoo_process_conference_logon(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CONFLOGOFF:
    - yahoo_process_conference_logoff(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CONFMSG:
    - yahoo_process_conference_message(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CHATONLINE:
    - yahoo_process_chat_online(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CHATLOGOUT:
    - yahoo_process_chat_logout(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CHATGOTO:
    - yahoo_process_chat_goto(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CHATJOIN:
    - yahoo_process_chat_join(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CHATLEAVE: /* XXX is this right? */
    - case YAHOO_SERVICE_CHATEXIT:
    - yahoo_process_chat_exit(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CHATINVITE: /* XXX never seen this one, might not do it right */
    - case YAHOO_SERVICE_CHATADDINVITE:
    - yahoo_process_chat_addinvite(gc, pkt);
    - break;
    - case YAHOO_SERVICE_COMMENT:
    - yahoo_process_chat_message(gc, pkt);
    - break;
    - case YAHOO_SERVICE_PRESENCE_PERM:
    - case YAHOO_SERVICE_PRESENCE_SESSION:
    - yahoo_process_presence(gc, pkt);
    - break;
    - case YAHOO_SERVICE_P2PFILEXFER:
    - /* This case had no break and continued; thus keeping it this way.*/
    - yahoo_process_p2p(gc, pkt); /* P2PFILEXFER handled the same way as process_p2p */
    - yahoo_process_p2pfilexfer(gc, pkt); /* redundant ??, need to have a break now */
    - case YAHOO_SERVICE_FILETRANSFER:
    - purple_debug_error("yahoo", "Legacy file transfers are not "
    - "supported anymore.\n");
    - break;
    - case YAHOO_SERVICE_PEERTOPEER:
    - yahoo_process_p2p(gc, pkt);
    - break;
    - case YAHOO_SERVICE_PICTURE:
    - yahoo_process_picture(gc, pkt);
    - break;
    - case YAHOO_SERVICE_PICTURE_CHECKSUM:
    - yahoo_process_picture_checksum(gc, pkt);
    - break;
    - case YAHOO_SERVICE_PICTURE_UPLOAD:
    - yahoo_process_picture_upload(gc, pkt);
    - break;
    - case YAHOO_SERVICE_PICTURE_UPDATE:
    - case YAHOO_SERVICE_AVATAR_UPDATE:
    - yahoo_process_avatar_update(gc, pkt);
    - break;
    - case YAHOO_SERVICE_AUDIBLE:
    - yahoo_process_audible(gc, pkt);
    - break;
    - case YAHOO_SERVICE_CONTACT_DETAILS:
    - yahoo_process_contact_details(gc, pkt);
    - break;
    - case YAHOO_SERVICE_FILETRANS_15:
    - yahoo_process_filetrans_15(gc, pkt);
    - break;
    - case YAHOO_SERVICE_FILETRANS_INFO_15:
    - yahoo_process_filetrans_info_15(gc, pkt);
    - break;
    - case YAHOO_SERVICE_FILETRANS_ACC_15:
    - yahoo_process_filetrans_acc_15(gc, pkt);
    - break;
    - case YAHOO_SERVICE_SMS_MSG:
    - yahoo_process_sms_message(gc, pkt);
    - break;
    -
    - default:
    - purple_debug_error("yahoo", "Unhandled service 0x%02x\n", pkt->service);
    - break;
    - }
    -}
    -
    -static void yahoo_pending(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PurpleConnection *gc = data;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - char buf[1024];
    - int len;
    -
    - len = read(yd->fd, buf, sizeof(buf));
    -
    - if (len < 0) {
    - gchar *tmp;
    -
    - if (errno == EAGAIN)
    - /* No worries */
    - return;
    -
    - tmp = g_strdup_printf(_("Lost connection with server: %s"),
    - g_strerror(errno));
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - return;
    - } else if (len == 0) {
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Server closed the connection"));
    - return;
    - }
    - purple_connection_update_last_received(gc);
    - yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen);
    - memcpy(yd->rxqueue + yd->rxlen, buf, len);
    - yd->rxlen += len;
    -
    - while (1) {
    - struct yahoo_packet *pkt;
    - int pos = 0;
    - int pktlen;
    -
    - if (yd->rxlen < YAHOO_PACKET_HDRLEN)
    - return;
    -
    - if (strncmp((char *)yd->rxqueue, "YMSG", MIN(4, yd->rxlen)) != 0) {
    - /* HEY! This isn't even a YMSG packet. What
    - * are you trying to pull? */
    - guchar *start;
    -
    - purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!\n");
    -
    - start = memchr(yd->rxqueue + 1, 'Y', yd->rxlen - 1);
    - if (start) {
    - g_memmove(yd->rxqueue, start, yd->rxlen - (start - yd->rxqueue));
    - yd->rxlen -= start - yd->rxqueue;
    - continue;
    - } else {
    - g_free(yd->rxqueue);
    - yd->rxqueue = NULL;
    - yd->rxlen = 0;
    - return;
    - }
    - }
    -
    - pos += 4; /* YMSG */
    - pos += 2;
    - pos += 2;
    -
    - pktlen = yahoo_get16(yd->rxqueue + pos); pos += 2;
    - purple_debug_misc("yahoo", "%d bytes to read, rxlen is %d\n", pktlen, yd->rxlen);
    -
    - if (yd->rxlen < (YAHOO_PACKET_HDRLEN + pktlen))
    - return;
    -
    - yahoo_packet_dump(yd->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
    -
    - pkt = yahoo_packet_new(0, 0, 0);
    -
    - pkt->service = yahoo_get16(yd->rxqueue + pos); pos += 2;
    - pkt->status = yahoo_get32(yd->rxqueue + pos); pos += 4;
    - purple_debug_misc("yahoo", "Yahoo Service: 0x%02x Status: %d\n",
    - pkt->service, pkt->status);
    - pkt->id = yahoo_get32(yd->rxqueue + pos); pos += 4;
    -
    - yahoo_packet_read(pkt, yd->rxqueue + pos, pktlen);
    -
    - yd->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
    - if (yd->rxlen) {
    - guchar *tmp = g_memdup(yd->rxqueue + YAHOO_PACKET_HDRLEN + pktlen, yd->rxlen);
    - g_free(yd->rxqueue);
    - yd->rxqueue = tmp;
    - } else {
    - g_free(yd->rxqueue);
    - yd->rxqueue = NULL;
    - }
    -
    - yahoo_packet_process(gc, pkt);
    -
    - yahoo_packet_free(pkt);
    - }
    -}
    -
    -static void yahoo_got_connected(gpointer data, gint source, const gchar *error_message)
    -{
    - PurpleConnection *gc = data;
    - YahooData *yd;
    - struct yahoo_packet *pkt;
    -
    - if (source < 0) {
    - gchar *tmp;
    - tmp = g_strdup_printf(_("Unable to connect: %s"), error_message);
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - return;
    - }
    -
    - yd = purple_connection_get_protocol_data(gc);
    - yd->fd = source;
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, yd->current_status, yd->session_id);
    -
    - yahoo_packet_hash_str(pkt, 1, purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))));
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - yd->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
    -}
    -
    -#if TRY_WEBMESSENGER_LOGIN
    -
    -static void yahoo_got_web_connected(gpointer data, gint source, const gchar *error_message)
    -{
    - PurpleConnection *gc = data;
    - YahooData *yd;
    - struct yahoo_packet *pkt;
    -
    - if (source < 0) {
    - gchar *tmp;
    - tmp = g_strdup_printf(_("Unable to connect: %s"), error_message);
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - return;
    - }
    -
    - yd = purple_connection_get_protocol_data(gc);
    - yd->fd = source;
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_WEBLOGIN, YAHOO_STATUS_WEBLOGIN, yd->session_id);
    -
    - yahoo_packet_hash(pkt, "sss", 0,
    - purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))),
    - 1, purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))),
    - 6, yd->auth);
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - g_free(yd->auth);
    - yd->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
    -}
    -
    -static void
    -yahoo_login_page_got(PurpleHttpConnection *hc, PurpleHttpResponse *resp,
    - gpointer _unused)
    -{
    - PurpleConnection *gc = purple_http_conn_get_purple_connection(hc);
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleHttpCookieJar *cjar;
    - GString *auth_s;
    - gchar *cookie;
    -
    - if (purple_http_response_get_code(resp) != 302) {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to connect"));
    - return;
    - }
    -
    - auth_s = g_string_new(NULL);
    - cjar = purple_http_conn_get_cookie_jar(hc);
    - cookie = purple_http_cookie_jar_get(cjar, "B");
    - if (cookie) {
    - g_string_append_printf(auth_s, "B=%s; ", cookie);
    - g_free(cookie);
    - }
    - cookie = purple_http_cookie_jar_get(cjar, "T");
    - if (cookie) {
    - g_string_append_printf(auth_s, "T=%s; ", cookie);
    - g_free(cookie);
    - }
    - cookie = purple_http_cookie_jar_get(cjar, "Y");
    - if (cookie) {
    - g_string_append_printf(auth_s, "Y=%s; ", cookie);
    - g_free(cookie);
    - }
    -
    - yd->auth = g_string_free(auth_s, FALSE);
    - /* Now we have our cookies to login with. I'll go get the milk. */
    -
    - /* XXX: wcs2.msg.dcn.yahoo.com is down, so I used
    - * YAHOO_PAGER_HOST_FALLBACK, but I'm not sure, if it is the correct
    - * host.
    - */
    - if (purple_proxy_connect(gc, account, YAHOO_PAGER_HOST_FALLBACK,
    - purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
    - yahoo_got_web_connected, gc) == NULL)
    - {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to connect"));
    - return;
    - }
    -}
    -
    -static void yahoo_login_page_hash_iter(const char *key, const char *val, GString *url)
    -{
    - if (!strcmp(key, "passwd") || !strcmp(key, "login"))
    - return;
    - g_string_append_c(url, '&');
    - g_string_append(url, key);
    - g_string_append_c(url, '=');
    - if (!strcmp(key, ".save") || !strcmp(key, ".js"))
    - g_string_append_c(url, '1');
    - else if (!strcmp(key, ".challenge"))
    - g_string_append(url, val);
    - else
    - g_string_append(url, purple_url_encode(val));
    -}
    -
    -static GHashTable *yahoo_login_page_hash(const char *buf, size_t len)
    -{
    - GHashTable *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    - const char *c = buf;
    - char *d;
    - char name[64], value[64];
    - int count;
    - int input_len = strlen("<input ");
    - int name_len = strlen("name=\"");
    - int value_len = strlen("value=\"");
    - while ((len > ((c - buf) + input_len))
    - && (c = strstr(c, "<input "))) {
    - if (!(c = g_strstr_len(c, len - (c - buf), "name=\"")))
    - continue;
    - c += name_len;
    - count = sizeof(name)-1;
    - for (d = name; (len > ((c - buf) + 1)) && *c!='"'
    - && count; c++, d++, count--)
    - *d = *c;
    - *d = '\0';
    - count = sizeof(value)-1;
    - if (!(d = g_strstr_len(c, len - (c - buf), "value=\"")))
    - continue;
    - d += value_len;
    - if (strchr(c, '>') < d)
    - break;
    - for (c = d, d = value; (len > ((c - buf) + 1))
    - && *c!='"' && count; c++, d++, count--)
    - *d = *c;
    - *d = '\0';
    - g_hash_table_insert(hash, g_strdup(name), g_strdup(value));
    - }
    - return hash;
    -}
    -
    -static void
    -yahoo_login_page_cb(PurpleHttpConnection *http_conn,
    - PurpleHttpResponse *response, gpointer _unused)
    -{
    - PurpleConnection *gc = purple_http_conn_get_purple_connection(http_conn);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - const char *pass = purple_connection_get_password(gc);
    - size_t len;
    - const gchar *got_data;
    - GHashTable *hash;
    - GString *url;
    - char md5[33], *hashp = md5, *chal;
    - int i;
    - PurpleCipher *cipher;
    - guchar digest[16];
    - PurpleHttpRequest *req;
    -
    - if (!purple_http_response_is_successful(response))
    - {
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - purple_http_response_get_error(response));
    - return;
    - }
    -
    - got_data = purple_http_response_get_data(response, &len);
    - hash = yahoo_login_page_hash(got_data, len);
    -
    - cipher = purple_md5_cipher_new();
    -
    - purple_cipher_append(cipher, (const guchar *)pass, strlen(pass));
    - purple_cipher_digest(cipher, digest, sizeof(digest));
    - for (i = 0; i < 16; ++i) {
    - g_snprintf(hashp, 3, "%02x", digest[i]);
    - hashp += 2;
    - }
    -
    - chal = g_strconcat(md5, g_hash_table_lookup(hash, ".challenge"), NULL);
    - purple_cipher_reset(cipher);
    - purple_cipher_append(cipher, (const guchar *)chal, strlen(chal));
    - purple_cipher_digest(cipher, digest, sizeof(digest));
    - hashp = md5;
    - for (i = 0; i < 16; ++i) {
    - g_snprintf(hashp, 3, "%02x", digest[i]);
    - hashp += 2;
    - }
    - /*
    - * I dunno why this is here and commented out.. but in case it's needed
    - * I updated it..
    -
    - purple_cipher_reset(cipher);
    - purple_cipher_append(cipher, md5, strlen(md5));
    - purple_cipher_digest(cipher, sizeof(digest), digest, NULL);
    - hashp = md5;
    - for (i = 0; i < 16; ++i) {
    - g_snprintf(hashp, 3, "%02x", digest[i]);
    - hashp += 2;
    - }
    - */
    - g_free(chal);
    -
    - url = g_string_new(NULL);
    - g_string_printf(url, "http://login.yahoo.com/config/login?login=%s&passwd=%s", purple_account_get_username(account), md5);
    - g_hash_table_foreach(hash, (GHFunc)yahoo_login_page_hash_iter, url);
    - url = g_string_append(url, "&.hash=1&.md5=1");
    -
    - g_hash_table_destroy(hash);
    - g_object_unref(cipher);
    -
    - req = purple_http_request_new(g_string_free(url, FALSE));
    - purple_http_request_set_max_redirects(req, 0);
    - purple_http_connection_set_add(yd->http_reqs,
    - purple_http_request(gc, req, yahoo_login_page_got, NULL));
    - purple_http_request_unref(req);
    -}
    -
    -#endif /* TRY_WEBMESSENGER_LOGIN */
    -
    -static void yahoo_picture_check(PurpleAccount *account)
    -{
    - PurpleConnection *gc = purple_account_get_connection(account);
    - PurpleImage *img = purple_buddy_icons_find_account_icon(account);
    -
    - yahoo_set_buddy_icon(gc, img);
    -
    - if (img)
    - g_object_unref(img);
    -}
    -
    -static int get_yahoo_status_from_purple_status(PurpleStatus *status)
    -{
    - PurplePresence *presence;
    - const char *status_id;
    - const char *msg;
    -
    - presence = purple_status_get_presence(status);
    - status_id = purple_status_get_id(status);
    - msg = purple_status_get_attr_string(status, "message");
    -
    - if ((msg != NULL) && (*msg != '\0')) {
    - return YAHOO_STATUS_CUSTOM;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AVAILABLE)) {
    - return YAHOO_STATUS_AVAILABLE;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BRB)) {
    - return YAHOO_STATUS_BRB;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BUSY)) {
    - return YAHOO_STATUS_BUSY;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATHOME)) {
    - return YAHOO_STATUS_NOTATHOME;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATDESK)) {
    - return YAHOO_STATUS_NOTATDESK;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTINOFFICE)) {
    - return YAHOO_STATUS_NOTINOFFICE;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONPHONE)) {
    - return YAHOO_STATUS_ONPHONE;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONVACATION)) {
    - return YAHOO_STATUS_ONVACATION;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_OUTTOLUNCH)) {
    - return YAHOO_STATUS_OUTTOLUNCH;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_STEPPEDOUT)) {
    - return YAHOO_STATUS_STEPPEDOUT;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_INVISIBLE)) {
    - return YAHOO_STATUS_INVISIBLE;
    - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AWAY)) {
    - return YAHOO_STATUS_CUSTOM;
    - } else if (purple_presence_is_idle(presence)) {
    - return YAHOO_STATUS_IDLE;
    - } else {
    - purple_debug_error("yahoo", "Unexpected PurpleStatus!\n");
    - return YAHOO_STATUS_AVAILABLE;
    - }
    -}
    -
    -static void yahoo_got_pager_server(PurpleHttpConnection *http_conn,
    - PurpleHttpResponse *response, gpointer _yd)
    -{
    - YahooData *yd = _yd;
    - PurpleConnection *gc = yd->gc;
    - PurpleAccount *a = purple_connection_get_account(gc);
    - gchar **strings = NULL, *cs_server = NULL;
    - int port = purple_account_get_int(a, "port", YAHOO_PAGER_PORT);
    - int stringslen = 0;
    - const gchar *got_data;
    -
    - if (!purple_http_response_is_successful(response)) {
    - purple_debug_error("yahoo", "Unable to retrieve server info: %s\n",
    - purple_http_response_get_error(response));
    -
    - if(purple_proxy_connect(gc, a, YAHOO_PAGER_HOST_FALLBACK, port,
    - yahoo_got_connected, gc) == NULL) {
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to connect"));
    - }
    - } else {
    - got_data = purple_http_response_get_data(response, NULL);
    - strings = g_strsplit(got_data, "\r\n", -1);
    -
    - if((stringslen = g_strv_length(strings)) > 1) {
    - int i;
    -
    - for(i = 0; i < stringslen; i++) {
    - if(g_ascii_strncasecmp(strings[i], "COLO_CAPACITY=", 14) == 0) {
    - purple_debug_info("yahoo", "Got COLO Capacity: %s\n", &(strings[i][14]));
    - } else if(g_ascii_strncasecmp(strings[i], "CS_IP_ADDRESS=", 14) == 0) {
    - cs_server = g_strdup(&strings[i][14]);
    - purple_debug_info("yahoo", "Got CS IP address: %s\n", cs_server);
    - }
    - }
    - }
    -
    - if(cs_server) { /* got an address; get on with connecting */
    - if(purple_proxy_connect(gc, a, cs_server, port, yahoo_got_connected, gc) == NULL)
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to connect"));
    - } else {
    - purple_debug_error("yahoo", "No CS address retrieved! Server "
    - "response:\n%s\n", got_data);
    -
    - if(purple_proxy_connect(gc, a, YAHOO_PAGER_HOST_FALLBACK, port,
    - yahoo_got_connected, gc) == NULL) {
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to connect"));
    - }
    - }
    - }
    -
    - g_strfreev(strings);
    - g_free(cs_server);
    -}
    -
    -void yahoo_login(PurpleAccount *account) {
    - PurpleConnection *gc = purple_account_get_connection(account);
    - PurpleHttpRequest *req;
    - YahooData *yd = g_new0(YahooData, 1);
    - PurpleStatus *status = purple_account_get_active_status(account);
    -
    - purple_connection_set_protocol_data(gc, yd);
    - purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML |
    - PURPLE_CONNECTION_FLAG_NO_BGCOLOR |
    - PURPLE_CONNECTION_FLAG_NO_URLDESC |
    - PURPLE_CONNECTION_FLAG_NO_IMAGES);
    -
    - purple_connection_update_progress(gc, _("Connecting"), 1, 2);
    -
    - purple_connection_set_display_name(gc, purple_account_get_username(account));
    -
    - yd->gc = gc;
    - yd->yahoo_local_p2p_server_fd = -1;
    - yd->fd = -1;
    - yd->txhandler = 0;
    - /* TODO: Is there a good grow size for the buffer? */
    - yd->txbuf = purple_circular_buffer_new(0);
    - yd->http_reqs = purple_http_connection_set_new();
    - yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free);
    - yd->imvironments = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    - yd->xfer_peer_idstring_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
    - yd->peers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
    - yahoo_p2p_disconnect_destroy_data);
    - yd->sms_carrier = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    - yd->yahoo_p2p_timer = purple_timeout_add_seconds(YAHOO_P2P_KEEPALIVE_SECS,
    - yahoo_p2p_keepalive, gc);
    - yd->confs = NULL;
    - yd->conf_id = 2;
    - yd->last_keepalive = yd->last_ping = time(NULL);
    -
    - yd->current_status = get_yahoo_status_from_purple_status(status);
    -
    - yahoo_picture_check(account);
    -
    - /* Get the pager server. Actually start connecting in the callback since we
    - * must have the contents of the HTTP response to proceed. */
    - req = purple_http_request_new(YAHOO_PAGER_HOST_REQ_URL);
    - purple_http_request_header_set(req, "User-Agent", YAHOO_CLIENT_USERAGENT);
    - purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
    - req, yahoo_got_pager_server, yd));
    - purple_http_request_unref(req);
    -
    - return;
    -}
    -
    -void yahoo_close(PurpleConnection *gc) {
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - GSList *l;
    -
    - if (yd->inpa) {
    - purple_input_remove(yd->inpa);
    - yd->inpa = 0;
    - }
    -
    - purple_http_connection_set_destroy(yd->http_reqs);
    - yd->http_reqs = NULL;
    -
    - for (l = yd->confs; l; l = l->next) {
    - PurpleChatConversation *conv = l->data;
    - GList *users;
    -
    - users = purple_chat_conversation_get_users(conv);
    - yahoo_conf_leave(yd,
    - purple_conversation_get_name(PURPLE_CONVERSATION(conv)),
    - purple_connection_get_display_name(gc), users);
    - g_list_free(users);
    - }
    - g_slist_free(yd->confs);
    -
    - g_slist_free_full(yd->cookies, g_free);
    -
    - yd->chat_online = FALSE;
    - if (yd->in_chat)
    - yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */
    -
    - purple_timeout_remove(yd->yahoo_p2p_timer);
    - if(yd->yahoo_p2p_server_timeout_handle != 0) {
    - purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle);
    - yd->yahoo_p2p_server_timeout_handle = 0;
    - }
    -
    - /* close p2p server if it is waiting for a peer to connect */
    - if (yd->yahoo_p2p_server_watcher) {
    - purple_input_remove(yd->yahoo_p2p_server_watcher);
    - yd->yahoo_p2p_server_watcher = 0;
    - }
    - if (yd->yahoo_local_p2p_server_fd >= 0) {
    - close(yd->yahoo_local_p2p_server_fd);
    - yd->yahoo_local_p2p_server_fd = -1;
    - }
    -
    - g_hash_table_destroy(yd->sms_carrier);
    - g_hash_table_destroy(yd->peers);
    - g_hash_table_destroy(yd->friends);
    - g_hash_table_destroy(yd->imvironments);
    - g_hash_table_destroy(yd->xfer_peer_idstring_map);
    - g_free(yd->chat_name);
    -
    - g_free(yd->cookie_y);
    - g_free(yd->cookie_t);
    - g_free(yd->cookie_b);
    -
    - if (yd->txhandler)
    - purple_input_remove(yd->txhandler);
    -
    - g_object_unref(G_OBJECT(yd->txbuf));
    -
    - if (yd->fd >= 0)
    - close(yd->fd);
    -
    - g_free(yd->rxqueue);
    - yd->rxlen = 0;
    - g_free(yd->picture_url);
    -
    - purple_http_conn_cancel(yd->picture_upload_hc);
    - if (yd->picture_upload_todo)
    - yahoo_buddy_icon_upload_data_free(yd->picture_upload_todo);
    - if (yd->ycht)
    - ycht_connection_close(yd->ycht);
    - if (yd->listen_data != NULL)
    - purple_network_listen_cancel(yd->listen_data);
    -
    - g_free(yd->pending_chat_room);
    - g_free(yd->pending_chat_id);
    - g_free(yd->pending_chat_topic);
    - g_free(yd->pending_chat_goto);
    - g_strfreev(yd->profiles);
    -
    - yahoo_personal_details_reset(&yd->ypd, TRUE);
    -
    - g_free(yd->current_list15_grp);
    -
    - g_free(yd);
    - purple_connection_set_protocol_data(gc, NULL);
    -}
    -
    -const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b)
    -{
    - return "yahoo";
    -}
    -
    -const char *yahoo_list_emblem(PurpleBuddy *b)
    -{
    - PurpleAccount *account;
    - PurpleConnection *gc;
    - YahooFriend *f;
    - PurplePresence *presence;
    -
    - if (!b || !(account = purple_buddy_get_account(b)) ||
    - !(gc = purple_account_get_connection(account)) ||
    - !purple_connection_get_protocol_data(gc))
    - return NULL;
    -
    - f = yahoo_friend_find(gc, purple_buddy_get_name(b));
    - if (!f) {
    - return "not-authorized";
    - }
    -
    - presence = purple_buddy_get_presence(b);
    -
    - if (purple_presence_is_online(presence)) {
    - if (yahoo_friend_get_game(f))
    - return "game";
    - }
    - return NULL;
    -}
    -
    -static const char *yahoo_get_status_string(enum yahoo_status a)
    -{
    - switch (a) {
    - case YAHOO_STATUS_BRB:
    - return _("Be Right Back");
    - case YAHOO_STATUS_BUSY:
    - return _("Busy");
    - case YAHOO_STATUS_NOTATHOME:
    - return _("Not at Home");
    - case YAHOO_STATUS_NOTATDESK:
    - return _("Not at Desk");
    - case YAHOO_STATUS_NOTINOFFICE:
    - return _("Not in Office");
    - case YAHOO_STATUS_ONPHONE:
    - return _("On the Phone");
    - case YAHOO_STATUS_ONVACATION:
    - return _("On Vacation");
    - case YAHOO_STATUS_OUTTOLUNCH:
    - return _("Out to Lunch");
    - case YAHOO_STATUS_STEPPEDOUT:
    - return _("Stepped Out");
    - case YAHOO_STATUS_INVISIBLE:
    - return _("Invisible");
    - case YAHOO_STATUS_IDLE:
    - return _("Idle");
    - case YAHOO_STATUS_OFFLINE:
    - return _("Offline");
    - default:
    - return _("Available");
    - }
    -}
    -
    -static void yahoo_initiate_conference(PurpleBlistNode *node, gpointer data) {
    -
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    -
    - GHashTable *components;
    - YahooData *yd;
    - int id;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *) node;
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    - yd = purple_connection_get_protocol_data(gc);
    - id = yd->conf_id;
    -
    - components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    - g_hash_table_replace(components, g_strdup("room"),
    - g_strdup_printf("%s-%d", purple_connection_get_display_name(gc), id));
    - g_hash_table_replace(components, g_strdup("topic"), g_strdup("Join my conference..."));
    - g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference"));
    - yahoo_c_join(gc, components);
    - g_hash_table_destroy(components);
    -
    - yahoo_c_invite(gc, id, "Join my conference...", purple_buddy_get_name(buddy));
    -}
    -
    -static void yahoo_presence_settings(PurpleBlistNode *node, gpointer data) {
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    - int presence_val = GPOINTER_TO_INT(data);
    -
    - buddy = (PurpleBuddy *) node;
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    -
    - yahoo_friend_update_presence(gc, purple_buddy_get_name(buddy), presence_val);
    -}
    -
    -static void yahoo_game(PurpleBlistNode *node, gpointer data) {
    -
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    -
    - const char *game;
    - char *game2;
    - char *t;
    - char url[256];
    - YahooFriend *f;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *) node;
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    -
    - f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
    - if (!f)
    - return;
    -
    - game = yahoo_friend_get_game(f);
    - if (!game)
    - return;
    -
    - t = game2 = g_strdup(strstr(game, "ante?room="));
    - while (*t && *t != '\t')
    - t++;
    - *t = 0;
    - g_snprintf(url, sizeof url, "http://games.yahoo.com/games/%s", game2);
    - purple_notify_uri(gc, url);
    - g_free(game2);
    -}
    -
    -char *yahoo_status_text(PurpleBuddy *b)
    -{
    - YahooFriend *f = NULL;
    - const char *msg;
    - char *msg2;
    - PurpleAccount *account;
    - PurpleConnection *gc;
    -
    - account = purple_buddy_get_account(b);
    - gc = purple_account_get_connection(account);
    - if (!gc || !purple_connection_get_protocol_data(gc))
    - return NULL;
    -
    - f = yahoo_friend_find(gc, purple_buddy_get_name(b));
    - if (!f)
    - return g_strdup(_("Not on server list"));
    -
    - switch (f->status) {
    - case YAHOO_STATUS_AVAILABLE:
    - return NULL;
    - case YAHOO_STATUS_IDLE:
    - if (f->idle == -1)
    - return g_strdup(yahoo_get_status_string(f->status));
    - return NULL;
    - case YAHOO_STATUS_CUSTOM:
    - if (!(msg = yahoo_friend_get_status_message(f)))
    - return NULL;
    - msg2 = g_markup_escape_text(msg, strlen(msg));
    - purple_util_chrreplace(msg2, '\n', ' ');
    - return msg2;
    -
    - default:
    - return g_strdup(yahoo_get_status_string(f->status));
    - }
    -}
    -
    -void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
    -{
    - YahooFriend *f;
    - char *status = NULL;
    - const char *presence = NULL;
    - PurpleAccount *account;
    -
    - account = purple_buddy_get_account(b);
    - f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b));
    - if (!f)
    - status = g_strdup_printf("\n%s", _("Not on server list"));
    - else {
    - switch (f->status) {
    - case YAHOO_STATUS_CUSTOM:
    - if (!yahoo_friend_get_status_message(f))
    - return;
    - status = g_strdup(yahoo_friend_get_status_message(f));
    - break;
    - case YAHOO_STATUS_OFFLINE:
    - break;
    - default:
    - status = g_strdup(yahoo_get_status_string(f->status));
    - break;
    - }
    -
    - switch (f->presence) {
    - case YAHOO_PRESENCE_ONLINE:
    - presence = _("Appear Online");
    - break;
    - case YAHOO_PRESENCE_PERM_OFFLINE:
    - presence = _("Appear Permanently Offline");
    - break;
    - case YAHOO_PRESENCE_DEFAULT:
    - break;
    - default:
    - purple_debug_error("yahoo", "Unknown presence in yahoo_tooltip_text\n");
    - break;
    - }
    - }
    -
    - if (status != NULL) {
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status);
    - g_free(status);
    - }
    -
    - if (presence != NULL)
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Presence"), presence);
    -
    - if (f && full) {
    - YahooPersonalDetails *ypd = &f->ypd;
    - if (ypd->phone.home && *ypd->phone.home)
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Home Phone Number"), ypd->phone.home);
    - if (ypd->phone.work && *ypd->phone.work)
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Work Phone Number"), ypd->phone.work);
    - if (ypd->phone.mobile && *ypd->phone.mobile)
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Mobile Phone Number"), ypd->phone.mobile);
    - }
    -}
    -
    -static void yahoo_addbuddyfrommenu_cb(PurpleBlistNode *node, gpointer data)
    -{
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *) node;
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    -
    - yahoo_add_buddy(gc, buddy, NULL, NULL);
    -}
    -
    -
    -static void yahoo_chat_goto_menu(PurpleBlistNode *node, gpointer data)
    -{
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *) node;
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    -
    - yahoo_chat_goto(gc, purple_buddy_get_name(buddy));
    -}
    -
    -static GList *build_presence_submenu(YahooFriend *f, PurpleConnection *gc) {
    - GList *m = NULL;
    - PurpleMenuAction *act;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    -
    - if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
    - if (f->presence != YAHOO_PRESENCE_ONLINE) {
    - act = purple_menu_action_new(_("Appear Online"),
    - PURPLE_CALLBACK(yahoo_presence_settings),
    - GINT_TO_POINTER(YAHOO_PRESENCE_ONLINE),
    - NULL);
    - m = g_list_append(m, act);
    - } else if (f->presence != YAHOO_PRESENCE_DEFAULT) {
    - act = purple_menu_action_new(_("Appear Offline"),
    - PURPLE_CALLBACK(yahoo_presence_settings),
    - GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
    - NULL);
    - m = g_list_append(m, act);
    - }
    - }
    -
    - if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) {
    - act = purple_menu_action_new(_("Don't Appear Permanently Offline"),
    - PURPLE_CALLBACK(yahoo_presence_settings),
    - GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
    - NULL);
    - m = g_list_append(m, act);
    - } else {
    - act = purple_menu_action_new(_("Appear Permanently Offline"),
    - PURPLE_CALLBACK(yahoo_presence_settings),
    - GINT_TO_POINTER(YAHOO_PRESENCE_PERM_OFFLINE),
    - NULL);
    - m = g_list_append(m, act);
    - }
    -
    - return m;
    -}
    -
    -static void yahoo_doodle_blist_node(PurpleBlistNode *node, gpointer data)
    -{
    - PurpleBuddy *b = (PurpleBuddy *)node;
    - PurpleAccount *account = purple_buddy_get_account(b);
    - PurpleConnection *gc = purple_account_get_connection(account);
    -
    - yahoo_doodle_initiate(gc, purple_buddy_get_name(b));
    -}
    -
    -#if 0
    -/* XXX: it doesn't seems to work */
    -static void
    -yahoo_userinfo_blist_node(PurpleBlistNode *node, gpointer data)
    -{
    - PurpleBuddy *b = (PurpleBuddy *)node;
    - PurpleAccount *account = purple_buddy_get_account(b);
    - PurpleConnection *gc = purple_account_get_connection(account);
    -
    - yahoo_set_userinfo_for_buddy(gc, b);
    -}
    -#endif
    -
    -static GList *yahoo_buddy_menu(PurpleBuddy *buddy)
    -{
    - GList *m = NULL;
    - PurpleMenuAction *act;
    -
    - PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - static char buf2[1024];
    - YahooFriend *f;
    -
    - f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
    -
    - if (!f && !yd->wm) {
    - act = purple_menu_action_new(_("Add Buddy"),
    - PURPLE_CALLBACK(yahoo_addbuddyfrommenu_cb),
    - NULL, NULL);
    - m = g_list_append(m, act);
    -
    - return m;
    -
    - }
    -
    - if (f && f->status != YAHOO_STATUS_OFFLINE) {
    - if (!yd->wm) {
    - act = purple_menu_action_new(_("Join in Chat"),
    - PURPLE_CALLBACK(yahoo_chat_goto_menu),
    - NULL, NULL);
    - m = g_list_append(m, act);
    - }
    -
    - act = purple_menu_action_new(_("Initiate Conference"),
    - PURPLE_CALLBACK(yahoo_initiate_conference),
    - NULL, NULL);
    - m = g_list_append(m, act);
    -
    - if (yahoo_friend_get_game(f)) {
    - const char *game = yahoo_friend_get_game(f);
    - char *room;
    - char *t;
    -
    - if ((room = strstr(game, "&follow="))) {/* skip ahead to the url */
    - while (*room && *room != '\t') /* skip to the tab */
    - room++;
    - t = room++; /* room as now at the name */
    - while (*t != '\n')
    - t++; /* replace the \n with a space */
    - *t = ' ';
    - g_snprintf(buf2, sizeof buf2, "%s", room);
    -
    - act = purple_menu_action_new(buf2,
    - PURPLE_CALLBACK(yahoo_game),
    - NULL, NULL);
    - m = g_list_append(m, act);
    - }
    - }
    - }
    -
    - if (f) {
    - act = purple_menu_action_new(_("Presence Settings"), NULL, NULL,
    - build_presence_submenu(f, gc));
    - m = g_list_append(m, act);
    -
    - act = purple_menu_action_new(_("Start Doodling"),
    - PURPLE_CALLBACK(yahoo_doodle_blist_node),
    - NULL, NULL);
    - m = g_list_append(m, act);
    -
    -#if 0
    - /* XXX: it doesn't seems to work */
    - act = purple_menu_action_new(_("Set User Info..."),
    - PURPLE_CALLBACK(yahoo_userinfo_blist_node),
    - NULL, NULL);
    - m = g_list_append(m, act);
    -#endif
    - }
    -
    - return m;
    -}
    -
    -GList *yahoo_blist_node_menu(PurpleBlistNode *node)
    -{
    - if(PURPLE_IS_BUDDY(node)) {
    - return yahoo_buddy_menu((PurpleBuddy *) node);
    - } else {
    - return NULL;
    - }
    -}
    -
    -static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - const char *name = yd->profiles[GPOINTER_TO_INT(purple_request_fields_get_choice(fields, "id"))];
    -
    - struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash_str(pkt, 3, name);
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - purple_connection_set_display_name(gc, name);
    -}
    -
    -static void
    -yahoo_get_inbox_token_cb(PurpleHttpConnection *http_conn,
    - PurpleHttpResponse *response, gpointer _unused)
    -{
    - PurpleConnection *gc =
    - purple_http_conn_get_purple_connection(http_conn);
    - gchar *url;
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    -
    - if (!purple_http_response_is_successful(response)) {
    - purple_debug_error("yahoo",
    - "Requesting mail login token failed: %s\n",
    - purple_http_response_get_error(response));
    - url = g_strdup(YAHOO_MAIL_URL);
    - } else {
    - /* Should we not be hardcoding the rd url? */
    - gchar *token;
    - token = g_strdup(purple_http_response_get_data(response, NULL));
    - g_strstrip(token);
    - url = g_strdup_printf(
    - "http://login.yahoo.com/config/reset_cookies_token?"
    - ".token=%s"
    - "&.done=http://us.rd.yahoo.com/messenger/client/%%3f"
    - "http://mail.yahoo.com/", token);
    - purple_str_wipe(token);
    - }
    -
    - /* Open the mailbox with the parsed url data */
    - purple_notify_uri(gc, url);
    -
    - g_free(url);
    -}
    -
    -
    -static void yahoo_show_inbox(PurpleProtocolAction *action)
    -{
    - /* Setup a cookie that can be used by the browser */
    -
    - PurpleConnection *gc = action->connection;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - PurpleHttpRequest *req;
    - PurpleHttpCookieJar *cookiejar;
    -
    - req = purple_http_request_new(
    - "https://login.yahoo.com/config/cookie_token");
    - purple_http_request_set_method(req, "POST");
    - purple_http_request_header_set(req, "User-Agent",
    - YAHOO_CLIENT_USERAGENT);
    - cookiejar = purple_http_request_get_cookie_jar(req);
    - purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t);
    - purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y);
    - purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
    - req, yahoo_get_inbox_token_cb, NULL));
    - purple_http_request_unref(req);
    -}
    -
    -#if 0
    -/* XXX: it doesn't seems to work */
    -static void
    -yahoo_set_userinfo_fn(PurpleProtocolAction *action)
    -{
    - yahoo_set_userinfo(action->connection);
    -}
    -#endif
    -
    -static void yahoo_show_act_id(PurpleProtocolAction *action)
    -{
    - PurpleRequestFields *fields;
    - PurpleRequestFieldGroup *group;
    - PurpleRequestField *field;
    - PurpleConnection *gc = (PurpleConnection *) action->connection;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - const char *name = purple_connection_get_display_name(gc);
    - int iter;
    -
    - fields = purple_request_fields_new();
    - group = purple_request_field_group_new(NULL);
    - purple_request_fields_add_group(fields, group);
    - field = purple_request_field_choice_new("id", _("Activate which ID?"), 0);
    - purple_request_field_group_add_field(group, field);
    -
    - for (iter = 0; yd->profiles[iter]; iter++) {
    - purple_request_field_choice_add(field, yd->profiles[iter], GINT_TO_POINTER(iter));
    - if (purple_strequal(yd->profiles[iter], name))
    - purple_request_field_choice_set_default_value(field, GINT_TO_POINTER(iter));
    - }
    -
    - purple_request_fields(gc, NULL, _("Select the ID you want to activate"), NULL,
    - fields,
    - _("OK"), G_CALLBACK(yahoo_act_id),
    - _("Cancel"), NULL,
    - purple_request_cpar_from_connection(gc), gc);
    -}
    -
    -static void yahoo_show_chat_goto(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - purple_request_input(gc, NULL, _("Join whom in chat?"), NULL,
    - "", FALSE, FALSE, NULL,
    - _("OK"), G_CALLBACK(yahoo_chat_goto),
    - _("Cancel"), NULL,
    - purple_request_cpar_from_connection(gc),
    - gc);
    -}
    -
    -GList *yahoo_get_actions(PurpleConnection *gc) {
    - GList *m = NULL;
    - PurpleProtocolAction *act;
    -
    -#if 0
    - /* XXX: it doesn't seems to work */
    - act = purple_protocol_action_new(_("Set User Info..."),
    - yahoo_set_userinfo_fn);
    - m = g_list_append(m, act);
    -#endif
    -
    - act = purple_protocol_action_new(_("Activate ID..."),
    - yahoo_show_act_id);
    - m = g_list_append(m, act);
    -
    - act = purple_protocol_action_new(_("Join User in Chat..."),
    - yahoo_show_chat_goto);
    - m = g_list_append(m, act);
    -
    - m = g_list_append(m, NULL);
    - act = purple_protocol_action_new(_("Open Inbox"),
    - yahoo_show_inbox);
    - m = g_list_append(m, act);
    -
    - return m;
    -}
    -
    -struct yahoo_sms_carrier_cb_data {
    - PurpleConnection *gc;
    - char *who;
    - char *what;
    -};
    -
    -static void yahoo_get_sms_carrier_cb(PurpleHttpConnection *http_conn,
    - PurpleHttpResponse *response, gpointer _sms_cb_data)
    -{
    - struct yahoo_sms_carrier_cb_data *sms_cb_data = _sms_cb_data;
    - PurpleConnection *gc = sms_cb_data->gc;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - char *status = NULL;
    - char *carrier = NULL;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleIMConversation *im = purple_conversations_find_im_with_account(sms_cb_data->who, account);
    -
    - if (!purple_http_response_is_successful(response)) {
    - purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
    - _("Can't send SMS. Unable to obtain mobile carrier."), 0);
    -
    - g_free(sms_cb_data->who);
    - g_free(sms_cb_data->what);
    - g_free(sms_cb_data);
    - return ;
    - } else {
    - const gchar *got_data = purple_http_response_get_data(response, NULL);
    - PurpleXmlNode *validate_data_root = purple_xmlnode_from_str(got_data, -1);
    - PurpleXmlNode *validate_data_child = purple_xmlnode_get_child(validate_data_root, "mobile_no");
    - const char *mobile_no = purple_xmlnode_get_attrib(validate_data_child, "msisdn");
    -
    - validate_data_root = purple_xmlnode_copy(validate_data_child);
    - validate_data_child = purple_xmlnode_get_child(validate_data_root, "status");
    - status = purple_xmlnode_get_data(validate_data_child);
    -
    - validate_data_child = purple_xmlnode_get_child(validate_data_root, "carrier");
    - carrier = purple_xmlnode_get_data(validate_data_child);
    -
    - purple_debug_info("yahoo", "SMS validate data: %s\n", got_data);
    -
    - if (status && g_str_equal(status, "Valid")) {
    - g_hash_table_insert(yd->sms_carrier,
    - g_strdup_printf("+%s", mobile_no), g_strdup(carrier));
    - yahoo_send_im(sms_cb_data->gc, purple_message_new_outgoing(
    - sms_cb_data->who, sms_cb_data->what, 0));
    - } else {
    - g_hash_table_insert(yd->sms_carrier,
    - g_strdup_printf("+%s", mobile_no), g_strdup("Unknown"));
    - purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
    - _("Can't send SMS. Unknown mobile carrier."), 0);
    - }
    -
    - purple_xmlnode_free(validate_data_child);
    - purple_xmlnode_free(validate_data_root);
    - g_free(sms_cb_data->who);
    - g_free(sms_cb_data->what);
    - g_free(sms_cb_data);
    - g_free(status);
    - g_free(carrier);
    - }
    -}
    -
    -static void yahoo_get_sms_carrier(PurpleConnection *gc, gpointer data)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - PurpleHttpRequest *req;
    - PurpleHttpCookieJar *cookiejar;
    - struct yahoo_sms_carrier_cb_data *sms_cb_data;
    - char *validate_request_str = NULL;
    - PurpleXmlNode *validate_request_root = NULL;
    - PurpleXmlNode *validate_request_child = NULL;
    -
    - if(!(sms_cb_data = data))
    - return;
    -
    - validate_request_root = purple_xmlnode_new("validate");
    - purple_xmlnode_set_attrib(validate_request_root, "intl", "us");
    - purple_xmlnode_set_attrib(validate_request_root, "version", YAHOO_CLIENT_VERSION);
    - purple_xmlnode_set_attrib(validate_request_root, "qos", "0");
    -
    - validate_request_child = purple_xmlnode_new_child(validate_request_root, "mobile_no");
    - purple_xmlnode_set_attrib(validate_request_child, "msisdn", sms_cb_data->who + 1);
    -
    - validate_request_str = purple_xmlnode_to_str(validate_request_root, NULL);
    -
    - purple_xmlnode_free(validate_request_child);
    - purple_xmlnode_free(validate_request_root);
    -
    - req = purple_http_request_new(NULL);
    - purple_http_request_set_url_printf(req, "http://validate.msg.yahoo.com"
    - "/mobileno?intl=us&version=%s", YAHOO_CLIENT_VERSION);
    - purple_http_request_set_method(req, "POST");
    - purple_http_request_header_set(req, "User-Agent",
    - YAHOO_CLIENT_USERAGENT);
    - cookiejar = purple_http_request_get_cookie_jar(req);
    - purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t);
    - purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y);
    - purple_http_request_set_contents(req, validate_request_str, -1);
    - purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
    - req, yahoo_get_sms_carrier_cb, data));
    - purple_http_request_unref(req);
    -
    - g_free(validate_request_str);
    -}
    -
    -int yahoo_send_im(PurpleConnection *gc, PurpleMessage *pmsg)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - struct yahoo_packet *pkt = NULL;
    - char *msg = yahoo_html_to_codes(purple_message_get_contents(pmsg));
    - char *msg2;
    - PurpleWhiteboard *wb;
    - int ret = 1;
    - gsize lenb = 0;
    - glong lenc = 0;
    - struct yahoo_p2p_data *p2p_data;
    - const gchar *rcpt = purple_message_get_recipient(pmsg);
    -
    - msg2 = yahoo_string_encode(gc, msg, TRUE);
    -
    - if(msg2) {
    - lenb = strlen(msg2);
    - lenc = g_utf8_strlen(msg2, -1);
    -
    - if(lenb > YAHOO_MAX_MESSAGE_LENGTH_BYTES || lenc > YAHOO_MAX_MESSAGE_LENGTH_CHARS) {
    - purple_debug_info("yahoo", "Message too big. Length is %" G_GSIZE_FORMAT
    - " bytes, %ld characters. Max is %d bytes, %d chars."
    - " Message is '%s'.\n", lenb, lenc, YAHOO_MAX_MESSAGE_LENGTH_BYTES,
    - YAHOO_MAX_MESSAGE_LENGTH_CHARS, msg2);
    - g_free(msg);
    - g_free(msg2);
    - return -E2BIG;
    - }
    - }
    -
    - if (rcpt[0] == '+') {
    - /* we have an sms to be sent */
    - gchar *carrier = NULL;
    - const char *alias = NULL;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleIMConversation *im = purple_conversations_find_im_with_account(rcpt, account);
    -
    - carrier = g_hash_table_lookup(yd->sms_carrier, rcpt);
    - if (!carrier) {
    - struct yahoo_sms_carrier_cb_data *sms_cb_data;
    - sms_cb_data = g_malloc(sizeof(struct yahoo_sms_carrier_cb_data));
    - sms_cb_data->gc = gc;
    - sms_cb_data->who = g_strdup(rcpt);
    - sms_cb_data->what = g_strdup(purple_message_get_contents(pmsg));
    -
    - purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
    - _("Getting mobile carrier to send the SMS."), 0);
    -
    - yahoo_get_sms_carrier(gc, sms_cb_data);
    -
    - g_free(msg);
    - g_free(msg2);
    - return ret;
    - }
    - else if( strcmp(carrier,"Unknown") == 0 ) {
    - purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
    - _("Can't send SMS. Unknown mobile carrier."), 0);
    -
    - g_free(msg);
    - g_free(msg2);
    - return -1;
    - }
    -
    - alias = purple_account_get_private_alias(account);
    - pkt = yahoo_packet_new(YAHOO_SERVICE_SMS_MSG, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash(pkt, "sssss",
    - 1, purple_connection_get_display_name(gc),
    - 69, alias,
    - 5, rcpt + 1,
    - 68, carrier,
    - 14, msg2);
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - g_free(msg);
    - g_free(msg2);
    -
    - return ret;
    - }
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id);
    - yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, rcpt);
    -
    - yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
    - yahoo_packet_hash_str(pkt, 14, msg2);
    -
    - /*
    - * IMVironment.
    - *
    - * If this message is to a user who is also Doodling with the local user,
    - * format the chat packet with the correct IMV information (thanks Yahoo!)
    - *
    - * Otherwise attempt to use the same IMVironment as the remote user,
    - * just so that we don't inadvertantly reset their IMVironment back
    - * to nothing.
    - *
    - * If they have not set an IMVironment, then use the default.
    - */
    - wb = purple_whiteboard_get_session(purple_connection_get_account(gc), rcpt);
    - if (wb)
    - yahoo_packet_hash_str(pkt, 63, DOODLE_IMV_KEY);
    - else
    - {
    - const char *imv;
    - imv = g_hash_table_lookup(yd->imvironments, rcpt);
    - if (imv != NULL)
    - yahoo_packet_hash_str(pkt, 63, imv);
    - else
    - yahoo_packet_hash_str(pkt, 63, ";0");
    - }
    -
    - yahoo_packet_hash_str(pkt, 64, "0"); /* no idea */
    - yahoo_packet_hash_str(pkt, 1002, "1"); /* no idea, Yahoo 6 or later only it seems */
    - if (!yd->picture_url)
    - yahoo_packet_hash_str(pkt, 206, "0"); /* 0 = no picture, 2 = picture, maybe 1 = avatar? */
    - else
    - yahoo_packet_hash_str(pkt, 206, "2");
    -
    - /* We may need to not send any packets over 2000 bytes, but I'm not sure yet. */
    - if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000) {
    - /* if p2p link exists, send through it. To-do: key 15, time value to be sent in case of p2p */
    - if( (p2p_data = g_hash_table_lookup(yd->peers, rcpt))) {
    - yahoo_packet_hash_int(pkt, 11, p2p_data->session_id);
    - yahoo_p2p_write_pkt(p2p_data->source, pkt);
    - }
    - else {
    - yahoo_packet_send(pkt, yd);
    - yahoo_send_p2p_pkt(gc, rcpt, 0); /* send p2p packet, with val_13=0 */
    - }
    - }
    - else
    - ret = -E2BIG;
    -
    - yahoo_packet_free(pkt);
    -
    - g_free(msg);
    - g_free(msg2);
    -
    - return ret;
    -}
    -
    -unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - struct yahoo_p2p_data *p2p_data;
    - struct yahoo_packet *pkt = NULL;
    -
    - /* Don't do anything if sms is being typed */
    - if( strncmp(who, "+", 1) == 0 )
    - return 0;
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, yd->session_id);
    -
    - /* check to see if p2p link exists, send through it */
    - if( (p2p_data = g_hash_table_lookup(yd->peers, who))) {
    - yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc),
    - 14, " ", 13, state == PURPLE_IM_TYPING ? "1" : "0",
    - 5, who, 11, p2p_data->session_id, 1002, "1"); /* To-do: key 15 to be sent in case of p2p */
    - yahoo_p2p_write_pkt(p2p_data->source, pkt);
    - yahoo_packet_free(pkt);
    - }
    - else { /* send through yahoo server */
    -
    - yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
    - 14, " ", 13, state == PURPLE_IM_TYPING ? "1" : "0",
    - 5, who, 1002, "1");
    - yahoo_packet_send_and_free(pkt, yd);
    - }
    -
    - return 0;
    -}
    -
    -static void yahoo_session_presence_remove(gpointer key, gpointer value, gpointer data)
    -{
    - YahooFriend *f = value;
    - if (f && f->presence == YAHOO_PRESENCE_ONLINE)
    - f->presence = YAHOO_PRESENCE_DEFAULT;
    -}
    -
    -void yahoo_set_status(PurpleAccount *account, PurpleStatus *status)
    -{
    - PurpleConnection *gc;
    - PurplePresence *presence;
    - YahooData *yd;
    - struct yahoo_packet *pkt;
    - int old_status;
    - const char *msg = NULL;
    - char *tmp = NULL;
    - char *conv_msg = NULL;
    -
    - if (!purple_status_is_active(status))
    - return;
    -
    - gc = purple_account_get_connection(account);
    - presence = purple_status_get_presence(status);
    - yd = purple_connection_get_protocol_data(gc);
    - old_status = yd->current_status;
    -
    - yd->current_status = get_yahoo_status_from_purple_status(status);
    -
    - if (yd->current_status == YAHOO_STATUS_CUSTOM)
    - {
    - msg = purple_status_get_attr_string(status, "message");
    -
    - if (purple_status_is_available(status)) {
    - tmp = yahoo_string_encode(gc, msg, TRUE);
    - conv_msg = purple_markup_strip_html(tmp);
    - g_free(tmp);
    - } else {
    - if ((msg == NULL) || (*msg == '\0'))
    - msg = _("Away");
    - tmp = yahoo_string_encode(gc, msg, TRUE);
    - conv_msg = purple_markup_strip_html(tmp);
    - g_free(tmp);
    - }
    - }
    -
    - if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
    - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash_str(pkt, 13, "2");
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - return;
    - }
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash_int(pkt, 10, yd->current_status);
    -
    - if (yd->current_status == YAHOO_STATUS_CUSTOM) {
    - yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
    - yahoo_packet_hash_str(pkt, 19, conv_msg);
    - } else {
    - yahoo_packet_hash_str(pkt, 19, "");
    - }
    -
    - g_free(conv_msg);
    -
    - if (purple_presence_is_idle(presence))
    - yahoo_packet_hash_str(pkt, 47, "2");
    - else {
    - if (!purple_status_is_available(status))
    - yahoo_packet_hash_str(pkt, 47, "1");
    - else
    - yahoo_packet_hash_str(pkt, 47, "0");
    - }
    -
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - if (old_status == YAHOO_STATUS_INVISIBLE) {
    - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash_str(pkt, 13, "1");
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - /* Any per-session presence settings are removed */
    - g_hash_table_foreach(yd->friends, yahoo_session_presence_remove, NULL);
    -
    - }
    -}
    -
    -void yahoo_set_idle(PurpleConnection *gc, int idle)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - struct yahoo_packet *pkt = NULL;
    - char *msg = NULL, *msg2 = NULL;
    - PurpleStatus *status = NULL;
    - gboolean invisible = FALSE;
    -
    - if (idle && yd->current_status != YAHOO_STATUS_CUSTOM)
    - yd->current_status = YAHOO_STATUS_IDLE;
    - else if (!idle && yd->current_status == YAHOO_STATUS_IDLE) {
    - status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
    - yd->current_status = get_yahoo_status_from_purple_status(status);
    - }
    -
    - invisible = (yd->current_status == YAHOO_STATUS_INVISIBLE);
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, yd->session_id);
    -
    - if (!idle && invisible)
    - yahoo_packet_hash_int(pkt, 10, YAHOO_STATUS_AVAILABLE);
    - else
    - yahoo_packet_hash_int(pkt, 10, yd->current_status);
    -
    - if (yd->current_status == YAHOO_STATUS_CUSTOM) {
    - const char *tmp;
    - if (status == NULL)
    - status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
    - tmp = purple_status_get_attr_string(status, "message");
    - if (tmp != NULL) {
    - msg = yahoo_string_encode(gc, tmp, TRUE);
    - msg2 = purple_markup_strip_html(msg);
    - yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
    - yahoo_packet_hash_str(pkt, 19, msg2);
    - } else {
    - /* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
    - * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message */
    - yahoo_packet_hash_str(pkt, 19, _("Away"));
    - }
    - } else {
    - yahoo_packet_hash_str(pkt, 19, "");
    - }
    -
    - if (idle)
    - yahoo_packet_hash_str(pkt, 47, "2");
    - else if (yd->current_status == YAHOO_STATUS_CUSTOM &&
    - !purple_status_is_available(status))
    - /* We are still unavailable in this case.
    - * Make sure Yahoo knows that */
    - yahoo_packet_hash_str(pkt, 47, "1");
    -
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - g_free(msg);
    - g_free(msg2);
    -}
    -
    -GList *yahoo_status_types(PurpleAccount *account)
    -{
    - PurpleStatusType *type;
    - GList *types = NULL;
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, YAHOO_STATUS_TYPE_AVAILABLE,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_AWAY,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_BRB, _("Be Right Back"), TRUE);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_BUSY,
    - _("Busy"), TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATHOME, _("Not at Home"), TRUE);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATDESK, _("Not at Desk"), TRUE);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTINOFFICE, _("Not in Office"), TRUE);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_ONPHONE, _("On the Phone"), TRUE);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new(PURPLE_STATUS_EXTENDED_AWAY, YAHOO_STATUS_TYPE_ONVACATION, _("On Vacation"), TRUE);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_OUTTOLUNCH, _("Out to Lunch"), TRUE);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_STEPPEDOUT, _("Stepped Out"), TRUE);
    - types = g_list_append(types, type);
    -
    -
    - type = purple_status_type_new(PURPLE_STATUS_INVISIBLE, YAHOO_STATUS_TYPE_INVISIBLE, NULL, TRUE);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new(PURPLE_STATUS_OFFLINE, YAHOO_STATUS_TYPE_OFFLINE, NULL, TRUE);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, YAHOO_STATUS_TYPE_MOBILE, NULL, FALSE, FALSE, TRUE);
    - types = g_list_append(types, type);
    -
    - return types;
    -}
    -
    -void yahoo_keepalive(PurpleConnection *gc)
    -{
    - struct yahoo_packet *pkt;
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - time_t now = time(NULL);
    -
    - /* We're only allowed to send a ping once an hour or the servers will boot us */
    - if ((now - yd->last_ping) >= PING_TIMEOUT) {
    - yd->last_ping = now;
    -
    - /* The native client will only send PING or CHATPING */
    - if (yd->chat_online) {
    - if (yd->wm) {
    - ycht_chat_send_keepalive(yd->ycht);
    - } else {
    - pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash_str(pkt, 109, purple_connection_get_display_name(gc));
    - yahoo_packet_send_and_free(pkt, yd);
    - }
    - } else {
    - pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_send_and_free(pkt, yd);
    - }
    - }
    -
    - if ((now - yd->last_keepalive) >= KEEPALIVE_TIMEOUT) {
    - yd->last_keepalive = now;
    - pkt = yahoo_packet_new(YAHOO_SERVICE_KEEPALIVE, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc));
    - yahoo_packet_send_and_free(pkt, yd);
    - }
    -
    -}
    -
    -void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - struct yahoo_packet *pkt;
    - const char *group = NULL;
    - char *group2;
    - const char *bname = purple_buddy_get_name(buddy);
    -
    - if (!yd->logged_in)
    - return;
    -
    - if (!purple_account_privacy_check(purple_connection_get_account(gc), bname))
    - return;
    -
    - group = purple_group_get_name(purple_buddy_get_group(buddy));
    -
    - group2 = yahoo_string_encode(gc, group, FALSE);
    - pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash(pkt, "ssssssssss",
    - 14, "",
    - 65, group2,
    - 97, "1", /* UTF-8 */
    - 1, purple_connection_get_display_name(gc),
    - 302, "319",
    - 300, "319",
    - 7, bname,
    - 334, "0",
    - 301, "319",
    - 303, "319");
    -
    - yahoo_packet_send_and_free(pkt, yd);
    - g_free(group2);
    -}
    -
    -void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - struct yahoo_packet *pkt;
    - GSList *buddies, *l;
    - PurpleGroup *g;
    - gboolean remove = TRUE;
    - char *cg;
    - const char *bname, *gname;
    - YahooFriend *f = NULL;
    -
    - bname = purple_buddy_get_name(buddy);
    - f = yahoo_friend_find(gc, bname);
    - if (!f)
    - return;
    -
    - gname = purple_group_get_name(group);
    - buddies = purple_blist_find_buddies(purple_connection_get_account(gc), bname);
    - for (l = buddies; l; l = l->next) {
    - g = purple_buddy_get_group(l->data);
    - if (purple_utf8_strcasecmp(gname, purple_group_get_name(g))) {
    - remove = FALSE;
    - break;
    - }
    - }
    -
    - g_slist_free(buddies);
    -
    - if (remove) {
    - g_hash_table_remove(yd->friends, bname);
    - f = NULL; /* f no longer valid - Just making it clear */
    - }
    -
    - cg = yahoo_string_encode(gc, gname, FALSE);
    - pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
    -
    - yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
    - 7, bname, 65, cg);
    - yahoo_packet_send_and_free(pkt, yd);
    - g_free(cg);
    -}
    -
    -void yahoo_add_deny(PurpleConnection *gc, const char *who) {
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - struct yahoo_packet *pkt;
    -
    - if (!yd->logged_in)
    - return;
    -
    - if (!who || who[0] == '\0')
    - return;
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
    -
    - yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "1");
    -
    - yahoo_packet_send_and_free(pkt, yd);
    -}
    -
    -void yahoo_rem_deny(PurpleConnection *gc, const char *who) {
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - struct yahoo_packet *pkt;
    -
    - if (!yd->logged_in)
    - return;
    -
    - if (!who || who[0] == '\0')
    - return;
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
    -
    - yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "2");
    -
    - yahoo_packet_send_and_free(pkt, yd);
    -}
    -
    -void yahoo_set_permit_deny(PurpleConnection *gc)
    -{
    - PurpleAccount *account;
    - GSList *deny;
    -
    - account = purple_connection_get_account(gc);
    -
    - switch (purple_account_get_privacy_type(account))
    - {
    - case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
    - for (deny = purple_account_privacy_get_denied(account); deny; deny = deny->next)
    - yahoo_rem_deny(gc, deny->data);
    - break;
    -
    - case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST:
    - case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
    - case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
    - case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
    - for (deny = purple_account_privacy_get_denied(account); deny; deny = deny->next)
    - yahoo_add_deny(gc, deny->data);
    - break;
    - }
    -}
    -
    -void yahoo_change_buddys_group(PurpleConnection *gc, const char *who,
    - const char *old_group, const char *new_group)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - struct yahoo_packet *pkt;
    - char *gpn, *gpo;
    - YahooFriend *f = yahoo_friend_find(gc, who);
    - const char *temp = NULL;
    -
    - /* Step 0: If they aren't on the server list anyway,
    - * don't bother letting the server know.
    - */
    - if (!f)
    - return;
    -
    - temp = who;
    -
    - /* If old and new are the same, we would probably
    - * end up deleting the buddy, which would be bad.
    - * This might happen because of the charset conversation.
    - */
    - gpn = yahoo_string_encode(gc, new_group, FALSE);
    - gpo = yahoo_string_encode(gc, old_group, FALSE);
    - if (!strcmp(gpn, gpo)) {
    - g_free(gpn);
    - g_free(gpo);
    - return;
    - }
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_CHGRP_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash(pkt, "ssssssss", 1, purple_connection_get_display_name(gc),
    - 302, "240", 300, "240", 7, temp, 224, gpo, 264, gpn, 301,
    - "240", 303, "240");
    - yahoo_packet_send_and_free(pkt, yd);
    -
    - g_free(gpn);
    - g_free(gpo);
    -}
    -
    -void yahoo_rename_group(PurpleConnection *gc, const char *old_name,
    - PurpleGroup *group, GList *moved_buddies)
    -{
    - YahooData *yd = purple_connection_get_protocol_data(gc);
    - struct yahoo_packet *pkt;
    - char *gpn, *gpo;
    -
    - gpn = yahoo_string_encode(gc, purple_group_get_name(group), FALSE);
    - gpo = yahoo_string_encode(gc, old_name, FALSE);
    - if (!strcmp(gpn, gpo)) {
    - g_free(gpn);
    - g_free(gpo);
    - return;
    - }
    -
    - pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, yd->session_id);
    - yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
    - 65, gpo, 67, gpn);
    - yahoo_packet_send_and_free(pkt, yd);
    - g_free(gpn);
    - g_free(gpo);
    -}
    -
    -/********************************* Commands **********************************/
    -
    -PurpleCmdRet
    -yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data) {
    - PurpleAccount *account = purple_conversation_get_account(c);
    -
    - if (*args && args[0])
    - return PURPLE_CMD_RET_FAILED;
    -
    - purple_protocol_send_attention(purple_account_get_connection(account), purple_conversation_get_name(c), YAHOO_BUZZ);
    -
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -PurpleCmdRet
    -yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd,
    - char **args, char **error, void *data)
    -{
    - GHashTable *comp;
    - PurpleConnection *gc;
    -
    - if (!args || !args[0])
    - return PURPLE_CMD_RET_FAILED;
    -
    - gc = purple_conversation_get_connection(conv);
    - purple_debug_info("yahoo", "Trying to join %s \n", args[0]);
    -
    - comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    - g_hash_table_replace(comp, g_strdup("room"), g_ascii_strdown(args[0], -1));
    - g_hash_table_replace(comp, g_strdup("type"), g_strdup("Chat"));
    -
    - yahoo_c_join(gc, comp);
    -
    - g_hash_table_destroy(comp);
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -PurpleCmdRet
    -yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd,
    - char **args, char **error, void *data)
    -{
    - PurpleAccount *account = purple_conversation_get_account(conv);
    - if (*args && args[0])
    - return PURPLE_CMD_RET_FAILED;
    - purple_roomlist_show_with_account(account);
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -gboolean yahoo_offline_message(const PurpleBuddy *buddy)
    -{
    - return TRUE;
    -}
    -
    -gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type)
    -{
    - PurpleIMConversation *im;
    -
    - im = purple_conversations_find_im_with_account(username,
    - purple_connection_get_account(gc));
    -
    - g_return_val_if_fail(im != NULL, FALSE);
    -
    - purple_debug_info("yahoo", "Sending <ding> on account %s to buddy %s.\n",
    - username, purple_conversation_get_name(PURPLE_CONVERSATION(im)));
    - purple_conversation_send_with_flags(PURPLE_CONVERSATION(im), "<ding>", PURPLE_MESSAGE_INVISIBLE);
    -
    - return TRUE;
    -}
    -
    -GList *yahoo_attention_types(PurpleAccount *account)
    -{
    - static GList *list = NULL;
    -
    - if (!list) {
    - /* Yahoo only supports one attention command: the 'buzz'. */
    - /* This is index number YAHOO_BUZZ. */
    - list = g_list_append(list, purple_attention_type_new("Buzz", _("Buzz"),
    - _("%s has buzzed you!"), _("Buzzing %s...")));
    - }
    -
    - return list;
    -}
    -
    -gssize
    -yahoo_get_max_message_size(PurpleConversation *conv)
    -{
    - return YAHOO_MAX_MESSAGE_LENGTH_CHARS;
    -}
    --- a/libpurple/protocols/yahoo/ymsg.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,363 +0,0 @@
    -/**
    - * @file ymsg.h The Yahoo! Protocol
    - *
    - * 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 _YMSG_H_
    -#define _YMSG_H_
    -
    -#include "circularbuffer.h"
    -#include "cmds.h"
    -#include "http.h"
    -#include "protocol.h"
    -#include "network.h"
    -
    -#define YAHOO_PAGER_HOST_REQ_URL "http://vcs2.msg.yahoo.com/capacity"
    -#define YAHOO_PAGER_HOST_FALLBACK "scsa.msg.yahoo.com"
    -#define YAHOO_PAGER_PORT 5050
    -#define YAHOO_PAGER_PORT_P2P 5101
    -#define YAHOO_LOGIN_URL "https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=%s"
    -#define YAHOO_TOKEN_URL "https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s"
    -#define YAHOO_P2P_KEEPALIVE_SECS 300
    -#define YAHOO_P2P_SERVER_TIMEOUT 10
    -#define YAHOO_PROFILE_URL "http://profiles.yahoo.com/"
    -#define YAHOO_MAIL_URL "http://rd.yahoo.com/messenger/client/?http://mail.yahoo.com/"
    -#define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com"
    -#define YAHOO_XFER_RELAY_HOST "relay.msg.yahoo.com"
    -#define YAHOO_XFER_RELAY_PORT 80
    -#define YAHOO_ROOMLIST_URL "http://insider.msg.yahoo.com/ycontent/"
    -#define YAHOO_ROOMLIST_LOCALE "us"
    -
    -#define YAHOO_AUDIBLE_URL "http://l.yimg.com/pu/dl/aud"
    -
    -#define WEBMESSENGER_URL "http://login.yahoo.com/config/login?.src=pg"
    -
    -#define YAHOO_SMS_CARRIER_URL "http://validate.msg.yahoo.com"
    -
    -#define YAHOO_USERINFO_URL "http://address.yahoo.com/yab/us?v=XM&sync=1&tags=short&useutf8=1&noclear=1&legenc=codepage-1252"
    -
    -#define YAHOO_PICURL_SETTING "picture_url"
    -#define YAHOO_PICCKSUM_SETTING "picture_checksum"
    -#define YAHOO_PICEXPIRE_SETTING "picture_expire"
    -
    -#define YAHOO_STATUS_TYPE_OFFLINE "offline"
    -#define YAHOO_STATUS_TYPE_AVAILABLE "available"
    -#define YAHOO_STATUS_TYPE_BRB "brb"
    -#define YAHOO_STATUS_TYPE_BUSY "busy"
    -#define YAHOO_STATUS_TYPE_NOTATHOME "notathome"
    -#define YAHOO_STATUS_TYPE_NOTATDESK "notatdesk"
    -#define YAHOO_STATUS_TYPE_NOTINOFFICE "notinoffice"
    -#define YAHOO_STATUS_TYPE_ONPHONE "onphone"
    -#define YAHOO_STATUS_TYPE_ONVACATION "onvacation"
    -#define YAHOO_STATUS_TYPE_OUTTOLUNCH "outtolunch"
    -#define YAHOO_STATUS_TYPE_STEPPEDOUT "steppedout"
    -#define YAHOO_STATUS_TYPE_AWAY "away"
    -#define YAHOO_STATUS_TYPE_INVISIBLE "invisible"
    -#define YAHOO_STATUS_TYPE_MOBILE "mobile"
    -
    -#define YAHOO_CLIENT_VERSION_ID "4194239"
    -#define YAHOO_CLIENT_VERSION "9.0.0.2162"
    -
    -#define YAHOO_CLIENT_USERAGENT "Mozilla/5.0"
    -#define YAHOO_CLIENT_USERAGENT_ALIAS "Mozilla/4.0 (compatible; MSIE 5.5)"
    -
    -/* Index into attention types list. */
    -#define YAHOO_BUZZ 0
    -
    -typedef enum {
    - YAHOO_PKT_TYPE_SERVER = 0,
    - YAHOO_PKT_TYPE_P2P
    -} yahoo_pkt_type;
    -
    -typedef enum {
    - YAHOO_P2P_WE_ARE_CLIENT =0,
    - YAHOO_P2P_WE_ARE_SERVER
    -} yahoo_p2p_connection_type;
    -
    -enum yahoo_status {
    - YAHOO_STATUS_AVAILABLE = 0,
    - YAHOO_STATUS_BRB,
    - YAHOO_STATUS_BUSY,
    - YAHOO_STATUS_NOTATHOME,
    - YAHOO_STATUS_NOTATDESK,
    - YAHOO_STATUS_NOTINOFFICE,
    - YAHOO_STATUS_ONPHONE,
    - YAHOO_STATUS_ONVACATION,
    - YAHOO_STATUS_OUTTOLUNCH,
    - YAHOO_STATUS_STEPPEDOUT,
    - YAHOO_STATUS_P2P = 11,
    - YAHOO_STATUS_INVISIBLE = 12,
    - YAHOO_STATUS_CUSTOM = 99,
    - YAHOO_STATUS_IDLE = 999,
    - YAHOO_STATUS_WEBLOGIN = 0x5a55aa55,
    - YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */
    - YAHOO_STATUS_TYPING = 0x16,
    - YAHOO_STATUS_DISCONNECTED = -1 /* 0xffffffff; in ymsg 15. doesnt mean the normal sense of 'disconnected' */
    -};
    -
    -struct yahoo_buddy_icon_upload_data {
    - PurpleConnection *gc;
    - char *filename;
    - GString *picture_data;
    -};
    -
    -struct yahoo_p2p_data {
    - PurpleConnection *gc;
    - char *host_ip;
    - char *host_username;
    - int val_13;
    - guint input_event;
    - gint source;
    - int session_id;
    - yahoo_p2p_connection_type connection_type;
    -};
    -
    -struct _YchtConn;
    -
    -typedef struct _YahooPersonalDetails {
    - char *id;
    -
    - struct {
    - char *first;
    - char *last;
    - char *middle;
    - char *nick;
    - } names;
    -
    - struct {
    - char *work;
    - char *home;
    - char *mobile;
    - } phone;
    -} YahooPersonalDetails;
    -
    -typedef struct {
    - PurpleConnection *gc;
    - int fd;
    - guint inpa;
    - guchar *rxqueue;
    - int rxlen;
    - PurpleCircularBuffer *txbuf;
    - guint txhandler;
    - GHashTable *friends;
    -
    - char **profiles; /* Multiple profiles can be associated with an account */
    - YahooPersonalDetails ypd;
    -
    - /**
    - * This is used to keep track of the IMVironment chosen
    - * by people you talk to. We don't do very much with
    - * this right now... but at least now if the remote user
    - * selects an IMVironment we won't reset it back to the
    - * default of nothing.
    - */
    - GHashTable *imvironments;
    -
    - int current_status;
    - gboolean logged_in;
    - GString *tmp_serv_blist, *tmp_serv_ilist, *tmp_serv_plist;
    - GSList *confs;
    - unsigned int conf_id; /* just a counter */
    - gboolean chat_online;
    - gboolean in_chat;
    - char *chat_name;
    - char *pending_chat_room;
    - char *pending_chat_id;
    - char *pending_chat_topic;
    - char *pending_chat_goto;
    - char *auth;
    - char *cookie_y;
    - char *cookie_t;
    - char *cookie_b;
    - int session_id;
    - gboolean wm; /* connected w/ web messenger method */
    - /* picture aka buddy icon stuff */
    - char *picture_url;
    - int picture_checksum;
    -
    - /* ew. we have to check the icon before we connect,
    - * but can't upload it til we're connected. */
    - struct yahoo_buddy_icon_upload_data *picture_upload_todo;
    - PurpleHttpConnection *picture_upload_hc;
    -
    - struct _YchtConn *ycht;
    -
    - /**
    - * This set contains HTTP connections
    - * for when we lookup people profile or photo information.
    - */
    - PurpleHttpConnectionSet *http_reqs;
    -
    - GHashTable *xfer_peer_idstring_map;/* Hey, i dont know, but putting this HashTable next to friends gives a run time fault... */
    - GSList *cookies;/* contains all cookies, including _y and _t */
    - PurpleNetworkListenData *listen_data;
    -
    - /**
    - * We may receive a list15 in multiple packets with no prior warning as to how many we'll be getting;
    - * the server expects us to keep track of the group for which it is sending us contact names.
    - */
    - char *current_list15_grp;
    - time_t last_ping;
    - time_t last_keepalive;
    - GHashTable *peers; /* information about p2p data */
    - int yahoo_p2p_timer;
    - int yahoo_local_p2p_server_fd;
    - int yahoo_p2p_server_watcher;
    - GHashTable *sms_carrier; /* sms carrier data */
    - guint yahoo_p2p_server_timeout_handle;
    -} YahooData;
    -
    -#define YAHOO_MAX_STATUS_MESSAGE_LENGTH (255)
    -
    -/*
    - * Current Maximum Length for Instant Messages
    - *
    - * This was found by experiment.
    - *
    - * The YMSG protocol allows a message of up to 948 bytes, but the official client
    - * limits to 800 characters. According to experiments I conducted, it seems that
    - * the discrepancy is to allow some leeway for messages with mixed single- and
    - * multi-byte characters, as I was able to send messages of 840 and 932 bytes
    - * by using some multibyte characters (some random Chinese or Japanese characters,
    - * to be precise). - rekkanoryo
    - */
    -#define YAHOO_MAX_MESSAGE_LENGTH_BYTES 948
    -#define YAHOO_MAX_MESSAGE_LENGTH_CHARS 800
    -
    -/* sometimes i wish prpls could #include things from other prpls. then i could just
    - * use the routines from libfaim and not have to admit to knowing how they work. */
    -#define yahoo_put16(buf, data) ( \
    - (*(buf) = (unsigned char)((data)>>8)&0xff), \
    - (*((buf)+1) = (unsigned char)(data)&0xff), \
    - 2)
    -#define yahoo_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
    -#define yahoo_put32(buf, data) ( \
    - (*((buf)) = (unsigned char)((data)>>24)&0xff), \
    - (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
    - (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
    - (*((buf)+3) = (unsigned char)(data)&0xff), \
    - 4)
    -#define yahoo_get32(buf) ((((*(buf))<<24)&0xff000000) + \
    - (((*((buf)+1))<<16)&0x00ff0000) + \
    - (((*((buf)+2))<< 8)&0x0000ff00) + \
    - (((*((buf)+3) )&0x000000ff)))
    -
    -/* util.c */
    -void yahoo_init_colorht(void);
    -void yahoo_dest_colorht(void);
    -char *yahoo_codes_to_html(const char *x);
    -
    -/**
    - * This function takes a normal HTML message and converts it to the message
    - * format used by Yahoo, which uses a frankensteinish combination of ANSI
    - * escape codes and broken HTML.
    - *
    - * It results in slightly different output than would be sent by official
    - * Yahoo clients. The two main differences are:
    - *
    - * 1. We always close all tags, whereas official Yahoo clients leave tags
    - * dangling open at the end of each message (and the client treats them
    - * as closed).
    - * 2. We always close inner tags first before closing outter tags.
    - *
    - * For example, if you want to send this message:
    - * <b> bold <i> bolditalic </i></b><i> italic </i>
    - * Official Yahoo clients would send:
    - * ESC[1m bold ESC[2m bolditalic ESC[x1m italic
    - * But we will send:
    - * ESC[1m bold ESC[2m bolditalic ESC[x2mESC[x1mESC[2m italic ESC[x2m
    - */
    -char *yahoo_html_to_codes(const char *src);
    -
    -gboolean
    -yahoo_account_use_http_proxy(PurpleConnection *conn);
    -
    -/**
    - * Encode some text to send to the yahoo server.
    - *
    - * @param gc The connection handle.
    - * @param str The null terminated utf8 string to encode.
    - * @param utf8 Whether to return a UTF-8 string.
    - * @return A g_malloc'ed string in the appropriate encoding. If utf8
    - * is true then the string is copied verbatim. Otherwise the
    - * encoding from account settings is used.
    - */
    -gchar *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean utf8);
    -
    -/**
    - * Decode some text received from the server.
    - *
    - * @param gc The gc handle.
    - * @param str The null terminated string to decode.
    - * @param utf8 Did the server tell us it was supposed to be utf8?
    - * @return The decoded, utf-8 string, which must be g_free()'d.
    - */
    -char *yahoo_string_decode(PurpleConnection *gc, const char *str, gboolean utf8);
    -
    -char *yahoo_convert_to_numeric(const char *str);
    -
    -/* yahoo_profile.c */
    -void yahoo_get_info(PurpleConnection *gc, const char *name);
    -
    -/* ymsg.h - these functions were formerly static but need not to be for the
    - * new two-protocol model. */
    -const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b);
    -const char *yahoo_list_emblem(PurpleBuddy *b);
    -char *yahoo_status_text(PurpleBuddy *b);
    -void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full);
    -GList *yahoo_status_types(PurpleAccount *account);
    -GList *yahoo_blist_node_menu(PurpleBlistNode *node);
    -void yahoo_login(PurpleAccount *account);
    -void yahoo_close(PurpleConnection *gc);
    -int yahoo_send_im(PurpleConnection *gc, PurpleMessage *msg);
    -unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state);
    -void yahoo_set_status(PurpleAccount *account, PurpleStatus *status);
    -void yahoo_set_idle(PurpleConnection *gc, int idle);
    -void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message);
    -void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
    -void yahoo_add_deny(PurpleConnection *gc, const char *who);
    -void yahoo_rem_deny(PurpleConnection *gc, const char *who);
    -void yahoo_set_permit_deny(PurpleConnection *gc);
    -void yahoo_keepalive(PurpleConnection *gc);
    -void yahoo_change_buddys_group(PurpleConnection *gc, const char *who, const char *old_group, const char *new_group);
    -void yahoo_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies);
    -gboolean yahoo_offline_message(const PurpleBuddy *buddy);
    -gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type);
    -GList *yahoo_attention_types(PurpleAccount *account);
    -
    -GList *yahoo_get_actions(PurpleConnection *gc);
    -void yahoopurple_register_commands(void);
    -gssize yahoo_get_max_message_size(PurpleConversation *conv);
    -
    -PurpleCmdRet yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data);
    -PurpleCmdRet yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data);
    -PurpleCmdRet yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data);
    -/* needed for xfer, thought theyd be useful for other enhancements later on
    - Returns list of cookies stored in yahoo_data formatted as a single null terminated string
    - returned value must be g_freed
    -*/
    -gchar* yahoo_get_cookies(PurpleConnection *gc);
    -
    -/* send p2p pkt containing our encoded ip, asking peer to connect to us */
    -void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13);
    -
    -#endif /* _YMSG_H_ */
    --- a/libpurple/proxy.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/proxy.c Mon Jun 05 16:36:29 2017 +0300
    @@ -27,7 +27,6 @@
    #define _PURPLE_PROXY_C_
    #include "internal.h"
    -#include "ciphers/md5hash.h"
    #include "debug.h"
    #include "http.h"
    #include "notify.h"
    --- a/libpurple/tag.sh Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/tag.sh Mon Jun 05 16:36:29 2017 +0300
    @@ -22,17 +22,6 @@
    if [ "$tag" == "auto" ] && [ "$arg" == "-shared" ]; then
    tag="CCLD"
    fi
    - if [ "$tag" == "PERL" ] && [ "${arg%(*}" == "Mkbootstrap" ]; then
    - object="${arg%;}"
    - is_final=1
    - break
    - fi
    - if [ "$tag" == "PERL" ] && [ "${arg%(*}" == "ExtUtils::ParseXS::process_file" ]; then
    - object="${arg#*output => \"}"
    - object="${object%\", *}"
    - is_final=1
    - break
    - fi
    ext_1=${arg#${arg%??}}
    if [ "${ext_1}" == ".c" ]; then
    file_1="$arg"
    @@ -51,10 +40,6 @@
    tag="CC"
    fi
    -if [ "$tag" == "PERL" ] && [ "$is_final" == 0 ]; then
    - object=`echo "$object" | sed -n 's|.*output *=> *"\([^"]*\)".*|\1|p'`
    -fi
    -
    if [ "$object" == "" ] && [ "${file_1}" != "" ]; then
    object="${file_1}"
    fi
    --- a/libpurple/tests/Makefile.am Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/tests/Makefile.am Mon Jun 05 16:36:29 2017 +0300
    @@ -8,12 +8,8 @@
    test_programs=\
    test_des \
    test_des3 \
    - test_hmac \
    test_image \
    test_md4 \
    - test_md5 \
    - test_sha1 \
    - test_sha256 \
    test_smiley \
    test_trie \
    test_util \
    @@ -25,24 +21,12 @@
    test_des3_SOURCES=test_des3.c
    test_des3_LDADD=$(COMMON_LIBS)
    -test_hmac_SOURCES=test_hmac.c
    -test_hmac_LDADD=$(COMMON_LIBS)
    -
    test_image_SOURCES=test_image.c
    test_image_LDADD=$(COMMON_LIBS)
    test_md4_SOURCES=test_md4.c
    test_md4_LDADD=$(COMMON_LIBS)
    -test_md5_SOURCES=test_md5.c
    -test_md5_LDADD=$(COMMON_LIBS)
    -
    -test_sha1_SOURCES=test_sha1.c
    -test_sha1_LDADD=$(COMMON_LIBS)
    -
    -test_sha256_SOURCES=test_sha256.c
    -test_sha256_LDADD=$(COMMON_LIBS)
    -
    test_smiley_SOURCES=test_smiley.c
    test_smiley_LDADD=$(COMMON_LIBS)
    --- a/libpurple/tests/test_hmac.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,401 +0,0 @@
    -/*
    - * 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 <glib.h>
    -
    -#include <purple.h>
    -
    -#include "ciphers/hmaccipher.h"
    -#include "ciphers/md5hash.h"
    -#include "ciphers/sha1hash.h"
    -
    -/******************************************************************************
    - * HMAC Tests
    - * See RFC2202 and some other NULL tests I made up
    - *****************************************************************************/
    -static void
    -test_hmac(gchar *data, size_t data_len,
    - const gchar *key, size_t key_len,
    - PurpleHash *hash, const gchar *digest)
    -{
    - PurpleCipher *cipher = NULL;
    - gchar cdigest[41];
    - gboolean ret = FALSE;
    -
    - cipher = purple_hmac_cipher_new(hash);
    - purple_cipher_set_key(cipher, (guchar *)key, key_len);
    -
    - purple_cipher_append(cipher, (guchar *)data, data_len);
    - ret = purple_cipher_digest_to_str(cipher, cdigest, sizeof(cdigest));
    -
    - g_assert(ret);
    - g_assert_cmpstr(digest, ==, cdigest);
    -
    - g_object_unref(G_OBJECT(cipher));
    - g_object_unref(G_OBJECT(hash));
    -}
    -
    -static void
    -test_hmac_md5_hi(void) {
    - test_hmac(
    - "Hi There",
    - 8,
    - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
    - 16,
    - purple_md5_hash_new(),
    - "9294727a3638bb1c13f48ef8158bfc9d"
    - );
    -}
    -
    -static void
    -test_hmac_md5_what(void) {
    - test_hmac(
    - "what do ya want for nothing?",
    - 28,
    - "Jefe",
    - 4,
    - purple_md5_hash_new(),
    - "750c783e6ab0b503eaa86e310a5db738"
    - );
    -}
    -
    -static void
    -test_hmac_md5_dd(void) {
    - test_hmac(
    - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
    - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
    - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
    - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
    - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
    - 50,
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
    - 16,
    - purple_md5_hash_new(),
    - "56be34521d144c88dbb8c733f0e8b3f6"
    - );
    -}
    -
    -static void
    -test_hmac_md5_cd(void) {
    - test_hmac(
    - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
    - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
    - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
    - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
    - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
    - 50,
    - "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
    - "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
    - "\x15\x16\x17\x18\x19",
    - 25,
    - purple_md5_hash_new(),
    - "697eaf0aca3a3aea3a75164746ffaa79"
    - );
    -}
    -
    -static void
    -test_hmac_md5_truncation(void) {
    - test_hmac(
    - "Test With Truncation",
    - 20,
    - "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
    - 16,
    - purple_md5_hash_new(),
    - "56461ef2342edc00f9bab995690efd4c"
    - );
    -}
    -
    -static void
    -test_hmac_md5_large_key(void) {
    - test_hmac(
    - "Test Using Larger Than Block-Size Key - Hash Key First",
    - 54,
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
    - 80,
    - purple_md5_hash_new(),
    - "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"
    - );
    -}
    -
    -static void
    -test_hmac_md5_large_key_and_data(void) {
    - test_hmac(
    - "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
    - 73,
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
    - 80,
    - purple_md5_hash_new(),
    - "6f630fad67cda0ee1fb1f562db3aa53e"
    - );
    -}
    -
    -static void
    -test_hmac_md5_null_key(void) {
    - test_hmac(
    - "Hi There",
    - 8,
    - "\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b"
    - "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
    - 20,
    - purple_md5_hash_new(),
    - "597bfd644b797a985561eeb03a169e59"
    - );
    -}
    -
    -static void
    -test_hmac_md5_null_text(void) {
    - test_hmac(
    - "Hi\x00There",
    - 8,
    - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
    - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
    - 20,
    - purple_md5_hash_new(),
    - "70be8e1b7b50dfcc335d6cd7992c564f"
    - );
    -}
    -
    -static void
    -test_hmac_md5_null_key_and_text(void) {
    - test_hmac(
    - "Hi\x00Th\x00re",
    - 8,
    - "\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34"
    - "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
    - 20,
    - purple_md5_hash_new(),
    - "b31bcbba35a33a067cbba9131cba4889"
    - );
    -}
    -
    -static void
    -test_hmac_sha1_hi(void) {
    - test_hmac(
    - "Hi There",
    - 8,
    - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
    - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
    - 20,
    - purple_sha1_hash_new(),
    - "b617318655057264e28bc0b6fb378c8ef146be00"
    - );
    -}
    -
    -static void
    -test_hmac_sha1_what(void) {
    - test_hmac(
    - "what do ya want for nothing?",
    - 28,
    - "Jefe",
    - 4,
    - purple_sha1_hash_new(),
    - "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"
    - );
    -}
    -
    -static void
    -test_hmac_sha1_dd(void) {
    - test_hmac(
    - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
    - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
    - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
    - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
    - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
    - 50,
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
    - 20,
    - purple_sha1_hash_new(),
    - "125d7342b9ac11cd91a39af48aa17b4f63f175d3"
    - );
    -}
    -
    -static void
    -test_hmac_sha1_cd(void) {
    - test_hmac(
    - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
    - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
    - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
    - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
    - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
    - 50,
    - "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
    - "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
    - "\x15\x16\x17\x18\x19",
    - 25,
    - purple_sha1_hash_new(),
    - "4c9007f4026250c6bc8414f9bf50c86c2d7235da"
    - );
    -}
    -
    -static void
    -test_hmac_sha1_truncation(void) {
    - test_hmac(
    - "Test With Truncation",
    - 20,
    - "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
    - "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
    - 20,
    - purple_sha1_hash_new(),
    - "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"
    - );
    -}
    -
    -static void
    -test_hmac_sha1_large_key(void) {
    - test_hmac(
    - "Test Using Larger Than Block-Size Key - Hash Key First",
    - 54,
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
    - 80,
    - purple_sha1_hash_new(),
    - "aa4ae5e15272d00e95705637ce8a3b55ed402112"
    - );
    -}
    -
    -static void
    -test_hmac_sha1_large_key_and_data(void) {
    - test_hmac(
    - "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
    - 73,
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
    - 80,
    - purple_sha1_hash_new(),
    - "e8e99d0f45237d786d6bbaa7965c7808bbff1a91"
    - );
    -}
    -
    -static void
    -test_hmac_sha1_null_key(void) {
    - test_hmac(
    - "Hi There",
    - 8,
    - "\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b"
    - "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
    - 20,
    - purple_sha1_hash_new(),
    - "eb62a2e0e33d300be669c52aab3f591bc960aac5"
    - );
    -}
    -
    -static void
    -test_hmac_sha1_null_text(void) {
    - test_hmac(
    - "Hi\x00There",
    - 8,
    - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
    - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
    - 20,
    - purple_sha1_hash_new(),
    - "31ca58d849e971e418e3439de2c6f83144b6abb7"
    - );
    -}
    -
    -static void
    -test_hmac_sha1_null_key_and_text(void) {
    - test_hmac(
    - "Hi\x00Th\x00re",
    - 8,
    - "\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34"
    - "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
    - 20,
    - purple_sha1_hash_new(),
    - "e6b8e2fede87aa09dcb13e554df1435e056eae36"
    - );
    -}
    -
    -gint
    -main(gint argc, gchar **argv) {
    - g_test_init(&argc, &argv, NULL);
    -
    - g_test_add_func("/hmac/md5/hi",
    - test_hmac_md5_hi);
    - g_test_add_func("/hmac/md5/what",
    - test_hmac_md5_what);
    - g_test_add_func("/hmac/md5/dd",
    - test_hmac_md5_dd);
    - g_test_add_func("/hmac/md5/cd",
    - test_hmac_md5_cd);
    - g_test_add_func("/hmac/md5/truncation",
    - test_hmac_md5_truncation);
    - g_test_add_func("/hmac/md5/large key",
    - test_hmac_md5_large_key);
    - g_test_add_func("/hmac/md5/large key and data",
    - test_hmac_md5_large_key_and_data);
    - g_test_add_func("/hmac/md5/null key",
    - test_hmac_md5_null_key);
    - g_test_add_func("/hmac/md5/null text",
    - test_hmac_md5_null_text);
    - g_test_add_func("/hmac/md5/null key and text",
    - test_hmac_md5_null_key_and_text);
    -
    - g_test_add_func("/hmac/sha1/hi",
    - test_hmac_sha1_hi);
    - g_test_add_func("/hmac/sha1/what",
    - test_hmac_sha1_what);
    - g_test_add_func("/hmac/sha1/dd",
    - test_hmac_sha1_dd);
    - g_test_add_func("/hmac/sha1/cd",
    - test_hmac_sha1_cd);
    - g_test_add_func("/hmac/sha1/truncation",
    - test_hmac_sha1_truncation);
    - g_test_add_func("/hmac/sha1/large key",
    - test_hmac_sha1_large_key);
    - g_test_add_func("/hmac/sha1/large key and data",
    - test_hmac_sha1_large_key_and_data);
    - g_test_add_func("/hmac/sha1/null key",
    - test_hmac_sha1_null_key);
    - g_test_add_func("/hmac/sha1/null text",
    - test_hmac_sha1_null_text);
    - g_test_add_func("/hmac/sha1/null key and text",
    - test_hmac_sha1_null_key_and_text);
    -
    - return g_test_run();
    -}
    --- a/libpurple/tests/test_md5.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,107 +0,0 @@
    -/*
    - * 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 <glib.h>
    -
    -#include <purple.h>
    -
    -#include "ciphers/md5hash.h"
    -
    -static void
    -test_md5hash(gchar *data, gchar *digest) {
    - PurpleHash *hash = NULL;
    - gchar cdigest[33];
    - gboolean ret = FALSE;
    -
    - hash = purple_md5_hash_new();
    -
    - purple_hash_append(hash, (guchar *)data, strlen(data));
    -
    - ret = purple_hash_digest_to_str(hash, cdigest, sizeof(cdigest));
    -
    - g_assert(ret);
    - g_assert_cmpstr(digest, ==, cdigest);
    -}
    -
    -static void
    -test_md5hash_empty_string(void) {
    - test_md5hash("",
    - "d41d8cd98f00b204e9800998ecf8427e");
    -}
    -
    -static void
    -test_md5hash_a(void) {
    - test_md5hash("a",
    - "0cc175b9c0f1b6a831c399e269772661");
    -}
    -
    -static void
    -test_md5hash_abc(void) {
    - test_md5hash("abc",
    - "900150983cd24fb0d6963f7d28e17f72");
    -}
    -
    -static void
    -test_md5hash_message_digest(void) {
    - test_md5hash("message digest",
    - "f96b697d7cb7938d525a2f31aaf161d0");
    -}
    -
    -static void
    -test_md5hash_a_to_z(void) {
    - test_md5hash("abcdefghijklmnopqrstuvwxyz",
    - "c3fcd3d76192e4007dfb496cca67e13b");
    -}
    -
    -static void
    -test_md5hash_A_to_Z_a_to_z_0_to_9(void) {
    - test_md5hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
    - "d174ab98d277d9f5a5611c2c9f419d9f");
    -}
    -
    -static void
    -test_md5hash_1_to_0_eight_times(void) {
    - test_md5hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890",
    - "57edf4a22be3c955ac49da2e2107b67a");
    -}
    -
    -gint
    -main(gint argc, gchar **argv) {
    - g_test_init(&argc, &argv, NULL);
    -
    - g_test_add_func("/hash/md5/empty-string",
    - test_md5hash_empty_string);
    - g_test_add_func("/hash/md5/a",
    - test_md5hash_a);
    - g_test_add_func("/hash/md5/abc",
    - test_md5hash_abc);
    - g_test_add_func("/hash/md5/message digest",
    - test_md5hash_message_digest);
    - g_test_add_func("/hash/md5/a to z",
    - test_md5hash_a_to_z);
    - g_test_add_func("/hash/md5/A to Z, a to z, 0 to 9" ,
    - test_md5hash_A_to_Z_a_to_z_0_to_9);
    - g_test_add_func("/hash/md5/1 to 0 eight times",
    - test_md5hash_1_to_0_eight_times);
    -
    - return g_test_run();
    -}
    --- a/libpurple/tests/test_sha1.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,101 +0,0 @@
    -/*
    - * 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 <glib.h>
    -
    -#include <purple.h>
    -
    -#include "ciphers/sha1hash.h"
    -
    -static void
    -test_sha1hash(gchar *data, gchar *digest) {
    - PurpleHash *hash = NULL;
    - gchar cdigest[41];
    - gboolean ret = FALSE;
    -
    - hash = purple_sha1_hash_new();
    -
    - if(data) {
    - purple_hash_append(hash, (guchar *)data, strlen(data));
    - } else {
    - gint j;
    - guchar buff[1000];
    -
    - memset(buff, 'a', 1000);
    -
    - for(j = 0; j < 1000; j++)
    - purple_hash_append(hash, buff, 1000);
    - }
    -
    - ret = purple_hash_digest_to_str(hash, cdigest, sizeof(cdigest));
    -
    - g_assert(ret);
    - g_assert_cmpstr(digest, ==, cdigest);
    -}
    -
    -static void
    -test_sha1hash_empty_string(void) {
    - test_sha1hash("",
    - "da39a3ee5e6b4b0d3255bfef95601890afd80709");
    -}
    -
    -static void
    -test_sha1hash_a(void) {
    - test_sha1hash("a",
    - "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8");
    -}
    -
    -static void
    -test_sha1hash_abc(void) {
    - test_sha1hash("abc",
    - "a9993e364706816aba3e25717850c26c9cd0d89d");
    -}
    -
    -static void
    -test_sha1hash_abcd_gibberish(void) {
    - test_sha1hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
    - "84983e441c3bd26ebaae4aa1f95129e5e54670f1");
    -}
    -
    -static void
    -test_sha1hash_1000_as_1000_times(void) {
    - test_sha1hash(NULL,
    - "34aa973cd4c4daa4f61eeb2bdbad27316534016f");
    -}
    -
    -gint
    -main(gint argc, gchar **argv) {
    - g_test_init(&argc, &argv, NULL);
    -
    - g_test_add_func("/hash/sha1/empty-string",
    - test_sha1hash_empty_string);
    - g_test_add_func("/hash/sha1/a",
    - test_sha1hash_a);
    - g_test_add_func("/hash/sha1/abc",
    - test_sha1hash_abc);
    - g_test_add_func("/hash/sha1/abcd_gibberish",
    - test_sha1hash_abcd_gibberish);
    - g_test_add_func("/hash/sha1/1000 a's 1000 times",
    - test_sha1hash_1000_as_1000_times);
    -
    - return g_test_run();
    -}
    --- a/libpurple/tests/test_sha256.c Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,101 +0,0 @@
    -/*
    - * 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 <glib.h>
    -
    -#include <purple.h>
    -
    -#include "ciphers/sha256hash.h"
    -
    -static void
    -test_sha256hash(gchar *data, gchar *digest) {
    - PurpleHash *hash = NULL;
    - gchar cdigest[65];
    - gboolean ret = FALSE;
    -
    - hash = purple_sha256_hash_new();
    -
    - if(data) {
    - purple_hash_append(hash, (guchar *)data, strlen(data));
    - } else {
    - gint j;
    - guchar buff[1000];
    -
    - memset(buff, 'a', 1000);
    -
    - for(j = 0; j < 1000; j++)
    - purple_hash_append(hash, buff, 1000);
    - }
    -
    - ret = purple_hash_digest_to_str(hash, cdigest, sizeof(cdigest));
    -
    - g_assert(ret);
    - g_assert_cmpstr(digest, ==, cdigest);
    -}
    -
    -static void
    -test_sha256hash_empty_string(void) {
    - test_sha256hash("",
    - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
    -}
    -
    -static void
    -test_sha256hash_a(void) {
    - test_sha256hash("a",
    - "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb");
    -}
    -
    -static void
    -test_sha256hash_abc(void) {
    - test_sha256hash("abc",
    - "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
    -}
    -
    -static void
    -test_sha256hash_abcd_gibberish(void) {
    - test_sha256hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
    - "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
    -}
    -
    -static void
    -test_sha256hash_1000_as_1000_times(void) {
    - test_sha256hash(NULL,
    - "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
    -}
    -
    -gint
    -main(gint argc, gchar **argv) {
    - g_test_init(&argc, &argv, NULL);
    -
    - g_test_add_func("/hash/sha256/empty-string",
    - test_sha256hash_empty_string);
    - g_test_add_func("/hash/sha256/a",
    - test_sha256hash_a);
    - g_test_add_func("/hash/sha256/abc",
    - test_sha256hash_abc);
    - g_test_add_func("/hash/sha256/abcd_gibberish",
    - test_sha256hash_abcd_gibberish);
    - g_test_add_func("/hash/sha256/1000 a's 1000 times",
    - test_sha256hash_1000_as_1000_times);
    -
    - return g_test_run();
    -}
    --- a/libpurple/tests/test_util.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/tests/test_util.c Mon Jun 05 16:36:29 2017 +0300
    @@ -43,24 +43,6 @@
    }
    /******************************************************************************
    - * base 64 tests
    - *****************************************************************************/
    -static void
    -test_util_base_64_encode(void) {
    - gchar *in = purple_base64_encode((const unsigned char *)"forty-two", 10);
    - g_assert_cmpstr("Zm9ydHktdHdvAA==", ==, in);
    -}
    -
    -static void
    -test_util_base_64_decode(void) {
    - gsize sz = 0;
    - guchar *out = purple_base64_decode("b3d0LXl0cm9mAA==", &sz);
    -
    - g_assert_cmpint(sz, ==, 10);
    - g_assert_cmpstr("owt-ytrof", ==, (gchar *)out);
    -}
    -
    -/******************************************************************************
    * filename escape tests
    *****************************************************************************/
    static void
    @@ -512,11 +494,6 @@
    g_test_add_func("/util/base/16/decode",
    test_util_base_16_decode);
    - g_test_add_func("/util/base/64/encode",
    - test_util_base_64_encode);
    - g_test_add_func("/util/base/64/decode",
    - test_util_base_64_decode);
    -
    g_test_add_func("/util/filename/escape",
    test_util_filename_escape);
    g_test_add_func("/util/filename/unescape",
    --- a/libpurple/tls-certificate-info.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/tls-certificate-info.c Mon Jun 05 16:36:29 2017 +0300
    @@ -23,7 +23,6 @@
    #include "internal.h"
    #include "tls-certificate-info.h"
    -#include "ciphers/sha1hash.h"
    #include "debug.h"
    #include "util.h"
    @@ -789,7 +788,7 @@
    GByteArray *
    purple_tls_certificate_get_fingerprint_sha1(GTlsCertificate *certificate)
    {
    - PurpleHash *hash;
    + GChecksum *hash;
    GByteArray *der = NULL;
    guint8 *data = NULL;
    gsize buf_size = 0;
    @@ -800,16 +799,16 @@
    g_return_val_if_fail(der != NULL, NULL);
    - hash = purple_sha1_hash_new();
    + hash = g_checksum_new(G_CHECKSUM_SHA1);
    - buf_size = purple_hash_get_digest_size(hash);
    + buf_size = g_checksum_type_get_length(G_CHECKSUM_SHA1);
    data = g_malloc(buf_size);
    - purple_hash_append(hash, der->data, der->len);
    + g_checksum_update(hash, der->data, der->len);
    g_byte_array_unref(der);
    - purple_hash_digest(hash, data, buf_size);
    - g_object_unref(hash);
    + g_checksum_get_digest(hash, data, &buf_size);
    + g_checksum_free(hash);
    return g_byte_array_new_take(data, buf_size);
    }
    --- a/libpurple/util.c Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/util.c Mon Jun 05 16:36:29 2017 +0300
    @@ -18,7 +18,6 @@
    */
    #include "internal.h"
    -#include "ciphers/md5hash.h"
    #include "conversation.h"
    #include "core.h"
    #include "debug.h"
    @@ -330,34 +329,12 @@
    return ascii;
    }
    -
    /**************************************************************************
    - * Base64 Functions
    + * Quoted Printable Functions (see RFC 2045).
    **************************************************************************/
    static const char xdigits[] =
    "0123456789abcdef";
    -gchar *
    -purple_base64_encode(const guchar *data, gsize len)
    -{
    - return g_base64_encode(data, len);
    -}
    -
    -guchar *
    -purple_base64_decode(const char *str, gsize *ret_len)
    -{
    - /*
    - * We want to allow ret_len to be NULL for backward compatibility,
    - * but g_base64_decode() requires a valid length variable. So if
    - * ret_len is NULL then pass in a dummy variable.
    - */
    - gsize unused;
    - return g_base64_decode(str, ret_len != NULL ? ret_len : &unused);
    -}
    -
    -/**************************************************************************
    - * Quoted Printable Functions (see RFC 2045).
    - **************************************************************************/
    guchar *
    purple_quotedp_decode(const char *str, gsize *ret_len)
    {
    @@ -517,7 +494,7 @@
    if (g_ascii_strcasecmp(encoding, "Q") == 0)
    decoded = purple_quotedp_decode(encoded_text, &dec_len);
    else if (g_ascii_strcasecmp(encoding, "B") == 0)
    - decoded = purple_base64_decode(encoded_text, &dec_len);
    + decoded = g_base64_decode(encoded_text, &dec_len);
    else
    decoded = NULL;
    if (decoded) {
    @@ -1424,8 +1401,7 @@
    purple_markup_unescape_entity(const char *text, int *length)
    {
    const char *pln;
    - int len, pound;
    - char temp[2];
    + int len;
    if (!text || *text != '&')
    return NULL;
    @@ -4961,9 +4937,8 @@
    const gchar *nonce,
    const gchar *client_nonce)
    {
    - PurpleHash *hasher;
    - gchar hash[33]; /* We only support MD5. */
    - gboolean digest_ok;
    + GChecksum *hasher;
    + gchar *hash;
    g_return_val_if_fail(username != NULL, NULL);
    g_return_val_if_fail(realm != NULL, NULL);
    @@ -4976,18 +4951,19 @@
    g_ascii_strcasecmp(algorithm, "MD5") ||
    g_ascii_strcasecmp(algorithm, "MD5-sess"), NULL);
    - hasher = purple_md5_hash_new();
    - g_return_val_if_fail(hash != NULL, NULL);
    -
    - purple_hash_append(hasher, (guchar *)username, strlen(username));
    - purple_hash_append(hasher, (guchar *)":", 1);
    - purple_hash_append(hasher, (guchar *)realm, strlen(realm));
    - purple_hash_append(hasher, (guchar *)":", 1);
    - purple_hash_append(hasher, (guchar *)password, strlen(password));
    + hasher = g_checksum_new(G_CHECKSUM_MD5);
    + g_return_val_if_fail(hasher != NULL, NULL);
    +
    + g_checksum_update(hasher, (guchar *)username, -1);
    + g_checksum_update(hasher, (guchar *)":", -1);
    + g_checksum_update(hasher, (guchar *)realm, -1);
    + g_checksum_update(hasher, (guchar *)":", -1);
    + g_checksum_update(hasher, (guchar *)password, -1);
    if (algorithm != NULL && !g_ascii_strcasecmp(algorithm, "MD5-sess"))
    {
    guchar digest[16];
    + gsize digest_len = 16;
    if (client_nonce == NULL)
    {
    @@ -4996,22 +4972,20 @@
    return NULL;
    }
    - purple_hash_digest(hasher, digest, sizeof(digest));
    -
    - purple_hash_reset(hasher);
    - purple_hash_append(hasher, digest, sizeof(digest));
    - purple_hash_append(hasher, (guchar *)":", 1);
    - purple_hash_append(hasher, (guchar *)nonce, strlen(nonce));
    - purple_hash_append(hasher, (guchar *)":", 1);
    - purple_hash_append(hasher, (guchar *)client_nonce, strlen(client_nonce));
    + g_checksum_get_digest(hasher, digest, &digest_len);
    +
    + g_checksum_reset(hasher);
    + g_checksum_update(hasher, digest, sizeof(digest));
    + g_checksum_update(hasher, (guchar *)":", -1);
    + g_checksum_update(hasher, (guchar *)nonce, -1);
    + g_checksum_update(hasher, (guchar *)":", -1);
    + g_checksum_update(hasher, (guchar *)client_nonce, -1);
    }
    - digest_ok = purple_hash_digest_to_str(hasher, hash, sizeof(hash));
    - g_object_unref(hasher);
    -
    - g_return_val_if_fail(digest_ok, NULL);
    -
    - return g_strdup(hash);
    + hash = g_strdup(g_checksum_get_string(hasher));
    + g_checksum_free(hasher);
    +
    + return hash;
    }
    gchar *purple_http_digest_calculate_response(
    @@ -5025,9 +4999,8 @@
    const gchar *client_nonce,
    const gchar *session_key)
    {
    - PurpleHash *hash;
    - static gchar hash2[33]; /* We only support MD5. */
    - gboolean digest_ok;
    + GChecksum *hash;
    + gchar *hash2;
    g_return_val_if_fail(method != NULL, NULL);
    g_return_val_if_fail(digest_uri != NULL, NULL);
    @@ -5046,85 +5019,83 @@
    g_ascii_strcasecmp(qop, "auth") ||
    g_ascii_strcasecmp(qop, "auth-int"), NULL);
    - hash = purple_md5_hash_new();
    + hash = g_checksum_new(G_CHECKSUM_MD5);
    g_return_val_if_fail(hash != NULL, NULL);
    - purple_hash_append(hash, (guchar *)method, strlen(method));
    - purple_hash_append(hash, (guchar *)":", 1);
    - purple_hash_append(hash, (guchar *)digest_uri, strlen(digest_uri));
    + g_checksum_update(hash, (guchar *)method, -1);
    + g_checksum_update(hash, (guchar *)":", -1);
    + g_checksum_update(hash, (guchar *)digest_uri, -1);
    if (qop != NULL && !g_ascii_strcasecmp(qop, "auth-int"))
    {
    - PurpleHash *hash2;
    - gchar entity_hash[33];
    + gchar *entity_hash;
    if (entity == NULL)
    {
    - g_object_unref(hash);
    + g_checksum_free(hash);
    purple_debug_error("hash", "Required entity missing for auth-int digest calculation.\n");
    return NULL;
    }
    - hash2 = purple_md5_hash_new();
    - purple_hash_append(hash2, (guchar *)entity, strlen(entity));
    - digest_ok = purple_hash_digest_to_str(hash2, entity_hash, sizeof(entity_hash));
    - g_object_unref(hash2);
    -
    - if (!digest_ok) {
    - g_object_unref(hash);
    + entity_hash = g_compute_checksum_for_string(G_CHECKSUM_MD5,
    + entity, -1);
    +
    + if (entity_hash == NULL) {
    + g_checksum_free(hash);
    g_return_val_if_reached(NULL);
    }
    - purple_hash_append(hash, (guchar *)":", 1);
    - purple_hash_append(hash, (guchar *)entity_hash, strlen(entity_hash));
    + g_checksum_update(hash, (guchar *)":", -1);
    + g_checksum_update(hash, (guchar *)entity_hash, -1);
    + g_free(entity_hash);
    }
    - digest_ok = purple_hash_digest_to_str(hash, hash2, sizeof(hash2));
    - purple_hash_reset(hash);
    -
    - if (!digest_ok) {
    - g_object_unref(hash);
    + hash2 = g_strdup(g_checksum_get_string(hash));
    + g_checksum_reset(hash);
    +
    + if (hash2 == NULL) {
    + g_checksum_free(hash);
    g_return_val_if_reached(NULL);
    }
    - purple_hash_append(hash, (guchar *)session_key, strlen(session_key));
    - purple_hash_append(hash, (guchar *)":", 1);
    - purple_hash_append(hash, (guchar *)nonce, strlen(nonce));
    - purple_hash_append(hash, (guchar *)":", 1);
    + g_checksum_update(hash, (guchar *)session_key, -1);
    + g_checksum_update(hash, (guchar *)":", -1);
    + g_checksum_update(hash, (guchar *)nonce, -1);
    + g_checksum_update(hash, (guchar *)":", -1);
    if (qop != NULL && *qop != '\0')
    {
    if (nonce_count == NULL)
    {
    - g_object_unref(hash);
    + g_checksum_free(hash);
    purple_debug_error("hash", "Required nonce_count missing for digest calculation.\n");
    return NULL;
    }
    if (client_nonce == NULL)
    {
    - g_object_unref(hash);
    + g_checksum_free(hash);
    purple_debug_error("hash", "Required client_nonce missing for digest calculation.\n");
    return NULL;
    }
    - purple_hash_append(hash, (guchar *)nonce_count, strlen(nonce_count));
    - purple_hash_append(hash, (guchar *)":", 1);
    - purple_hash_append(hash, (guchar *)client_nonce, strlen(client_nonce));
    - purple_hash_append(hash, (guchar *)":", 1);
    -
    - purple_hash_append(hash, (guchar *)qop, strlen(qop));
    -
    - purple_hash_append(hash, (guchar *)":", 1);
    + g_checksum_update(hash, (guchar *)nonce_count, -1);
    + g_checksum_update(hash, (guchar *)":", -1);
    + g_checksum_update(hash, (guchar *)client_nonce, -1);
    + g_checksum_update(hash, (guchar *)":", -1);
    +
    + g_checksum_update(hash, (guchar *)qop, -1);
    +
    + g_checksum_update(hash, (guchar *)":", -1);
    }
    - purple_hash_append(hash, (guchar *)hash2, strlen(hash2));
    - digest_ok = purple_hash_digest_to_str(hash, hash2, sizeof(hash2));
    - g_object_unref(hash);
    -
    - g_return_val_if_fail(digest_ok, NULL);
    -
    - return g_strdup(hash2);
    + g_checksum_update(hash, (guchar *)hash2, -1);
    + g_free(hash2);
    +
    + hash2 = g_strdup(g_checksum_get_string(hash));
    + g_checksum_free(hash);
    +
    + return hash2;
    }
    int
    --- a/libpurple/util.h Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/util.h Mon Jun 05 16:36:29 2017 +0300
    @@ -287,43 +287,6 @@
    */
    gchar *purple_base16_encode_chunked(const guchar *data, gsize len);
    -
    -/**************************************************************************/
    -/* Base64 Functions */
    -/**************************************************************************/
    -
    -/**
    - * purple_base64_encode:
    - * @data: The data to convert.
    - * @len: The length of the data.
    - *
    - * Converts a chunk of binary data to its base-64 equivalent.
    - *
    - * See purple_base64_decode()
    - *
    - * Returns: The base-64 string in the ASCII encoding. Must be
    - * g_free'd when no longer needed.
    - */
    -gchar *purple_base64_encode(const guchar *data, gsize len);
    -
    -/**
    - * purple_base64_decode:
    - * @str: The base-64 string to convert to raw data.
    - * @ret_len: The length of the returned data. You can
    - * pass in NULL if you're sure that you know
    - * the length of the decoded data, or if you
    - * know you'll be able to use strlen to
    - * determine the length, etc.
    - *
    - * Converts an ASCII string of base-64 encoded data to
    - * the binary equivalent.
    - *
    - * See purple_base64_encode()
    - *
    - * Returns: The raw data. Must be g_free'd when no longer needed.
    - */
    -guchar *purple_base64_decode(const char *str, gsize *ret_len);
    -
    /**************************************************************************/
    /* Quoted Printable Functions */
    /**************************************************************************/
    --- a/libpurple/win32/global.mak Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/win32/global.mak Mon Jun 05 16:36:29 2017 +0300
    @@ -25,7 +25,6 @@
    LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.9.2_daa1
    MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa3
    NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.24-nspr-4.12
    -PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.20.1.1
    SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.12
    GSTREAMER_TOP ?= $(WIN32_DEV_TOP)/gstreamer-0.10.13
    GCC_SSP_TOP ?= $(shell dirname $(shell which $(CC)))
    @@ -40,15 +39,12 @@
    PIDGIN_INSTALL_DIR := $(PIDGIN_TREE_TOP)/win32-install-dir
    PURPLE_INSTALL_DIR := $(PIDGIN_TREE_TOP)/win32-install-dir
    PIDGIN_INSTALL_PLUGINS_DIR := $(PIDGIN_INSTALL_DIR)/plugins
    -PIDGIN_INSTALL_PERL_DIR := $(PIDGIN_INSTALL_PLUGINS_DIR)/perl
    PURPLE_INSTALL_PLUGINS_DIR := $(PURPLE_INSTALL_DIR)/plugins
    -PURPLE_INSTALL_PERL_DIR := $(PURPLE_INSTALL_PLUGINS_DIR)/perl
    PURPLE_INSTALL_PO_DIR := $(PURPLE_INSTALL_DIR)/locale
    # Important (enough) locations in our source code
    PURPLE_TOP := $(PIDGIN_TREE_TOP)/libpurple
    PURPLE_PLUGINS_TOP := $(PURPLE_TOP)/plugins
    -PURPLE_PERL_TOP := $(PURPLE_PLUGINS_TOP)/perl
    PIDGIN_TOP := $(PIDGIN_TREE_TOP)/pidgin
    PIDGIN_PIXMAPS_TOP := $(PIDGIN_TOP)/pixmaps
    PIDGIN_PLUGINS_TOP := $(PIDGIN_TOP)/plugins
    @@ -63,7 +59,6 @@
    PURPLE_PURPLE_H := $(PURPLE_TOP)/purple.h
    PURPLE_VERSION_H := $(PURPLE_TOP)/version.h
    PURPLE_DLL := $(PURPLE_TOP)/libpurple.dll
    -PURPLE_PERL_DLL := $(PURPLE_PERL_TOP)/perl.dll
    PIDGIN_DLL := $(PIDGIN_TOP)/pidgin.dll
    PIDGIN_EXE := $(PIDGIN_TOP)/pidgin.exe
    PIDGIN_PORTABLE_EXE := $(PIDGIN_TOP)/pidgin-portable.exe
    --- a/libpurple/win32/rules.mak Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/win32/rules.mak Mon Jun 05 16:36:29 2017 +0300
    @@ -3,9 +3,6 @@
    %.o: %.c
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@ -c $<
    -%.c: %.xs
    - $(TAG) "PERL" $(PERL) -MExtUtils::ParseXS -e 'ExtUtils::ParseXS::process_file(filename => "$<", output => "$@", typemap => "$(PURPLE_PERL_TOP)/common/typemap");'
    -
    %.o: %.rc
    @echo -e " GEN\t$@"
    @$(WINDRES) -I$(PURPLE_TOP) -i $< -o $@
    --- a/libpurple/win32/targets.mak Sun May 28 13:26:27 2017 +0300
    +++ b/libpurple/win32/targets.mak Mon Jun 05 16:36:29 2017 +0300
    @@ -35,9 +35,6 @@
    $(PURPLE_DLL) $(PURPLE_DLL).a: $(PURPLE_VERSION_H)
    $(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE) libpurple.dll
    -$(PURPLE_PERL_DLL) $(PURPLE_PERL_DLL).a:
    - $(MAKE) -C $(PURPLE_PERL_TOP) -f $(MINGW_MAKEFILE) perl.dll
    -
    $(PIDGIN_DLL) $(PIDGIN_DLL).a:
    $(MAKE_at) $(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE) pidgin.dll
    @@ -48,9 +45,6 @@
    $(PIDGIN_INSTALL_DIR):
    mkdir -p $(PIDGIN_INSTALL_DIR)
    -$(PIDGIN_INSTALL_PERL_DIR):
    - mkdir -p $(PIDGIN_INSTALL_PERL_DIR)
    -
    $(PIDGIN_INSTALL_PLUGINS_DIR):
    mkdir -p $(PIDGIN_INSTALL_PLUGINS_DIR)
    @@ -59,6 +53,3 @@
    #$(PURPLE_INSTALL_PLUGINS_DIR):
    # mkdir -p $(PURPLE_INSTALL_PLUGINS_DIR)
    -
    -#$(PURPLE_INSTALL_PERL_DIR):
    -# mkdir -p $(PURPLE_INSTALL_PERL_DIR)
    --- a/pidgin/gtkdialogs.c Sun May 28 13:26:27 2017 +0300
    +++ b/pidgin/gtkdialogs.c Mon Jun 05 16:36:29 2017 +0300
    @@ -683,25 +683,6 @@
    g_string_append(str, "<dt>Network Security Services (NSS):</dt><dd>Disabled</dd>");
    #endif
    -#warning TODO: Check for perl.
    - if (purple_plugins_find_plugin("core-perl") != NULL)
    - g_string_append(str, "<dt>Perl:</dt><dd>Enabled</dd>");
    - else
    - g_string_append(str, "<dt>Perl:</dt><dd>Disabled</dd>");
    -
    -#warning TODO: Check for tcl.
    - if (purple_plugins_find_plugin("core-tcl") != NULL) {
    - g_string_append(str, "<dt>Tcl:</dt><dd>Enabled</dd>");
    -#ifdef HAVE_TK
    - g_string_append(str, "<dt>Tk:</dt><dd>Enabled</dd>");
    -#else
    - g_string_append(str, "<dt>Tk:</dt><dd>Disabled</dd>");
    -#endif
    - } else {
    - g_string_append(str, "<dt>Tcl:</dt><dd>Disabled</dd>");
    - g_string_append(str, "<dt>Tk:</dt><dd>Disabled</dd>");
    - }
    -
    #ifdef USE_IDN
    g_string_append(str, "<dt>UTF-8 DNS (IDN):</dt><dd>Enabled</dd>");
    #else
    --- a/pidgin/gtkwebview.c Sun May 28 13:26:27 2017 +0300
    +++ b/pidgin/gtkwebview.c Mon Jun 05 16:36:29 2017 +0300
    @@ -204,8 +204,7 @@
    gchar *b64, *src;
    const gchar *type;
    - b64 = purple_base64_encode(
    - purple_image_get_data(img),
    + b64 = g_base64_encode(purple_image_get_data(img),
    purple_image_get_data_size(img));
    type = purple_image_get_mimetype(img);
    src = g_strdup_printf("data:%s;base64,%s", type, b64);
    --- a/pidgin/plugins/Makefile.mingw Sun May 28 13:26:27 2017 +0300
    +++ b/pidgin/plugins/Makefile.mingw Mon Jun 05 16:36:29 2017 +0300
    @@ -8,7 +8,6 @@
    include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
    DISCO_PLUGIN := ./disco
    -GTKPERL_PLUGIN := ./perl
    TICKER_PLUGIN := ./ticker
    TRANSPARENCY_PLUGIN := ./win32/transparency
    WINPREFS_PLUGIN := ./win32/winprefs
    @@ -66,14 +65,12 @@
    all: plugins
    $(MAKE_at) $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE)
    - $(MAKE_at) $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE)
    $(MAKE_at) $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE)
    $(MAKE_at) $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE)
    $(MAKE_at) $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE)
    install: all $(PIDGIN_INSTALL_PLUGINS_DIR)
    $(MAKE_at) $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE) install
    - $(MAKE_at) $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE) install
    $(MAKE_at) $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE) install
    $(MAKE_at) $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE) install
    $(MAKE_at) $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE) install
    @@ -106,7 +103,6 @@
    clean:
    rm -f *.o *.dll
    $(MAKE_at) $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE) clean
    - $(MAKE_at) $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE) clean
    $(MAKE_at) $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE) clean
    $(MAKE_at) $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE) clean
    $(MAKE_at) $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE) clean
    --- a/pidgin/plugins/imgupload.c Sun May 28 13:26:27 2017 +0300
    +++ b/pidgin/plugins/imgupload.c Mon Jun 05 16:36:29 2017 +0300
    @@ -121,7 +121,7 @@
    "Client-ID " IMGUP_IMGUR_CLIENT_ID);
    /* TODO: make it a plain, multipart/form-data request */
    - img_data = purple_base64_encode(purple_image_get_data(image),
    + img_data = g_base64_encode(purple_image_get_data(image),
    purple_image_get_data_size(image));
    img_data_e = g_uri_escape_string(img_data, NULL, FALSE);
    g_free(img_data);
    --- a/pidgin/plugins/perl/Makefile.am Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,106 +0,0 @@
    -perl_dirs = common
    -
    -common_sources = \
    - common/GtkAccount.xs \
    - common/GtkBlist.xs \
    - common/GtkConn.xs \
    - common/GtkConv.xs \
    - common/GtkConvWin.xs \
    - common/GtkDebug.xs \
    - common/GtkDialogs.xs \
    - common/GtkFt.xs \
    - common/GtkLog.xs \
    - common/GtkMenuTray.xs \
    - common/GtkPlugin.xs \
    - common/GtkPluginPref.xs \
    - common/GtkPounce.xs \
    - common/GtkPrefs.xs \
    - common/GtkPrivacy.xs \
    - common/GtkRoomlist.xs \
    - common/GtkSavedStatuses.xs \
    - common/GtkSession.xs \
    - common/GtkSound.xs \
    - common/GtkStatusBox.xs \
    - common/GtkUtils.xs \
    - common/Makefile.PL.in \
    - common/Pidgin.pm \
    - common/Pidgin.xs \
    - common/gtkmodule.h \
    - common/typemap
    -
    -EXTRA_DIST = \
    - Makefile.mingw \
    - common/Makefile.mingw \
    - $(common_sources)
    -
    -common/Makefile: common/Makefile.PL
    - $(AM_V_GEN)if test "x${top_srcdir}" != "x${top_builddir}"; then \
    - for f in ${common_sources}; do \
    - srcloc=${srcdir}; \
    - case $$srcloc in /*) ;; *) srcloc=../${srcdir} ;; esac; \
    - ${LN_S} -f $$srcloc/$$f $$f; \
    - done; \
    - fi
    - $(AM_V_at)cd common && $(perlpath) Makefile.PL > /dev/null
    -
    -common/Makefile.PL: common/Makefile.PL.in $(top_builddir)/config.status
    - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
    -
    -all-local: common/Makefile
    - @for dir in $(perl_dirs); do \
    - cd $$dir && \
    - if [ ! -f Makefile ]; then \
    - $(perlpath) Makefile.PL; \
    - fi && \
    - ($(MAKE) CC="@$(abs_top_srcdir)/libpurple/tag.sh CC $(CC)" LD="@$(abs_top_srcdir)/libpurple/tag.sh LD $(CC)" PERLRUN="@$(abs_top_srcdir)/libpurple/tag.sh PERL $(PERL)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" CP="@cp" RM_F="@rm -f" CHMOD="@chmod" $(PERL_EXTRA_OPTS) || \
    - $(MAKE) CC="@$(abs_top_srcdir)/libpurple/tag.sh CC $(CC)" LD="@$(abs_top_srcdir)/libpurple/tag.sh LD $(CC)" PERLRUN="@$(abs_top_srcdir)/libpurple/tag.sh PERL $(PERL)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" CP="@cp" RM_F="@rm -f" CHMOD="@chmod" $(PERL_EXTRA_OPTS)) && \
    - cd ..; \
    - done
    -
    -install-exec-local:
    - @for dir in $(perl_dirs); do \
    - cd $$dir; \
    - $(MAKE) install; \
    - cd ..; \
    - done
    -
    -# Evil Hack (TM)
    -# ... which doesn't work with DESTDIR installs. FIXME?
    -uninstall-local:
    - @for dir in $(perl_dirs); do \
    - cd $$dir && \
    - `$(MAKE) uninstall | grep unlink | sed -e 's#/usr#${prefix}#' -e 's#unlink#rm -f#'` && \
    - cd ..; \
    - done
    -
    -clean-generic:
    - @for dir in $(perl_dirs); do \
    - cd $$dir; \
    - $(MAKE) clean; \
    - cd ..; \
    - done
    - cd common ; rm -rf *.c *.o pm_to_blib Pidgin.bs MYMETA.* blib/*/.exists blib/*/auto/Pidgin blib/*/Pidgin.*pm ; cd ..
    - rm -f *.so
    -
    -distclean-generic:
    - @for dir in $(perl_dirs); do \
    - cd $$dir; \
    - $(MAKE) realclean; \
    - rm -f Makefile.PL; \
    - rm -f Makefile.old; \
    - rm -f Makefile; \
    - cd ..; \
    - done
    -
    - @rm -f Makefile
    -
    -AM_CPPFLAGS = \
    - -I$(top_srcdir) \
    - -I$(top_srcdir)/libpurple \
    - -I$(top_builddir)/libpurple \
    - -I$(top_srcdir)/pidgin \
    - $(DEBUG_CFLAGS) \
    - $(GTK_CFLAGS) \
    - $(GPLUGIN_CFLAGS) \
    - $(PLUGIN_CFLAGS) \
    - $(PERL_CFLAGS)
    --- a/pidgin/plugins/perl/Makefile.mingw Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,28 +0,0 @@
    -#
    -# Makefile.mingw
    -#
    -# Description: Makefile for perl plugin loader plugin.
    -#
    -
    -PIDGIN_TREE_TOP := ../../..
    -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
    -
    -#we cannot include win32dep.h, but we need struct sockaddr_in6 definition
    -CFLAGS += -include ws2tcpip.h
    -
    -##
    -## TARGET DEFINITIONS
    -##
    -.PHONY: all install clean
    -
    -all:
    - $(MAKE_at) $(MAKE) -C ./common -f $(MINGW_MAKEFILE)
    -
    -install: all $(PIDGIN_INSTALL_PLUGINS_DIR)
    - $(MAKE_at) $(MAKE) -C ./common -f $(MINGW_MAKEFILE) install
    -
    -##
    -## CLEAN RULES
    -##
    -clean:
    - $(MAKE_at) $(MAKE) -C ./common -f $(MINGW_MAKEFILE) clean
    --- a/pidgin/plugins/perl/common/GtkAccount.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,24 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Account PACKAGE = Pidgin::Account PREFIX = pidgin_account_
    -PROTOTYPES: ENABLE
    -
    -Purple::Handle
    -pidgin_accounts_get_handle()
    -
    -MODULE = Pidgin::Account PACKAGE = Pidgin::Account::Dialog PREFIX = pidgin_account_dialog_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_account_dialog_show(type, account)
    - Pidgin::Account::Dialog::Type type
    - Purple::Account account
    -
    -MODULE = Pidgin::Account PACKAGE = Pidgin::Account::Window PREFIX = pidgin_accounts_window_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_accounts_window_show()
    -
    -void
    -pidgin_accounts_window_hide()
    --- a/pidgin/plugins/perl/common/GtkBlist.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,71 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::BuddyList PACKAGE = Pidgin::BuddyList PREFIX = pidgin_blist_
    -PROTOTYPES: ENABLE
    -
    -Purple::Handle
    -pidgin_blist_get_handle()
    -
    -Pidgin::BuddyList
    -pidgin_blist_get_default_gtk_blist()
    -
    -void
    -pidgin_blist_refresh(list)
    - Purple::BuddyList list
    -
    -void
    -pidgin_blist_update_refresh_timeout()
    -
    -gboolean
    -pidgin_blist_node_is_contact_expanded(node)
    - Purple::BuddyList::Node node
    -
    -void
    -pidgin_blist_toggle_visibility()
    -
    -void
    -pidgin_blist_visibility_manager_add()
    -
    -void
    -pidgin_blist_visibility_manager_remove()
    -
    -void
    -pidgin_blist_get_sort_methods()
    -PREINIT:
    - GList *l;
    -PPCODE:
    - for (l = pidgin_blist_get_sort_methods(); l != NULL; l = l->next) {
    - XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Pidgin::BuddyList::SortMethod")));
    - }
    -
    -void
    -pidgin_blist_sort_method_reg(id, name, func)
    - const char * id
    - const char * name
    - Pidgin::BuddyList::SortFunction func
    -
    -void
    -pidgin_blist_sort_method_unreg(id)
    - const char * id
    -
    -void
    -pidgin_blist_sort_method_set(id)
    - const char * id
    -
    -void
    -pidgin_blist_setup_sort_methods()
    -
    -void
    -pidgin_blist_update_accounts_menu()
    -
    -void
    -pidgin_blist_update_plugin_actions()
    -
    -void
    -pidgin_blist_update_sort_methods()
    -
    -gboolean
    -pidgin_blist_joinchat_is_showable()
    -
    -void
    -pidgin_blist_joinchat_show()
    --- a/pidgin/plugins/perl/common/GtkConn.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,7 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Connection PACKAGE = Pidgin::Connection PREFIX = pidgin_connection_
    -PROTOTYPES: ENABLE
    -
    -Purple::Handle
    -pidgin_connection_get_handle()
    --- a/pidgin/plugins/perl/common/GtkConv.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,67 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Conversation PACKAGE = Pidgin::Conversation PREFIX = pidgin_conv_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_conv_update_buddy_icon(im)
    - Purple::IMConversation im
    -
    -void
    -pidgin_conv_switch_active_conversation(conv)
    - Purple::Conversation conv
    -
    -void
    -pidgin_conv_update_buttons_by_protocol(conv)
    - Purple::Conversation conv
    -
    -void
    -pidgin_conv_present_conversation(conv)
    - Purple::Conversation conv
    -
    -Pidgin::Conversation::Window
    -pidgin_conv_get_window(conv)
    - Pidgin::Conversation conv
    -
    -void
    -pidgin_conv_new(class, conv)
    - Purple::Conversation conv
    - C_ARGS:
    - conv
    -
    -gboolean
    -pidgin_conv_is_hidden(gtkconv)
    - Pidgin::Conversation gtkconv
    -
    -void
    -pidgin_conv_get_gtkconv(conv)
    - Purple::Conversation conv
    -PPCODE:
    - if (conv != NULL && PIDGIN_IS_PIDGIN_CONVERSATION(conv))
    - XPUSHs(sv_2mortal(purple_perl_bless_object(
    - PIDGIN_CONVERSATION(conv),
    - "Pidgin::Conversation")));
    -
    -MODULE = Pidgin::Conversation PACKAGE = Pidgin::Conversations PREFIX = pidgin_conversations_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_conversations_get_unseen_all(min_state, hidden_only, max_count)
    - Pidgin::UnseenState min_state
    - gboolean hidden_only
    - guint max_count
    -
    -void
    -pidgin_conversations_get_unseen_ims(min_state, hidden_only, max_count)
    - Pidgin::UnseenState min_state
    - gboolean hidden_only
    - guint max_count
    -
    -void
    -pidgin_conversations_get_unseen_chats(min_state, hidden_only, max_count)
    - Pidgin::UnseenState min_state
    - gboolean hidden_only
    - guint max_count
    -
    -Purple::Handle
    -pidgin_conversations_get_handle()
    --- a/pidgin/plugins/perl/common/GtkConvWin.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,139 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Conversation::Window PACKAGE = Pidgin::Conversation::Window PREFIX = pidgin_conv_window_
    -PROTOTYPES: ENABLE
    -
    -Pidgin::Conversation::Window
    -pidgin_conv_window_new(class)
    - C_ARGS: /* void */
    -
    -void
    -pidgin_conv_window_destroy(win)
    - Pidgin::Conversation::Window win
    -
    -void
    -pidgin_conv_window_show(win)
    - Pidgin::Conversation::Window win
    -
    -void
    -pidgin_conv_window_hide(win)
    - Pidgin::Conversation::Window win
    -
    -void
    -pidgin_conv_window_raise(win)
    - Pidgin::Conversation::Window win
    -
    -void
    -pidgin_conv_window_switch_gtkconv(win, gtkconv)
    - Pidgin::Conversation::Window win
    - Pidgin::Conversation gtkconv
    -
    -void
    -pidgin_conv_window_add_gtkconv(win, gtkconv)
    - Pidgin::Conversation::Window win
    - Pidgin::Conversation gtkconv
    -
    -void
    -pidgin_conv_window_remove_gtkconv(win, gtkconv)
    - Pidgin::Conversation::Window win
    - Pidgin::Conversation gtkconv
    -
    -Pidgin::Conversation
    -pidgin_conv_window_get_gtkconv_at_index(win, index)
    - Pidgin::Conversation::Window win
    - int index
    -
    -Pidgin::Conversation
    -pidgin_conv_window_get_active_gtkconv(win)
    - Pidgin::Conversation::Window win
    -
    -Purple::Conversation
    -pidgin_conv_window_get_active_conversation(win)
    - Pidgin::Conversation::Window win
    -
    -gboolean
    -pidgin_conv_window_is_active_conversation(conv)
    - Purple::Conversation conv
    -
    -gboolean
    -pidgin_conv_window_has_focus(win)
    - Pidgin::Conversation::Window win
    -
    -void
    -pidgin_conv_window_get_gtkconvs(win)
    - Pidgin::Conversation::Window win
    -PREINIT:
    - GList *l;
    -PPCODE:
    - for (l = pidgin_conv_window_get_gtkconvs(win); l != NULL; l = l->next) {
    - XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Pidgin::Conversation")));
    - }
    -
    -guint
    -pidgin_conv_window_get_gtkconv_count(win)
    - Pidgin::Conversation::Window win
    -
    -Pidgin::Conversation::Window
    -pidgin_conv_window_first_im()
    -
    -Pidgin::Conversation::Window
    -pidgin_conv_window_last_im()
    -
    -Pidgin::Conversation::Window
    -pidgin_conv_window_first_chat()
    -
    -Pidgin::Conversation::Window
    -pidgin_conv_window_last_chat()
    -
    -MODULE = Pidgin::Conversation::Window PACKAGE = Pidgin::Conversation::Placement PREFIX = pidgin_conv_placement_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_conv_placement_get_options()
    -PREINIT:
    - GList *l;
    -PPCODE:
    - for (l = pidgin_conv_placement_get_options(); l != NULL; l = l->next) {
    - XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Pidgin::Conversation::Window")));
    - }
    -
    -void
    -pidgin_conv_placement_add_fnc(id, name, fnc)
    - const char * id
    - const char * name
    - Pidgin::Conversation::PlacementFunc fnc
    -
    -void
    -pidgin_conv_placement_remove_fnc(id)
    - const char * id
    -
    -const char *
    -pidgin_conv_placement_get_name(id)
    - const char * id
    -
    -Pidgin::Conversation::PlacementFunc
    -pidgin_conv_placement_get_fnc(id)
    - const char * id
    -
    -void
    -pidgin_conv_placement_set_current_func(func)
    - Pidgin::Conversation::PlacementFunc func
    -
    -Pidgin::Conversation::PlacementFunc
    -pidgin_conv_placement_get_current_func()
    -
    -void
    -pidgin_conv_placement_place(gtkconv)
    - Pidgin::Conversation gtkconv
    -
    -MODULE = Pidgin::Conversation::Window PACKAGE = Pidgin::Conversation::Windows PREFIX = pidgin_conv_windows_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_conv_windows_get_list()
    -PREINIT:
    - GList *l;
    -PPCODE:
    - for (l = pidgin_conv_windows_get_list(); l != NULL; l = l->next) {
    - XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Pidgin::Conversation::Window")));
    - }
    --- a/pidgin/plugins/perl/common/GtkDebug.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,16 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Debug PACKAGE = Pidgin::Debug PREFIX = pidgin_debug_
    -PROTOTYPES: ENABLE
    -
    -Purple::Handle
    -pidgin_debug_get_handle()
    -
    -MODULE = Pidgin::Debug PACKAGE = Pidgin::Debug::Window PREFIX = pidgin_debug_window_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_debug_window_show()
    -
    -void
    -pidgin_debug_window_hide()
    --- a/pidgin/plugins/perl/common/GtkDialogs.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,48 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Dialogs PACKAGE = Pidgin::Dialogs PREFIX = pidgin_dialogs_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_dialogs_destroy_all()
    -
    -void
    -pidgin_dialogs_about()
    -
    -void
    -pidgin_dialogs_im()
    -
    -void
    -pidgin_dialogs_im_with_user(account, username)
    - Purple::Account account
    - const char * username
    -
    -void
    -pidgin_dialogs_info()
    -
    -void
    -pidgin_dialogs_log()
    -
    -void
    -pidgin_dialogs_alias_buddy(buddy)
    - Purple::BuddyList::Buddy buddy
    -
    -void
    -pidgin_dialogs_alias_chat(chat)
    - Purple::BuddyList::Chat chat
    -
    -void
    -pidgin_dialogs_remove_buddy(buddy)
    - Purple::BuddyList::Buddy buddy
    -
    -void
    -pidgin_dialogs_remove_group(group)
    - Purple::BuddyList::Group group
    -
    -void
    -pidgin_dialogs_remove_chat(chat)
    - Purple::BuddyList::Chat chat
    -
    -void
    -pidgin_dialogs_remove_contact(contact)
    - Purple::BuddyList::Contact contact
    --- a/pidgin/plugins/perl/common/GtkFt.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,50 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Xfer PACKAGE = Pidgin::Xfer PREFIX = pidgin_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_set_xfer_dialog(dialog)
    - Pidgin::Xfer::Dialog dialog
    -
    -Pidgin::Xfer::Dialog
    -pidgin_get_xfer_dialog()
    -
    -MODULE = Pidgin::Xfer PACKAGE = Pidgin::Xfer::Dialog PREFIX = pidgin_xfer_dialog_
    -PROTOTYPES: ENABLE
    -
    -Pidgin::Xfer::Dialog
    -pidgin_xfer_dialog_new(class)
    - C_ARGS: /* void */
    -
    -void
    -pidgin_xfer_dialog_destroy(dialog)
    - Pidgin::Xfer::Dialog dialog
    -
    -void
    -pidgin_xfer_dialog_show(dialog = NULL)
    - Pidgin::Xfer::Dialog dialog
    -
    -void
    -pidgin_xfer_dialog_hide(dialog)
    - Pidgin::Xfer::Dialog dialog
    -
    -void
    -pidgin_xfer_dialog_add_xfer(dialog, xfer)
    - Pidgin::Xfer::Dialog dialog
    - Purple::Xfer xfer
    -
    -void
    -pidgin_xfer_dialog_remove_xfer(dialog, xfer)
    - Pidgin::Xfer::Dialog dialog
    - Purple::Xfer xfer
    -
    -void
    -pidgin_xfer_dialog_cancel_xfer(dialog, xfer)
    - Pidgin::Xfer::Dialog dialog
    - Purple::Xfer xfer
    -
    -void
    -pidgin_xfer_dialog_update_xfer(dialog, xfer)
    - Pidgin::Xfer::Dialog dialog
    - Purple::Xfer xfer
    --- a/pidgin/plugins/perl/common/GtkLog.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,23 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Log PACKAGE = Pidgin::Log PREFIX = pidgin_log_
    -PROTOTYPES: ENABLE
    -
    -Purple::Handle
    -pidgin_log_get_handle()
    -
    -void
    -pidgin_log_show(type, buddyname, account)
    - Purple::LogType type
    - const char * buddyname
    - Purple::Account account
    -
    -void
    -pidgin_log_show_contact(contact)
    - Purple::BuddyList::Contact contact
    -
    -MODULE = Pidgin::Log PACKAGE = Pidgin::SysLog PREFIX = pidgin_syslog_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_syslog_show()
    --- a/pidgin/plugins/perl/common/GtkMenuTray.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,33 +0,0 @@
    -#include "gtkmodule.h"
    -
    -/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
    - * I thought about using the one from libgtk2-perl but wasn't sure how to go
    - * about doing that.
    -Gtk::Widget
    -pidgin_menu_tray_new()
    -
    -Gtk::Widget
    -pidgin_menu_tray_get_box(menu_tray)
    - Pidgin::MenuTray menu_tray
    -
    -void
    -pidgin_menu_tray_append(menu_tray, widget, tooltip)
    - Pidgin::MenuTray menu_tray
    - Gtk::Widget widget
    - const char * tooltip
    -
    -void
    -pidgin_menu_tray_prepend(menu_tray, widget, tooltip)
    - Pidgin::MenuTray menu_tray
    - Gtk::Widget widget
    - const char * tooltip
    -
    -void
    -pidgin_menu_tray_set_tooltip(menu_tray, widget, tooltip)
    - Pidgin::MenuTray menu_tray
    - Gtk::Widget widget
    - const char * tooltip
    -*/
    -
    -MODULE = Pidgin::MenuTray PACKAGE = Pidgin::MenuTray PREFIX = pidgin_menu_tray
    -PROTOTYPES: ENABLE
    --- a/pidgin/plugins/perl/common/GtkPlugin.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,13 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Plugin PACKAGE = Pidgin::Plugins PREFIX = pidgin_plugins_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_plugins_save()
    -
    -MODULE = Pidgin::Plugin PACKAGE = Pidgin::Plugin::Dialog PREFIX = pidgin_plugin_dialog_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_plugin_dialog_show()
    --- a/pidgin/plugins/perl/common/GtkPluginPref.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,4 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::PluginPref PACKAGE = Pidgin::PluginPref PREFIX = pidgin_plugin_pref_
    -PROTOTYPES: ENABLE
    --- a/pidgin/plugins/perl/common/GtkPounce.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,25 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Pounce PACKAGE = Pidgin::Pounce PREFIX = pidgin_pounce_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_pounce_editor_show(account, name, cur_pounce)
    - Purple::Account account
    - const char * name
    - Purple::Pounce cur_pounce
    -
    -MODULE = Pidgin::Pounce PACKAGE = Pidgin::Pounces PREFIX = pidgin_pounces_
    -PROTOTYPES: ENABLE
    -
    -Purple::Handle
    -pidgin_pounces_get_handle()
    -
    -MODULE = Pidgin::Pounce PACKAGE = Pidgin::Pounces::Manager PREFIX = pidgin_pounces_manager_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_pounces_manager_show()
    -
    -void
    -pidgin_pounces_manager_hide()
    --- a/pidgin/plugins/perl/common/GtkPrefs.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,7 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Prefs PACKAGE = Pidgin::Prefs PREFIX = pidgin_prefs_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_prefs_show()
    --- a/pidgin/plugins/perl/common/GtkPrivacy.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,23 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Privacy PACKAGE = Pidgin::Privacy PREFIX = pidgin_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_request_add_permit(account, name)
    - Purple::Account account
    - const char * name
    -
    -void
    -pidgin_request_add_block(account, name)
    - Purple::Account account
    - const char * name
    -
    -MODULE = Pidgin::Privacy PACKAGE = Pidgin::Privacy::Dialog PREFIX = pidgin_privacy_dialog_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_privacy_dialog_show()
    -
    -void
    -pidgin_privacy_dialog_hide()
    --- a/pidgin/plugins/perl/common/GtkRoomlist.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,17 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Roomlist PACKAGE = Pidgin::Roomlist PREFIX = pidgin_roomlist_
    -PROTOTYPES: ENABLE
    -
    -gboolean
    -pidgin_roomlist_is_showable()
    -
    -MODULE = Pidgin::Roomlist PACKAGE = Pidgin::Roomlist::Dialog PREFIX = pidgin_roomlist_dialog_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_roomlist_dialog_show()
    -
    -void
    -pidgin_roomlist_dialog_show_with_account(account)
    - Purple::Account account
    --- a/pidgin/plugins/perl/common/GtkSavedStatuses.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,24 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Status PACKAGE = Pidgin::Status PREFIX = pidgin_status_
    -PROTOTYPES: ENABLE
    -
    -Purple::Handle
    -pidgin_status_get_handle()
    -
    -MODULE = Pidgin::Status PACKAGE = Pidgin::Status::Editor PREFIX = pidgin_status_editor_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_status_editor_show(edit, status)
    - gboolean edit
    - Purple::SavedStatus status
    -
    -MODULE = Pidgin::Status PACKAGE = Pidgin::Status::Window PREFIX = pidgin_status_window_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_status_window_show()
    -
    -void
    -pidgin_status_window_hide()
    --- a/pidgin/plugins/perl/common/GtkSession.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,13 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Session PACKAGE = Pidgin::Session PREFIX = pidgin_session_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_session_init(argv0, previous_id, config_dir)
    - gchar * argv0
    - gchar * previous_id
    - gchar * config_dir
    -
    -void
    -pidgin_session_end()
    --- a/pidgin/plugins/perl/common/GtkSound.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,15 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Sound PACKAGE = Pidgin::Sound PREFIX = pidgin_sound_
    -PROTOTYPES: ENABLE
    -
    -const char *
    -pidgin_sound_get_event_option(event)
    - Purple::SoundEventID event
    -
    -const char *
    -pidgin_sound_get_event_label(event)
    - Purple::SoundEventID event
    -
    -Purple::Handle
    -pidgin_sound_get_handle()
    --- a/pidgin/plugins/perl/common/GtkStatusBox.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,41 +0,0 @@
    -#include "gtkmodule.h"
    -
    -/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
    - * I thought about using the one from libgtk2-perl but wasn't sure how to go
    - * about doing that.
    -Gtk::Widget
    -pidgin_status_box_new()
    -
    -Gtk::Widget
    -pidgin_status_box_new_with_account(account)
    - Purple::Account account
    -
    -void
    -pidgin_status_box_add(status_box, type, pixbuf, text, sec_text, data)
    - Pidgin::StatusBox status_box
    - Pidgin::StatusBox::ItemType type
    - GdkPixbuf pixbuf
    - const char * text
    - const char * sec_text
    - gpointer data
    -*/
    -
    -MODULE = Pidgin::StatusBox PACKAGE = Pidgin::StatusBox PREFIX = pidgin_status_box_
    -PROTOTYPES: ENABLE
    -
    -void
    -pidgin_status_box_add_separator(status_box)
    - Pidgin::StatusBox status_box
    -
    -void
    -pidgin_status_box_set_connecting(status_box, connecting)
    - Pidgin::StatusBox status_box
    - gboolean connecting
    -
    -void
    -pidgin_status_box_pulse_connecting(status_box)
    - Pidgin::StatusBox status_box
    -
    -gchar_own *
    -pidgin_status_box_get_message(status_box)
    - Pidgin::StatusBox status_box
    --- a/pidgin/plugins/perl/common/GtkUtils.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,17 +0,0 @@
    -#include "gtkmodule.h"
    -
    -MODULE = Pidgin::Utils PACKAGE = Pidgin::Utils PREFIX = pidgin_
    -PROTOTYPES: ENABLE
    -
    -gboolean
    -pidgin_save_accels(data)
    - gpointer data
    -
    -void
    -pidgin_load_accels()
    -
    -gchar_own *
    -pidgin_convert_buddy_icon(plugin, path, size)
    - Purple::Plugin plugin
    - const char * path
    - size_t *size
    --- a/pidgin/plugins/perl/common/MANIFEST Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,25 +0,0 @@
    -GtkAccount.xs
    -GtkBlist.xs
    -GtkConn.xs
    -GtkConv.xs
    -GtkConvWin.xs
    -GtkDebug.xs
    -GtkDialogs.xs
    -GtkFt.xs
    -GtkLog.xs
    -GtkMenuTray.xs
    -GtkPlugin.xs
    -GtkPluginPref.xs
    -GtkPounce.xs
    -GtkPrefs.xs
    -GtkPrivacy.xs
    -GtkRoomlist.xs
    -GtkSavedStatuses.xs
    -GtkSession.xs
    -GtkSound.xs
    -GtkStatusBox.xs
    -GtkUtils.xs
    -MANIFEST
    -Pidgin.pm
    -Pidgin.xs
    -typemap
    --- a/pidgin/plugins/perl/common/Makefile.PL.in Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,28 +0,0 @@
    -use 5.006;
    -use ExtUtils::MakeMaker;
    -# See lib/ExtUtils/MakeMaker.pm for details of how to influence the contents
    -# of the Makefile that is written.
    -WriteMakefile(
    - 'NAME' => 'Pidgin',
    - 'VERSION' => '@VERSION@',
    - ($] >= 5.005 ? ## Add these new keywords supported since 5.005
    - ('ABSTRACT_FROM' => '@srcdir@/Pidgin.pm', # finds $ABSTRACT
    - 'AUTHOR' => 'Pidgin <https://pidgin.im/>') : ()),
    - 'DEFINE' => '@DEBUG_CFLAGS@ -Wno-float-equal',
    - 'dynamic_lib' => { 'OTHERLDFLAGS' => '@LDFLAGS@' },
    - 'INC' => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_builddir@ -I@top_srcdir@/libpurple -I@top_srcdir@/pidgin @GTK_CFLAGS@ @WEBKIT_CFLAGS@ -DHAVE_CONFIG_H',
    - 'OBJECT' => '$(O_FILES)', # link all the C files too
    - 'TYPEMAPS' => ["@top_srcdir@/libpurple/plugins/perl/common/typemap"],
    -# 'OPTIMIZE' => '-g', # For debugging.
    - 'INSTALLDIRS' => 'vendor',
    - 'INSTALL_BASE' => '$(prefix)',
    - 'INSTALLVENDORARCH' => '$(libdir)/pidgin-$(PURPLE_MAJOR_VERSION)/perl',
    - 'INSTALLVENDORMAN3DIR' => '$(mandir)/man3',
    - 'macro' => {
    - 'prefix' => '@prefix@',
    - 'exec_prefix' => '@exec_prefix@',
    - 'libdir' => '@libdir@',
    - 'mandir' => '@mandir@',
    - 'datarootdir' => '@datarootdir@',
    - },
    -);
    --- a/pidgin/plugins/perl/common/Makefile.mingw Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,117 +0,0 @@
    -#
    -# Makefile.mingw
    -#
    -# Description: Makefile for Pidgin perl module.
    -#
    -
    -PIDGIN_TREE_TOP := ../../../..
    -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
    -
    -#we cannot include win32dep.h, but we need struct sockaddr_in6 definition
    -CFLAGS += -include ws2tcpip.h
    -
    -GCCWARNINGS += -Wno-comment -Wno-unused -Wno-nested-externs
    -
    -DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES))
    -
    -TARGET = Pidgin
    -
    -##
    -## INCLUDE PATHS
    -##
    -INCLUDE_PATHS = -I. \
    - -I$(PIDGIN_TREE_TOP) \
    - -I$(PURPLE_TOP) \
    - -I$(PURPLE_TOP)/win32 \
    - -I$(PIDGIN_TOP) \
    - -I$(PIDGIN_TOP)/win32 \
    - -I$(GTK_TOP)/include \
    - -I$(GTK_TOP)/include/atk-1.0 \
    - -I$(GTK_TOP)/include/cairo \
    - -I$(GTK_TOP)/include/glib-2.0 \
    - -I$(GTK_TOP)/include/gtk-2.0 \
    - -I$(GTK_TOP)/include/pango-1.0 \
    - -I$(GTK_TOP)/include/gdk-pixbuf-2.0 \
    - -I$(GTK_TOP)/lib/glib-2.0/include \
    - -I$(GTK_TOP)/lib/gtk-2.0/include \
    - -I$(LIBSOUP_TOP)/include/libsoup-2.4 \
    - -I$(WEBKITGTK_TOP)/include/webkitgtk-1.0 \
    - -I$(PERL_LIB_TOP)/include
    -
    -LIB_PATHS += \
    - -L$(PERL_LIB_TOP)/lib \
    - -L$(PURPLE_TOP) \
    - -L$(PIDGIN_TOP) \
    - -L$(PURPLE_PERL_TOP) \
    - -L$(GTK_TOP)/lib
    -
    -##
    -## SOURCES, OBJECTS
    -##
    -XS_FILES = \
    - Pidgin.xs \
    - GtkAccount.xs \
    - GtkBlist.xs \
    - GtkConn.xs \
    - GtkConv.xs \
    - GtkConvWin.xs \
    - GtkDebug.xs \
    - GtkDialogs.xs \
    - GtkFt.xs \
    - GtkLog.xs \
    - GtkMenuTray.xs \
    - GtkPlugin.xs \
    - GtkPluginPref.xs \
    - GtkPounce.xs \
    - GtkPrefs.xs \
    - GtkPrivacy.xs \
    - GtkRoomlist.xs \
    - GtkSavedStatuses.xs \
    - GtkSound.xs \
    - GtkStatusBox.xs \
    - GtkUtils.xs
    -
    -
    -C_FILES = $(XS_FILES:%.xs=%.c)
    -OBJECTS = $(C_FILES:%.c=%.o)
    -
    -##
    -## LIBRARIES
    -##
    -LIBS = -lperl520 \
    - -lperl \
    - -lpurple \
    - -lpidgin \
    - -lglib-2.0
    -
    -include $(PIDGIN_COMMON_RULES)
    -
    -##
    -## TARGETS
    -##
    -.PHONY: all install clean
    -
    -all: $(TARGET).dll
    -
    -$(PURPLE_INSTALL_PERL_DIR)/Purple.pm:
    - $(MAKE) -C $(PURPLE_PERL_TOP)/common -f $(MINGW_MAKEFILE) install
    -
    -install: all $(PURPLE_INSTALL_PERL_DIR)/Purple.pm
    - rm -f $(PIDGIN_INSTALL_PERL_DIR)/$(TARGET).dll $(PIDGIN_INSTALL_PERL_DIR)/auto/Pidgin/$(TARGET).pm
    - mkdir -p $(PIDGIN_INSTALL_PERL_DIR)
    - cp $(TARGET).pm $(PIDGIN_INSTALL_PERL_DIR)
    - mkdir -p $(PIDGIN_INSTALL_PERL_DIR)/auto/Pidgin
    - cp $(TARGET).dll $(PIDGIN_INSTALL_PERL_DIR)/auto/Pidgin
    -
    -$(C_FILES): $(PIDGIN_CONFIG_H)
    -
    -$(TARGET).dll: $(PIDGIN_DLL).a $(PURPLE_PERL_DLL).a $(OBJECTS)
    - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(DLL_LD_FLAGS) $(LIBS) -o $(TARGET).dll
    -
    -##
    -## CLEAN
    -##
    -clean:
    - rm -f *.o $(C_FILES) $(TARGET).dll
    -
    -include $(PIDGIN_COMMON_TARGETS)
    --- a/pidgin/plugins/perl/common/Pidgin.pm Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,59 +0,0 @@
    -package Pidgin;
    -
    -use 5.008;
    -use strict;
    -use warnings;
    -use Carp;
    -
    -use Purple;
    -
    -require XSLoader;
    -XSLoader::load('Pidgin', $VERSION);
    -
    -1;
    -__END__
    -
    -=head1 NAME
    -
    -Pidgin - Perl extension for the Pidgin instant messenger.
    -
    -=head1 SYNOPSIS
    -
    - use Pidgin;
    -
    -=head1 ABSTRACT
    -
    - This module provides the interface for using perl scripts as plugins in
    - Pidgin, with access to the Pidgin Gtk interface functions.
    -
    -=head1 DESCRIPTION
    -
    -This module provides the interface for using perl scripts as plugins in Pidgin,
    -with access to the Pidgin Gtk interface functions. With this, developers can
    -write perl scripts that can be loaded in Pidgin as plugins. The script can
    -interact with IMs, chats, accounts, the buddy list, pidgin signals, and more.
    -
    -The API for the perl interface is very similar to that of the Pidgin C API,
    -which can be viewed at https://developer.pidgin.im/doxygen/ or in the header files
    -in the Pidgin source tree.
    -
    -=head1 FUNCTIONS
    -
    -=over
    -
    -=back
    -
    -=head1 SEE ALSO
    -Pidgin C API documentation - https://developer.pidgin.im/doxygen/
    -
    -The Pidgin perl module.
    -
    -Pidgin website - https://pidgin.im/
    -
    -=head1 AUTHOR
    -
    -Etan Reisner, E<lt>deryni@gmail.comE<gt>
    -
    -=head1 COPYRIGHT AND LICENSE
    -
    -Copyright 2006 by Etan Reisner
    --- a/pidgin/plugins/perl/common/Pidgin.xs Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,60 +0,0 @@
    -#define PIDGIN_PERL_BOOT_PROTO(x) \
    - void boot_Pidgin__##x(pTHX_ CV *cv)
    -
    -#define PIDGIN_PERL_BOOT(x) \
    - purple_perl_callXS(boot_Pidgin__##x, cv, mark)
    -
    -#include "gtkmodule.h"
    -
    -/* Prototypes for the BOOT section below. */
    -PIDGIN_PERL_BOOT_PROTO(Account);
    -PIDGIN_PERL_BOOT_PROTO(BuddyList);
    -PIDGIN_PERL_BOOT_PROTO(Connection);
    -PIDGIN_PERL_BOOT_PROTO(Conversation);
    -PIDGIN_PERL_BOOT_PROTO(Conversation__Window);
    -PIDGIN_PERL_BOOT_PROTO(Debug);
    -PIDGIN_PERL_BOOT_PROTO(Dialogs);
    -PIDGIN_PERL_BOOT_PROTO(Log);
    -PIDGIN_PERL_BOOT_PROTO(MenuTray);
    -PIDGIN_PERL_BOOT_PROTO(Plugin);
    -PIDGIN_PERL_BOOT_PROTO(PluginPref);
    -PIDGIN_PERL_BOOT_PROTO(Pounce);
    -PIDGIN_PERL_BOOT_PROTO(Prefs);
    -PIDGIN_PERL_BOOT_PROTO(Privacy);
    -PIDGIN_PERL_BOOT_PROTO(Roomlist);
    -PIDGIN_PERL_BOOT_PROTO(Status);
    -#ifndef _WIN32
    -PIDGIN_PERL_BOOT_PROTO(Session);
    -#endif
    -PIDGIN_PERL_BOOT_PROTO(Sound);
    -PIDGIN_PERL_BOOT_PROTO(StatusBox);
    -PIDGIN_PERL_BOOT_PROTO(Utils);
    -PIDGIN_PERL_BOOT_PROTO(Xfer);
    -
    -MODULE = Pidgin PACKAGE = Pidgin PREFIX = pidgin_
    -PROTOTYPES: ENABLE
    -
    -BOOT:
    - PIDGIN_PERL_BOOT(Account);
    - PIDGIN_PERL_BOOT(BuddyList);
    - PIDGIN_PERL_BOOT(Connection);
    - PIDGIN_PERL_BOOT(Conversation);
    - PIDGIN_PERL_BOOT(Conversation__Window);
    - PIDGIN_PERL_BOOT(Debug);
    - PIDGIN_PERL_BOOT(Dialogs);
    - PIDGIN_PERL_BOOT(Log);
    - PIDGIN_PERL_BOOT(MenuTray);
    - PIDGIN_PERL_BOOT(Plugin);
    - PIDGIN_PERL_BOOT(PluginPref);
    - PIDGIN_PERL_BOOT(Pounce);
    - PIDGIN_PERL_BOOT(Prefs);
    - PIDGIN_PERL_BOOT(Privacy);
    - PIDGIN_PERL_BOOT(Roomlist);
    - PIDGIN_PERL_BOOT(Status);
    -#ifndef _WIN32
    - PIDGIN_PERL_BOOT(Session);
    -#endif
    - PIDGIN_PERL_BOOT(Sound);
    - PIDGIN_PERL_BOOT(StatusBox);
    - PIDGIN_PERL_BOOT(Utils);
    - PIDGIN_PERL_BOOT(Xfer);
    --- a/pidgin/plugins/perl/common/gtkmodule.h Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,70 +0,0 @@
    -/* Allow the Perl code to see deprecated functions, so we can continue to
    - * export them to Perl plugins. */
    -#undef PIDGIN_DISABLE_DEPRECATED
    -
    -typedef struct group *Pidgin__Group;
    -
    -#define group perl_group
    -
    -#include <glib.h>
    -#include <gtk/gtk.h>
    -#ifdef _WIN32
    -#undef pipe
    -#endif
    -
    -#define SILENT_NO_TAINT_SUPPORT 0
    -#define NO_TAINT_SUPPORT 0
    -
    -#include <EXTERN.h>
    -#include <perl.h>
    -#include <XSUB.h>
    -
    -#undef group
    -
    -#include <plugins/perl/common/module.h>
    -
    -#include "gtkaccount.h"
    -#include "gtkblist.h"
    -#include "gtkconn.h"
    -#include "gtkconv.h"
    -#include "gtkconvwin.h"
    -#include "gtkdebug.h"
    -#include "gtkdialogs.h"
    -#include "gtkxfer.h"
    -#include "gtklog.h"
    -#include "gtkmenutray.h"
    -#include "gtkplugin.h"
    -#include "gtkpluginpref.h"
    -#include "gtkpounce.h"
    -#include "gtkprefs.h"
    -#include "gtkprivacy.h"
    -#include "gtkroomlist.h"
    -#include "gtksavedstatuses.h"
    -#include "gtksession.h"
    -#include "gtksound.h"
    -#include "gtkstatusbox.h"
    -#include "gtkutils.h"
    -
    -/* gtkaccount.h */
    -typedef PidginAccountDialogType Pidgin__Account__Dialog__Type;
    -
    -/* gtkblist.h */
    -typedef PidginBuddyList * Pidgin__BuddyList;
    -typedef pidgin_blist_sort_function Pidgin__BuddyList__SortFunction;
    -
    -/* gtkconv.h */
    -typedef PidginConversation * Pidgin__Conversation;
    -typedef PidginUnseenState Pidgin__UnseenState;
    -
    -/* gtkconvwin.h */
    -typedef PidginConvWindow * Pidgin__Conversation__Window;
    -typedef PidginConvPlacementFunc Pidgin__Conversation__PlacementFunc;
    -
    -/* gtkxfer.h */
    -typedef PidginXferDialog * Pidgin__Xfer__Dialog;
    -
    -/* gtkmenutray.h */
    -typedef PidginMenuTray * Pidgin__MenuTray;
    -
    -/* gtkstatusbox.h */
    -typedef PidginStatusBox * Pidgin__StatusBox;
    --- a/pidgin/plugins/perl/common/typemap Sun May 28 13:26:27 2017 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,12 +0,0 @@
    -TYPEMAP
    -
    -Pidgin::Account::Dialog::Type T_IV
    -Pidgin::BuddyList T_PurpleObj
    -Pidgin::BuddyList::SortFunction T_PurpleObj
    -Pidgin::Conversation T_PurpleObj
    -Pidgin::Conversation::PlacementFunc T_PurpleObj
    -Pidgin::Conversation::Window T_PurpleObj
    -Pidgin::Xfer::Dialog T_PurpleObj
    -Pidgin::MenuTray T_PurpleObj
    -Pidgin::StatusBox T_PurpleObj
    -Pidgin::UnseenState T_IV
    --- a/pidgin/plugins/raw.c Sun May 28 13:26:27 2017 +0300
    +++ b/pidgin/plugins/raw.c Mon Jun 05 16:36:29 2017 +0300
    @@ -119,7 +119,7 @@
    "category", N_("Protocol utility"),
    "summary", N_("Lets you send raw input to text-based protocols."),
    "description", N_("Lets you send raw input to text-based protocols "
    - "(XMPP, MSN, IRC, TOC). Hit 'Enter' in the entry "
    + "(XMPP, IRC, TOC). Hit 'Enter' in the entry "
    "box to send. Watch the debug window."),
    "authors", authors,
    "website", PURPLE_WEBSITE,
    --- a/pidgin/win32/nsis/pidgin-installer.nsi Sun May 28 13:26:27 2017 +0300
    +++ b/pidgin/win32/nsis/pidgin-installer.nsi Mon Jun 05 16:36:29 2017 +0300
    @@ -543,7 +543,6 @@
    Delete "$INSTDIR\plugins\gtkbuddynote.dll"
    Delete "$INSTDIR\plugins\history.dll"
    Delete "$INSTDIR\plugins\internalkeyring.dll"
    - Delete "$INSTDIR\plugins\ssl-gnutls.dll"
    Delete "$INSTDIR\plugins\webkit.dll"
    Delete "$INSTDIR\plugins\wincred.dll"
    Delete "$INSTDIR\plugins\iconaway.dll"
    @@ -564,16 +563,12 @@
    Delete "$INSTDIR\plugins\markerline.dll"
    Delete "$INSTDIR\plugins\newline.dll"
    Delete "$INSTDIR\plugins\notify.dll"
    - Delete "$INSTDIR\plugins\nss-prefs.dll"
    Delete "$INSTDIR\plugins\offlinemsg.dll"
    - Delete "$INSTDIR\plugins\perl.dll"
    Delete "$INSTDIR\plugins\pidginrc.dll"
    Delete "$INSTDIR\plugins\psychic.dll"
    Delete "$INSTDIR\plugins\relnot.dll"
    Delete "$INSTDIR\plugins\sendbutton.dll"
    Delete "$INSTDIR\plugins\spellchk.dll"
    - Delete "$INSTDIR\plugins\ssl-nss.dll"
    - Delete "$INSTDIR\plugins\ssl.dll"
    Delete "$INSTDIR\plugins\statenotify.dll"
    Delete "$INSTDIR\plugins\ticker.dll"
    Delete "$INSTDIR\plugins\timestamp.dll"
    @@ -582,15 +577,6 @@
    Delete "$INSTDIR\plugins\winprefs.dll"
    Delete "$INSTDIR\plugins\xmppconsole.dll"
    Delete "$INSTDIR\plugins\xmppdisco.dll"
    - Delete "$INSTDIR\plugins\perl\auto\Pidgin\Pidgin.dll"
    - RMDir "$INSTDIR\plugins\perl\auto\Pidgin"
    - Delete "$INSTDIR\plugins\perl\auto\Purple\autosplit.ix"
    - Delete "$INSTDIR\plugins\perl\auto\Purple\Purple.dll"
    - RMDir "$INSTDIR\plugins\perl\auto\Purple"
    - RMDir "$INSTDIR\plugins\perl\auto"
    - Delete "$INSTDIR\plugins\perl\Pidgin.pm"
    - Delete "$INSTDIR\plugins\perl\Purple.pm"
    - RMDir "$INSTDIR\plugins\perl"
    RMDir "$INSTDIR\plugins"
    Delete "$INSTDIR\sasl2\libanonymous-3.dll"
    Delete "$INSTDIR\sasl2\libcrammd5-3.dll"
    --- a/po/POTFILES.skip Sun May 28 13:26:27 2017 +0300
    +++ b/po/POTFILES.skip Mon Jun 05 16:36:29 2017 +0300
    @@ -1,138 +1,6 @@
    libpurple/data/purple-url-handler.desktop.in.in
    -libpurple/plugins/mono/loader/mono.c
    -libpurple/plugins/perl/common/Account.c
    -libpurple/plugins/perl/common/AccountOpts.c
    -libpurple/plugins/perl/common/BuddyIcon.c
    -libpurple/plugins/perl/common/BuddyList.c
    -libpurple/plugins/perl/common/Certificate.c
    -libpurple/plugins/perl/common/Cipher.c
    -libpurple/plugins/perl/common/Cmds.c
    -libpurple/plugins/perl/common/Connection.c
    -libpurple/plugins/perl/common/Conversation.c
    -libpurple/plugins/perl/common/Core.c
    -libpurple/plugins/perl/common/Debug.c
    -libpurple/plugins/perl/common/FT.c
    -libpurple/plugins/perl/common/Idle.c
    -libpurple/plugins/perl/common/Log.c
    -libpurple/plugins/perl/common/Network.c
    -libpurple/plugins/perl/common/Notify.c
    -libpurple/plugins/perl/common/Plugin.c
    -libpurple/plugins/perl/common/PluginPref.c
    -libpurple/plugins/perl/common/Pounce.c
    -libpurple/plugins/perl/common/Prefs.c
    -libpurple/plugins/perl/common/Presence.c
    -libpurple/plugins/perl/common/Privacy.c
    -libpurple/plugins/perl/common/Proxy.c
    -libpurple/plugins/perl/common/Prpl.c
    -libpurple/plugins/perl/common/Purple.c
    -libpurple/plugins/perl/common/Request.c
    -libpurple/plugins/perl/common/Roomlist.c
    -libpurple/plugins/perl/common/SSLConn.c
    -libpurple/plugins/perl/common/SavedStatuses.c
    -libpurple/plugins/perl/common/Server.c
    -libpurple/plugins/perl/common/Signal.c
    -libpurple/plugins/perl/common/Smiley.c
    -libpurple/plugins/perl/common/Sound.c
    -libpurple/plugins/perl/common/Status.c
    -libpurple/plugins/perl/common/Stringref.c
    -libpurple/plugins/perl/common/Util.c
    -libpurple/plugins/perl/common/Whiteboard.c
    -libpurple/plugins/perl/common/XMLNode.c
    -libpurple/plugins/perl/common/Xfer.c
    -libpurple/plugins/perl/perl.c
    -libpurple/plugins/tcl/tcl.c
    libpurple/protocols/null/nullprpl.c
    pidgin/data/pidgin.desktop.in.in
    -pidgin/plugins/crazychat/cc_pidgin_plugin.c
    -pidgin/plugins/perl/common/GtkAccount.c
    -pidgin/plugins/perl/common/GtkBlist.c
    -pidgin/plugins/perl/common/GtkConn.c
    -pidgin/plugins/perl/common/GtkConv.c
    -pidgin/plugins/perl/common/GtkConvWin.c
    -pidgin/plugins/perl/common/GtkDebug.c
    -pidgin/plugins/perl/common/GtkDialogs.c
    -pidgin/plugins/perl/common/GtkFt.c
    -pidgin/plugins/perl/common/GtkIMHtml.c
    -pidgin/plugins/perl/common/GtkIMHtmlToolbar.c
    -pidgin/plugins/perl/common/GtkLog.c
    -pidgin/plugins/perl/common/GtkMenuTray.c
    -pidgin/plugins/perl/common/GtkPlugin.c
    -pidgin/plugins/perl/common/GtkPluginPref.c
    -pidgin/plugins/perl/common/GtkPounce.c
    -pidgin/plugins/perl/common/GtkPrefs.c
    -pidgin/plugins/perl/common/GtkPrivacy.c
    -pidgin/plugins/perl/common/GtkRoomlist.c
    -pidgin/plugins/perl/common/GtkSavedStatuses.c
    -pidgin/plugins/perl/common/GtkSession.c
    -pidgin/plugins/perl/common/GtkSound.c
    -pidgin/plugins/perl/common/GtkStatusBox.c
    -pidgin/plugins/perl/common/GtkThemes.c
    -pidgin/plugins/perl/common/GtkUtils.c
    -pidgin/plugins/perl/common/Pidgin.c
    sub/libpurple/data/purple-url-handler.desktop.in
    -sub/libpurple/plugins/perl/common/Account.c
    -sub/libpurple/plugins/perl/common/AccountOpts.c
    -sub/libpurple/plugins/perl/common/BuddyIcon.c
    -sub/libpurple/plugins/perl/common/BuddyList.c
    -sub/libpurple/plugins/perl/common/Certificate.c
    -sub/libpurple/plugins/perl/common/Cipher.c
    -sub/libpurple/plugins/perl/common/Cmds.c
    -sub/libpurple/plugins/perl/common/Connection.c
    -sub/libpurple/plugins/perl/common/Conversation.c
    -sub/libpurple/plugins/perl/common/Core.c
    -sub/libpurple/plugins/perl/common/Debug.c
    -sub/libpurple/plugins/perl/common/FT.c
    -sub/libpurple/plugins/perl/common/Idle.c
    -sub/libpurple/plugins/perl/common/ImgStore.c
    -sub/libpurple/plugins/perl/common/Log.c
    -sub/libpurple/plugins/perl/common/Network.c
    -sub/libpurple/plugins/perl/common/Notify.c
    -sub/libpurple/plugins/perl/common/Plugin.c
    -sub/libpurple/plugins/perl/common/PluginPref.c
    -sub/libpurple/plugins/perl/common/Pounce.c
    -sub/libpurple/plugins/perl/common/Prefs.c
    -sub/libpurple/plugins/perl/common/Privacy.c
    -sub/libpurple/plugins/perl/common/Proxy.c
    -sub/libpurple/plugins/perl/common/Prpl.c
    -sub/libpurple/plugins/perl/common/Purple.c
    -sub/libpurple/plugins/perl/common/Request.c
    -sub/libpurple/plugins/perl/common/Roomlist.c
    -sub/libpurple/plugins/perl/common/SSLConn.c
    -sub/libpurple/plugins/perl/common/SavedStatuses.c
    -sub/libpurple/plugins/perl/common/Server.c
    -sub/libpurple/plugins/perl/common/Signal.c
    -sub/libpurple/plugins/perl/common/Smiley.c
    -sub/libpurple/plugins/perl/common/Sound.c
    -sub/libpurple/plugins/perl/common/Status.c
    -sub/libpurple/plugins/perl/common/Stringref.c
    -sub/libpurple/plugins/perl/common/Util.c
    -sub/libpurple/plugins/perl/common/Whiteboard.c
    -sub/libpurple/plugins/perl/common/XMLNode.c
    sub/libpurple/protocols/null/nullprpl.c
    sub/pidgin/data/pidgin.desktop.in
    -sub/pidgin/plugins/crazychat/cc_pidgin_plugin.c
    -sub/pidgin/plugins/perl/common/GtkAccount.c
    -sub/pidgin/plugins/perl/common/GtkBlist.c
    -sub/pidgin/plugins/perl/common/GtkConn.c
    -sub/pidgin/plugins/perl/common/GtkConv.c
    -sub/pidgin/plugins/perl/common/GtkConvWin.c
    -sub/pidgin/plugins/perl/common/GtkDebug.c
    -sub/pidgin/plugins/perl/common/GtkDialogs.c
    -sub/pidgin/plugins/perl/common/GtkFt.c
    -sub/pidgin/plugins/perl/common/GtkIMHtml.c
    -sub/pidgin/plugins/perl/common/GtkIMHtmlToolbar.c
    -sub/pidgin/plugins/perl/common/GtkLog.c
    -sub/pidgin/plugins/perl/common/GtkMenuTray.c
    -sub/pidgin/plugins/perl/common/GtkPlugin.c
    -sub/pidgin/plugins/perl/common/GtkPluginPref.c
    -sub/pidgin/plugins/perl/common/GtkPounce.c
    -sub/pidgin/plugins/perl/common/GtkPrefs.c
    -sub/pidgin/plugins/perl/common/GtkPrivacy.c
    -sub/pidgin/plugins/perl/common/GtkRoomlist.c
    -sub/pidgin/plugins/perl/common/GtkSavedStatuses.c
    -sub/pidgin/plugins/perl/common/GtkSession.c
    -sub/pidgin/plugins/perl/common/GtkSound.c
    -sub/pidgin/plugins/perl/common/GtkStatusBox.c
    -sub/pidgin/plugins/perl/common/GtkThemes.c
    -sub/pidgin/plugins/perl/common/GtkUtils.c
    -sub/pidgin/plugins/perl/common/Pidgin.c