pidgin/pidgin

Remove the oscar protocol
release-2.x.y
2021-04-08, Gary Kramlich
d12f1daf83ff
Parents 0611c060a02c
Children 98f8af044651
Remove the oscar protocol

ICQ moved to a new protocol in 2017 and AIM was shutdown in 2017 as well.
Therefore, this code has been doing absolutely nothing for us.

Testing Done:
Compiled and ran on linux, need to test windows yet.

Reviewed at https://reviews.imfreedom.org/r/598/
  • +1 -4
    README
  • +10 -26
    configure.ac
  • +1 -2
    doc/finch.1.in
  • +0 -0
    doc/oscar/On_Sending_Files_via_OSCAR.odt
  • +0 -0
    doc/oscar/On_Sending_Files_via_OSCAR.pdf
  • +0 -0
    doc/oscar/Receive_Codepaths.odg
  • +0 -0
    doc/oscar/Send_Codepaths.odg
  • +2 -8
    doc/pidgin.1.in
  • +1 -1
    libpurple/plugins/psychic.c
  • +1 -1
    libpurple/protocols/Makefile.am
  • +1 -1
    libpurple/protocols/Makefile.mingw
  • +0 -49
    libpurple/protocols/oscar/AUTHORS
  • +0 -504
    libpurple/protocols/oscar/COPYING
  • +0 -82
    libpurple/protocols/oscar/Makefile.am
  • +0 -130
    libpurple/protocols/oscar/Makefile.mingw
  • +0 -131
    libpurple/protocols/oscar/authorization.c
  • +0 -288
    libpurple/protocols/oscar/bstream.c
  • +0 -646
    libpurple/protocols/oscar/clientlogin.c
  • +0 -285
    libpurple/protocols/oscar/encoding.c
  • +0 -46
    libpurple/protocols/oscar/encoding.h
  • +0 -246
    libpurple/protocols/oscar/family_admin.c
  • +0 -238
    libpurple/protocols/oscar/family_alert.c
  • +0 -630
    libpurple/protocols/oscar/family_auth.c
  • +0 -184
    libpurple/protocols/oscar/family_bart.c
  • +0 -92
    libpurple/protocols/oscar/family_bos.c
  • +0 -153
    libpurple/protocols/oscar/family_buddy.c
  • +0 -398
    libpurple/protocols/oscar/family_chat.c
  • +0 -445
    libpurple/protocols/oscar/family_chatnav.c
  • +0 -1981
    libpurple/protocols/oscar/family_feedbag.c
  • +0 -2143
    libpurple/protocols/oscar/family_icbm.c
  • +0 -793
    libpurple/protocols/oscar/family_icq.c
  • +0 -1556
    libpurple/protocols/oscar/family_locate.c
  • +0 -1142
    libpurple/protocols/oscar/family_oservice.c
  • +0 -84
    libpurple/protocols/oscar/family_popup.c
  • +0 -64
    libpurple/protocols/oscar/family_stats.c
  • +0 -157
    libpurple/protocols/oscar/family_userlookup.c
  • +0 -1127
    libpurple/protocols/oscar/flap_connection.c
  • +0 -431
    libpurple/protocols/oscar/kerberos.c
  • +0 -154
    libpurple/protocols/oscar/libaim.c
  • +0 -169
    libpurple/protocols/oscar/libicq.c
  • +0 -133
    libpurple/protocols/oscar/misc.c
  • +0 -179
    libpurple/protocols/oscar/msgcookie.c
  • +0 -628
    libpurple/protocols/oscar/odc.c
  • +0 -824
    libpurple/protocols/oscar/oft.c
  • +0 -5873
    libpurple/protocols/oscar/oscar.c
  • +0 -1355
    libpurple/protocols/oscar/oscar.h
  • +0 -157
    libpurple/protocols/oscar/oscar_data.c
  • +0 -124
    libpurple/protocols/oscar/oscarcommon.h
  • +0 -1127
    libpurple/protocols/oscar/peer.c
  • +0 -282
    libpurple/protocols/oscar/peer.h
  • +0 -355
    libpurple/protocols/oscar/peer_proxy.c
  • +0 -95
    libpurple/protocols/oscar/rxhandlers.c
  • +0 -165
    libpurple/protocols/oscar/snac.c
  • +0 -287
    libpurple/protocols/oscar/snactypes.h
  • +0 -830
    libpurple/protocols/oscar/tlv.c
  • +0 -542
    libpurple/protocols/oscar/userinfo.c
  • +0 -326
    libpurple/protocols/oscar/util.c
  • +0 -140
    libpurple/protocols/oscar/visibility.c
  • +0 -32
    libpurple/protocols/oscar/visibility.h
  • +1 -29
    libpurple/purple-url-handler
  • +0 -2
    libpurple/tests/Makefile.am
  • +0 -47
    libpurple/tests/test_oscar_util.c
  • +1 -4
    pidgin.apspec.in
  • +4 -7
    pidgin.spec.in
  • +0 -50
    pidgin/gtkstatusbox.c
  • +0 -43
    pidgin/gtkutils.c
  • +0 -12
    pidgin/pixmaps/Makefile.am
  • +0 -0
    pidgin/pixmaps/protocols/16/aim.png
  • +0 -0
    pidgin/pixmaps/protocols/16/icq.png
  • +0 -265
    pidgin/pixmaps/protocols/16/scalable/aim.svg
  • +0 -579
    pidgin/pixmaps/protocols/16/scalable/icq.svg
  • +0 -0
    pidgin/pixmaps/protocols/22/aim.png
  • +0 -0
    pidgin/pixmaps/protocols/22/icq.png
  • +0 -187
    pidgin/pixmaps/protocols/22/scalable/aim.svg
  • +0 -291
    pidgin/pixmaps/protocols/22/scalable/icq.svg
  • +0 -0
    pidgin/pixmaps/protocols/48/aim.png
  • +0 -0
    pidgin/pixmaps/protocols/48/icq.png
  • +0 -188
    pidgin/pixmaps/protocols/scalable/aim.svg
  • +0 -440
    pidgin/pixmaps/protocols/scalable/icq.svg
  • +0 -1
    pidgin/win32/nsis/pidgin-installer.nsi
  • --- a/README Mon Apr 05 20:41:42 2021 -0500
    +++ b/README Thu Apr 08 22:30:53 2021 -0500
    @@ -4,7 +4,7 @@
    libpurple is a library intended to be used by programmers seeking
    to write an IM client that connects to many IM networks. It supports
    -AIM, ICQ, and XMPP, among others.
    +XMPP among other protocols.
    Pidgin is a graphical IM client written in C which uses the GTK+
    toolkit.
    @@ -12,9 +12,6 @@
    Finch is a text-based IM client written in C which uses the ncurses
    toolkit.
    -These programs are not endorsed by, nor affiliated with, AOL nor any
    -other company in any way.
    -
    BUILD
    =====
    --- a/configure.ac Mon Apr 05 20:41:42 2021 -0500
    +++ b/configure.ac Thu Apr 08 22:30:53 2021 -0500
    @@ -1160,7 +1160,7 @@
    fi
    if test "x$STATIC_PRPLS" = "xall" ; then
    - STATIC_PRPLS="bonjour gg irc jabber novell oscar sametime silc simple zephyr"
    + STATIC_PRPLS="bonjour gg irc jabber novell sametime silc simple zephyr"
    fi
    if test "x$have_meanwhile" != "xyes" ; then
    STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'`
    @@ -1180,33 +1180,22 @@
    load_proto=
    for i in $STATIC_PRPLS ; do
    dnl Ugly special case for "libsilcpurple.la":
    - dnl ... and Ugly special case for multi-protocol oscar
    - if test \( "x$i" = "xoscar" -o "x$i" = "xaim" -o "x$i" = "xicq" \) -a "x$static_oscar" != "xyes"; then
    - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/oscar/liboscar.la"
    - extern_init="$extern_init extern gboolean purple_init_aim_plugin();"
    - extern_init="$extern_init extern gboolean purple_init_icq_plugin();"
    - load_proto="$load_proto purple_init_aim_plugin();"
    - load_proto="$load_proto purple_init_icq_plugin();"
    + if test "x$i" = "xsilc"; then
    + STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib${i}purple.la"
    + elif test "x$i" = "xsilc10"; then
    + STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libsilcpurple.la"
    else
    - if test "x$i" = "xsilc"; then
    - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib${i}purple.la"
    - elif test "x$i" = "xsilc10"; then
    - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libsilcpurple.la"
    - else
    - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.la"
    - fi
    - extern_init="$extern_init extern gboolean purple_init_${i}_plugin();"
    - load_proto="$load_proto purple_init_${i}_plugin();"
    + STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.la"
    fi
    + extern_init="$extern_init extern gboolean purple_init_${i}_plugin();"
    + load_proto="$load_proto purple_init_${i}_plugin();"
    +
    case $i in
    bonjour) static_bonjour=yes ;;
    gg) static_gg=yes ;;
    irc) static_irc=yes ;;
    jabber) static_jabber=yes ;;
    novell) static_novell=yes ;;
    - oscar) static_oscar=yes ;;
    - aim) static_oscar=yes ;;
    - icq) static_oscar=yes ;;
    sametime) static_sametime=yes ;;
    silc) static_silc=yes ;;
    silc10) static_silc=yes ;;
    @@ -1220,7 +1209,6 @@
    AM_CONDITIONAL(STATIC_IRC, test "x$static_irc" = "xyes")
    AM_CONDITIONAL(STATIC_JABBER, test "x$static_jabber" = "xyes")
    AM_CONDITIONAL(STATIC_NOVELL, test "x$static_novell" = "xyes")
    -AM_CONDITIONAL(STATIC_OSCAR, test "x$static_oscar" = "xyes")
    AM_CONDITIONAL(STATIC_SAMETIME, test "x$static_sametime" = "xyes" -a "x$have_meanwhile" = "xyes")
    AM_CONDITIONAL(STATIC_SILC, test "x$static_silc" = "xyes" -a "x$have_silc" = "xyes")
    AM_CONDITIONAL(STATIC_SIMPLE, test "x$static_simple" = "xyes")
    @@ -1231,7 +1219,7 @@
    AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`])
    if test "x$DYNAMIC_PRPLS" = "xall" ; then
    - DYNAMIC_PRPLS="bonjour gg irc jabber novell oscar sametime silc simple zephyr"
    + DYNAMIC_PRPLS="bonjour gg irc jabber novell sametime silc simple zephyr"
    fi
    if test "x$have_meanwhile" != "xyes"; then
    DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'`
    @@ -1254,9 +1242,6 @@
    jabber) dynamic_jabber=yes ;;
    novell) dynamic_novell=yes ;;
    null) dynamic_null=yes ;;
    - oscar) dynamic_oscar=yes ;;
    - aim) dynamic_oscar=yes ;;
    - icq) dynamic_oscar=yes ;;
    sametime) dynamic_sametime=yes ;;
    silc) dynamic_silc=yes ;;
    silc10) dynamic_silc=yes ;;
    @@ -2604,7 +2589,6 @@
    libpurple/protocols/jabber/Makefile
    libpurple/protocols/novell/Makefile
    libpurple/protocols/null/Makefile
    - libpurple/protocols/oscar/Makefile
    libpurple/protocols/sametime/Makefile
    libpurple/protocols/silc/Makefile
    libpurple/protocols/silc10/Makefile
    --- a/doc/finch.1.in Mon Apr 05 20:41:42 2021 -0500
    +++ b/doc/finch.1.in Thu Apr 08 22:30:53 2021 -0500
    @@ -30,10 +30,9 @@
    .SH DESCRIPTION
    .PP
    \fBfinch\fR is a console-based modular messaging client based on libpurple
    -which is capable of connecting to AIM, XMPP, ICQ, IRC, SILC,
    +which is capable of connecting to XMPP, IRC, SILC,
    Novell GroupWise, Lotus Sametime, Zephyr, Gadu-Gadu, and QQ all at once. It has
    many common features found in other clients, as well as many unique features.
    -Finch is not endorsed by or affiliated with America Online, ICQ, or Microsoft.
    .SH OPTIONS
    The following options are provided by \fBfinch\fR using the standard GNU
    Binary file doc/oscar/On_Sending_Files_via_OSCAR.odt has changed
    Binary file doc/oscar/On_Sending_Files_via_OSCAR.pdf has changed
    Binary file doc/oscar/Receive_Codepaths.odg has changed
    Binary file doc/oscar/Send_Codepaths.odg has changed
    --- a/doc/pidgin.1.in Mon Apr 05 20:41:42 2021 -0500
    +++ b/doc/pidgin.1.in Thu Apr 08 22:30:53 2021 -0500
    @@ -29,10 +29,9 @@
    .SH DESCRIPTION
    .PP
    \fBpidgin\fR is a graphical modular messaging client based on libpurple
    -which is capable of connecting to AIM, XMPP, ICQ, IRC, SILC,
    +which is capable of connecting to XMPP, IRC, SILC,
    Novell GroupWise, Lotus Sametime, Zephyr, Gadu-Gadu, and QQ all at once. It has
    many common features found in other clients, as well as many unique features.
    -Pidgin is not endorsed by or affiliated with America Online, ICQ, or Microsoft.
    .PP
    Pidgin can be extended by plugins written in multiple programming languages and
    controlled through DBus or \fBpurple-remote\fR.
    @@ -92,7 +91,7 @@
    other protocols, aliases are saved only locally.
    .TP
    .B Protocol
    -A messaging service. AIM, XMPP, Zephyr, etc. are protocols. Others may
    +A messaging service. XMPP, Zephyr, etc. are protocols. Others may
    call these "service types," "account types," "services," and so on.
    .SH BUDDY LIST
    @@ -332,11 +331,6 @@
    message in Pidgin to determine idle. \fBNever\fR disables idle reporting.
    .TP
    -.B Auto-reply
    -Determines when to send an auto-reply on protocols which support it
    -(currently only AIM).
    -
    -.TP
    .B Change status when idle
    When enabled, this uses the \fBMinutes before becoming idle\fR and \fBChange
    status to\fR preferences described below to set status on idle.
    --- a/libpurple/plugins/psychic.c Mon Apr 05 20:41:42 2021 -0500
    +++ b/libpurple/plugins/psychic.c Thu Apr 08 22:30:53 2021 -0500
    @@ -21,7 +21,7 @@
    #define PLUGIN_SUMMARY N_("Psychic mode for incoming conversation")
    #define PLUGIN_DESC N_("Causes conversation windows to appear as other" \
    " users begin to message you. This works for" \
    - " AIM, ICQ, XMPP, and Sametime")
    + " XMPP and Sametime")
    #define PLUGIN_AUTHOR "Christopher O'Brien <siege@preoccupied.net>"
    --- a/libpurple/protocols/Makefile.am Mon Apr 05 20:41:42 2021 -0500
    +++ b/libpurple/protocols/Makefile.am Thu Apr 08 22:30:53 2021 -0500
    @@ -1,5 +1,5 @@
    EXTRA_DIST = Makefile.mingw
    -DIST_SUBDIRS = bonjour gg irc jabber novell null oscar sametime silc silc10 simple zephyr
    +DIST_SUBDIRS = bonjour gg irc jabber novell null sametime silc silc10 simple zephyr
    SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS)
    --- a/libpurple/protocols/Makefile.mingw Mon Apr 05 20:41:42 2021 -0500
    +++ b/libpurple/protocols/Makefile.mingw Thu Apr 08 22:30:53 2021 -0500
    @@ -8,7 +8,7 @@
    PIDGIN_TREE_TOP := ../..
    include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
    -SUBDIRS = gg irc jabber novell null oscar sametime silc simple bonjour
    +SUBDIRS = gg irc jabber novell null sametime silc simple bonjour
    .PHONY: all install clean
    --- a/libpurple/protocols/oscar/AUTHORS Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,49 +0,0 @@
    -
    -N: ComBOTS Product GmbH (htfv)
    -T: 2007
    -E: foss@combots.com
    -
    -N: Jonathan Clark
    -T: 2005-2006
    -E: ardentlygnarley a.t users d.o.t sourceforge d.o.t net
    -
    -N: Mark Doliner
    -T: 2001-2006
    -H: markdoliner
    -E: thekingant a.t users d.o.t sourceforge d.o.t net
    -W: http://kingant.net/
    -
    -N: Adam Fritzler
    -T: 1998-2001
    -H: mid
    -E: mid a.t auk d.o.t cx
    -W: http://www.auk.cx/~mid,http://www.auk.cx/faim
    -D: Wrote most of the wap of crap that you see before you.
    -
    -N: Josh Myer
    -T: 1998-2001
    -E: josh a.t joshisanerd d.o.t com
    -D: OFT/ODC (not quite finished yet..), random little things, Munger-At-Large, compile-time warnings.
    -
    -N: Daniel M. Pomerantz
    -H: dmprantz
    -D: Made initial versions cross platform
    -
    -N: Daniel Reed
    -T: 1998-2001
    -H: n, linuxkitty
    -E: n a.t ml d.o.t org
    -W: http://users.n.ml.org/n/
    -D: Fixed aim_snac.c
    -
    -N: Eric Warmenhoven
    -T: 1998-2001
    -E: warmenhoven a.t linux d.o.t com
    -D: Some OFT info, initial author of the libpurple-side of the oscar protocol plugin
    -
    -N: Brock Wilcox
    -T: 1998-2001
    -H: awwaiid
    -E: awwaiid a.t auk d.o.t cx
    -D: Figured out original password roasting
    -
    --- a/libpurple/protocols/oscar/COPYING Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,504 +0,0 @@
    - GNU LESSER GENERAL PUBLIC LICENSE
    - Version 2.1, February 1999
    -
    - Copyright (C) 1991, 1999 Free Software Foundation, Inc.
    - 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - Everyone is permitted to copy and distribute verbatim copies
    - of this license document, but changing it is not allowed.
    -
    -[This is the first released version of the Lesser GPL. It also counts
    - as the successor of the GNU Library Public License, version 2, hence
    - the version number 2.1.]
    -
    - Preamble
    -
    - The licenses for most software are designed to take away your
    -freedom to share and change it. By contrast, the GNU General Public
    -Licenses are intended to guarantee your freedom to share and change
    -free software--to make sure the software is free for all its users.
    -
    - This license, the Lesser General Public License, applies to some
    -specially designated software packages--typically libraries--of the
    -Free Software Foundation and other authors who decide to use it. You
    -can use it too, but we suggest you first think carefully about whether
    -this license or the ordinary General Public License is the better
    -strategy to use in any particular case, based on the explanations below.
    -
    - When we speak of free software, we are referring to freedom of use,
    -not price. Our General Public Licenses are designed to make sure that
    -you have the freedom to distribute copies of free software (and charge
    -for this service if you wish); that you receive source code or can get
    -it if you want it; that you can change the software and use pieces of
    -it in new free programs; and that you are informed that you can do
    -these things.
    -
    - To protect your rights, we need to make restrictions that forbid
    -distributors to deny you these rights or to ask you to surrender these
    -rights. These restrictions translate to certain responsibilities for
    -you if you distribute copies of the library or if you modify it.
    -
    - For example, if you distribute copies of the library, whether gratis
    -or for a fee, you must give the recipients all the rights that we gave
    -you. You must make sure that they, too, receive or can get the source
    -code. If you link other code with the library, you must provide
    -complete object files to the recipients, so that they can relink them
    -with the library after making changes to the library and recompiling
    -it. And you must show them these terms so they know their rights.
    -
    - We protect your rights with a two-step method: (1) we copyright the
    -library, and (2) we offer you this license, which gives you legal
    -permission to copy, distribute and/or modify the library.
    -
    - To protect each distributor, we want to make it very clear that
    -there is no warranty for the free library. Also, if the library is
    -modified by someone else and passed on, the recipients should know
    -that what they have is not the original version, so that the original
    -author's reputation will not be affected by problems that might be
    -introduced by others.
    -
    - Finally, software patents pose a constant threat to the existence of
    -any free program. We wish to make sure that a company cannot
    -effectively restrict the users of a free program by obtaining a
    -restrictive license from a patent holder. Therefore, we insist that
    -any patent license obtained for a version of the library must be
    -consistent with the full freedom of use specified in this license.
    -
    - Most GNU software, including some libraries, is covered by the
    -ordinary GNU General Public License. This license, the GNU Lesser
    -General Public License, applies to certain designated libraries, and
    -is quite different from the ordinary General Public License. We use
    -this license for certain libraries in order to permit linking those
    -libraries into non-free programs.
    -
    - When a program is linked with a library, whether statically or using
    -a shared library, the combination of the two is legally speaking a
    -combined work, a derivative of the original library. The ordinary
    -General Public License therefore permits such linking only if the
    -entire combination fits its criteria of freedom. The Lesser General
    -Public License permits more lax criteria for linking other code with
    -the library.
    -
    - We call this license the "Lesser" General Public License because it
    -does Less to protect the user's freedom than the ordinary General
    -Public License. It also provides other free software developers Less
    -of an advantage over competing non-free programs. These disadvantages
    -are the reason we use the ordinary General Public License for many
    -libraries. However, the Lesser license provides advantages in certain
    -special circumstances.
    -
    - For example, on rare occasions, there may be a special need to
    -encourage the widest possible use of a certain library, so that it becomes
    -a de-facto standard. To achieve this, non-free programs must be
    -allowed to use the library. A more frequent case is that a free
    -library does the same job as widely used non-free libraries. In this
    -case, there is little to gain by limiting the free library to free
    -software only, so we use the Lesser General Public License.
    -
    - In other cases, permission to use a particular library in non-free
    -programs enables a greater number of people to use a large body of
    -free software. For example, permission to use the GNU C Library in
    -non-free programs enables many more people to use the whole GNU
    -operating system, as well as its variant, the GNU/Linux operating
    -system.
    -
    - Although the Lesser General Public License is Less protective of the
    -users' freedom, it does ensure that the user of a program that is
    -linked with the Library has the freedom and the wherewithal to run
    -that program using a modified version of the Library.
    -
    - The precise terms and conditions for copying, distribution and
    -modification follow. Pay close attention to the difference between a
    -"work based on the library" and a "work that uses the library". The
    -former contains code derived from the library, whereas the latter must
    -be combined with the library in order to run.
    -
    - GNU LESSER GENERAL PUBLIC LICENSE
    - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
    -
    - 0. This License Agreement applies to any software library or other
    -program which contains a notice placed by the copyright holder or
    -other authorized party saying it may be distributed under the terms of
    -this Lesser General Public License (also called "this License").
    -Each licensee is addressed as "you".
    -
    - A "library" means a collection of software functions and/or data
    -prepared so as to be conveniently linked with application programs
    -(which use some of those functions and data) to form executables.
    -
    - The "Library", below, refers to any such software library or work
    -which has been distributed under these terms. A "work based on the
    -Library" means either the Library or any derivative work under
    -copyright law: that is to say, a work containing the Library or a
    -portion of it, either verbatim or with modifications and/or translated
    -straightforwardly into another language. (Hereinafter, translation is
    -included without limitation in the term "modification".)
    -
    - "Source code" for a work means the preferred form of the work for
    -making modifications to it. For a library, complete source code means
    -all the source code for all modules it contains, plus any associated
    -interface definition files, plus the scripts used to control compilation
    -and installation of the library.
    -
    - Activities other than copying, distribution and modification are not
    -covered by this License; they are outside its scope. The act of
    -running a program using the Library is not restricted, and output from
    -such a program is covered only if its contents constitute a work based
    -on the Library (independent of the use of the Library in a tool for
    -writing it). Whether that is true depends on what the Library does
    -and what the program that uses the Library does.
    -
    - 1. You may copy and distribute verbatim copies of the Library's
    -complete source code as you receive it, in any medium, provided that
    -you conspicuously and appropriately publish on each copy an
    -appropriate copyright notice and disclaimer of warranty; keep intact
    -all the notices that refer to this License and to the absence of any
    -warranty; and distribute a copy of this License along with the
    -Library.
    -
    - You may charge a fee for the physical act of transferring a copy,
    -and you may at your option offer warranty protection in exchange for a
    -fee.
    -
    - 2. You may modify your copy or copies of the Library or any portion
    -of it, thus forming a work based on the Library, and copy and
    -distribute such modifications or work under the terms of Section 1
    -above, provided that you also meet all of these conditions:
    -
    - a) The modified work must itself be a software library.
    -
    - b) You must cause the files modified to carry prominent notices
    - stating that you changed the files and the date of any change.
    -
    - c) You must cause the whole of the work to be licensed at no
    - charge to all third parties under the terms of this License.
    -
    - d) If a facility in the modified Library refers to a function or a
    - table of data to be supplied by an application program that uses
    - the facility, other than as an argument passed when the facility
    - is invoked, then you must make a good faith effort to ensure that,
    - in the event an application does not supply such function or
    - table, the facility still operates, and performs whatever part of
    - its purpose remains meaningful.
    -
    - (For example, a function in a library to compute square roots has
    - a purpose that is entirely well-defined independent of the
    - application. Therefore, Subsection 2d requires that any
    - application-supplied function or table used by this function must
    - be optional: if the application does not supply it, the square
    - root function must still compute square roots.)
    -
    -These requirements apply to the modified work as a whole. If
    -identifiable sections of that work are not derived from the Library,
    -and can be reasonably considered independent and separate works in
    -themselves, then this License, and its terms, do not apply to those
    -sections when you distribute them as separate works. But when you
    -distribute the same sections as part of a whole which is a work based
    -on the Library, the distribution of the whole must be on the terms of
    -this License, whose permissions for other licensees extend to the
    -entire whole, and thus to each and every part regardless of who wrote
    -it.
    -
    -Thus, it is not the intent of this section to claim rights or contest
    -your rights to work written entirely by you; rather, the intent is to
    -exercise the right to control the distribution of derivative or
    -collective works based on the Library.
    -
    -In addition, mere aggregation of another work not based on the Library
    -with the Library (or with a work based on the Library) on a volume of
    -a storage or distribution medium does not bring the other work under
    -the scope of this License.
    -
    - 3. You may opt to apply the terms of the ordinary GNU General Public
    -License instead of this License to a given copy of the Library. To do
    -this, you must alter all the notices that refer to this License, so
    -that they refer to the ordinary GNU General Public License, version 2,
    -instead of to this License. (If a newer version than version 2 of the
    -ordinary GNU General Public License has appeared, then you can specify
    -that version instead if you wish.) Do not make any other change in
    -these notices.
    -
    - Once this change is made in a given copy, it is irreversible for
    -that copy, so the ordinary GNU General Public License applies to all
    -subsequent copies and derivative works made from that copy.
    -
    - This option is useful when you wish to copy part of the code of
    -the Library into a program that is not a library.
    -
    - 4. You may copy and distribute the Library (or a portion or
    -derivative of it, under Section 2) in object code or executable form
    -under the terms of Sections 1 and 2 above provided that you accompany
    -it with the complete corresponding machine-readable source code, which
    -must be distributed under the terms of Sections 1 and 2 above on a
    -medium customarily used for software interchange.
    -
    - If distribution of object code is made by offering access to copy
    -from a designated place, then offering equivalent access to copy the
    -source code from the same place satisfies the requirement to
    -distribute the source code, even though third parties are not
    -compelled to copy the source along with the object code.
    -
    - 5. A program that contains no derivative of any portion of the
    -Library, but is designed to work with the Library by being compiled or
    -linked with it, is called a "work that uses the Library". Such a
    -work, in isolation, is not a derivative work of the Library, and
    -therefore falls outside the scope of this License.
    -
    - However, linking a "work that uses the Library" with the Library
    -creates an executable that is a derivative of the Library (because it
    -contains portions of the Library), rather than a "work that uses the
    -library". The executable is therefore covered by this License.
    -Section 6 states terms for distribution of such executables.
    -
    - When a "work that uses the Library" uses material from a header file
    -that is part of the Library, the object code for the work may be a
    -derivative work of the Library even though the source code is not.
    -Whether this is true is especially significant if the work can be
    -linked without the Library, or if the work is itself a library. The
    -threshold for this to be true is not precisely defined by law.
    -
    - If such an object file uses only numerical parameters, data
    -structure layouts and accessors, and small macros and small inline
    -functions (ten lines or less in length), then the use of the object
    -file is unrestricted, regardless of whether it is legally a derivative
    -work. (Executables containing this object code plus portions of the
    -Library will still fall under Section 6.)
    -
    - Otherwise, if the work is a derivative of the Library, you may
    -distribute the object code for the work under the terms of Section 6.
    -Any executables containing that work also fall under Section 6,
    -whether or not they are linked directly with the Library itself.
    -
    - 6. As an exception to the Sections above, you may also combine or
    -link a "work that uses the Library" with the Library to produce a
    -work containing portions of the Library, and distribute that work
    -under terms of your choice, provided that the terms permit
    -modification of the work for the customer's own use and reverse
    -engineering for debugging such modifications.
    -
    - You must give prominent notice with each copy of the work that the
    -Library is used in it and that the Library and its use are covered by
    -this License. You must supply a copy of this License. If the work
    -during execution displays copyright notices, you must include the
    -copyright notice for the Library among them, as well as a reference
    -directing the user to the copy of this License. Also, you must do one
    -of these things:
    -
    - a) Accompany the work with the complete corresponding
    - machine-readable source code for the Library including whatever
    - changes were used in the work (which must be distributed under
    - Sections 1 and 2 above); and, if the work is an executable linked
    - with the Library, with the complete machine-readable "work that
    - uses the Library", as object code and/or source code, so that the
    - user can modify the Library and then relink to produce a modified
    - executable containing the modified Library. (It is understood
    - that the user who changes the contents of definitions files in the
    - Library will not necessarily be able to recompile the application
    - to use the modified definitions.)
    -
    - b) Use a suitable shared library mechanism for linking with the
    - Library. A suitable mechanism is one that (1) uses at run time a
    - copy of the library already present on the user's computer system,
    - rather than copying library functions into the executable, and (2)
    - will operate properly with a modified version of the library, if
    - the user installs one, as long as the modified version is
    - interface-compatible with the version that the work was made with.
    -
    - c) Accompany the work with a written offer, valid for at
    - least three years, to give the same user the materials
    - specified in Subsection 6a, above, for a charge no more
    - than the cost of performing this distribution.
    -
    - d) If distribution of the work is made by offering access to copy
    - from a designated place, offer equivalent access to copy the above
    - specified materials from the same place.
    -
    - e) Verify that the user has already received a copy of these
    - materials or that you have already sent this user a copy.
    -
    - For an executable, the required form of the "work that uses the
    -Library" must include any data and utility programs needed for
    -reproducing the executable from it. However, as a special exception,
    -the materials to be distributed need not include anything that is
    -normally distributed (in either source or binary form) with the major
    -components (compiler, kernel, and so on) of the operating system on
    -which the executable runs, unless that component itself accompanies
    -the executable.
    -
    - It may happen that this requirement contradicts the license
    -restrictions of other proprietary libraries that do not normally
    -accompany the operating system. Such a contradiction means you cannot
    -use both them and the Library together in an executable that you
    -distribute.
    -
    - 7. You may place library facilities that are a work based on the
    -Library side-by-side in a single library together with other library
    -facilities not covered by this License, and distribute such a combined
    -library, provided that the separate distribution of the work based on
    -the Library and of the other library facilities is otherwise
    -permitted, and provided that you do these two things:
    -
    - a) Accompany the combined library with a copy of the same work
    - based on the Library, uncombined with any other library
    - facilities. This must be distributed under the terms of the
    - Sections above.
    -
    - b) Give prominent notice with the combined library of the fact
    - that part of it is a work based on the Library, and explaining
    - where to find the accompanying uncombined form of the same work.
    -
    - 8. You may not copy, modify, sublicense, link with, or distribute
    -the Library except as expressly provided under this License. Any
    -attempt otherwise to copy, modify, sublicense, link with, or
    -distribute the Library is void, and will automatically terminate your
    -rights under this License. However, parties who have received copies,
    -or rights, from you under this License will not have their licenses
    -terminated so long as such parties remain in full compliance.
    -
    - 9. You are not required to accept this License, since you have not
    -signed it. However, nothing else grants you permission to modify or
    -distribute the Library or its derivative works. These actions are
    -prohibited by law if you do not accept this License. Therefore, by
    -modifying or distributing the Library (or any work based on the
    -Library), you indicate your acceptance of this License to do so, and
    -all its terms and conditions for copying, distributing or modifying
    -the Library or works based on it.
    -
    - 10. Each time you redistribute the Library (or any work based on the
    -Library), the recipient automatically receives a license from the
    -original licensor to copy, distribute, link with or modify the Library
    -subject to these terms and conditions. You may not impose any further
    -restrictions on the recipients' exercise of the rights granted herein.
    -You are not responsible for enforcing compliance by third parties with
    -this License.
    -
    - 11. If, as a consequence of a court judgment or allegation of patent
    -infringement or for any other reason (not limited to patent issues),
    -conditions are imposed on you (whether by court order, agreement or
    -otherwise) that contradict the conditions of this License, they do not
    -excuse you from the conditions of this License. If you cannot
    -distribute so as to satisfy simultaneously your obligations under this
    -License and any other pertinent obligations, then as a consequence you
    -may not distribute the Library at all. For example, if a patent
    -license would not permit royalty-free redistribution of the Library by
    -all those who receive copies directly or indirectly through you, then
    -the only way you could satisfy both it and this License would be to
    -refrain entirely from distribution of the Library.
    -
    -If any portion of this section is held invalid or unenforceable under any
    -particular circumstance, the balance of the section is intended to apply,
    -and the section as a whole is intended to apply in other circumstances.
    -
    -It is not the purpose of this section to induce you to infringe any
    -patents or other property right claims or to contest validity of any
    -such claims; this section has the sole purpose of protecting the
    -integrity of the free software distribution system which is
    -implemented by public license practices. Many people have made
    -generous contributions to the wide range of software distributed
    -through that system in reliance on consistent application of that
    -system; it is up to the author/donor to decide if he or she is willing
    -to distribute software through any other system and a licensee cannot
    -impose that choice.
    -
    -This section is intended to make thoroughly clear what is believed to
    -be a consequence of the rest of this License.
    -
    - 12. If the distribution and/or use of the Library is restricted in
    -certain countries either by patents or by copyrighted interfaces, the
    -original copyright holder who places the Library under this License may add
    -an explicit geographical distribution limitation excluding those countries,
    -so that distribution is permitted only in or among countries not thus
    -excluded. In such case, this License incorporates the limitation as if
    -written in the body of this License.
    -
    - 13. The Free Software Foundation may publish revised and/or new
    -versions of the Lesser General Public License from time to time.
    -Such new versions will be similar in spirit to the present version,
    -but may differ in detail to address new problems or concerns.
    -
    -Each version is given a distinguishing version number. If the Library
    -specifies a version number of this License which applies to it and
    -"any later version", you have the option of following the terms and
    -conditions either of that version or of any later version published by
    -the Free Software Foundation. If the Library does not specify a
    -license version number, you may choose any version ever published by
    -the Free Software Foundation.
    -
    - 14. If you wish to incorporate parts of the Library into other free
    -programs whose distribution conditions are incompatible with these,
    -write to the author to ask for permission. For software which is
    -copyrighted by the Free Software Foundation, write to the Free
    -Software Foundation; we sometimes make exceptions for this. Our
    -decision will be guided by the two goals of preserving the free status
    -of all derivatives of our free software and of promoting the sharing
    -and reuse of software generally.
    -
    - NO WARRANTY
    -
    - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
    -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
    -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
    -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
    -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
    -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
    -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
    -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
    -
    - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
    -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
    -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
    -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
    -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
    -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
    -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
    -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
    -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    -DAMAGES.
    -
    - END OF TERMS AND CONDITIONS
    -
    - How to Apply These Terms to Your New Libraries
    -
    - If you develop a new library, and you want it to be of the greatest
    -possible use to the public, we recommend making it free software that
    -everyone can redistribute and change. You can do so by permitting
    -redistribution under these terms (or, alternatively, under the terms of the
    -ordinary General Public License).
    -
    - To apply these terms, attach the following notices to the library. It is
    -safest to attach them to the start of each source file to most effectively
    -convey the exclusion of warranty; and each file should have at least the
    -"copyright" line and a pointer to where the full notice is found.
    -
    - <one line to give the library's name and a brief idea of what it does.>
    - Copyright (C) <year> <name of author>
    -
    - This library is free software; you can redistribute it and/or
    - modify it under the terms of the GNU Lesser General Public
    - License as published by the Free Software Foundation; either
    - version 2 of the License, or (at your option) any later version.
    -
    - This library 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
    - Lesser General Public License for more details.
    -
    - You should have received a copy of the GNU Lesser General Public
    - License along with this library; if not, write to the Free Software
    - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -
    -Also add information on how to contact you by electronic and paper mail.
    -
    -You should also get your employer (if you work as a programmer) or your
    -school, if any, to sign a "copyright disclaimer" for the library, if
    -necessary. Here is a sample; alter the names:
    -
    - Yoyodyne, Inc., hereby disclaims all copyright interest in the
    - library `Frob' (a library for tweaking knobs) written by James Random Hacker.
    -
    - <signature of Ty Coon>, 1 April 1990
    - Ty Coon, President of Vice
    -
    -That's all there is to it!
    -
    -
    --- a/libpurple/protocols/oscar/Makefile.am Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,82 +0,0 @@
    -EXTRA_DIST = \
    - COPYING \
    - AUTHORS \
    - Makefile.mingw
    -
    -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
    -
    -OSCARSOURCES = \
    - authorization.c \
    - bstream.c \
    - clientlogin.c \
    - kerberos.c \
    - encoding.c \
    - encoding.h \
    - family_admin.c \
    - family_alert.c \
    - family_auth.c \
    - family_bart.c \
    - family_bos.c \
    - family_buddy.c \
    - family_chat.c \
    - family_chatnav.c \
    - family_icq.c \
    - family_icbm.c \
    - family_locate.c \
    - family_oservice.c \
    - family_popup.c \
    - family_feedbag.c \
    - family_stats.c \
    - family_userlookup.c \
    - flap_connection.c \
    - misc.c \
    - msgcookie.c \
    - odc.c \
    - oft.c \
    - oscar.c \
    - oscar.h \
    - oscarcommon.h \
    - oscar_data.c \
    - peer.c \
    - peer.h \
    - peer_proxy.c \
    - rxhandlers.c \
    - snac.c \
    - snactypes.h \
    - tlv.c \
    - userinfo.c \
    - util.c \
    - visibility.c \
    - visibility.h
    -
    -AM_CFLAGS = $(st)
    -
    -libaim_la_LDFLAGS = -module -avoid-version
    -libicq_la_LDFLAGS = -module -avoid-version
    -if STATIC_OSCAR
    -
    -st = -DPURPLE_STATIC_PRPL
    -noinst_LTLIBRARIES = liboscar.la
    -liboscar_la_SOURCES = $(OSCARSOURCES) libaim.c libicq.c
    -liboscar_la_CFLAGS = $(AM_CFLAGS)
    -
    -else
    -
    -st =
    -pkg_LTLIBRARIES = liboscar.la libaim.la libicq.la
    -liboscar_la_SOURCES = $(OSCARSOURCES)
    -liboscar_la_LIBADD = $(GLIB_LIBS)
    -
    -libaim_la_SOURCES = libaim.c
    -libaim_la_LIBADD = liboscar.la
    -
    -libicq_la_SOURCES = libicq.c
    -libicq_la_LIBADD = liboscar.la
    -
    -endif
    -
    -AM_CPPFLAGS = \
    - -I$(top_srcdir)/libpurple \
    - -I$(top_builddir)/libpurple \
    - $(GLIB_CFLAGS) \
    - $(DEBUG_CFLAGS)
    --- a/libpurple/protocols/oscar/Makefile.mingw Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,130 +0,0 @@
    -#
    -# Makefile.mingw
    -#
    -# Description: Makefile for win32 (mingw) version of liboscar
    -#
    -
    -PIDGIN_TREE_TOP := ../../..
    -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
    -
    -TARGET = liboscar
    -AIM_TARGET = libaim
    -ICQ_TARGET = libicq
    -TYPE = PLUGIN
    -
    -# Static or Plugin...
    -ifeq ($(TYPE),STATIC)
    - DEFINES += -DSTATIC
    - DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR)
    -else
    - ifeq ($(TYPE),PLUGIN)
    - DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR)
    - endif
    -endif
    -
    -##
    -## INCLUDE PATHS
    -##
    -INCLUDE_PATHS += -I. \
    - -I$(GTK_TOP)/include \
    - -I$(GTK_TOP)/include/glib-2.0 \
    - -I$(GTK_TOP)/lib/glib-2.0/include \
    - -I$(PURPLE_TOP) \
    - -I$(PURPLE_TOP)/win32 \
    - -I$(PIDGIN_TREE_TOP)
    -
    -LIB_PATHS += -L$(GTK_TOP)/lib \
    - -L$(PURPLE_TOP) \
    - -L.
    -
    -##
    -## SOURCES, OBJECTS
    -##
    -C_SRC = \
    - authorization.c \
    - bstream.c \
    - clientlogin.c \
    - encoding.c \
    - family_admin.c \
    - family_alert.c \
    - family_auth.c \
    - family_bart.c \
    - family_bos.c \
    - family_buddy.c \
    - family_chat.c \
    - family_chatnav.c \
    - family_feedbag.c \
    - family_icbm.c \
    - family_icq.c \
    - family_locate.c \
    - family_oservice.c \
    - family_popup.c \
    - family_stats.c \
    - family_userlookup.c \
    - flap_connection.c \
    - kerberos.c \
    - misc.c \
    - msgcookie.c \
    - odc.c \
    - oft.c \
    - oscar.c \
    - oscar_data.c \
    - peer.c \
    - peer_proxy.c \
    - rxhandlers.c \
    - snac.c \
    - tlv.c \
    - userinfo.c \
    - util.c \
    - visibility.c
    -
    -OBJECTS = $(C_SRC:%.c=%.o)
    -
    -AIM_C_SRC = libaim.c
    -AIM_OBJECTS = $(AIM_C_SRC:%.c=%.o)
    -
    -ICQ_C_SRC = libicq.c
    -ICQ_OBJECTS = $(ICQ_C_SRC:%.c=%.o)
    -
    -##
    -## LIBRARIES
    -##
    -LIBS = \
    - -lglib-2.0 \
    - -lintl \
    - -lws2_32 \
    - -lpurple
    -
    -include $(PIDGIN_COMMON_RULES)
    -
    -##
    -## TARGET DEFINITIONS
    -##
    -.PHONY: all install clean
    -
    -all: $(TARGET).dll $(AIM_TARGET).dll $(ICQ_TARGET).dll
    -
    -install: all $(DLL_INSTALL_DIR)
    - cp $(AIM_TARGET).dll $(ICQ_TARGET).dll $(DLL_INSTALL_DIR)
    - cp $(TARGET).dll $(PURPLE_INSTALL_DIR)
    -
    -$(OBJECTS): $(PURPLE_CONFIG_H)
    -
    -$(TARGET).dll.a $(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS)
    - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--out-implib,$(TARGET).dll.a -o $(TARGET).dll
    -
    -$(AIM_TARGET).dll: $(TARGET).dll.a $(AIM_OBJECTS)
    - $(CC) -shared $(AIM_OBJECTS) $(LIB_PATHS) $(LIBS) -loscar $(DLL_LD_FLAGS) -o $(AIM_TARGET).dll
    -
    -$(ICQ_TARGET).dll: $(TARGET).dll.a $(ICQ_OBJECTS)
    - $(CC) -shared $(ICQ_OBJECTS) $(LIB_PATHS) $(LIBS) -loscar $(DLL_LD_FLAGS) -o $(ICQ_TARGET).dll
    -
    -##
    -## CLEAN RULES
    -##
    -clean:
    - rm -f $(OBJECTS) $(TARGET).dll $(TARGET).dll.a
    - rm -f $(AIM_OBJECTS) $(AIM_TARGET).dll
    - rm -f $(ICQ_OBJECTS) $(ICQ_TARGET).dll
    -
    -include $(PIDGIN_COMMON_TARGETS)
    --- a/libpurple/protocols/oscar/authorization.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,131 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Everything related to OSCAR authorization requests.
    - */
    -
    -#include "oscar.h"
    -#include "request.h"
    -
    -/* When you ask other people for authorization */
    -void
    -oscar_auth_sendrequest(PurpleConnection *gc, const char *bname, const char *msg)
    -{
    - OscarData *od;
    - PurpleAccount *account;
    - PurpleBuddy *buddy;
    - PurpleGroup *group;
    - const char *gname;
    -
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    - buddy = purple_find_buddy(account, bname);
    - if (buddy != NULL)
    - group = purple_buddy_get_group(buddy);
    - else
    - group = NULL;
    -
    - if (group != NULL)
    - {
    - gname = purple_group_get_name(group);
    - purple_debug_info("oscar", "ssi: adding buddy %s to group %s\n",
    - bname, gname);
    - aim_ssi_sendauthrequest(od, bname, msg ? msg : _("Please authorize me so I can add you to my buddy list."));
    - if (!aim_ssi_itemlist_finditem(od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY))
    - {
    - aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, TRUE);
    -
    - /* Mobile users should always be online */
    - if (bname[0] == '+') {
    - purple_prpl_got_user_status(account,
    - purple_buddy_get_name(buddy),
    - OSCAR_STATUS_ID_AVAILABLE, NULL);
    - purple_prpl_got_user_status(account,
    - purple_buddy_get_name(buddy),
    - OSCAR_STATUS_ID_MOBILE, NULL);
    - }
    - }
    - }
    -}
    -
    -static void
    -oscar_auth_grant(gpointer cbdata)
    -{
    - struct name_data *data = cbdata;
    - PurpleConnection *gc = data->gc;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - aim_ssi_sendauthreply(od, data->name, 0x01, NULL);
    -
    - oscar_free_name_data(data);
    -}
    -
    -static void
    -oscar_auth_dontgrant(struct name_data *data, char *msg)
    -{
    - PurpleConnection *gc = data->gc;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - aim_ssi_sendauthreply(od, data->name, 0x00, msg ? msg : _("No reason given."));
    -
    - oscar_free_name_data(data);
    -}
    -
    -static void
    -oscar_auth_dontgrant_msgprompt(gpointer cbdata)
    -{
    - struct name_data *data = cbdata;
    - purple_request_input(data->gc, NULL, _("Authorization Denied Message:"),
    - NULL, _("No reason given."), TRUE, FALSE, NULL,
    - _("_OK"), G_CALLBACK(oscar_auth_dontgrant),
    - _("_Cancel"), G_CALLBACK(oscar_free_name_data),
    - purple_connection_get_account(data->gc), data->name, NULL,
    - data);
    -}
    -
    -void
    -oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored)
    -{
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    -
    - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *) node;
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    - oscar_auth_sendrequest(gc, purple_buddy_get_name(buddy), NULL);
    -}
    -
    -/* When other people ask you for authorization */
    -void
    -oscar_auth_recvrequest(PurpleConnection *gc, gchar *name, gchar *nick, gchar *reason)
    -{
    - PurpleAccount* account = purple_connection_get_account(gc);
    - struct name_data *data = g_new(struct name_data, 1);
    -
    - data->gc = gc;
    - data->name = name;
    - data->nick = nick;
    -
    - purple_account_request_authorization(account, data->name, NULL, data->nick,
    - reason, purple_find_buddy(account, data->name) != NULL,
    - oscar_auth_grant, oscar_auth_dontgrant_msgprompt, data);
    -}
    --- a/libpurple/protocols/oscar/bstream.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,288 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * This file contains all functions needed to use bstreams.
    - */
    -
    -#include "oscar.h"
    -
    -int byte_stream_new(ByteStream *bs, size_t len)
    -{
    - if (bs == NULL)
    - return -1;
    -
    - return byte_stream_init(bs, g_malloc(len), len);
    -}
    -
    -int byte_stream_init(ByteStream *bs, guint8 *data, size_t len)
    -{
    - if (bs == NULL)
    - return -1;
    -
    - bs->data = data;
    - bs->len = len;
    - bs->offset = 0;
    -
    - return 0;
    -}
    -
    -void byte_stream_destroy(ByteStream *bs)
    -{
    - g_free(bs->data);
    -}
    -
    -size_t byte_stream_bytes_left(ByteStream *bs)
    -{
    - return bs->len - bs->offset;
    -}
    -
    -int byte_stream_curpos(ByteStream *bs)
    -{
    - return bs->offset;
    -}
    -
    -int byte_stream_setpos(ByteStream *bs, size_t off)
    -{
    - g_return_val_if_fail(off <= bs->len, -1);
    -
    - bs->offset = off;
    - return off;
    -}
    -
    -void byte_stream_rewind(ByteStream *bs)
    -{
    - byte_stream_setpos(bs, 0);
    -}
    -
    -/*
    - * N can be negative, which can be used for going backwards
    - * in a bstream.
    - */
    -int byte_stream_advance(ByteStream *bs, int n)
    -{
    - g_return_val_if_fail(byte_stream_curpos(bs) + n >= 0, 0);
    - g_return_val_if_fail((gsize)n <= byte_stream_bytes_left(bs), 0);
    -
    - bs->offset += n;
    - return n;
    -}
    -
    -guint8 byte_stream_get8(ByteStream *bs)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0);
    -
    - bs->offset++;
    - return aimutil_get8(bs->data + bs->offset - 1);
    -}
    -
    -guint16 byte_stream_get16(ByteStream *bs)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0);
    -
    - bs->offset += 2;
    - return aimutil_get16(bs->data + bs->offset - 2);
    -}
    -
    -guint32 byte_stream_get32(ByteStream *bs)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0);
    -
    - bs->offset += 4;
    - return aimutil_get32(bs->data + bs->offset - 4);
    -}
    -
    -guint8 byte_stream_getle8(ByteStream *bs)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0);
    -
    - bs->offset++;
    - return aimutil_getle8(bs->data + bs->offset - 1);
    -}
    -
    -guint16 byte_stream_getle16(ByteStream *bs)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0);
    -
    - bs->offset += 2;
    - return aimutil_getle16(bs->data + bs->offset - 2);
    -}
    -
    -guint32 byte_stream_getle32(ByteStream *bs)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0);
    -
    - bs->offset += 4;
    - return aimutil_getle32(bs->data + bs->offset - 4);
    -}
    -
    -static void byte_stream_getrawbuf_nocheck(ByteStream *bs, guint8 *buf, size_t len)
    -{
    - memcpy(buf, bs->data + bs->offset, len);
    - bs->offset += len;
    -}
    -
    -int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, size_t len)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, 0);
    -
    - byte_stream_getrawbuf_nocheck(bs, buf, len);
    - return len;
    -}
    -
    -guint8 *byte_stream_getraw(ByteStream *bs, size_t len)
    -{
    - guint8 *ob;
    -
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, NULL);
    -
    - ob = g_malloc(len);
    - byte_stream_getrawbuf_nocheck(bs, ob, len);
    - return ob;
    -}
    -
    -char *byte_stream_getstr(ByteStream *bs, size_t len)
    -{
    - char *ob;
    -
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, NULL);
    -
    - ob = g_malloc(len + 1);
    - byte_stream_getrawbuf_nocheck(bs, (guint8 *)ob, len);
    - ob[len] = '\0';
    - return ob;
    -}
    -
    -int byte_stream_put8(ByteStream *bs, guint8 v)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0);
    -
    - bs->offset += aimutil_put8(bs->data + bs->offset, v);
    - return 1;
    -}
    -
    -int byte_stream_put16(ByteStream *bs, guint16 v)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0);
    -
    - bs->offset += aimutil_put16(bs->data + bs->offset, v);
    - return 2;
    -}
    -
    -int byte_stream_put32(ByteStream *bs, guint32 v)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0);
    -
    - bs->offset += aimutil_put32(bs->data + bs->offset, v);
    - return 1;
    -}
    -
    -int byte_stream_putle8(ByteStream *bs, guint8 v)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0);
    -
    - bs->offset += aimutil_putle8(bs->data + bs->offset, v);
    - return 1;
    -}
    -
    -int byte_stream_putle16(ByteStream *bs, guint16 v)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0);
    -
    - bs->offset += aimutil_putle16(bs->data + bs->offset, v);
    - return 2;
    -}
    -
    -int byte_stream_putle32(ByteStream *bs, guint32 v)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0);
    -
    - bs->offset += aimutil_putle32(bs->data + bs->offset, v);
    - return 1;
    -}
    -
    -
    -int byte_stream_putraw(ByteStream *bs, const guint8 *v, size_t len)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, 0);
    -
    - memcpy(bs->data + bs->offset, v, len);
    - bs->offset += len;
    - return len;
    -}
    -
    -int byte_stream_putstr(ByteStream *bs, const char *str)
    -{
    - return byte_stream_putraw(bs, (guint8 *)str, strlen(str));
    -}
    -
    -int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, size_t len)
    -{
    - g_return_val_if_fail(byte_stream_bytes_left(srcbs) >= len, 0);
    - g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, 0);
    -
    - memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len);
    - bs->offset += len;
    - srcbs->offset += len;
    - return len;
    -}
    -
    -int byte_stream_putuid(ByteStream *bs, OscarData *od)
    -{
    - PurpleAccount *account;
    -
    - account = purple_connection_get_account(od->gc);
    -
    - return byte_stream_putle32(bs, atoi(purple_account_get_username(account)));
    -}
    -
    -void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data)
    -{
    - byte_stream_put16(bs, type);
    -
    - if (data != NULL && data->len > 0) {
    - /* Flags. 0x04 means "this asset has data attached to it" */
    - byte_stream_put8(bs, 0x04); /* Flags */
    - byte_stream_put8(bs, data->len); /* Length */
    - byte_stream_rewind(data);
    - byte_stream_putbs(bs, data, data->len); /* Data */
    - } else {
    - byte_stream_put8(bs, 0x00); /* No flags */
    - byte_stream_put8(bs, 0x00); /* Length */
    - /* No data */
    - }
    -}
    -
    -void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr)
    -{
    - ByteStream data;
    - size_t len = datastr != NULL ? strlen(datastr) : 0;
    -
    - if (len > 0) {
    - byte_stream_new(&data, 2 + len + 2);
    - byte_stream_put16(&data, len); /* Length */
    - byte_stream_putstr(&data, datastr); /* String */
    - byte_stream_put16(&data, 0x0000); /* Unknown */
    - byte_stream_put_bart_asset(bs, type, &data);
    - byte_stream_destroy(&data);
    - } else {
    - byte_stream_put_bart_asset(bs, type, NULL);
    - }
    -}
    --- a/libpurple/protocols/oscar/clientlogin.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,646 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/**
    - * This file implements AIM's clientLogin procedure for authenticating
    - * users. This replaces the older MD5-based and XOR-based
    - * authentication methods that use SNAC family 0x0017.
    - *
    - * This doesn't use SNACs or FLAPs at all. It makes http and https
    - * POSTs to AOL to validate the user based on the password they
    - * provided to us. Upon successful authentication we request a
    - * connection to the BOS server by calling startOSCARsession. The
    - * AOL server gives us the hostname and port number to use, as well
    - * as the cookie to use to authenticate to the BOS server. And then
    - * everything else is the same as with BUCP.
    - *
    - * For details, see:
    - * http://dev.aol.com/aim/oscar/#AUTH
    - * http://dev.aol.com/authentication_for_clients
    - */
    -
    -#include "oscar.h"
    -#include "oscarcommon.h"
    -
    -#include "cipher.h"
    -#include "core.h"
    -
    -#define AIM_LOGIN_HOST "api.screenname.aol.com"
    -#define ICQ_LOGIN_HOST "api.login.icq.net"
    -
    -#define AIM_API_HOST "api.oscar.aol.com"
    -#define ICQ_API_HOST "api.icq.net"
    -
    -#define CLIENT_LOGIN_PAGE "/auth/clientLogin"
    -#define START_OSCAR_SESSION_PAGE "/aim/startOSCARSession"
    -
    -#define HTTPS_FORMAT_URL(host, page) "https://" host page
    -
    -static const gchar *client_login_urls[] = {
    - HTTPS_FORMAT_URL(AIM_LOGIN_HOST, CLIENT_LOGIN_PAGE),
    - HTTPS_FORMAT_URL(ICQ_LOGIN_HOST, CLIENT_LOGIN_PAGE),
    -};
    -
    -static const gchar *start_oscar_session_urls[] = {
    - HTTPS_FORMAT_URL(AIM_API_HOST, START_OSCAR_SESSION_PAGE),
    - HTTPS_FORMAT_URL(ICQ_API_HOST, START_OSCAR_SESSION_PAGE),
    -};
    -
    -static const gchar *get_client_login_url(OscarData *od)
    -{
    - return client_login_urls[od->icq ? 1 : 0];
    -}
    -
    -static const gchar *get_start_oscar_session_url(OscarData *od)
    -{
    - return start_oscar_session_urls[od->icq ? 1 : 0];
    -}
    -
    -static const char *get_client_key(OscarData *od)
    -{
    - return oscar_get_ui_info_string(
    - od->icq ? "prpl-icq-clientkey" : "prpl-aim-clientkey",
    - od->icq ? ICQ_DEFAULT_CLIENT_KEY : AIM_DEFAULT_CLIENT_KEY);
    -}
    -
    -static gchar *generate_error_message(xmlnode *resp, const char *url)
    -{
    - xmlnode *text;
    - xmlnode *status_code_node;
    - gboolean have_error_code = TRUE;
    - gchar *err = NULL;
    - gchar *details = NULL;
    -
    - status_code_node = xmlnode_get_child(resp, "statusCode");
    - if (status_code_node) {
    - gchar *status_code;
    -
    - /* We can get 200 OK here if the server omitted something we think it shouldn't have (see #12783).
    - * No point in showing the "Ok" string to the user.
    - */
    - status_code = xmlnode_get_data_unescaped(status_code_node);
    - if (purple_strequal(status_code, "200")) {
    - have_error_code = FALSE;
    - }
    - }
    - if (have_error_code && resp && (text = xmlnode_get_child(resp, "statusText"))) {
    - details = xmlnode_get_data(text);
    - }
    -
    - if (details && *details) {
    - err = g_strdup_printf(_("Received unexpected response from %s: %s"), url, details);
    - } else {
    - err = g_strdup_printf(_("Received unexpected response from %s"), url);
    - }
    -
    - g_free(details);
    - return err;
    -}
    -
    -/**
    - * @return A null-terminated base64 encoded version of the HMAC
    - * calculated using the given key and data.
    - */
    -static gchar *hmac_sha256(const char *key, const char *message)
    -{
    - PurpleCipherContext *context;
    - guchar digest[32];
    -
    - context = purple_cipher_context_new_by_name("hmac", NULL);
    - purple_cipher_context_set_option(context, "hash", "sha256");
    - purple_cipher_context_set_key(context, (guchar *)key);
    - purple_cipher_context_append(context, (guchar *)message, strlen(message));
    - purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
    - purple_cipher_context_destroy(context);
    -
    - return purple_base64_encode(digest, sizeof(digest));
    -}
    -
    -/**
    - * @return A base-64 encoded HMAC-SHA256 signature created using the
    - * technique documented at
    - * http://dev.aol.com/authentication_for_clients#signing
    - */
    -static gchar *generate_signature(const char *method, const char *url, const char *parameters, const char *session_key)
    -{
    - char *encoded_url, *signature_base_string, *signature;
    - const char *encoded_parameters;
    -
    - encoded_url = g_strdup(purple_url_encode(url));
    - encoded_parameters = purple_url_encode(parameters);
    - signature_base_string = g_strdup_printf("%s&%s&%s",
    - method, encoded_url, encoded_parameters);
    - g_free(encoded_url);
    -
    - signature = hmac_sha256(session_key, signature_base_string);
    - g_free(signature_base_string);
    -
    - return signature;
    -}
    -
    -static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **host, unsigned short *port, char **cookie, char **tls_certname)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - xmlnode *response_node, *tmp_node, *data_node;
    - xmlnode *host_node = NULL, *port_node = NULL, *cookie_node = NULL, *tls_node = NULL;
    - char *tmp;
    - guint code;
    - const gchar *encryption_type = purple_account_get_string(purple_connection_get_account(gc), "encryption", OSCAR_DEFAULT_ENCRYPTION);
    -
    - /* Parse the response as XML */
    - response_node = xmlnode_from_str(response, response_len);
    - if (response_node == NULL)
    - {
    - char *msg;
    - purple_debug_error("oscar", "startOSCARSession could not parse "
    - "response as XML: %s\n", response);
    - /* Note to translators: %s in this string is a URL */
    - msg = generate_error_message(response_node,
    - get_start_oscar_session_url(od));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - return FALSE;
    - }
    -
    - /* Grab the necessary XML nodes */
    - tmp_node = xmlnode_get_child(response_node, "statusCode");
    - data_node = xmlnode_get_child(response_node, "data");
    - if (data_node != NULL) {
    - host_node = xmlnode_get_child(data_node, "host");
    - port_node = xmlnode_get_child(data_node, "port");
    - cookie_node = xmlnode_get_child(data_node, "cookie");
    - }
    -
    - /* Make sure we have a status code */
    - if (tmp_node == NULL || (tmp = xmlnode_get_data_unescaped(tmp_node)) == NULL) {
    - char *msg;
    - purple_debug_error("oscar", "startOSCARSession response was "
    - "missing statusCode: %s\n", response);
    - msg = generate_error_message(response_node,
    - get_start_oscar_session_url(od));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - /* Make sure the status code was 200 */
    - code = atoi(tmp);
    - if (code != 200)
    - {
    - xmlnode *status_detail_node;
    - guint status_detail = 0;
    -
    - status_detail_node = xmlnode_get_child(response_node,
    - "statusDetailCode");
    - if (status_detail_node) {
    - gchar *data = xmlnode_get_data(status_detail_node);
    - if (data) {
    - status_detail = atoi(data);
    - g_free(data);
    - }
    - }
    -
    - purple_debug_error("oscar", "startOSCARSession response statusCode "
    - "was %s: %s\n", tmp, response);
    -
    - if ((code == 401 && status_detail != 1014) || code == 607)
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_OTHER_ERROR,
    - _("You have been connecting and disconnecting too "
    - "frequently. Wait ten minutes and try again. If "
    - "you continue to try, you will need to wait even "
    - "longer."));
    - else {
    - char *msg;
    - msg = generate_error_message(response_node,
    - get_start_oscar_session_url(od));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg);
    - g_free(msg);
    - }
    -
    - g_free(tmp);
    - xmlnode_free(response_node);
    - return FALSE;
    - }
    - g_free(tmp);
    -
    - /* Make sure we have everything else */
    - if (data_node == NULL || host_node == NULL || port_node == NULL || cookie_node == NULL)
    - {
    - char *msg;
    - purple_debug_error("oscar", "startOSCARSession response was missing "
    - "something: %s\n", response);
    - msg = generate_error_message(response_node,
    - get_start_oscar_session_url(od));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - if (!purple_strequal(encryption_type, OSCAR_NO_ENCRYPTION)) {
    - tls_node = xmlnode_get_child(data_node, "tlsCertName");
    - if (tls_node != NULL) {
    - *tls_certname = xmlnode_get_data_unescaped(tls_node);
    - } else {
    - if (purple_strequal(encryption_type, OSCAR_OPPORTUNISTIC_ENCRYPTION)) {
    - purple_debug_warning("oscar", "We haven't received a tlsCertName to use. We will not do SSL to BOS.\n");
    - } else {
    - purple_debug_error("oscar", "startOSCARSession was missing tlsCertName: %s\n", response);
    - purple_connection_error_reason(
    - gc,
    - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
    - _("You required encryption in your account settings, but one of the servers doesn't support it."));
    - xmlnode_free(response_node);
    - return FALSE;
    - }
    - }
    - }
    -
    - /* Extract data from the XML */
    - *host = xmlnode_get_data_unescaped(host_node);
    - tmp = xmlnode_get_data_unescaped(port_node);
    - *cookie = xmlnode_get_data_unescaped(cookie_node);
    -
    - if (*host == NULL || **host == '\0' || tmp == NULL || *tmp == '\0' || *cookie == NULL || **cookie == '\0')
    - {
    - char *msg;
    - purple_debug_error("oscar", "startOSCARSession response was missing "
    - "something: %s\n", response);
    - msg = generate_error_message(response_node,
    - get_start_oscar_session_url(od));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - g_free(*host);
    - g_free(tmp);
    - g_free(*cookie);
    - xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - *port = atoi(tmp);
    - g_free(tmp);
    -
    - return TRUE;
    -}
    -
    -static void start_oscar_session_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
    -{
    - OscarData *od;
    - PurpleConnection *gc;
    - char *host, *cookie;
    - char *tls_certname = NULL;
    - unsigned short port;
    - guint8 *cookiedata;
    - gsize cookiedata_len = 0;
    -
    - od = user_data;
    - gc = od->gc;
    -
    - od->url_data = NULL;
    -
    - if (error_message != NULL || len == 0) {
    - gchar *tmp;
    - /* Note to translators: The first %s is a URL, the second is an
    - error message. */
    - tmp = g_strdup_printf(_("Error requesting %s: %s"),
    - get_start_oscar_session_url(od), error_message ?
    - error_message : _("The server returned an empty response"));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - return;
    - }
    -
    - if (!parse_start_oscar_session_response(gc, url_text, len, &host, &port, &cookie, &tls_certname))
    - return;
    -
    - cookiedata = purple_base64_decode(cookie, &cookiedata_len);
    - oscar_connect_to_bos(gc, od, host, port, cookiedata, cookiedata_len, tls_certname);
    - g_free(cookiedata);
    -
    - g_free(host);
    - g_free(cookie);
    - g_free(tls_certname);
    -}
    -
    -static void send_start_oscar_session(OscarData *od, const char *token, const char *session_key, time_t hosttime)
    -{
    - char *query_string, *signature, *url;
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - const gchar *encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION);
    -
    - /*
    - * Construct the GET parameters.
    - */
    - query_string = g_strdup_printf("a=%s"
    - "&distId=%d"
    - "&f=xml"
    - "&k=%s"
    - "&ts=%" PURPLE_TIME_T_MODIFIER
    - "&useTLS=%d",
    - purple_url_encode(token),
    - oscar_get_ui_info_int(od->icq ? "prpl-icq-distid" : "prpl-aim-distid",
    - od->icq ? ICQ_DEFAULT_DIST_ID : AIM_DEFAULT_DIST_ID),
    - get_client_key(od),
    - hosttime,
    - !purple_strequal(encryption_type, OSCAR_NO_ENCRYPTION));
    - signature = generate_signature("GET", get_start_oscar_session_url(od),
    - query_string, session_key);
    - url = g_strdup_printf("%s?%s&sig_sha256=%s", get_start_oscar_session_url(od),
    - query_string, signature);
    - g_free(query_string);
    - g_free(signature);
    -
    - /* Make the request */
    - od->url_data = purple_util_fetch_url_request_len_with_account(account,
    - url, TRUE, NULL, FALSE, NULL, FALSE, -1,
    - start_oscar_session_cb, od);
    - g_free(url);
    -}
    -
    -/**
    - * This function parses the given response from a clientLogin request
    - * and extracts the useful information.
    - *
    - * @param gc The PurpleConnection. If the response data does
    - * not indicate then purple_connection_error_reason()
    - * will be called to close this connection.
    - * @param response The response data from the clientLogin request.
    - * @param response_len The length of the above response, or -1 if
    - * @response is NUL terminated.
    - * @param token If parsing was successful then this will be set to
    - * a newly allocated string containing the token. The
    - * caller should g_free this string when it is finished
    - * with it. On failure this value will be untouched.
    - * @param secret If parsing was successful then this will be set to
    - * a newly allocated string containing the secret. The
    - * caller should g_free this string when it is finished
    - * with it. On failure this value will be untouched.
    - * @param hosttime If parsing was successful then this will be set to
    - * the time on the OpenAuth Server in seconds since the
    - * Unix epoch. On failure this value will be untouched.
    - *
    - * @return TRUE if the request was successful and we were able to
    - * extract all info we need. Otherwise FALSE.
    - */
    -static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **token, char **secret, time_t *hosttime)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - xmlnode *response_node, *tmp_node, *data_node;
    - xmlnode *secret_node = NULL, *hosttime_node = NULL, *token_node = NULL, *tokena_node = NULL;
    - char *tmp;
    -
    - /* Parse the response as XML */
    - response_node = xmlnode_from_str(response, response_len);
    - if (response_node == NULL)
    - {
    - char *msg;
    - purple_debug_error("oscar", "clientLogin could not parse "
    - "response as XML: %s\n", response);
    - msg = generate_error_message(response_node,
    - get_client_login_url(od));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - return FALSE;
    - }
    -
    - /* Grab the necessary XML nodes */
    - tmp_node = xmlnode_get_child(response_node, "statusCode");
    - data_node = xmlnode_get_child(response_node, "data");
    - if (data_node != NULL) {
    - secret_node = xmlnode_get_child(data_node, "sessionSecret");
    - hosttime_node = xmlnode_get_child(data_node, "hostTime");
    - token_node = xmlnode_get_child(data_node, "token");
    - if (token_node != NULL)
    - tokena_node = xmlnode_get_child(token_node, "a");
    - }
    -
    - /* Make sure we have a status code */
    - if (tmp_node == NULL || (tmp = xmlnode_get_data_unescaped(tmp_node)) == NULL) {
    - char *msg;
    - purple_debug_error("oscar", "clientLogin response was "
    - "missing statusCode: %s\n", response);
    - msg = generate_error_message(response_node,
    - get_client_login_url(od));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - /* Make sure the status code was 200 */
    - if (!purple_strequal(tmp, "200"))
    - {
    - int status_code, status_detail_code = 0;
    -
    - status_code = atoi(tmp);
    - g_free(tmp);
    - tmp_node = xmlnode_get_child(response_node, "statusDetailCode");
    - if (tmp_node != NULL && (tmp = xmlnode_get_data_unescaped(tmp_node)) != NULL) {
    - status_detail_code = atoi(tmp);
    - g_free(tmp);
    - }
    -
    - purple_debug_error("oscar", "clientLogin response statusCode "
    - "was %d (%d): %s\n", status_code, status_detail_code, response);
    -
    - if (status_code == 330 && status_detail_code == 3011) {
    - PurpleAccount *account = purple_connection_get_account(gc);
    - if (!purple_account_get_remember_password(account))
    - purple_account_set_password(account, NULL);
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
    - _("Incorrect password"));
    - } else if (status_code == 330 && status_detail_code == 3015) {
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
    - _("Server requested that you fill out a CAPTCHA in order to "
    - "sign in, but this client does not currently support CAPTCHAs."));
    - } else if (status_code == 401 && status_detail_code == 3019) {
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_OTHER_ERROR,
    - _("AOL does not allow your screen name to authenticate here"));
    - } else {
    - char *msg;
    - msg = generate_error_message(response_node,
    - get_client_login_url(od));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg);
    - g_free(msg);
    - }
    -
    - xmlnode_free(response_node);
    - return FALSE;
    - }
    - g_free(tmp);
    -
    - /* Make sure we have everything else */
    - if (data_node == NULL || secret_node == NULL ||
    - token_node == NULL || tokena_node == NULL)
    - {
    - char *msg;
    - purple_debug_error("oscar", "clientLogin response was missing "
    - "something: %s\n", response);
    - msg = generate_error_message(response_node,
    - get_client_login_url(od));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - /* Extract data from the XML */
    - *token = xmlnode_get_data_unescaped(tokena_node);
    - *secret = xmlnode_get_data_unescaped(secret_node);
    - tmp = xmlnode_get_data_unescaped(hosttime_node);
    - if (*token == NULL || **token == '\0' || *secret == NULL || **secret == '\0' || tmp == NULL || *tmp == '\0')
    - {
    - char *msg;
    - purple_debug_error("oscar", "clientLogin response was missing "
    - "something: %s\n", response);
    - msg = generate_error_message(response_node,
    - get_client_login_url(od));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - g_free(*token);
    - g_free(*secret);
    - g_free(tmp);
    - xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - *hosttime = strtol(tmp, NULL, 10);
    - g_free(tmp);
    -
    - xmlnode_free(response_node);
    -
    - return TRUE;
    -}
    -
    -static void client_login_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
    -{
    - OscarData *od;
    - PurpleConnection *gc;
    - char *token, *secret, *session_key;
    - time_t hosttime;
    - int password_len;
    - char *password;
    -
    - od = user_data;
    - gc = od->gc;
    -
    - od->url_data = NULL;
    -
    - if (error_message != NULL || len == 0) {
    - gchar *tmp;
    - tmp = g_strdup_printf(_("Error requesting %s: %s"),
    - get_client_login_url(od), error_message ?
    - error_message : _("The server returned an empty response"));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - return;
    - }
    -
    - if (!parse_client_login_response(gc, url_text, len, &token, &secret, &hosttime))
    - return;
    -
    - password_len = strlen(purple_connection_get_password(gc));
    - password = g_strdup_printf("%.*s",
    - od->icq ? MIN(password_len, MAXICQPASSLEN) : password_len,
    - purple_connection_get_password(gc));
    - session_key = hmac_sha256(password, secret);
    - g_free(password);
    - g_free(secret);
    -
    - send_start_oscar_session(od, token, session_key, hosttime);
    -
    - g_free(token);
    - g_free(session_key);
    -}
    -
    -/**
    - * This function sends a request to
    - * https://api.screenname.aol.com/auth/clientLogin with the user's
    - * username and password and receives the user's session key, which is
    - * used to request a connection to the BOSS server.
    - */
    -void send_client_login(OscarData *od, const char *username)
    -{
    - PurpleConnection *gc;
    - GString *request, *body;
    - const char *tmp;
    - char *password;
    - int password_len;
    -
    - gc = od->gc;
    -
    - /*
    - * We truncate ICQ passwords to 8 characters. There is probably a
    - * limit for AIM passwords, too, but we really only need to do
    - * this for ICQ because older ICQ clients let you enter a password
    - * as long as you wanted and then they truncated it silently.
    - *
    - * And we can truncate based on the number of bytes and not the
    - * number of characters because passwords for AIM and ICQ are
    - * supposed to be plain ASCII (I don't know if this has always been
    - * the case, though).
    - */
    - tmp = purple_connection_get_password(gc);
    - password_len = strlen(tmp);
    - password = g_strndup(tmp, od->icq ? MIN(password_len, MAXICQPASSLEN) : password_len);
    -
    - /* Construct the body of the HTTP POST request */
    - body = g_string_new("");
    - g_string_append_printf(body, "devId=%s", get_client_key(od));
    - g_string_append_printf(body, "&f=xml");
    - g_string_append_printf(body, "&pwd=%s", purple_url_encode(password));
    - g_string_append_printf(body, "&s=%s", purple_url_encode(username));
    - g_free(password);
    -
    - /* Construct an HTTP POST request */
    - request = g_string_new("POST /auth/clientLogin HTTP/1.0\r\n"
    - "Connection: close\r\n"
    - "Accept: */*\r\n");
    -
    - /* Tack on the body */
    - g_string_append_printf(request, "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n");
    - g_string_append_printf(request, "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n", body->len);
    - g_string_append_len(request, body->str, body->len);
    - g_string_free(body, TRUE);
    -
    - /* Send the POST request */
    - od->url_data = purple_util_fetch_url_request_len_with_account(
    - purple_connection_get_account(gc), get_client_login_url(od),
    - TRUE, NULL, FALSE, request->str, FALSE, -1,
    - client_login_cb, od);
    - g_string_free(request, TRUE);
    -}
    --- a/libpurple/protocols/oscar/encoding.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,285 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "encoding.h"
    -
    -static gchar *
    -encoding_multi_convert_to_utf8(const gchar *text, gssize textlen, const gchar *encodings, GError **error, gboolean fallback)
    -{
    - gchar *utf8 = NULL;
    - const gchar *begin = encodings;
    - const gchar *end = NULL;
    - gchar *curr_encoding = NULL; /* allocated buffer for encoding name */
    - const gchar *curr_encoding_ro = NULL; /* read-only encoding name */
    -
    - if (!encodings) {
    - purple_debug_error("oscar", "encodings is NULL");
    - return NULL;
    - }
    -
    - for (;;)
    - {
    - /* extract next encoding */
    - end = strchr(begin, ',');
    - if (!end) {
    - curr_encoding_ro = begin;
    - } else { /* allocate buffer for encoding */
    - curr_encoding = g_strndup(begin, end - begin);
    - if (!curr_encoding) {
    - purple_debug_error("oscar", "Error allocating memory for encoding");
    - break;
    - }
    - curr_encoding_ro = curr_encoding;
    - }
    -
    - if (!g_ascii_strcasecmp(curr_encoding_ro, "utf-8") && g_utf8_validate(text, textlen, NULL)) {
    - break;
    - }
    -
    - utf8 = g_convert(text, textlen, "UTF-8", curr_encoding_ro, NULL, NULL, NULL);
    -
    - if (!end) /* last occurence. do not free curr_encoding: buffer was'nt allocated */
    - break;
    -
    - g_free(curr_encoding); /* free allocated buffer for encoding here */
    -
    - if (utf8) /* text was successfully converted */
    - break;
    -
    - begin = end + 1;
    - }
    -
    - if (!utf8 && fallback)
    - { /* "begin" points to last encoding */
    - utf8 = g_convert_with_fallback(text, textlen, "UTF-8", begin, "?", NULL, NULL, error);
    - }
    -
    - return utf8;
    -}
    -
    -static gchar *
    -encoding_extract(const char *encoding)
    -{
    - char *begin, *end;
    -
    - if (encoding == NULL) {
    - return NULL;
    - }
    -
    - if (!g_str_has_prefix(encoding, "text/aolrtf; charset=") &&
    - !g_str_has_prefix(encoding, "text/x-aolrtf; charset=") &&
    - !g_str_has_prefix(encoding, "text/plain; charset=")) {
    - return g_strdup(encoding);
    - }
    -
    - begin = strchr(encoding, '"');
    - end = strrchr(encoding, '"');
    -
    - if ((begin == NULL) || (end == NULL) || (begin >= end)) {
    - return g_strdup(encoding);
    - }
    -
    - return g_strndup(begin+1, (end-1) - begin);
    -}
    -
    -gchar *
    -oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen)
    -{
    - gchar *utf8 = NULL;
    - const gchar *glib_encoding = NULL;
    - gchar *extracted_encoding = encoding_extract(encoding);
    -
    - if (extracted_encoding == NULL || *extracted_encoding == '\0') {
    - purple_debug_info("oscar", "Empty encoding, assuming UTF-8\n");
    - } else if (!g_ascii_strcasecmp(extracted_encoding, "iso-8859-1")) {
    - glib_encoding = "iso-8859-1";
    - } else if (!g_ascii_strcasecmp(extracted_encoding, "ISO-8859-1-Windows-3.1-Latin-1") || !g_ascii_strcasecmp(extracted_encoding, "us-ascii")) {
    - glib_encoding = "Windows-1252";
    - } else if (!g_ascii_strcasecmp(extracted_encoding, "unicode-2-0")) {
    - glib_encoding = "UTF-16BE";
    - } else if (g_ascii_strcasecmp(extracted_encoding, "utf-8")) {
    - glib_encoding = extracted_encoding;
    - }
    -
    - if (glib_encoding != NULL) {
    - utf8 = encoding_multi_convert_to_utf8(text, textlen, glib_encoding, NULL, FALSE);
    - }
    -
    - /*
    - * If utf8 is still NULL then either the encoding is utf-8 or
    - * we have been unable to convert the text to utf-8 from the encoding
    - * that was specified. So we check if the text is valid utf-8 then
    - * just copy it.
    - */
    - if (utf8 == NULL) {
    - if (textlen != 0 && *text != '\0' && !g_utf8_validate(text, textlen, NULL))
    - utf8 = g_strdup(_("(There was an error receiving this message. The buddy you are speaking with is probably using a different encoding than expected. If you know what encoding he is using, you can specify it in the advanced account options for your AIM/ICQ account.)"));
    - else
    - utf8 = g_strndup(text, textlen);
    - }
    -
    - g_free(extracted_encoding);
    - return utf8;
    -}
    -
    -gchar *
    -oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg)
    -{
    - const char *charset = NULL;
    - char *ret = NULL;
    -
    - if (msg == NULL)
    - return NULL;
    -
    - if (g_utf8_validate(msg, -1, NULL))
    - return g_strdup(msg);
    -
    - if (od->icq)
    - charset = purple_account_get_string(account, "encoding", NULL);
    -
    - if(charset && *charset)
    - ret = encoding_multi_convert_to_utf8(msg, -1, charset, NULL, FALSE);
    -
    - if(!ret)
    - ret = purple_utf8_try_convert(msg);
    -
    - return ret;
    -}
    -
    -static gchar *
    -oscar_convert_to_utf8(const gchar *data, gsize datalen, const char *charsetstr, gboolean fallback)
    -{
    - gchar *ret = NULL;
    - GError *err = NULL;
    -
    - if ((charsetstr == NULL) || (*charsetstr == '\0'))
    - return NULL;
    -
    - if (g_ascii_strcasecmp("UTF-8", charsetstr)) {
    - ret = encoding_multi_convert_to_utf8(data, datalen, charsetstr, &err, fallback);
    - if (err != NULL) {
    - purple_debug_warning("oscar", "Conversion from %s failed: %s.\n",
    - charsetstr, err->message);
    - g_error_free(err);
    - }
    - } else {
    - if (g_utf8_validate(data, datalen, NULL))
    - ret = g_strndup(data, datalen);
    - else
    - purple_debug_warning("oscar", "String is not valid UTF-8.\n");
    - }
    -
    - return ret;
    -}
    -
    -gchar *
    -oscar_decode_im(PurpleAccount *account, const char *sourcebn, guint16 charset, const gchar *data, gsize datalen)
    -{
    - gchar *ret = NULL;
    - /* charsetstr1 is always set to what the correct encoding should be. */
    - const gchar *charsetstr1, *charsetstr2, *charsetstr3 = NULL;
    -
    - if ((datalen == 0) || (data == NULL))
    - return NULL;
    -
    - if (charset == AIM_CHARSET_UNICODE) {
    - charsetstr1 = "UTF-16BE";
    - charsetstr2 = "UTF-8";
    - } else if (charset == AIM_CHARSET_LATIN_1) {
    - if ((sourcebn != NULL) && oscar_util_valid_name_icq(sourcebn))
    - charsetstr1 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
    - else
    - charsetstr1 = "ISO-8859-1";
    - charsetstr2 = "UTF-8";
    - } else if (charset == AIM_CHARSET_ASCII) {
    - /* Should just be "ASCII" */
    - charsetstr1 = "ASCII";
    - charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
    - } else if (charset == 0x000d) {
    - /* iChat sending unicode over a Direct IM connection = UTF-8 */
    - /* Mobile AIM client on multiple devices (including Blackberry Tour, Nokia 3100, and LG VX6000) = ISO-8859-1 */
    - charsetstr1 = "UTF-8";
    - charsetstr2 = "ISO-8859-1";
    - charsetstr3 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
    - } else {
    - /* Unknown, hope for valid UTF-8... */
    - charsetstr1 = "UTF-8";
    - charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
    - }
    -
    - purple_debug_info("oscar", "Parsing IM, charset=0x%04hx, datalen=%" G_GSIZE_FORMAT ", choice1=%s, choice2=%s, choice3=%s\n",
    - charset, datalen, charsetstr1, charsetstr2, (charsetstr3 ? charsetstr3 : ""));
    -
    - ret = oscar_convert_to_utf8(data, datalen, charsetstr1, FALSE);
    - if (ret == NULL) {
    - if (charsetstr3 != NULL) {
    - /* Try charsetstr2 without allowing substitutions, then fall through to charsetstr3 if needed */
    - ret = oscar_convert_to_utf8(data, datalen, charsetstr2, FALSE);
    - if (ret == NULL)
    - ret = oscar_convert_to_utf8(data, datalen, charsetstr3, TRUE);
    - } else {
    - /* Try charsetstr2, allowing substitutions */
    - ret = oscar_convert_to_utf8(data, datalen, charsetstr2, TRUE);
    - }
    - }
    - if (ret == NULL) {
    - char *str, *salvage, *tmp;
    -
    - str = g_malloc(datalen + 1);
    - strncpy(str, data, datalen);
    - str[datalen] = '\0';
    - salvage = purple_utf8_salvage(str);
    - tmp = g_strdup_printf(_("(There was an error receiving this message. Either you and %s have different encodings selected, or %s has a buggy client.)"),
    - sourcebn, sourcebn);
    - ret = g_strdup_printf("%s %s", salvage, tmp);
    - g_free(tmp);
    - g_free(str);
    - g_free(salvage);
    - }
    -
    - return ret;
    -}
    -
    -static guint16
    -get_simplest_charset(const char *utf8)
    -{
    - while (*utf8)
    - {
    - if ((unsigned char)(*utf8) > 0x7f) {
    - /* not ASCII! */
    - return AIM_CHARSET_UNICODE;
    - }
    - utf8++;
    - }
    - return AIM_CHARSET_ASCII;
    -}
    -
    -gchar *
    -oscar_encode_im(const gchar *msg, gsize *result_len, guint16 *charset, gchar **charsetstr)
    -{
    - guint16 msg_charset = get_simplest_charset(msg);
    - if (charset != NULL) {
    - *charset = msg_charset;
    - }
    - if (charsetstr != NULL) {
    - *charsetstr = msg_charset == AIM_CHARSET_ASCII ? "us-ascii" : "unicode-2-0";
    - }
    - return g_convert(msg, -1, msg_charset == AIM_CHARSET_ASCII ? "ASCII" : "UTF-16BE", "UTF-8", NULL, result_len, NULL);
    -}
    --- a/libpurple/protocols/oscar/encoding.h Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,46 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#ifndef _ENCODING_H_
    -#define _ENCODING_H_
    -
    -#include "oscar.h"
    -#include "oscarcommon.h"
    -
    -gchar * oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen);
    -gchar * oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg);
    -
    -/**
    - * This attemps to decode an incoming IM into a UTF8 string.
    - *
    - * We try decoding using two different character sets. The charset
    - * specified in the IM determines the order in which we attempt to
    - * decode. We do this because there are lots of broken ICQ clients
    - * that don't correctly send non-ASCII messages. And if Purple isn't
    - * able to deal with that crap, then people complain like banshees.
    - */
    -gchar * oscar_decode_im(PurpleAccount *account, const char *sourcebn, guint16 charset, const gchar *data, gsize datalen);
    -
    -/**
    - * Figure out what encoding to use when sending a given outgoing message.
    - */
    -gchar * oscar_encode_im(const gchar *msg, gsize *result_len, guint16 *charset, gchar **charsetstr);
    -
    -#endif
    \ No newline at end of file
    --- a/libpurple/protocols/oscar/family_admin.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,246 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0007 - Account Administration.
    - *
    - * Used for stuff like changing the formating of your username, changing your
    - * email address, requesting an account confirmation email, getting account info,
    - */
    -
    -#include "oscar.h"
    -
    -/**
    - * Subtype 0x0002 - Request a bit of account info.
    - *
    - * Info should be one of the following:
    - * 0x0001 - Username formatting
    - * 0x0011 - Email address
    - * 0x0013 - Unknown
    - */
    -void
    -aim_admin_getinfo(OscarData *od, FlapConnection *conn, guint16 info)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - byte_stream_new(&bs, 4);
    -
    - byte_stream_put16(&bs, info);
    - byte_stream_put16(&bs, 0x0000);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0002, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtypes 0x0003 and 0x0005 - Parse account info.
    - *
    - * Called in reply to both an information request (subtype 0x0002) and
    - * an information change (subtype 0x0004).
    - */
    -static void
    -infochange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_rxcallback_t userfunc;
    - char *url=NULL, *sn=NULL, *email=NULL;
    - guint16 perms, tlvcount, err=0;
    -
    - perms = byte_stream_get16(bs);
    - tlvcount = byte_stream_get16(bs);
    -
    - while (tlvcount && byte_stream_bytes_left(bs)) {
    - guint16 type, length;
    -
    - type = byte_stream_get16(bs);
    - length = byte_stream_get16(bs);
    -
    - switch (type) {
    - case 0x0001: {
    - g_free(sn);
    - sn = byte_stream_getstr(bs, length);
    - } break;
    -
    - case 0x0004: {
    - g_free(url);
    - url = byte_stream_getstr(bs, length);
    - } break;
    -
    - case 0x0008: {
    - err = byte_stream_get16(bs);
    - } break;
    -
    - case 0x0011: {
    - g_free(email);
    - if (length == 0)
    - email = g_strdup("*suppressed");
    - else
    - email = byte_stream_getstr(bs, length);
    - } break;
    - }
    -
    - tlvcount--;
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - userfunc(od, conn, frame, (snac->subtype == 0x0005) ? 1 : 0, perms, err, url, sn, email);
    -
    - g_free(sn);
    - g_free(url);
    - g_free(email);
    -}
    -
    -/**
    - * Subtype 0x0004 - Set the formatting of username (change spaces and capitalization).
    - */
    -void
    -aim_admin_setnick(OscarData *od, FlapConnection *conn, const char *newnick)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - byte_stream_new(&bs, 2+2+strlen(newnick));
    -
    - aim_tlvlist_add_str(&tlvlist, 0x0001, newnick);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0004 - Change password.
    - */
    -void
    -aim_admin_changepasswd(OscarData *od, FlapConnection *conn, const char *newpw, const char *curpw)
    -{
    - ByteStream bs;
    - GSList *tlvlist = NULL;
    - aim_snacid_t snacid;
    -
    - byte_stream_new(&bs, 4+strlen(curpw)+4+strlen(newpw));
    -
    - /* new password TLV t(0002) */
    - aim_tlvlist_add_str(&tlvlist, 0x0002, newpw);
    -
    - /* current password TLV t(0012) */
    - aim_tlvlist_add_str(&tlvlist, 0x0012, curpw);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0004 - Change email address.
    - */
    -void
    -aim_admin_setemail(OscarData *od, FlapConnection *conn, const char *newemail)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - byte_stream_new(&bs, 2+2+strlen(newemail));
    -
    - aim_tlvlist_add_str(&tlvlist, 0x0011, newemail);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/*
    - * Subtype 0x0006 - Request account confirmation.
    - *
    - * This will cause an email to be sent to the address associated with
    - * the account. By following the instructions in the mail, you can
    - * get the TRIAL flag removed from your account.
    - *
    - */
    -void
    -aim_admin_reqconfirm(OscarData *od, FlapConnection *conn)
    -{
    - aim_genericreq_n(od, conn, SNAC_FAMILY_ADMIN, 0x0006);
    -}
    -
    -/**
    - * Subtype SNAC_FAMILY_ADMIN - Account confirmation request acknowledgement.
    - */
    -static int
    -accountconfirm(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 status;
    - /* GSList *tlvlist; */
    -
    - status = byte_stream_get16(bs);
    - /* Status is 0x0013 if unable to confirm at this time */
    -
    - /* tlvlist = aim_tlvlist_read(bs); */
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, status);
    -
    - /* aim_tlvlist_free(tlvlist); */
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if ((snac->subtype == 0x0003) || (snac->subtype == 0x0005)) {
    - infochange(od, conn, mod, frame, snac, bs);
    - return 1;
    - } else if (snac->subtype == SNAC_FAMILY_ADMIN)
    - return accountconfirm(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int admin_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_ADMIN;
    - mod->version = 0x0001;
    - mod->toolid = 0x0010;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "admin", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_alert.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,238 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0018 - Email notification
    - *
    - * Used for being alerted when the email address(es) associated with
    - * your username get new electronic-m. For normal AIM accounts, you
    - * get the email address username@netscape.net. AOL accounts have
    - * username@aol.com, and can also activate a netscape.net account.
    - * Note: This information might be out of date.
    - */
    -
    -#include "oscar.h"
    -
    -/**
    - * Subtype 0x0006 - Request information about your email account
    - *
    - * @param od The oscar session.
    - * @param conn The email connection for this session.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int
    -aim_email_sendcookies(OscarData *od)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ALERT)))
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 2+16+16);
    -
    - /* Number of cookies to follow */
    - byte_stream_put16(&bs, 0x0002);
    -
    - /* Cookie */
    - byte_stream_put16(&bs, 0x5d5e);
    - byte_stream_put16(&bs, 0x1708);
    - byte_stream_put16(&bs, 0x55aa);
    - byte_stream_put16(&bs, 0x11d3);
    - byte_stream_put16(&bs, 0xb143);
    - byte_stream_put16(&bs, 0x0060);
    - byte_stream_put16(&bs, 0xb0fb);
    - byte_stream_put16(&bs, 0x1ecb);
    -
    - /* Cookie */
    - byte_stream_put16(&bs, 0xb380);
    - byte_stream_put16(&bs, 0x9ad8);
    - byte_stream_put16(&bs, 0x0dba);
    - byte_stream_put16(&bs, 0x11d5);
    - byte_stream_put16(&bs, 0x9f8a);
    - byte_stream_put16(&bs, 0x0060);
    - byte_stream_put16(&bs, 0xb0ee);
    - byte_stream_put16(&bs, 0x0631);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ALERT, 0x0006, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -
    -/**
    - * Subtype 0x0007 - Receive information about your email account
    - *
    - * So I don't even know if you can have multiple 16 byte keys,
    - * but this is coded so it will handle that, and handle it well.
    - * This tells you if you have unread mail or not, the URL you
    - * should use to access that mail, and the domain name for the
    - * email account (username@domainname.com). If this is the
    - * first 0x0007 SNAC you've received since you signed on, or if
    - * this is just a periodic status update, this will also contain
    - * the number of unread emails that you have.
    - */
    -static int
    -parseinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - struct aim_emailinfo *new;
    - GSList *tlvlist;
    - guint8 *cookie8, *cookie16;
    - int tmp, havenewmail = 0; /* Used to tell the client we have _new_ mail */
    -
    - char *alertitle = NULL, *alerturl = NULL;
    -
    - cookie8 = byte_stream_getraw(bs, 8); /* Possibly the code used to log you in to mail? */
    - cookie16 = byte_stream_getraw(bs, 16); /* Mail cookie sent above */
    -
    - /* See if we already have some info associated with this cookie */
    - for (new = od->emailinfo; (new && memcmp(cookie16, new->cookie16, 16)); new = new->next);
    - if (new) {
    - /* Free some of the old info, if it exists */
    - g_free(new->cookie8);
    - g_free(new->cookie16);
    - g_free(new->url);
    - g_free(new->domain);
    - } else {
    - /* We don't already have info, so create a new struct for it */
    - new = g_new0(struct aim_emailinfo, 1);
    - new->next = od->emailinfo;
    - od->emailinfo = new;
    - }
    -
    - new->cookie8 = cookie8;
    - new->cookie16 = cookie16;
    -
    - tlvlist = aim_tlvlist_readnum(bs, byte_stream_get16(bs));
    -
    - tmp = aim_tlv_get16(tlvlist, 0x0080, 1);
    - if (tmp) {
    - if (new->nummsgs < tmp)
    - havenewmail = 1;
    - new->nummsgs = tmp;
    - } else {
    - /* If they don't send a 0x0080 TLV, it means we definitely have new mail */
    - /* (ie. this is not just another status update) */
    - havenewmail = 1;
    - new->nummsgs++; /* We know we have at least 1 new email */
    - }
    - new->url = aim_tlv_getstr(tlvlist, 0x0007, 1);
    - if (!(new->unread = aim_tlv_get8(tlvlist, 0x0081, 1))) {
    - havenewmail = 0;
    - new->nummsgs = 0;
    - }
    - new->domain = aim_tlv_getstr(tlvlist, 0x0082, 1);
    - new->flag = aim_tlv_get16(tlvlist, 0x0084, 1);
    -
    - alertitle = aim_tlv_getstr(tlvlist, 0x0005, 1);
    - alerturl = aim_tlv_getstr(tlvlist, 0x000d, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, new, havenewmail, alertitle, (alerturl ? alerturl + 2 : NULL));
    -
    - aim_tlvlist_free(tlvlist);
    -
    - g_free(alertitle);
    - g_free(alerturl);
    -
    - return ret;
    -}
    -
    -/**
    - * Subtype 0x0016 - Send something or other
    - *
    - * @param od The oscar session.
    - * @param conn The email connection for this session.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int
    -aim_email_activate(OscarData *od)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ALERT)))
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 1+16);
    -
    - /* I would guess this tells AIM that you want updates for your mail accounts */
    - /* ...but I really have no idea */
    - byte_stream_put8(&bs, 0x02);
    - byte_stream_put32(&bs, 0x04000000);
    - byte_stream_put32(&bs, 0x04000000);
    - byte_stream_put32(&bs, 0x04000000);
    - byte_stream_put32(&bs, 0x00000000);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ALERT, 0x0016, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0007)
    - return parseinfo(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -static void
    -email_shutdown(OscarData *od, aim_module_t *mod)
    -{
    - while (od->emailinfo)
    - {
    - struct aim_emailinfo *tmp = od->emailinfo;
    - od->emailinfo = od->emailinfo->next;
    - g_free(tmp->cookie16);
    - g_free(tmp->cookie8);
    - g_free(tmp->url);
    - g_free(tmp->domain);
    - g_free(tmp);
    - }
    -
    - return;
    -}
    -
    -int
    -email_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_ALERT;
    - mod->version = 0x0001;
    - mod->toolid = 0x0010;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "alert", sizeof(mod->name));
    - mod->snachandler = snachandler;
    - mod->shutdown = email_shutdown;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_auth.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,630 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0017 - Authentication.
    - *
    - * Deals with the authorizer for SNAC-based login, and also old-style
    - * non-SNAC login.
    - *
    - */
    -
    -#include "oscar.h"
    -
    -#include <ctype.h>
    -
    -#include "cipher.h"
    -
    -/* #define USE_XOR_FOR_ICQ */
    -
    -#ifdef USE_XOR_FOR_ICQ
    -/**
    - * Encode a password using old XOR method
    - *
    - * This takes a const pointer to a (null terminated) string
    - * containing the unencoded password. It also gets passed
    - * an already allocated buffer to store the encoded password.
    - * This buffer should be the exact length of the password without
    - * the null. The encoded password buffer /is not %NULL terminated/.
    - *
    - * The encoding_table seems to be a fixed set of values. We'll
    - * hope it doesn't change over time!
    - *
    - * This is only used for the XOR method, not the better MD5 method.
    - *
    - * @param password Incoming password.
    - * @param encoded Buffer to put encoded password.
    - */
    -static int
    -aim_encode_password(const char *password, guint8 *encoded)
    -{
    - guint8 encoding_table[] = {
    - 0xf3, 0x26, 0x81, 0xc4,
    - 0x39, 0x86, 0xdb, 0x92,
    - 0x71, 0xa3, 0xb9, 0xe6,
    - 0x53, 0x7a, 0x95, 0x7c
    - };
    - unsigned int i;
    -
    - for (i = 0; i < strlen(password); i++)
    - encoded[i] = (password[i] ^ encoding_table[i]);
    -
    - return 0;
    -}
    -#endif
    -
    -#ifdef USE_OLD_MD5
    -static int
    -aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
    -{
    - PurpleCipherContext *context;
    -
    - context = purple_cipher_context_new_by_name("md5", NULL);
    - purple_cipher_context_append(context, (const guchar *)key, strlen(key));
    - purple_cipher_context_append(context, (const guchar *)password, password_len);
    - purple_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
    - purple_cipher_context_digest(context, 16, digest, NULL);
    - purple_cipher_context_destroy(context);
    -
    - return 0;
    -}
    -#else
    -static int
    -aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
    -{
    - PurpleCipher *cipher;
    - PurpleCipherContext *context;
    - guchar passdigest[16];
    -
    - cipher = purple_ciphers_find_cipher("md5");
    -
    - context = purple_cipher_context_new(cipher, NULL);
    - purple_cipher_context_append(context, (const guchar *)password, password_len);
    - purple_cipher_context_digest(context, 16, passdigest, NULL);
    - purple_cipher_context_destroy(context);
    -
    - context = purple_cipher_context_new(cipher, NULL);
    - purple_cipher_context_append(context, (const guchar *)key, strlen(key));
    - purple_cipher_context_append(context, passdigest, 16);
    - purple_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
    - purple_cipher_context_digest(context, 16, digest, NULL);
    - purple_cipher_context_destroy(context);
    -
    - return 0;
    -}
    -#endif
    -
    -#ifdef USE_XOR_FOR_ICQ
    -/*
    - * Part two of the ICQ hack. Note the ignoring of the key.
    - */
    -static int
    -goddamnicq2(OscarData *od, FlapConnection *conn, const char *sn, const char *password, ClientInfo *ci)
    -{
    - FlapFrame *frame;
    - GSList *tlvlist = NULL;
    - int passwdlen;
    - guint8 *password_encoded;
    - guint32 distrib;
    -
    - passwdlen = strlen(password);
    - password_encoded = (guint8 *)g_malloc(passwdlen+1);
    - if (passwdlen > MAXICQPASSLEN)
    - passwdlen = MAXICQPASSLEN;
    -
    - frame = flap_frame_new(od, 0x01, 1152);
    -
    - aim_encode_password(password, password_encoded);
    -
    - distrib = oscar_get_ui_info_int(
    - od->icq ? "prpl-icq-distid" : "prpl-aim-distid",
    - ci->distrib);
    -
    - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
    - aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
    - aim_tlvlist_add_raw(&tlvlist, 0x0002, passwdlen, password_encoded);
    -
    - if (ci->clientstring != NULL)
    - aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring);
    - else {
    - gchar *clientstring = oscar_get_clientstring();
    - aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring);
    - g_free(clientstring);
    - }
    - aim_tlvlist_add_16(&tlvlist, 0x0016, (guint16)ci->clientid);
    - aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major);
    - aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor);
    - aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point);
    - aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build);
    - aim_tlvlist_add_32(&tlvlist, 0x0014, distrib); /* distribution chan */
    - aim_tlvlist_add_str(&tlvlist, 0x000f, ci->lang);
    - aim_tlvlist_add_str(&tlvlist, 0x000e, ci->country);
    -
    - aim_tlvlist_write(&frame->data, &tlvlist);
    -
    - g_free(password_encoded);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send(conn, frame);
    -
    - return 0;
    -}
    -#endif
    -
    -/*
    - * Subtype 0x0002
    - *
    - * This is the initial login request packet.
    - *
    - * NOTE!! If you want/need to make use of the aim_sendmemblock() function,
    - * then the client information you send here must exactly match the
    - * executable that you're pulling the data from.
    - *
    - * Java AIM 1.1.19:
    - * clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001"
    - * clientid = 0x0001
    - * major = 0x0001
    - * minor = 0x0001
    - * point = (not sent)
    - * build = 0x0013
    - * unknown= (not sent)
    - *
    - * AIM for Linux 1.1.112:
    - * clientstring = "AOL Instant Messenger (SM)"
    - * clientid = 0x1d09
    - * major = 0x0001
    - * minor = 0x0001
    - * point = 0x0001
    - * build = 0x0070
    - * unknown= 0x0000008b
    - * serverstore = 0x01
    - *
    - * @param truncate_pass Truncate the password to 8 characters. This
    - * usually happens for AOL accounts. We are told that we
    - * should truncate it if the 0x0017/0x0007 SNAC contains
    - * a TLV of type 0x0026 with data 0x0000.
    - * @param allow_multiple_logins Allow multiple logins? If TRUE, the AIM
    - * server will prompt the user when multiple logins occur. If
    - * FALSE, existing connections (on other clients) will be
    - * disconnected automatically as we connect.
    - */
    -int
    -aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins)
    -{
    - FlapFrame *frame;
    - GSList *tlvlist = NULL;
    - guint8 digest[16];
    - aim_snacid_t snacid;
    - size_t password_len;
    - guint32 distrib;
    -
    - if (!ci || !sn || !password)
    - return -EINVAL;
    -
    -#ifdef USE_XOR_FOR_ICQ
    - /* If we're signing on an ICQ account then use the older, XOR login method */
    - if (aim_snvalid_icq(sn))
    - return goddamnicq2(od, conn, sn, password, ci);
    -#endif
    -
    - frame = flap_frame_new(od, 0x02, 1152);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, 0x0002, 0x0000, NULL, 0);
    - aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0002, snacid);
    -
    - aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
    -
    - /* Truncate ICQ and AOL passwords, if necessary */
    - password_len = strlen(password);
    - if (oscar_util_valid_name_icq(sn) && (password_len > MAXICQPASSLEN))
    - password_len = MAXICQPASSLEN;
    - else if (truncate_pass && password_len > 8)
    - password_len = 8;
    -
    - aim_encode_password_md5(password, password_len, key, digest);
    -
    - distrib = oscar_get_ui_info_int(
    - od->icq ? "prpl-icq-distid" : "prpl-aim-distid",
    - ci->distrib);
    -
    - aim_tlvlist_add_raw(&tlvlist, 0x0025, 16, digest);
    -
    -#ifndef USE_OLD_MD5
    - aim_tlvlist_add_noval(&tlvlist, 0x004c);
    -#endif
    -
    - if (ci->clientstring != NULL)
    - aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring);
    - else {
    - gchar *clientstring = oscar_get_clientstring();
    - aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring);
    - g_free(clientstring);
    - }
    - aim_tlvlist_add_16(&tlvlist, 0x0016, (guint16)ci->clientid);
    - aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major);
    - aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor);
    - aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point);
    - aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build);
    - aim_tlvlist_add_32(&tlvlist, 0x0014, distrib);
    - aim_tlvlist_add_str(&tlvlist, 0x000f, ci->lang);
    - aim_tlvlist_add_str(&tlvlist, 0x000e, ci->country);
    -
    - /*
    - * If set, old-fashioned buddy lists will not work. You will need
    - * to use SSI.
    - */
    - aim_tlvlist_add_8(&tlvlist, 0x004a, (allow_multiple_logins ? 0x01 : 0x03));
    -
    - aim_tlvlist_write(&frame->data, &tlvlist);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send(conn, frame);
    -
    - return 0;
    -}
    -
    -/*
    - * This is sent back as a general response to the login command.
    - * It can be either an error or a success, depending on the
    - * presence of certain TLVs.
    - *
    - * The client should check the value passed as errorcode. If
    - * its nonzero, there was an error.
    - */
    -static int
    -parse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - GSList *tlvlist;
    - aim_rxcallback_t userfunc;
    - struct aim_authresp_info *info;
    - int ret = 0;
    -
    - info = g_new0(struct aim_authresp_info, 1);
    -
    - /*
    - * Read block of TLVs. All further data is derived
    - * from what is parsed here.
    - */
    - tlvlist = aim_tlvlist_read(bs);
    -
    - /*
    - * No matter what, we should have a username.
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) {
    - info->bn = aim_tlv_getstr(tlvlist, 0x0001, 1);
    - purple_connection_set_display_name(od->gc, info->bn);
    - }
    -
    - /*
    - * Check for an error code. If so, we should also
    - * have an error url.
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0008, 1))
    - info->errorcode = aim_tlv_get16(tlvlist, 0x0008, 1);
    - if (aim_tlv_gettlv(tlvlist, 0x0004, 1))
    - info->errorurl = aim_tlv_getstr(tlvlist, 0x0004, 1);
    -
    - /*
    - * BOS server address.
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
    - info->bosip = aim_tlv_getstr(tlvlist, 0x0005, 1);
    -
    - /*
    - * Authorization cookie.
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
    - aim_tlv_t *tmptlv;
    -
    - tmptlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
    - if (tmptlv != NULL)
    - {
    - info->cookielen = tmptlv->length;
    - info->cookie = tmptlv->value;
    - }
    - }
    -
    - /*
    - * The email address attached to this account
    - * Not available for ICQ or @mac.com logins.
    - * If you receive this TLV, then you are allowed to use
    - * family 0x0018 to check the status of your email.
    - * XXX - Not really true!
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0011, 1))
    - info->email = aim_tlv_getstr(tlvlist, 0x0011, 1);
    -
    - /*
    - * The registration status. (Not real sure what it means.)
    - * Not available for ICQ or @mac.com logins.
    - *
    - * 1 = No disclosure
    - * 2 = Limited disclosure
    - * 3 = Full disclosure
    - *
    - * This has to do with whether your email address is available
    - * to other users or not. AFAIK, this feature is no longer used.
    - *
    - * Means you can use the admin family? (0x0007)
    - *
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0013, 1))
    - info->regstatus = aim_tlv_get16(tlvlist, 0x0013, 1);
    -
    - if (aim_tlv_gettlv(tlvlist, 0x0040, 1))
    - info->latestbeta.build = aim_tlv_get32(tlvlist, 0x0040, 1);
    - if (aim_tlv_gettlv(tlvlist, 0x0041, 1))
    - info->latestbeta.url = aim_tlv_getstr(tlvlist, 0x0041, 1);
    - if (aim_tlv_gettlv(tlvlist, 0x0042, 1))
    - info->latestbeta.info = aim_tlv_getstr(tlvlist, 0x0042, 1);
    - if (aim_tlv_gettlv(tlvlist, 0x0043, 1))
    - info->latestbeta.name = aim_tlv_getstr(tlvlist, 0x0043, 1);
    -
    - if (aim_tlv_gettlv(tlvlist, 0x0044, 1))
    - info->latestrelease.build = aim_tlv_get32(tlvlist, 0x0044, 1);
    - if (aim_tlv_gettlv(tlvlist, 0x0045, 1))
    - info->latestrelease.url = aim_tlv_getstr(tlvlist, 0x0045, 1);
    - if (aim_tlv_gettlv(tlvlist, 0x0046, 1))
    - info->latestrelease.info = aim_tlv_getstr(tlvlist, 0x0046, 1);
    - if (aim_tlv_gettlv(tlvlist, 0x0047, 1))
    - info->latestrelease.name = aim_tlv_getstr(tlvlist, 0x0047, 1);
    -
    - /*
    - * URL to change password.
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0054, 1))
    - info->chpassurl = aim_tlv_getstr(tlvlist, 0x0054, 1);
    -
    - od->authinfo = info;
    -
    - if ((userfunc = aim_callhandler(od, snac ? snac->family : SNAC_FAMILY_AUTH, snac ? snac->subtype : 0x0003)))
    - ret = userfunc(od, conn, frame, info);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -#ifdef USE_XOR_FOR_ICQ
    -/*
    - * Subtype 0x0007 (kind of) - Send a fake type 0x0007 SNAC to the client
    - *
    - * This is a bit confusing.
    - *
    - * Normal SNAC login goes like this:
    - * - connect
    - * - server sends flap version
    - * - client sends flap version
    - * - client sends username (17/6)
    - * - server sends hash key (17/7)
    - * - client sends auth request (17/2 -- aim_send_login)
    - * - server yells
    - *
    - * XOR login (for ICQ) goes like this:
    - * - connect
    - * - server sends flap version
    - * - client sends auth request which contains flap version (aim_send_login)
    - * - server yells
    - *
    - * For the client API, we make them implement the most complicated version,
    - * and for the simpler version, we fake it and make it look like the more
    - * complicated process.
    - *
    - * This is done by giving the client a faked key, just so we can convince
    - * them to call aim_send_login right away, which will detect the session
    - * flag that says this is XOR login and ignore the key, sending an ICQ
    - * login request instead of the normal SNAC one.
    - *
    - * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/.
    - */
    -static int
    -goddamnicq(OscarData *od, FlapConnection *conn, const char *sn)
    -{
    - FlapFrame frame;
    - aim_rxcallback_t userfunc;
    -
    - if ((userfunc = aim_callhandler(od, SNAC_FAMILY_AUTH, 0x0007)))
    - userfunc(od, conn, &frame, "");
    -
    - return 0;
    -}
    -#endif
    -
    -/*
    - * Subtype 0x0006
    - *
    - * In AIM 3.5 protocol, the first stage of login is to request login from the
    - * Authorizer, passing it the username for verification. If the name is
    - * invalid, a 0017/0003 is spit back, with the standard error contents. If
    - * valid, a 0017/0007 comes back, which is the signal to send it the main
    - * login command (0017/0002).
    - *
    - */
    -int
    -aim_request_login(OscarData *od, FlapConnection *conn, const char *sn)
    -{
    - FlapFrame *frame;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - if (!od || !conn || !sn)
    - return -EINVAL;
    -
    -#ifdef USE_XOR_FOR_ICQ
    - if (aim_snvalid_icq(sn))
    - return goddamnicq(od, conn, sn);
    -#endif
    -
    - frame = flap_frame_new(od, 0x02, 10+2+2+strlen(sn)+8);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, 0x0006, 0x0000, NULL, 0);
    - aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0006, snacid);
    -
    - aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
    -
    - /* Tell the server we support SecurID logins. */
    - aim_tlvlist_add_noval(&tlvlist, 0x004b);
    -
    - /* Unknown. Sent in recent WinAIM clients.*/
    - aim_tlvlist_add_noval(&tlvlist, 0x005a);
    -
    - aim_tlvlist_write(&frame->data, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send(conn, frame);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0007
    - *
    - * Middle handler for 0017/0007 SNACs. Contains the auth key prefixed
    - * by only its length in a two byte word.
    - *
    - * Calls the client, which should then use the value to call aim_send_login.
    - *
    - */
    -static int
    -keyparse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int keylen, ret = 1;
    - aim_rxcallback_t userfunc;
    - char *keystr;
    - GSList *tlvlist;
    - gboolean truncate_pass;
    -
    - keylen = byte_stream_get16(bs);
    - keystr = byte_stream_getstr(bs, keylen);
    - tlvlist = aim_tlvlist_read(bs);
    -
    - /*
    - * If the truncate_pass TLV exists then we should truncate the
    - * user's password to 8 characters. This flag is sent to us
    - * when logging in with an AOL user's username.
    - */
    - truncate_pass = aim_tlv_gettlv(tlvlist, 0x0026, 1) != NULL;
    -
    - /* XXX - When GiantGrayPanda signed on AIM I got a thing asking me to register
    - * for the netscape network. This SNAC had a type 0x0058 TLV with length 10.
    - * Data is 0x0007 0004 3e19 ae1e 0006 0004 0000 0005 */
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, keystr, (int)truncate_pass);
    -
    - g_free(keystr);
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/**
    - * Subtype 0x000a
    - *
    - * Receive SecurID request.
    - */
    -static int
    -got_securid_request(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame);
    -
    - return ret;
    -}
    -
    -/**
    - * Subtype 0x000b
    - *
    - * Send SecurID response.
    - */
    -int
    -aim_auth_securid_send(OscarData *od, const char *securid)
    -{
    - FlapConnection *conn;
    - FlapFrame *frame;
    - int len;
    -
    - if (!od || !(conn = flap_connection_getbytype_all(od, SNAC_FAMILY_AUTH)) || !securid)
    - return -EINVAL;
    -
    - len = strlen(securid);
    -
    - frame = flap_frame_new(od, 0x02, 10+2+len);
    -
    - /* aim_snacid_t snacid = */ aim_cachesnac(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_RESPONSE, 0x0000, NULL, 0);
    - aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_RESPONSE, 0);
    -
    - byte_stream_put16(&frame->data, len);
    - byte_stream_putstr(&frame->data, securid);
    -
    - flap_connection_send(conn, frame);
    -
    - return 0;
    -}
    -
    -static void
    -auth_shutdown(OscarData *od, aim_module_t *mod)
    -{
    - if (od->authinfo != NULL)
    - {
    - g_free(od->authinfo->bn);
    - g_free(od->authinfo->bosip);
    - g_free(od->authinfo->errorurl);
    - g_free(od->authinfo->email);
    - g_free(od->authinfo->chpassurl);
    - g_free(od->authinfo->latestrelease.name);
    - g_free(od->authinfo->latestrelease.url);
    - g_free(od->authinfo->latestrelease.info);
    - g_free(od->authinfo->latestbeta.name);
    - g_free(od->authinfo->latestbeta.url);
    - g_free(od->authinfo->latestbeta.info);
    - g_free(od->authinfo);
    - }
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0003)
    - return parse(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0007)
    - return keyparse(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000a)
    - return got_securid_request(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -auth_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_AUTH;
    - mod->version = 0x0000;
    - mod->flags = 0;
    - strncpy(mod->name, "auth", sizeof(mod->name));
    - mod->snachandler = snachandler;
    - mod->shutdown = auth_shutdown;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_bart.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,184 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0010 - Server stored buddy art
    - *
    - * Used for storing and retrieving your cute little buddy icon
    - * from the AIM servers.
    - *
    - */
    -
    -#include "oscar.h"
    -
    -/**
    - * Subtype 0x0002 - Upload your icon.
    - *
    - * @param od The oscar session.
    - * @param icon The raw data of the icon image file.
    - * @param iconlen Length of the raw data of the icon image file.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int
    -aim_bart_upload(OscarData *od, const guint8 *icon, guint16 iconlen)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !icon || !iconlen)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 2 + 2 + iconlen);
    -
    - /* The reference number for the icon */
    - byte_stream_put16(&bs, 1);
    -
    - /* The icon */
    - byte_stream_put16(&bs, iconlen);
    - byte_stream_putraw(&bs, icon, iconlen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_BART, 0x0002, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/**
    - * Subtype 0x0003 - Acknowledgement for uploading a buddy icon.
    - *
    - * You get this honky after you upload a buddy icon.
    - */
    -static int
    -uploadack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    -
    - byte_stream_get16(bs);
    - byte_stream_get16(bs);
    - byte_stream_get8(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame);
    -
    - return ret;
    -}
    -
    -/**
    - * Subtype 0x0004 - Request someone's icon.
    - *
    - * @param od The oscar session.
    - * @param bn The name of the buddy whose icon you are requesting.
    - * @param iconcsum The MD5 checksum of the icon you are requesting.
    - * @param iconcsumlen Length of the MD5 checksum given above. Should be 10 bytes.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int
    -aim_bart_request(OscarData *od, const char *bn, guint8 iconcsumtype, const guint8 *iconcsum, guint16 iconcsumlen)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !bn || !strlen(bn) || !iconcsum || !iconcsumlen)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 1+strlen(bn) + 4 + 1+iconcsumlen);
    -
    - /* Buddy name */
    - byte_stream_put8(&bs, strlen(bn));
    - byte_stream_putstr(&bs, bn);
    -
    - /* Some numbers. You like numbers, right? */
    - byte_stream_put8(&bs, 0x01);
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put8(&bs, iconcsumtype);
    -
    - /* Icon string */
    - byte_stream_put8(&bs, iconcsumlen);
    - byte_stream_putraw(&bs, iconcsum, iconcsumlen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_BART, 0x0004, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/**
    - * Subtype 0x0005 - Receive a buddy icon.
    - *
    - * This is sent in response to a buddy icon request.
    - */
    -static int
    -parseicon(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - char *bn;
    - guint16 iconlen;
    - guint8 iconcsumtype, iconcsumlen, *iconcsum, *icon;
    -
    - bn = byte_stream_getstr(bs, byte_stream_get8(bs));
    - byte_stream_get16(bs); /* flags */
    - iconcsumtype = byte_stream_get8(bs);
    - iconcsumlen = byte_stream_get8(bs);
    - iconcsum = byte_stream_getraw(bs, iconcsumlen);
    - iconlen = byte_stream_get16(bs);
    - icon = byte_stream_getraw(bs, iconlen);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, bn, iconcsumtype, iconcsum, iconcsumlen, icon, iconlen);
    -
    - g_free(bn);
    - g_free(iconcsum);
    - g_free(icon);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0003)
    - return uploadack(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0005)
    - return parseicon(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -bart_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_BART;
    - mod->version = 0x0001;
    - mod->toolid = 0x0010;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "bart", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_bos.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,92 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0009 - Basic Oscar Service.
    - *
    - * The functionality of this family has been replaced by SSI.
    - */
    -
    -#include "oscar.h"
    -
    -#include <string.h>
    -
    -/* Subtype 0x0002 - Request BOS rights. */
    -void
    -aim_bos_reqrights(OscarData *od, FlapConnection *conn)
    -{
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_BOS, 0x0002);
    -}
    -
    -/* Subtype 0x0003 - BOS Rights. */
    -static int rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_rxcallback_t userfunc;
    - GSList *tlvlist;
    - guint16 maxpermits = 0, maxdenies = 0;
    - int ret = 0;
    -
    - /*
    - * TLVs follow
    - */
    - tlvlist = aim_tlvlist_read(bs);
    -
    - /*
    - * TLV type 0x0001: Maximum number of buddies on permit list.
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
    - maxpermits = aim_tlv_get16(tlvlist, 0x0001, 1);
    -
    - /*
    - * TLV type 0x0002: Maximum number of buddies on deny list.
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0002, 1))
    - maxdenies = aim_tlv_get16(tlvlist, 0x0002, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, maxpermits, maxdenies);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0003)
    - return rights(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -bos_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_BOS;
    - mod->version = 0x0001;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "bos", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_buddy.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,153 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0003 (SNAC_FAMILY_BUDDY) - Old-style Buddylist Management (non-SSI).
    - *
    - */
    -
    -#include "oscar.h"
    -
    -#include <string.h>
    -
    -/*
    - * Subtype 0x0002 - Request rights.
    - *
    - * Request Buddy List rights.
    - *
    - */
    -void
    -aim_buddylist_reqrights(OscarData *od, FlapConnection *conn)
    -{
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_REQRIGHTS);
    -}
    -
    -/*
    - * Subtype 0x0003 - Rights.
    - *
    - */
    -static int
    -rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_rxcallback_t userfunc;
    - GSList *tlvlist;
    - guint16 maxbuddies = 0, maxwatchers = 0;
    - int ret = 0;
    -
    - /*
    - * TLVs follow
    - */
    - tlvlist = aim_tlvlist_read(bs);
    -
    - /*
    - * TLV type 0x0001: Maximum number of buddies.
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
    - maxbuddies = aim_tlv_get16(tlvlist, 0x0001, 1);
    -
    - /*
    - * TLV type 0x0002: Maximum number of watchers.
    - *
    - * Watchers are other users who have you on their buddy
    - * list. (This is called the "reverse list" by a certain
    - * other IM protocol.)
    - *
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0002, 1))
    - maxwatchers = aim_tlv_get16(tlvlist, 0x0002, 1);
    -
    - /*
    - * TLV type 0x0003: Unknown.
    - *
    - * ICQ only?
    - */
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, maxbuddies, maxwatchers);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtypes 0x000b (SNAC_SUBTYPE_BUDDY_ONCOMING) and 0x000c (SNAC_SUBTYPE_BUDDY_OFFGOING) - Change in buddy status
    - *
    - * Oncoming Buddy notifications contain a subset of the
    - * user information structure. It's close enough to run
    - * through aim_info_extract() however.
    - *
    - * Although the offgoing notification contains no information,
    - * it is still in a format parsable by aim_info_extract().
    - *
    - */
    -static int
    -buddychange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_userinfo_t userinfo;
    - aim_rxcallback_t userfunc;
    -
    - aim_info_extract(od, bs, &userinfo);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, &userinfo);
    -
    - if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING &&
    - userinfo.capabilities & OSCAR_CAPABILITY_XTRAZ) {
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - PurpleBuddy *buddy = purple_find_buddy(account, userinfo.bn);
    -
    - if (buddy) {
    - PurplePresence *presence = purple_buddy_get_presence(buddy);
    -
    - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD))
    - icq_im_xstatus_request(od, userinfo.bn);
    - }
    - }
    - aim_info_free(&userinfo);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == SNAC_SUBTYPE_BUDDY_RIGHTSINFO)
    - return rights(od, conn, mod, frame, snac, bs);
    - else if ((snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING) || (snac->subtype == SNAC_SUBTYPE_BUDDY_OFFGOING))
    - return buddychange(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -buddylist_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_BUDDY;
    - mod->version = 0x0001;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "buddy", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_chat.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,398 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x000e - Routines for the Chat service.
    - *
    - */
    -
    -#include "oscar.h"
    -
    -#include <string.h>
    -
    -/* Stored in the ->internal of chat connections */
    -struct chatconnpriv
    -{
    - guint16 exchange;
    - char *name;
    - guint16 instance;
    -};
    -
    -void
    -flap_connection_destroy_chat(OscarData *od, FlapConnection *conn)
    -{
    - struct chatconnpriv *ccp = (struct chatconnpriv *)conn->internal;
    -
    - if (ccp)
    - g_free(ccp->name);
    - g_free(ccp);
    -
    - return;
    -}
    -
    -int
    -aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo)
    -{
    - if (!bs || !outinfo)
    - return 0;
    -
    - outinfo->exchange = byte_stream_get16(bs);
    - outinfo->namelen = byte_stream_get8(bs);
    - outinfo->name = (char *)byte_stream_getraw(bs, outinfo->namelen);
    - outinfo->instance = byte_stream_get16(bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0002 - General room information. Lots of stuff.
    - *
    - * Values I know are in here but I haven't attached
    - * them to any of the 'Unknown's:
    - * - Language (English)
    - *
    - */
    -static int
    -infoupdate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_rxcallback_t userfunc;
    - int ret = 0;
    - guint8 detaillevel = 0;
    - struct aim_chat_roominfo roominfo;
    - GSList *tlvlist;
    - guint16 maxmsglen, maxvisiblemsglen;
    -
    - aim_chat_readroominfo(bs, &roominfo);
    -
    - detaillevel = byte_stream_get8(bs);
    -
    - if (detaillevel != 0x02) {
    - purple_debug_misc("oscar", "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel);
    - return 1;
    - }
    -
    - byte_stream_get16(bs); /* skip the TLV count */
    -
    - /*
    - * Everything else are TLVs.
    - */
    - tlvlist = aim_tlvlist_read(bs);
    -
    - /*
    - * Type 0x00d1: Maximum Message Length
    - */
    - maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1);
    -
    - /*
    - * Type 0x00da: Maximum visible message length
    - */
    - maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) {
    - ret = userfunc(od, conn, frame, maxmsglen, maxvisiblemsglen);
    - }
    -
    - g_free(roominfo.name);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/* Subtypes 0x0003 and 0x0004 */
    -static int
    -userlistchange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_userinfo_t *userinfo = NULL;
    - aim_rxcallback_t userfunc;
    - int curcount = 0, ret = 0;
    -
    - while (byte_stream_bytes_left(bs)) {
    - curcount++;
    - userinfo = g_realloc(userinfo, curcount * sizeof(aim_userinfo_t));
    - aim_info_extract(od, bs, &userinfo[curcount-1]);
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, curcount, userinfo);
    -
    - aim_info_free(userinfo);
    - g_free(userinfo);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0005 - Send a Chat Message.
    - *
    - * Possible flags:
    - * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages
    - * should be sent to their sender.
    - * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse
    - * (Note that WinAIM does not honor this,
    - * and displays the message as normal.)
    - *
    - * XXX convert this to use tlvchains
    - */
    -int
    -aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language)
    -{
    - int i;
    - ByteStream bs;
    - IcbmCookie *cookie;
    - aim_snacid_t snacid;
    - guint8 ckstr[8];
    - GSList *tlvlist = NULL, *inner_tlvlist = NULL;
    -
    - if (!od || !conn || !msg || (msglen <= 0))
    - return 0;
    -
    - byte_stream_new(&bs, 1142);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_CHAT, 0x0005, 0x0000, NULL, 0);
    -
    - /*
    - * Cookie
    - *
    - * XXX mkcookie should generate the cookie and cache it in one
    - * operation to preserve uniqueness.
    - */
    - for (i = 0; i < 8; i++)
    - ckstr[i] = (guint8)rand();
    -
    - cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
    - cookie->data = NULL; /* XXX store something useful here */
    -
    - aim_cachecookie(od, cookie);
    -
    - /* ICBM Header */
    - byte_stream_putraw(&bs, ckstr, 8); /* Cookie */
    - byte_stream_put16(&bs, 0x0003); /* Channel */
    -
    - /*
    - * Type 1: Flag meaning this message is destined to the room.
    - */
    - aim_tlvlist_add_noval(&tlvlist, 0x0001);
    -
    - /*
    - * Type 6: Reflect
    - */
    - if (!(flags & AIM_CHATFLAGS_NOREFLECT))
    - aim_tlvlist_add_noval(&tlvlist, 0x0006);
    -
    - /*
    - * Type 7: Autoresponse
    - */
    - if (flags & AIM_CHATFLAGS_AWAY)
    - aim_tlvlist_add_noval(&tlvlist, 0x0007);
    -
    - /*
    - * SubTLV: Type 1: Message
    - */
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0001, msglen, (guchar *)msg);
    -
    - /*
    - * SubTLV: Type 2: Encoding
    - */
    - if (encoding != NULL)
    - aim_tlvlist_add_str(&inner_tlvlist, 0x0002, encoding);
    -
    - /*
    - * SubTLV: Type 3: Language
    - */
    - if (language != NULL)
    - aim_tlvlist_add_str(&inner_tlvlist, 0x0003, language);
    -
    - /*
    - * Type 5: Message block. Contains more TLVs.
    - *
    - * This could include other information... We just
    - * put in a message TLV however.
    - *
    - */
    - aim_tlvlist_add_frozentlvlist(&tlvlist, 0x0005, &inner_tlvlist);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_CHAT, 0x0005, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0006
    - *
    - * We could probably include this in the normal ICBM parsing
    - * code as channel 0x0003, however, since only the start
    - * would be the same, we might as well do it here.
    - *
    - * General outline of this SNAC:
    - * snac
    - * cookie
    - * channel id
    - * tlvlist
    - * unknown
    - * source user info
    - * name
    - * evility
    - * userinfo tlvs
    - * online time
    - * etc
    - * message metatlv
    - * message tlv
    - * message string
    - * possibly others
    - *
    - */
    -static int
    -incomingim_ch3(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0, i;
    - aim_rxcallback_t userfunc;
    - aim_userinfo_t userinfo;
    - guint8 cookie[8];
    - guint16 channel;
    - GSList *tlvlist;
    - char *msg = NULL;
    - int len = 0;
    - char *encoding = NULL, *language = NULL;
    - IcbmCookie *ck;
    - aim_tlv_t *tlv;
    - ByteStream tbs;
    -
    - memset(&userinfo, 0, sizeof(aim_userinfo_t));
    -
    - /*
    - * Read ICBM Cookie.
    - */
    - for (i = 0; i < 8; i++)
    - cookie[i] = byte_stream_get8(bs);
    -
    - if ((ck = aim_uncachecookie(od, cookie, AIM_COOKIETYPE_CHAT))) {
    - g_free(ck->data);
    - g_free(ck);
    - }
    -
    - /*
    - * Channel ID
    - *
    - * Channel 0x0003 is used for chat messages.
    - *
    - */
    - channel = byte_stream_get16(bs);
    -
    - if (channel != 0x0003) {
    - purple_debug_misc("oscar", "faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
    - return 0;
    - }
    -
    - /*
    - * Start parsing TLVs right away.
    - */
    - tlvlist = aim_tlvlist_read(bs);
    -
    - /*
    - * Type 0x0003: Source User Information
    - */
    - tlv = aim_tlv_gettlv(tlvlist, 0x0003, 1);
    - if (tlv != NULL)
    - {
    - byte_stream_init(&tbs, tlv->value, tlv->length);
    - aim_info_extract(od, &tbs, &userinfo);
    - }
    -
    - /*
    - * Type 0x0005: Message Block. Conains more TLVs.
    - */
    - tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1);
    - if (tlv != NULL)
    - {
    - GSList *inner_tlvlist;
    - aim_tlv_t *inner_tlv;
    -
    - byte_stream_init(&tbs, tlv->value, tlv->length);
    - inner_tlvlist = aim_tlvlist_read(&tbs);
    -
    - /*
    - * Type 0x0001: Message.
    - */
    - inner_tlv = aim_tlv_gettlv(inner_tlvlist, 0x0001, 1);
    - if (inner_tlv != NULL)
    - {
    - len = inner_tlv->length;
    - msg = aim_tlv_getvalue_as_string(inner_tlv);
    - }
    -
    - /*
    - * Type 0x0002: Encoding.
    - */
    - encoding = aim_tlv_getstr(inner_tlvlist, 0x0002, 1);
    -
    - /*
    - * Type 0x0003: Language.
    - */
    - language = aim_tlv_getstr(inner_tlvlist, 0x0003, 1);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, &userinfo, len, msg, encoding, language);
    -
    - aim_info_free(&userinfo);
    - g_free(msg);
    - g_free(encoding);
    - g_free(language);
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0002)
    - return infoupdate(od, conn, mod, frame, snac, bs);
    - else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004))
    - return userlistchange(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0006)
    - return incomingim_ch3(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -chat_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_CHAT;
    - mod->version = 0x0001;
    - mod->toolid = 0x0010;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "chat", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_chatnav.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,445 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x000d - Handle ChatNav.
    - *
    - * The ChatNav(igation) service does various things to keep chat
    - * alive. It provides room information, room searching and creating,
    - * as well as giving users the right ("permission") to use chat.
    - *
    - */
    -
    -#include "oscar.h"
    -
    -static int
    -error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_snac_t *snac2;
    - guint16 error, chatnav_error;
    - GSList *tlvlist;
    -
    - snac2 = aim_remsnac(od, snac->id);
    - if (!snac2) {
    - purple_debug_warning("oscar", "chatnav error: received response to unknown request (%08x)\n", snac->id);
    - return 0;
    - }
    -
    - if (snac2->family != SNAC_FAMILY_CHATNAV) {
    - purple_debug_warning("oscar", "chatnav error: received response that maps to corrupt request (fam=%04x)\n", snac2->family);
    - g_free(snac2->data);
    - g_free(snac2);
    - return 0;
    - }
    -
    - /*
    - * We now know what the original SNAC subtype was.
    - */
    - if (snac2->type == 0x0008) /* create room */
    - {
    - error = byte_stream_get16(bs);
    - tlvlist = aim_tlvlist_read(bs);
    - chatnav_error = aim_tlv_get16(tlvlist, 0x0008, 1);
    -
    - purple_debug_warning("oscar",
    - "Could not join room, error=0x%04hx, chatnav_error=0x%04hx\n",
    - error, chatnav_error);
    - purple_notify_error(od->gc, NULL, _("Could not join chat room"),
    - chatnav_error == 0x0033 ? _("Invalid chat room name") : _("Unknown error"));
    -
    - ret = 1;
    - }
    -
    - g_free(snac2->data);
    - g_free(snac2);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0002
    - *
    - * conn must be a chatnav connection!
    - *
    - */
    -void aim_chatnav_reqrights(OscarData *od, FlapConnection *conn)
    -{
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_CHATNAV, 0x0002);
    -}
    -
    -/*
    - * Subtype 0x0008
    - */
    -int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name, guint16 exchange)
    -{
    - static const char ck[] = {"create"};
    - static const char lang[] = {"en"};
    - static const char charset[] = {"us-ascii"};
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - byte_stream_new(&bs, 1142);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_CHATNAV, 0x0008, 0x0000, NULL, 0);
    -
    - /* exchange */
    - byte_stream_put16(&bs, exchange);
    -
    - /*
    - * This looks to be a big hack. You'll note that this entire
    - * SNAC is just a room info structure, but the hard room name,
    - * here, is set to "create".
    - *
    - * Either this goes on the "list of questions concerning
    - * why-the-hell-did-you-do-that", or this value is completely
    - * ignored. Without experimental evidence, but a good knowledge of
    - * AOL style, I'm going to guess that it is the latter, and that
    - * the value of the room name in create requests is ignored.
    - */
    - byte_stream_put8(&bs, strlen(ck));
    - byte_stream_putstr(&bs, ck);
    -
    - /*
    - * instance
    - *
    - * Setting this to 0xffff apparently assigns the last instance.
    - *
    - */
    - byte_stream_put16(&bs, 0xffff);
    -
    - /* detail level */
    - byte_stream_put8(&bs, 0x01);
    -
    - aim_tlvlist_add_str(&tlvlist, 0x00d3, name);
    - aim_tlvlist_add_str(&tlvlist, 0x00d6, charset);
    - aim_tlvlist_add_str(&tlvlist, 0x00d7, lang);
    -
    - /* tlvcount */
    - byte_stream_put16(&bs, aim_tlvlist_count(tlvlist));
    - aim_tlvlist_write(&bs, &tlvlist);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_CHATNAV, 0x0008, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -static int
    -parseinfo_perms(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs, aim_snac_t *snac2)
    -{
    - aim_rxcallback_t userfunc;
    - int ret = 0;
    - struct aim_chat_exchangeinfo *exchanges = NULL;
    - int curexchange;
    - aim_tlv_t *exchangetlv;
    - guint8 maxrooms = 0;
    - GSList *tlvlist, *innerlist;
    -
    - tlvlist = aim_tlvlist_read(bs);
    -
    - /*
    - * Type 0x0002: Maximum concurrent rooms.
    - */
    - if (aim_tlv_gettlv(tlvlist, 0x0002, 1))
    - maxrooms = aim_tlv_get8(tlvlist, 0x0002, 1);
    -
    - /*
    - * Type 0x0003: Exchange information
    - *
    - * There can be any number of these, each one
    - * representing another exchange.
    - *
    - */
    - for (curexchange = 0; ((exchangetlv = aim_tlv_gettlv(tlvlist, 0x0003, curexchange+1))); ) {
    - ByteStream tbs;
    -
    - byte_stream_init(&tbs, exchangetlv->value, exchangetlv->length);
    -
    - curexchange++;
    -
    - exchanges = g_realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
    -
    - /* exchange number */
    - exchanges[curexchange-1].number = byte_stream_get16(&tbs);
    - innerlist = aim_tlvlist_read(&tbs);
    -
    - /*
    - * Type 0x0002: Unknown
    - */
    - if (aim_tlv_gettlv(innerlist, 0x0002, 1)) {
    - guint16 classperms;
    -
    - classperms = aim_tlv_get16(innerlist, 0x0002, 1);
    -
    - purple_debug_misc("oscar", "faim: class permissions %x\n", classperms);
    - }
    -
    - /*
    - * Type 0x00c9: Flags
    - *
    - * 1 Evilable
    - * 2 Nav Only
    - * 4 Instancing Allowed
    - * 8 Occupant Peek Allowed
    - *
    - */
    - if (aim_tlv_gettlv(innerlist, 0x00c9, 1))
    - exchanges[curexchange-1].flags = aim_tlv_get16(innerlist, 0x00c9, 1);
    -
    - /*
    - * Type 0x00d3: Exchange Description
    - */
    - if (aim_tlv_gettlv(innerlist, 0x00d3, 1))
    - exchanges[curexchange-1].name = aim_tlv_getstr(innerlist, 0x00d3, 1);
    - else
    - exchanges[curexchange-1].name = NULL;
    -
    - /*
    - * Type 0x00d5: Creation Permissions
    - *
    - * 0 Creation not allowed
    - * 1 Room creation allowed
    - * 2 Exchange creation allowed
    - *
    - */
    - if (aim_tlv_gettlv(innerlist, 0x00d5, 1))
    - aim_tlv_get8(innerlist, 0x00d5, 1); /* createperms */
    -
    - /*
    - * Type 0x00d6: Character Set (First Time)
    - */
    - if (aim_tlv_gettlv(innerlist, 0x00d6, 1))
    - exchanges[curexchange-1].charset1 = aim_tlv_getstr(innerlist, 0x00d6, 1);
    - else
    - exchanges[curexchange-1].charset1 = NULL;
    -
    - /*
    - * Type 0x00d7: Language (First Time)
    - */
    - if (aim_tlv_gettlv(innerlist, 0x00d7, 1))
    - exchanges[curexchange-1].lang1 = aim_tlv_getstr(innerlist, 0x00d7, 1);
    - else
    - exchanges[curexchange-1].lang1 = NULL;
    -
    - /*
    - * Type 0x00d8: Character Set (Second Time)
    - */
    - if (aim_tlv_gettlv(innerlist, 0x00d8, 1))
    - exchanges[curexchange-1].charset2 = aim_tlv_getstr(innerlist, 0x00d8, 1);
    - else
    - exchanges[curexchange-1].charset2 = NULL;
    -
    - /*
    - * Type 0x00d9: Language (Second Time)
    - */
    - if (aim_tlv_gettlv(innerlist, 0x00d9, 1))
    - exchanges[curexchange-1].lang2 = aim_tlv_getstr(innerlist, 0x00d9, 1);
    - else
    - exchanges[curexchange-1].lang2 = NULL;
    -
    - aim_tlvlist_free(innerlist);
    - }
    -
    - /*
    - * Call client.
    - */
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, snac2->type, maxrooms, curexchange, exchanges);
    -
    - for (curexchange--; curexchange >= 0; curexchange--) {
    - g_free(exchanges[curexchange].name);
    - g_free(exchanges[curexchange].charset1);
    - g_free(exchanges[curexchange].lang1);
    - g_free(exchanges[curexchange].charset2);
    - g_free(exchanges[curexchange].lang2);
    - }
    - g_free(exchanges);
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -static int
    -parseinfo_create(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs, aim_snac_t *snac2)
    -{
    - aim_rxcallback_t userfunc;
    - GSList *tlvlist, *innerlist;
    - char *ck = NULL, *fqcn = NULL, *name = NULL;
    - guint16 exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0;
    - guint32 createtime = 0;
    - guint8 createperms = 0, detaillevel;
    - int cklen;
    - aim_tlv_t *bigblock;
    - int ret = 0;
    - ByteStream bbbs;
    -
    - tlvlist = aim_tlvlist_read(bs);
    -
    - if (!(bigblock = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
    - purple_debug_misc("oscar", "no bigblock in top tlv in create room response\n");
    - aim_tlvlist_free(tlvlist);
    - return 0;
    - }
    -
    - byte_stream_init(&bbbs, bigblock->value, bigblock->length);
    -
    - exchange = byte_stream_get16(&bbbs);
    - cklen = byte_stream_get8(&bbbs);
    - ck = byte_stream_getstr(&bbbs, cklen);
    - instance = byte_stream_get16(&bbbs);
    - detaillevel = byte_stream_get8(&bbbs);
    -
    - if (detaillevel != 0x02) {
    - purple_debug_misc("oscar", "unknown detaillevel in create room response (0x%02x)\n", detaillevel);
    - aim_tlvlist_free(tlvlist);
    - g_free(ck);
    - return 0;
    - }
    -
    - unknown = byte_stream_get16(&bbbs);
    -
    - innerlist = aim_tlvlist_read(&bbbs);
    -
    - if (aim_tlv_gettlv(innerlist, 0x006a, 1))
    - fqcn = aim_tlv_getstr(innerlist, 0x006a, 1);
    -
    - if (aim_tlv_gettlv(innerlist, 0x00c9, 1))
    - flags = aim_tlv_get16(innerlist, 0x00c9, 1);
    -
    - if (aim_tlv_gettlv(innerlist, 0x00ca, 1))
    - createtime = aim_tlv_get32(innerlist, 0x00ca, 1);
    -
    - if (aim_tlv_gettlv(innerlist, 0x00d1, 1))
    - maxmsglen = aim_tlv_get16(innerlist, 0x00d1, 1);
    -
    - if (aim_tlv_gettlv(innerlist, 0x00d2, 1))
    - maxoccupancy = aim_tlv_get16(innerlist, 0x00d2, 1);
    -
    - if (aim_tlv_gettlv(innerlist, 0x00d3, 1))
    - name = aim_tlv_getstr(innerlist, 0x00d3, 1);
    -
    - if (aim_tlv_gettlv(innerlist, 0x00d5, 1))
    - createperms = aim_tlv_get8(innerlist, 0x00d5, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) {
    - ret = userfunc(od, conn, frame, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck);
    - }
    -
    - g_free(ck);
    - g_free(name);
    - g_free(fqcn);
    - aim_tlvlist_free(innerlist);
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0009
    - *
    - * Since multiple things can trigger this callback, we must lookup the
    - * snacid to determine the original snac subtype that was called.
    - *
    - * XXX This isn't really how this works. But this is: Every d/9 response
    - * has a 16bit value at the beginning. That matches to:
    - * Short Desc = 1
    - * Full Desc = 2
    - * Instance Info = 4
    - * Nav Short Desc = 8
    - * Nav Instance Info = 16
    - * And then everything is really asynchronous. There is no specific
    - * attachment of a response to a create room request, for example. Creating
    - * the room yields no different a response than requesting the room's info.
    - *
    - */
    -static int
    -parseinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_snac_t *snac2;
    - int ret = 0;
    -
    - if (!(snac2 = aim_remsnac(od, snac->id))) {
    - purple_debug_misc("oscar", "faim: chatnav_parse_info: received response to unknown request! (%08x)\n", snac->id);
    - return 0;
    - }
    -
    - if (snac2->family != SNAC_FAMILY_CHATNAV) {
    - purple_debug_misc("oscar", "faim: chatnav_parse_info: received response that maps to corrupt request! (fam=%04x)\n", snac2->family);
    - g_free(snac2->data);
    - g_free(snac2);
    - return 0;
    - }
    -
    - /*
    - * We now know what the original SNAC subtype was.
    - */
    - if (snac2->type == 0x0002) /* request chat rights */
    - ret = parseinfo_perms(od, conn, mod, frame, snac, bs, snac2);
    - else if (snac2->type == 0x0003) /* request exchange info */
    - purple_debug_misc("oscar", "chatnav_parse_info: response to exchange info\n");
    - else if (snac2->type == 0x0004) /* request room info */
    - purple_debug_misc("oscar", "chatnav_parse_info: response to room info\n");
    - else if (snac2->type == 0x0005) /* request more room info */
    - purple_debug_misc("oscar", "chatnav_parse_info: response to more room info\n");
    - else if (snac2->type == 0x0006) /* request occupant list */
    - purple_debug_misc("oscar", "chatnav_parse_info: response to occupant info\n");
    - else if (snac2->type == 0x0007) /* search for a room */
    - purple_debug_misc("oscar", "chatnav_parse_info: search results\n");
    - else if (snac2->type == 0x0008) /* create room */
    - ret = parseinfo_create(od, conn, mod, frame, snac, bs, snac2);
    - else
    - purple_debug_misc("oscar", "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type);
    -
    - if (snac2)
    - g_free(snac2->data);
    - g_free(snac2);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return error(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0009)
    - return parseinfo(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -chatnav_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_CHATNAV;
    - mod->version = 0x0001;
    - mod->toolid = 0x0010;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "chatnav", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_feedbag.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1981 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0013 - Server-Side/Stored Information.
    - *
    - * Relatively new facility that allows certain types of information, such as
    - * a user's buddy list, permit/deny list, and permit/deny preferences, to be
    - * stored on the server, so that they can be accessed from any client.
    - *
    - * We keep 2 copies of SSI data:
    - * 1) An exact copy of what is stored on the AIM servers.
    - * 2) A local copy that we make changes to, and then send diffs
    - * between this and the exact copy to keep them in sync.
    - *
    - * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list
    - * that is given to them (i.e. they don't send SNACs).
    - *
    - * The SNAC sending and receiving functions are lower down in the file, and
    - * they're simpler. They are in the order of the subtypes they deal with,
    - * starting with the request rights function (subtype 0x0002), then parse
    - * rights (subtype 0x0003), then--well, you get the idea.
    - *
    - * This is entirely too complicated.
    - * You don't know the half of it.
    - *
    - */
    -
    -#include "oscar.h"
    -#include "debug.h"
    -
    -static int aim_ssi_addmoddel(OscarData *od);
    -
    -/**
    - * List types based on http://dev.aol.com/aim/oscar/#FEEDBAG (archive.org)
    - * and http://iserverd.khstu.ru/oscar/ssi_item.html
    - *
    - * @param type The type of a list item as integer number, as provided by an aim_ssi_item struct.
    - * @return Returns the name of the item type as a character string.
    - */
    -static const gchar*
    -aim_ssi_type_to_string(guint16 type)
    -{
    - struct TypeStringPair
    - {
    - guint16 type;
    - const gchar *string;
    - };
    - static const struct TypeStringPair type_strings[] = {
    - { 0x0000, "Buddy" },
    - { 0x0001, "Group" },
    - { 0x0002, "Permit/Visible" },
    - { 0x0003, "Deny/Invisible" },
    - { 0x0004, "PDInfo" },
    - { 0x0005, "PresencePrefs" },
    - { 0x0006, "Non-Buddy Info" },
    - { 0x0009, "ClientPrefs" },
    - { 0x000e, "ICQDeny/Ignore" },
    - { 0x0014, "Buddy Icon" },
    - { 0x0015, "Recent Buddies" },
    - { 0x0019, "Non-Buddy" },
    - { 0x001d, "Vanity Info" },
    - { 0x0020, "ICQ-MDir" },
    - { 0x0029, "Facebook" },
    - };
    - size_t i;
    - for (i = 0; i < G_N_ELEMENTS(type_strings); i++) {
    - if (type_strings[i].type == type) {
    - return type_strings[i].string;
    - }
    - }
    - return "unknown";
    -}
    -
    -/** For debug log output: Appends a line containing information about a given list item to a string.
    - *
    - * @param str String to which the line will be appended.
    - * @param prefix A string which will be prepended to the line.
    - * @param item List item from which information is extracted.
    - */
    -static void
    -aim_ssi_item_debug_append(GString *str, char *prefix, struct aim_ssi_item *item)
    -{
    - g_string_append_printf(str,
    - "%s gid=0x%04hx, bid=0x%04hx, list_type=0x%04hx [%s], name=%s.\n",
    - prefix, item->gid, item->bid, item->type, aim_ssi_type_to_string(item->type),
    - item->name ? item->name : "(null)");
    -}
    -
    -/**
    - * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
    - *
    - * @param list A pointer to a pointer to the current list of items.
    - * @param name A null terminated string containing the group name, or NULL
    - * if you want to modify the master group.
    - * @return Return a pointer to the modified item.
    - */
    -static void
    -aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
    -{
    - int newlen;
    - struct aim_ssi_item *cur, *group;
    -
    - /* Find the group */
    - if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP)))
    - return;
    -
    - /* Find the length for the new additional data */
    - newlen = 0;
    - if (group->gid == 0x0000) {
    - for (cur=list; cur; cur=cur->next)
    - if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
    - newlen += 2;
    - } else {
    - for (cur=list; cur; cur=cur->next)
    - if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
    - newlen += 2;
    - }
    -
    - /* Build the new TLV list */
    - if (newlen > 0) {
    - guint8 *newdata;
    -
    - newdata = (guint8 *)g_malloc((newlen)*sizeof(guint8));
    - newlen = 0;
    - if (group->gid == 0x0000) {
    - for (cur=list; cur; cur=cur->next)
    - if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
    - newlen += aimutil_put16(newdata+newlen, cur->gid);
    - } else {
    - for (cur=list; cur; cur=cur->next)
    - if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
    - newlen += aimutil_put16(newdata+newlen, cur->bid);
    - }
    - aim_tlvlist_replace_raw(&group->data, 0x00c8, newlen, newdata);
    -
    - g_free(newdata);
    - }
    -}
    -
    -/**
    - * Locally add a new item to the given item list.
    - *
    - * @param list A pointer to a pointer to the current list of items.
    - * @param name A null terminated string of the name of the new item, or NULL if the
    - * item should have no name.
    - * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something.
    - * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something.
    - * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc.
    - * @param data The additional data for the new item.
    - * @return A pointer to the newly created item.
    - */
    -static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, guint16 gid, guint16 bid, guint16 type, GSList *data)
    -{
    - gboolean exists;
    - struct aim_ssi_item *cur, *new;
    -
    - new = g_new(struct aim_ssi_item, 1);
    -
    - /* Set the name */
    - new->name = g_strdup(name);
    -
    - /* Set the group ID# and buddy ID# */
    - new->gid = gid;
    - new->bid = bid;
    - if (type == AIM_SSI_TYPE_GROUP) {
    - if ((new->gid == 0xFFFF) && name) {
    - do {
    - new->gid += 0x0001;
    - exists = FALSE;
    - for (cur = *list; cur != NULL; cur = cur->next)
    - if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid)) {
    - exists = TRUE;
    - break;
    - }
    - } while (exists);
    - }
    - } else if (new->gid == 0x0000) {
    - /*
    - * This is weird, but apparently items in the root group can't
    - * have a buddy ID equal to any group ID. You'll get error
    - * 0x0003 when trying to add, which is "item already exists"
    - */
    - if (new->bid == 0xFFFF) {
    - do {
    - new->bid += 0x0001;
    - exists = FALSE;
    - for (cur = *list; cur != NULL; cur = cur->next)
    - if (cur->bid == new->bid || cur->gid == new->bid) {
    - exists = TRUE;
    - break;
    - }
    - } while (exists);
    - }
    - } else {
    - if (new->bid == 0xFFFF) {
    - do {
    - new->bid += 0x0001;
    - exists = FALSE;
    - for (cur = *list; cur != NULL; cur = cur->next)
    - if (cur->bid == new->bid && cur->gid == new->gid) {
    - exists = TRUE;
    - break;
    - }
    - } while (exists);
    - }
    - }
    -
    - /* Set the type */
    - new->type = type;
    -
    - /* Set the TLV list */
    - new->data = aim_tlvlist_copy(data);
    -
    - /* Add the item to the list in the correct numerical position. Fancy, eh? */
    - if (*list) {
    - if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) {
    - new->next = *list;
    - *list = new;
    - } else {
    - struct aim_ssi_item *prev;
    - for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next);
    - new->next = prev->next;
    - prev->next = new;
    - }
    - } else {
    - new->next = *list;
    - *list = new;
    - }
    -
    - return new;
    -}
    -
    -/**
    - * Locally delete an item from the given item list.
    - *
    - * @param list A pointer to a pointer to the current list of items.
    - * @param del A pointer to the item you want to remove from the list.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del)
    -{
    - if (!(*list) || !del)
    - return -EINVAL;
    -
    - /* Remove the item from the list */
    - if (*list == del) {
    - *list = (*list)->next;
    - } else {
    - struct aim_ssi_item *cur;
    - for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next);
    - if (cur->next)
    - cur->next = del->next;
    - }
    -
    - /* Free the removed item */
    - g_free(del->name);
    - aim_tlvlist_free(del->data);
    - g_free(del);
    -
    - return 0;
    -}
    -
    -/**
    - * Compare two items to see if they have the same data.
    - *
    - * @param cur1 A pointer to a pointer to the first item.
    - * @param cur2 A pointer to a pointer to the second item.
    - * @return Return 0 if no differences, or a number if there are differences.
    - */
    -static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2)
    -{
    - if (!cur1 || !cur2)
    - return 1;
    -
    - if (cur1->data && !cur2->data)
    - return 2;
    -
    - if (!cur1->data && cur2->data)
    - return 3;
    -
    - if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data)))
    - return 4;
    -
    - if (cur1->name && !cur2->name)
    - return 5;
    -
    - if (!cur1->name && cur2->name)
    - return 6;
    -
    - if (cur1->name && cur2->name && oscar_util_name_compare(cur1->name, cur2->name))
    - return 7;
    -
    - if (cur1->gid != cur2->gid)
    - return 8;
    -
    - if (cur1->bid != cur2->bid)
    - return 9;
    -
    - if (cur1->type != cur2->type)
    - return 10;
    -
    - return 0;
    -}
    -
    -static gboolean aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item)
    -{
    - struct aim_ssi_item *cur;
    - for (cur=list; cur; cur=cur->next)
    - if (cur == item)
    - return TRUE;
    - return FALSE;
    -}
    -
    -/**
    - * Locally find an item given a group ID# and a buddy ID#.
    - *
    - * @param list A pointer to the current list of items.
    - * @param gid The group ID# of the desired item.
    - * @param bid The buddy ID# of the desired item.
    - * @return Return a pointer to the item if found, else return NULL;
    - */
    -struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid)
    -{
    - struct aim_ssi_item *cur;
    - for (cur=list; cur; cur=cur->next)
    - if ((cur->gid == gid) && (cur->bid == bid))
    - return cur;
    - return NULL;
    -}
    -
    -/**
    - * Locally find an item given a group name, buddy name, and type. If group name
    - * and buddy name are null, then just return the first item of the given type.
    - *
    - * @param list A pointer to the current list of items.
    - * @param gn The group name of the desired item.
    - * @param bn The buddy name of the desired item.
    - * @param type The type of the desired item.
    - * @return Return a pointer to the item if found, else return NULL.
    - */
    -struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *bn, guint16 type)
    -{
    - struct aim_ssi_item *cur;
    - if (!list)
    - return NULL;
    -
    - if (gn && bn) { /* For finding buddies in groups */
    - for (cur=list; cur; cur=cur->next)
    - if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) {
    - struct aim_ssi_item *curg;
    - for (curg=list; curg; curg=curg->next)
    - if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(oscar_util_name_compare(curg->name, gn)))
    - return cur;
    - }
    -
    - } else if (gn) { /* For finding groups */
    - for (cur=list; cur; cur=cur->next) {
    - if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(oscar_util_name_compare(cur->name, gn))) {
    - return cur;
    - }
    - }
    -
    - } else if (bn) { /* For finding permits, denies, and ignores */
    - for (cur=list; cur; cur=cur->next) {
    - if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) {
    - return cur;
    - }
    - }
    -
    - /* For stuff without names--permit deny setting, visibility mask, etc. */
    - } else for (cur=list; cur; cur=cur->next) {
    - if ((cur->type == type) && (!cur->name))
    - return cur;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * Check if the given buddy exists in any group in the buddy list.
    - *
    - * @param list A pointer to the current list of items.
    - * @param bn The group name of the desired item.
    - * @return Return a pointer to the name of the item if found, else return NULL;
    - */
    -struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *bn)
    -{
    - if (!bn)
    - return NULL;
    - return aim_ssi_itemlist_finditem(list, NULL, bn, AIM_SSI_TYPE_BUDDY);
    -}
    -
    -/**
    - * Locally find the parent item of the given buddy name.
    - *
    - * @param list A pointer to the current list of items.
    - * @param bn The buddy name of the desired item.
    - * @return Return a pointer to the name of the item if found, else return NULL;
    - */
    -char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *bn)
    -{
    - struct aim_ssi_item *cur, *curg;
    - if (!list || !bn)
    - return NULL;
    - if (!(cur = aim_ssi_itemlist_exists(list, bn)))
    - return NULL;
    - if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
    - return NULL;
    - return curg->name;
    -}
    -
    -/**
    - * Locally find the permit/deny setting item, and return the setting.
    - *
    - * @param list A pointer to the current list of items.
    - * @return Return the current SSI permit deny setting, or 0 if no setting was found.
    - */
    -int aim_ssi_getpermdeny(struct aim_ssi_item *list)
    -{
    - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
    - if (cur) {
    - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00ca, 1);
    - if (tlv && tlv->value)
    - return aimutil_get8(tlv->value);
    - }
    - return 0;
    -}
    -
    -/**
    - * Locally find the presence flag item, and return the setting. The returned setting is a
    - * bitmask of the preferences. See the AIM_SSI_PRESENCE_FLAG_* #defines in oscar.h.
    - *
    - * @param list A pointer to the current list of items.
    - * @return Return the current set of preferences.
    - */
    -guint32 aim_ssi_getpresence(struct aim_ssi_item *list)
    -{
    - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
    - if (cur) {
    - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c9, 1);
    - if (tlv && tlv->length)
    - return aimutil_get32(tlv->value);
    - }
    - return 0xFFFFFFFF;
    -}
    -
    -/**
    - * Locally find the alias of the given buddy.
    - *
    - * @param list A pointer to the current list of items.
    - * @param gn The group of the buddy.
    - * @param bn The name of the buddy.
    - * @return A pointer to a NULL terminated string that is the buddy's
    - * alias, or NULL if the buddy has no alias. You should free
    - * this returned value!
    - */
    -char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *bn)
    -{
    - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
    - if (cur) {
    - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x0131, 1);
    - if (tlv && tlv->length)
    - return g_strndup((const gchar *)tlv->value, tlv->length);
    - }
    - return NULL;
    -}
    -
    -/**
    - * Locally find the comment of the given buddy.
    - *
    - * @param list A pointer to the current list of items.
    - * @param gn The group of the buddy.
    - * @param bn The name of the buddy.
    - * @return A pointer to a NULL terminated string that is the buddy's
    - * comment, or NULL if the buddy has no comment. You should free
    - * this returned value!
    - */
    -char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *bn)
    -{
    - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
    - if (cur) {
    - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1);
    - if (tlv && tlv->length) {
    - return g_strndup((const gchar *)tlv->value, tlv->length);
    - }
    - }
    - return NULL;
    -}
    -
    -/**
    - * Locally find if you are waiting for authorization for a buddy.
    - *
    - * @param list A pointer to the current list of items.
    - * @param gn The group of the buddy.
    - * @param bn The name of the buddy.
    - * @return 1 if you are waiting for authorization; 0 if you are not
    - */
    -gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *bn)
    -{
    - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
    - if (cur) {
    - if (aim_tlv_gettlv(cur->data, 0x0066, 1))
    - return TRUE;
    - }
    - return FALSE;
    -}
    -
    -/**
    - * If there are changes, then create temporary items and
    - * call addmoddel.
    - *
    - * @param od The oscar session.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -static int aim_ssi_sync(OscarData *od)
    -{
    - struct aim_ssi_item *cur1, *cur2;
    - struct aim_ssi_tmp *cur, *new;
    - int n = 0;
    - GString *debugstr = g_string_new("");
    -
    - /*
    - * The variable "n" is used to limit the number of addmoddel's that
    - * are performed in a single SNAC. It will hopefully keep the size
    - * of the SNAC below the maximum SNAC size.
    - */
    -
    - if (!od)
    - return -EINVAL;
    -
    - /* If we're waiting for an ack, we shouldn't do anything else */
    - if (od->ssi.waiting_for_ack)
    - return 0;
    -
    - /*
    - * Compare the 2 lists and create an aim_ssi_tmp for each difference.
    - * We should only send either additions, modifications, or deletions
    - * before waiting for an acknowledgement. So first do deletions, then
    - * additions, then modifications. Also, both the official and the local
    - * list should be in ascending numerical order for the group ID#s and the
    - * buddy ID#s, which makes things more efficient. I think.
    - */
    -
    - /* Deletions */
    - if (!od->ssi.pending) {
    - for (cur1=od->ssi.official; cur1 && (n < 15); cur1=cur1->next) {
    - if (!aim_ssi_itemlist_find(od->ssi.local, cur1->gid, cur1->bid)) {
    - n++;
    - new = g_new(struct aim_ssi_tmp, 1);
    - new->action = SNAC_SUBTYPE_FEEDBAG_DEL;
    - new->ack = 0xffff;
    - new->name = NULL;
    - new->item = cur1;
    - new->next = NULL;
    - if (od->ssi.pending) {
    - for (cur=od->ssi.pending; cur->next; cur=cur->next);
    - cur->next = new;
    - } else
    - od->ssi.pending = new;
    - aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1);
    - }
    - }
    - }
    -
    - /* Additions */
    - if (!od->ssi.pending) {
    - for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) {
    - if (!aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid)) {
    - n++;
    - new = g_new(struct aim_ssi_tmp, 1);
    - new->action = SNAC_SUBTYPE_FEEDBAG_ADD;
    - new->ack = 0xffff;
    - new->name = NULL;
    - new->item = cur1;
    - new->next = NULL;
    - if (od->ssi.pending) {
    - for (cur=od->ssi.pending; cur->next; cur=cur->next);
    - cur->next = new;
    - } else
    - od->ssi.pending = new;
    - aim_ssi_item_debug_append(debugstr, "Adding item ", cur1);
    - }
    - }
    - }
    -
    - /* Modifications */
    - if (!od->ssi.pending) {
    - for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) {
    - cur2 = aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid);
    - if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
    - n++;
    - new = g_new(struct aim_ssi_tmp, 1);
    - new->action = SNAC_SUBTYPE_FEEDBAG_MOD;
    - new->ack = 0xffff;
    - new->name = NULL;
    - new->item = cur1;
    - new->next = NULL;
    - if (od->ssi.pending) {
    - for (cur=od->ssi.pending; cur->next; cur=cur->next);
    - cur->next = new;
    - } else
    - od->ssi.pending = new;
    - aim_ssi_item_debug_append(debugstr, "Modifying item ", cur1);
    - }
    - }
    - }
    - if (debugstr->len > 0) {
    - purple_debug_info("oscar", "%s", debugstr->str);
    - if (purple_debug_is_verbose()) {
    - g_string_truncate(debugstr, 0);
    - for (cur1 = od->ssi.local; cur1; cur1 = cur1->next)
    - aim_ssi_item_debug_append(debugstr, "\t", cur1);
    - purple_debug_misc("oscar", "Dumping item list of account %s:\n%s",
    - purple_connection_get_account(od->gc)->username, debugstr->str);
    - }
    - }
    - g_string_free(debugstr, TRUE);
    -
    - /* We're out of stuff to do, so tell the AIM servers we're done and exit */
    - if (!od->ssi.pending) {
    - if (od->ssi.in_transaction) {
    - aim_ssi_modend(od);
    - od->ssi.in_transaction = FALSE;
    - }
    - return 0;
    - }
    -
    - /* If this is the first in a series of add/mod/del
    - * requests then send the "begin transaction" message. */
    - if (!od->ssi.in_transaction)
    - {
    - aim_ssi_modbegin(od);
    - od->ssi.in_transaction = TRUE;
    - }
    -
    - /* Make sure we don't send anything else between now
    - * and when we receive the ack for the following operation */
    - od->ssi.waiting_for_ack = TRUE;
    -
    - /* Now go mail off our data and wait 4 to 6 weeks */
    - return aim_ssi_addmoddel(od);;
    -}
    -
    -/**
    - * Free all SSI data.
    - *
    - * This doesn't remove it from the server, that's different.
    - *
    - * @param od The oscar odion.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -static void
    -aim_ssi_freelist(OscarData *od)
    -{
    - struct aim_ssi_item *cur, *del;
    - struct aim_ssi_tmp *curtmp, *deltmp;
    -
    - cur = od->ssi.official;
    - while (cur) {
    - del = cur;
    - cur = cur->next;
    - g_free(del->name);
    - aim_tlvlist_free(del->data);
    - g_free(del);
    - }
    -
    - cur = od->ssi.local;
    - while (cur) {
    - del = cur;
    - cur = cur->next;
    - g_free(del->name);
    - aim_tlvlist_free(del->data);
    - g_free(del);
    - }
    -
    - curtmp = od->ssi.pending;
    - while (curtmp) {
    - deltmp = curtmp;
    - curtmp = curtmp->next;
    - g_free(deltmp);
    - }
    -
    - od->ssi.numitems = 0;
    - od->ssi.official = NULL;
    - od->ssi.local = NULL;
    - od->ssi.pending = NULL;
    - od->ssi.timestamp = (time_t)0;
    -}
    -
    -/**
    - * This "cleans" the ssi list. It does the following:
    - * 1) Makes sure all buddies, permits, and denies have names.
    - * 2) Makes sure that all buddies are in a group that exist.
    - * 3) Deletes any empty groups
    - *
    - * @param od The oscar odion.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_cleanlist(OscarData *od)
    -{
    - struct aim_ssi_item *cur, *next;
    -
    - if (!od)
    - return -EINVAL;
    -
    - /* Delete any buddies, permits, or denies with empty names. */
    - /* If there are any buddies directly in the master group, add them to a real group. */
    - /* DESTROY any buddies that are directly in the master group. */
    - /* Do the same for buddies that are in a non-existant group. */
    - /* This will kind of mess up if you hit the item limit, but this function isn't too critical */
    - cur = od->ssi.local;
    - while (cur) {
    - next = cur->next;
    - if (!cur->name) {
    - if (cur->type == AIM_SSI_TYPE_BUDDY)
    - aim_ssi_delbuddy(od, NULL, NULL);
    - else if (cur->type == AIM_SSI_TYPE_PERMIT || cur->type == AIM_SSI_TYPE_DENY || cur->type == AIM_SSI_TYPE_ICQDENY)
    - aim_ssi_del_from_private_list(od, NULL, cur->type);
    - } else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(od->ssi.local, cur->gid, 0x0000)))) {
    - char *alias = aim_ssi_getalias(od->ssi.local, NULL, cur->name);
    - aim_ssi_addbuddy(od, cur->name, "orphans", NULL, alias, NULL, NULL, FALSE);
    - aim_ssi_delbuddy(od, cur->name, NULL);
    - g_free(alias);
    - }
    - cur = next;
    - }
    -
    - /* Make sure there aren't any duplicate buddies in a group, or duplicate permits or denies */
    - cur = od->ssi.local;
    - while (cur) {
    - if ((cur->type == AIM_SSI_TYPE_BUDDY) || (cur->type == AIM_SSI_TYPE_PERMIT) || (cur->type == AIM_SSI_TYPE_DENY))
    - {
    - struct aim_ssi_item *cur2, *next2;
    - cur2 = cur->next;
    - while (cur2) {
    - next2 = cur2->next;
    - if ((cur->type == cur2->type) && (cur->gid == cur2->gid) && (cur->name != NULL) && (cur2->name != NULL) && (!oscar_util_name_compare(cur->name, cur2->name))) {
    - aim_ssi_itemlist_del(&od->ssi.local, cur2);
    - }
    - cur2 = next2;
    - }
    - }
    - cur = cur->next;
    - }
    -
    - /* If we've made any changes then sync our list with the server's */
    - return aim_ssi_sync(od);
    -}
    -
    -/**
    - * Add a buddy to the list.
    - *
    - * @param od The oscar odion.
    - * @param name The name of the item.
    - * @param group The group of the item.
    - * @param data A TLV list to use as the additional data for this item.
    - * @param alias The alias/nickname of the item, or NULL.
    - * @param comment The buddy comment for the item, or NULL.
    - * @param smsnum The locally assigned SMS number, or NULL.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *data, const char *alias, const char *comment, const char *smsnum, gboolean needauth)
    -{
    - struct aim_ssi_item *parent;
    -
    - if (!od || !name || !group)
    - return -EINVAL;
    -
    - /* Find the parent */
    - if (!(parent = aim_ssi_itemlist_finditem(od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) {
    - /* Find the parent's parent (the master group) */
    - if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
    - aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
    -
    - /* Add the parent */
    - parent = aim_ssi_itemlist_add(&od->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
    -
    - /* Modify the parent's parent (the master group) */
    - aim_ssi_itemlist_rebuildgroup(od->ssi.local, NULL);
    - }
    -
    - /* Create a TLV list for the new buddy */
    - if (needauth)
    - aim_tlvlist_add_noval(&data, 0x0066);
    - if (alias != NULL)
    - aim_tlvlist_add_str(&data, 0x0131, alias);
    - if (smsnum != NULL)
    - aim_tlvlist_add_str(&data, 0x013a, smsnum);
    - if (comment != NULL)
    - aim_tlvlist_add_str(&data, 0x013c, comment);
    -
    - /* Add that bad boy */
    - aim_ssi_itemlist_add(&od->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data);
    - aim_tlvlist_free(data);
    -
    - /* Modify the parent group */
    - aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
    -
    - /* Sync our local list with the server list */
    - return aim_ssi_sync(od);
    -}
    -
    -int
    -aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type)
    -{
    - if (!od || !name || !od->ssi.received_data)
    - return -EINVAL;
    -
    - if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
    - aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
    -
    - aim_ssi_itemlist_add(&od->ssi.local, name, 0x0000, 0xFFFF, list_type, NULL);
    - return aim_ssi_sync(od);
    -}
    -
    -int
    -aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type)
    -{
    - struct aim_ssi_item *del;
    -
    - if (!od)
    - return -EINVAL;
    -
    - if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, NULL, name, list_type)))
    - return -EINVAL;
    -
    - aim_ssi_itemlist_del(&od->ssi.local, del);
    - return aim_ssi_sync(od);
    -}
    -
    -/**
    - * Deletes a buddy from the list.
    - *
    - * @param od The oscar odion.
    - * @param name The name of the item, or NULL.
    - * @param group The group of the item, or NULL.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_delbuddy(OscarData *od, const char *name, const char *group)
    -{
    - struct aim_ssi_item *del;
    -
    - if (!od)
    - return -EINVAL;
    -
    - /* Find the buddy */
    - if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
    - return -EINVAL;
    -
    - /* Remove the item from the list */
    - aim_ssi_itemlist_del(&od->ssi.local, del);
    -
    - /* Modify the parent group */
    - aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
    -
    - /* Sync our local list with the server list */
    - return aim_ssi_sync(od);
    -}
    -
    -/**
    - * Deletes a group from the list.
    - *
    - * @param od The oscar odion.
    - * @param group The name of the group.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_delgroup(OscarData *od, const char *group)
    -{
    - struct aim_ssi_item *del;
    - aim_tlv_t *tlv;
    -
    - if (!od)
    - return -EINVAL;
    -
    - /* Find the group */
    - if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)))
    - return -EINVAL;
    -
    - /* Don't delete the group if it's not empty */
    - tlv = aim_tlv_gettlv(del->data, 0x00c8, 1);
    - if (tlv && tlv->length > 0)
    - return -EINVAL;
    -
    - /* Remove the item from the list */
    - aim_ssi_itemlist_del(&od->ssi.local, del);
    -
    - /* Modify the parent group */
    - aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
    -
    - /* Sync our local list with the server list */
    - return aim_ssi_sync(od);
    -}
    -
    -/**
    - * Move a buddy from one group to another group. This basically just deletes the
    - * buddy and re-adds it.
    - *
    - * @param od The oscar odion.
    - * @param oldgn The group that the buddy is currently in.
    - * @param newgn The group that the buddy should be moved in to.
    - * @param bn The name of the buddy to be moved.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *bn)
    -{
    - struct aim_ssi_item *buddy;
    - GSList *data;
    -
    - /* Find the buddy */
    - buddy = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, bn, AIM_SSI_TYPE_BUDDY);
    - if (buddy == NULL)
    - return -EINVAL;
    -
    - /* Make a copy of the buddy's TLV list */
    - data = aim_tlvlist_copy(buddy->data);
    -
    - /* Delete the old item */
    - aim_ssi_delbuddy(od, bn, oldgn);
    -
    - /* Add the new item using the EXACT SAME TLV list */
    - aim_ssi_addbuddy(od, bn, newgn, data, NULL, NULL, NULL, FALSE);
    -
    - return 0;
    -}
    -
    -/**
    - * Change the alias stored on the server for a given buddy.
    - *
    - * @param od The oscar odion.
    - * @param gn The group that the buddy is currently in.
    - * @param bn The name of the buddy.
    - * @param alias The new alias for the buddy, or NULL if you want to remove
    - * a buddy's comment.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias)
    -{
    - struct aim_ssi_item *tmp;
    -
    - if (!od || !gn || !bn)
    - return -EINVAL;
    -
    - if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
    - return -EINVAL;
    -
    - /* Either add or remove the 0x0131 TLV from the TLV chain */
    - if ((alias != NULL) && (strlen(alias) > 0))
    - aim_tlvlist_replace_str(&tmp->data, 0x0131, alias);
    - else
    - aim_tlvlist_remove(&tmp->data, 0x0131);
    -
    - /* Sync our local list with the server list */
    - return aim_ssi_sync(od);
    -}
    -
    -/**
    - * Change the comment stored on the server for a given buddy.
    - *
    - * @param od The oscar odion.
    - * @param gn The group that the buddy is currently in.
    - * @param bn The name of the buddy.
    - * @param alias The new comment for the buddy, or NULL if you want to remove
    - * a buddy's comment.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *comment)
    -{
    - struct aim_ssi_item *tmp;
    -
    - if (!od || !gn || !bn)
    - return -EINVAL;
    -
    - if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
    - return -EINVAL;
    -
    - /* Either add or remove the 0x0131 TLV from the TLV chain */
    - if ((comment != NULL) && (strlen(comment) > 0))
    - aim_tlvlist_replace_str(&tmp->data, 0x013c, comment);
    - else
    - aim_tlvlist_remove(&tmp->data, 0x013c);
    -
    - /* Sync our local list with the server list */
    - return aim_ssi_sync(od);
    -}
    -
    -/**
    - * Rename a group.
    - *
    - * @param od The oscar odion.
    - * @param oldgn The old group name.
    - * @param newgn The new group name.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn)
    -{
    - struct aim_ssi_item *group;
    -
    - if (!od || !oldgn || !newgn)
    - return -EINVAL;
    -
    - if (!(group = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
    - return -EINVAL;
    -
    - g_free(group->name);
    - group->name = g_strdup(newgn);
    -
    - /* Sync our local list with the server list */
    - return aim_ssi_sync(od);
    -}
    -
    -/**
    - * Stores your permit/deny setting on the server, and starts using it.
    - *
    - * @param od The oscar odion.
    - * @param permdeny Your permit/deny setting. For ICQ accounts, it actually affects your visibility
    - * and has nothing to do with blocking. Can be one of the following:
    - * 1 - Allow all users
    - * 2 - Block all users
    - * 3 - Allow only the users below
    - * 4 - Block only the users below
    - * 5 - Allow only users on my buddy list
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny)
    -{
    - struct aim_ssi_item *tmp;
    -
    - if (!od || !od->ssi.received_data)
    - return -EINVAL;
    -
    - /* Find the PDINFO item, or add it if it does not exist */
    - if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) {
    - /* Make sure the master group exists */
    - if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
    - aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
    -
    - tmp = aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL);
    - }
    -
    - /* Need to add the 0x00ca TLV to the TLV chain */
    - aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny);
    -
    - /* Sync our local list with the server list */
    - return aim_ssi_sync(od);
    -}
    -
    -/**
    - * Set buddy icon information
    - *
    - * @param od The oscar odion.
    - * @param iconcsum The MD5 checksum of the icon you are using.
    - * @param iconcsumlen Length of the MD5 checksum given above. Should be 0x10 bytes.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_seticon(OscarData *od, const guint8 *iconsum, guint8 iconsumlen)
    -{
    - struct aim_ssi_item *tmp;
    - guint8 *csumdata;
    -
    - if (!od || !iconsum || !iconsumlen || !od->ssi.received_data)
    - return -EINVAL;
    -
    - /* Find the ICONINFO item, or add it if it does not exist */
    - if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
    - /* Make sure the master group exists */
    - if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
    - aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
    -
    - tmp = aim_ssi_itemlist_add(&od->ssi.local, "1", 0x0000, 0xFFFF, AIM_SSI_TYPE_ICONINFO, NULL);
    - }
    -
    - /* Need to add the 0x00d5 TLV to the TLV chain */
    - csumdata = (guint8 *)g_malloc((iconsumlen+2)*sizeof(guint8));
    - (void)aimutil_put8(&csumdata[0], 0x00);
    - (void)aimutil_put8(&csumdata[1], iconsumlen);
    - memcpy(&csumdata[2], iconsum, iconsumlen);
    - aim_tlvlist_replace_raw(&tmp->data, 0x00d5, (iconsumlen+2) * sizeof(guint8), csumdata);
    - g_free(csumdata);
    -
    - /* Need to add the 0x0131 TLV to the TLV chain, used to cache the icon */
    - aim_tlvlist_replace_noval(&tmp->data, 0x0131);
    -
    - /* Sync our local list with the server list */
    - aim_ssi_sync(od);
    - return 0;
    -}
    -
    -/**
    - * Remove a reference to a server stored buddy icon. This will make your
    - * icon stop showing up to other people.
    - *
    - * Really this function just sets the icon to a dummy value. It's weird...
    - * but I think the dummy value basically means "I don't have an icon!"
    - *
    - * @param od The oscar session.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_delicon(OscarData *od)
    -{
    - const guint8 csumdata[] = {0x02, 0x01, 0xd2, 0x04, 0x72};
    -
    - return aim_ssi_seticon(od, csumdata, 5);
    -}
    -
    -/**
    - * Stores your setting for various SSI settings. Whether you
    - * should show up as idle or not, etc.
    - *
    - * @param od The oscar odion.
    - * @param presence A bitmask of the first 32 entries [0-31] from
    - * http://dev.aol.com/aim/oscar/#FEEDBAG__BUDDY_PREFS
    - * 0x00000002 - Hide "eBuddy group" (whatever that is)
    - * 0x00000400 - Allow others to see your idle time
    - * 0x00020000 - Don't show Recent Buddies
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_ssi_setpresence(OscarData *od, guint32 presence) {
    - struct aim_ssi_item *tmp;
    -
    - if (!od || !od->ssi.received_data)
    - return -EINVAL;
    -
    - /* Find the PRESENCEPREFS item, or add it if it does not exist */
    - if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) {
    - /* Make sure the master group exists */
    - if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
    - aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
    -
    - tmp = aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL);
    - }
    -
    - /* Need to add the x00c9 TLV to the TLV chain */
    - aim_tlvlist_replace_32(&tmp->data, 0x00c9, presence);
    -
    - /* Sync our local list with the server list */
    - return aim_ssi_sync(od);
    -}
    -
    -/*
    - * Subtype 0x0002 - Request SSI Rights.
    - */
    -int aim_ssi_reqrights(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
    - return -EINVAL;
    -
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQRIGHTS);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0003 - SSI Rights Information.
    - */
    -static int parserights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0, i;
    - aim_rxcallback_t userfunc;
    - GSList *tlvlist;
    - aim_tlv_t *tlv;
    - ByteStream bstream;
    - guint16 *maxitems;
    -
    - /* This SNAC is made up of a bunch of TLVs */
    - tlvlist = aim_tlvlist_read(bs);
    -
    - /* TLV 0x0004 contains the maximum number of each item */
    - if (!(tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
    - aim_tlvlist_free(tlvlist);
    - return 0;
    - }
    -
    - byte_stream_init(&bstream, tlv->value, tlv->length);
    -
    - maxitems = (guint16 *)g_malloc((tlv->length/2)*sizeof(guint16));
    -
    - for (i=0; i<(tlv->length/2); i++)
    - maxitems[i] = byte_stream_get16(&bstream);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, tlv->length/2, maxitems);
    -
    - aim_tlvlist_free(tlvlist);
    - g_free(maxitems);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and
    - * revision number.
    - *
    - */
    -int aim_ssi_reqdata(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
    - return -EINVAL;
    -
    - /* Free any current data, just in case */
    - aim_ssi_freelist(od);
    -
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQDATA);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0006 - SSI Data.
    - */
    -static int parsedata(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint8 fmtver; /* guess */
    - guint16 namelen, gid, bid, type;
    - char *name;
    - GSList *data;
    - GString *debugstr = g_string_new("");
    -
    - fmtver = byte_stream_get8(bs); /* Version of ssi data. Should be 0x00 */
    - od->ssi.numitems += byte_stream_get16(bs); /* # of items in this SSI SNAC */
    -
    - /* Read in the list */
    - while (byte_stream_bytes_left(bs) > 4) { /* last four bytes are timestamp */
    - if ((namelen = byte_stream_get16(bs)))
    - name = byte_stream_getstr(bs, namelen);
    - else
    - name = NULL;
    - gid = byte_stream_get16(bs);
    - bid = byte_stream_get16(bs);
    - type = byte_stream_get16(bs);
    - data = aim_tlvlist_readlen(bs, byte_stream_get16(bs));
    - aim_ssi_item_debug_append(debugstr, "\t", aim_ssi_itemlist_add(&od->ssi.official, name, gid, bid, type, data));
    - g_free(name);
    - aim_tlvlist_free(data);
    - }
    - purple_debug_misc("oscar", "Reading items from tlvlist for account %s:\n%s",
    - purple_connection_get_account(od->gc)->username, debugstr->str);
    - g_string_free(debugstr, TRUE);
    -
    - /* Read in the timestamp */
    - od->ssi.timestamp = byte_stream_get32(bs);
    -
    - if (!(snac->flags & 0x0001)) {
    - /* Make a copy of the list */
    - struct aim_ssi_item *cur;
    - for (cur=od->ssi.official; cur; cur=cur->next)
    - aim_ssi_itemlist_add(&od->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);
    -
    - od->ssi.received_data = TRUE;
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, fmtver, od->ssi.numitems, od->ssi.timestamp);
    - }
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0007 - SSI Activate Data.
    - *
    - * Should be sent after receiving 13/6 or 13/f to tell the server you
    - * are ready to begin using the list. It will promptly give you the
    - * presence information for everyone in your list and put your permit/deny
    - * settings into effect.
    - *
    - */
    -int aim_ssi_enable(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
    - return -EINVAL;
    -
    - aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, 0x0007);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s).
    - *
    - * Sends the SNAC to add, modify, or delete items from the server-stored
    - * information. These 3 SNACs all have an identical structure. The only
    - * difference is the subtype that is set for the SNAC.
    - *
    - */
    -static int aim_ssi_addmoddel(OscarData *od)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen;
    - struct aim_ssi_tmp *cur;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !od->ssi.pending || !od->ssi.pending->item)
    - return -EINVAL;
    -
    - /* Calculate total SNAC size */
    - bslen = 0;
    - for (cur=od->ssi.pending; cur; cur=cur->next) {
    - bslen += 10; /* For length, GID, BID, type, and length */
    - if (cur->item->name)
    - bslen += strlen(cur->item->name);
    - if (cur->item->data)
    - bslen += aim_tlvlist_size(cur->item->data);
    - }
    -
    - byte_stream_new(&bs, bslen);
    -
    - for (cur=od->ssi.pending; cur; cur=cur->next) {
    - byte_stream_put16(&bs, cur->item->name ? strlen(cur->item->name) : 0);
    - if (cur->item->name)
    - byte_stream_putstr(&bs, cur->item->name);
    - byte_stream_put16(&bs, cur->item->gid);
    - byte_stream_put16(&bs, cur->item->bid);
    - byte_stream_put16(&bs, cur->item->type);
    - byte_stream_put16(&bs, cur->item->data ? aim_tlvlist_size(cur->item->data) : 0);
    - if (cur->item->data)
    - aim_tlvlist_write(&bs, &cur->item->data);
    - }
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0008 - Incoming SSI add.
    - *
    - * Sent by the server, for example, when someone is added to
    - * your "Recent Buddies" group.
    - */
    -static int parseadd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - char *name;
    - guint16 len, gid, bid, type;
    - GSList *data;
    -
    - while (byte_stream_bytes_left(bs)) {
    - if ((len = byte_stream_get16(bs)))
    - name = byte_stream_getstr(bs, len);
    - else
    - name = NULL;
    - gid = byte_stream_get16(bs);
    - bid = byte_stream_get16(bs);
    - type = byte_stream_get16(bs);
    - if ((len = byte_stream_get16(bs)))
    - data = aim_tlvlist_readlen(bs, len);
    - else
    - data = NULL;
    -
    - aim_ssi_itemlist_add(&od->ssi.local, name, gid, bid, type, data);
    - aim_ssi_itemlist_add(&od->ssi.official, name, gid, bid, type, data);
    - aim_tlvlist_free(data);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, snac->subtype, type, name);
    -
    - g_free(name);
    - }
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0009 - Incoming SSI mod.
    - */
    -static int parsemod(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - char *name;
    - guint16 len, gid, bid, type;
    - GSList *data;
    - struct aim_ssi_item *item;
    -
    - while (byte_stream_bytes_left(bs)) {
    - if ((len = byte_stream_get16(bs)))
    - name = byte_stream_getstr(bs, len);
    - else
    - name = NULL;
    - gid = byte_stream_get16(bs);
    - bid = byte_stream_get16(bs);
    - type = byte_stream_get16(bs);
    - if ((len = byte_stream_get16(bs)))
    - data = aim_tlvlist_readlen(bs, len);
    - else
    - data = NULL;
    -
    - /* Replace the 2 local items with the given one */
    - if ((item = aim_ssi_itemlist_find(od->ssi.local, gid, bid))) {
    - item->type = type;
    - g_free(item->name);
    - item->name = g_strdup(name);
    - aim_tlvlist_free(item->data);
    - item->data = aim_tlvlist_copy(data);
    - }
    -
    - if ((item = aim_ssi_itemlist_find(od->ssi.official, gid, bid))) {
    - item->type = type;
    - g_free(item->name);
    - item->name = g_strdup(name);
    - aim_tlvlist_free(item->data);
    - item->data = aim_tlvlist_copy(data);
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, snac->subtype, type, name);
    -
    - g_free(name);
    - aim_tlvlist_free(data);
    - }
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x000a - Incoming SSI del.
    - *
    - * XXX - It would probably be good for the client to actually do something when it gets this.
    - */
    -static int parsedel(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 gid, bid;
    - struct aim_ssi_item *del;
    -
    - while (byte_stream_bytes_left(bs)) {
    - byte_stream_advance(bs, byte_stream_get16(bs));
    - gid = byte_stream_get16(bs);
    - bid = byte_stream_get16(bs);
    - byte_stream_get16(bs);
    - byte_stream_advance(bs, byte_stream_get16(bs));
    -
    - if ((del = aim_ssi_itemlist_find(od->ssi.local, gid, bid)))
    - aim_ssi_itemlist_del(&od->ssi.local, del);
    - if ((del = aim_ssi_itemlist_find(od->ssi.official, gid, bid)))
    - aim_ssi_itemlist_del(&od->ssi.official, del);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame);
    - }
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x000e - SSI Add/Mod/Del Ack.
    - *
    - * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
    - *
    - */
    -static int parseack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - struct aim_ssi_tmp *cur, *del;
    -
    - /* Read in the success/failure flags from the ack SNAC */
    - cur = od->ssi.pending;
    - while (cur && (byte_stream_bytes_left(bs)>0)) {
    - cur->ack = byte_stream_get16(bs);
    - cur = cur->next;
    - }
    -
    - /*
    - * If outcome is 0, then add the item to the item list, or replace the other item,
    - * or remove the old item. If outcome is non-zero, then remove the item from the
    - * local list, or unmodify it, or add it.
    - */
    - for (cur=od->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) {
    - if (cur->item) {
    - if (cur->ack) {
    - /* Our action was unsuccessful, so change the local list back to how it was */
    - if (cur->action == SNAC_SUBTYPE_FEEDBAG_ADD) {
    - /* Remove the item from the local list */
    - /* Make sure cur->item is still valid memory */
    - if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
    - cur->name = g_strdup(cur->item->name);
    - aim_ssi_itemlist_del(&od->ssi.local, cur->item);
    - }
    - cur->item = NULL;
    -
    - } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_MOD) {
    - /* Replace the local item with the item from the official list */
    - if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
    - struct aim_ssi_item *cur1;
    - if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
    - g_free(cur->item->name);
    - cur->item->name = g_strdup(cur1->name);
    - aim_tlvlist_free(cur->item->data);
    - cur->item->data = aim_tlvlist_copy(cur1->data);
    - }
    - } else
    - cur->item = NULL;
    -
    - } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_DEL) {
    - /* Add the item back into the local list */
    - if (aim_ssi_itemlist_valid(od->ssi.official, cur->item)) {
    - aim_ssi_itemlist_add(&od->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
    - } else
    - cur->item = NULL;
    - }
    -
    - } else {
    - /* Do the exact opposite */
    - if (cur->action == SNAC_SUBTYPE_FEEDBAG_ADD) {
    - /* Add the local item to the official list */
    - if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
    - aim_ssi_itemlist_add(&od->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
    - } else
    - cur->item = NULL;
    -
    - } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_MOD) {
    - /* Replace the official item with the item from the local list */
    - if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
    - struct aim_ssi_item *cur1;
    - if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
    - g_free(cur1->name);
    - cur1->name = g_strdup(cur->item->name);
    - aim_tlvlist_free(cur1->data);
    - cur1->data = aim_tlvlist_copy(cur->item->data);
    - }
    - } else
    - cur->item = NULL;
    -
    - } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_DEL) {
    - /* Remove the item from the official list */
    - if (aim_ssi_itemlist_valid(od->ssi.official, cur->item))
    - aim_ssi_itemlist_del(&od->ssi.official, cur->item);
    - cur->item = NULL;
    - }
    -
    - }
    - } /* End if (cur->item) */
    - } /* End for loop */
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, od->ssi.pending);
    -
    - /* Free all aim_ssi_tmp's with an outcome */
    - cur = od->ssi.pending;
    - while (cur && (cur->ack != 0xffff)) {
    - del = cur;
    - cur = cur->next;
    - g_free(del->name);
    - g_free(del);
    - }
    - od->ssi.pending = cur;
    -
    - /* If we're not waiting for any more acks, then send more SNACs */
    - if (!od->ssi.pending) {
    - od->ssi.waiting_for_ack = FALSE;
    - aim_ssi_sync(od);
    - }
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x000f - SSI Data Unchanged.
    - *
    - * Response to aim_ssi_reqifchanged() if the server-side data is not newer than
    - * posted local stamp/revision.
    - *
    - */
    -static int parsedataunchanged(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    -
    - od->ssi.received_data = TRUE;
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0011 - SSI Begin Data Modification.
    - *
    - * Tell the server you're going to start modifying data. This marks
    - * the beginning of a transaction.
    - */
    -int aim_ssi_modbegin(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
    - return -EINVAL;
    -
    - aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_EDITSTART);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0012 - SSI End Data Modification.
    - *
    - * Tell the server you're finished modifying data. The marks the end
    - * of a transaction.
    - */
    -int aim_ssi_modend(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
    - return -EINVAL;
    -
    - aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_EDITSTOP);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0015 - Receive an authorization grant
    - */
    -static int receiveauthgrant(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 tmp;
    - char *bn, *msg, *tmpstr;
    -
    - /* Read buddy name */
    - tmp = byte_stream_get8(bs);
    - if (!tmp) {
    - purple_debug_warning("oscar", "Dropping auth grant SNAC "
    - "because username was empty\n");
    - return 0;
    - }
    - bn = byte_stream_getstr(bs, tmp);
    - if (!g_utf8_validate(bn, -1, NULL)) {
    - purple_debug_warning("oscar", "Dropping auth grant SNAC "
    - "because the username was not valid UTF-8\n");
    - g_free(bn);
    - }
    -
    - /* Read message */
    - tmp = byte_stream_get16(bs);
    - if (tmp) {
    - msg = byte_stream_getstr(bs, tmp);
    - if (!g_utf8_validate(msg, -1, NULL)) {
    - /* Ugh, msg isn't UTF8. Let's salvage. */
    - purple_debug_warning("oscar", "Got non-UTF8 message in auth "
    - "grant from %s\n", bn);
    - tmpstr = purple_utf8_salvage(msg);
    - g_free(msg);
    - msg = tmpstr;
    - }
    - } else
    - msg = NULL;
    -
    - /* Unknown */
    - tmp = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, bn, msg);
    -
    - g_free(bn);
    - g_free(msg);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0018 - Send authorization request
    - *
    - * Sends a request for authorization to the given contact. The request will either be
    - * granted, denied, or dropped.
    - *
    - */
    -int aim_ssi_sendauthrequest(OscarData *od, const char *bn, const char *msg)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 1+strlen(bn) + 2+(msg ? strlen(msg)+1 : 0) + 2);
    -
    - /* Username */
    - byte_stream_put8(&bs, strlen(bn));
    - byte_stream_putstr(&bs, bn);
    -
    - /* Message (null terminated) */
    - byte_stream_put16(&bs, msg ? strlen(msg) : 0);
    - if (msg) {
    - byte_stream_putstr(&bs, msg);
    - byte_stream_put8(&bs, 0x00);
    - }
    -
    - /* Unknown */
    - byte_stream_put16(&bs, 0x0000);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0019 - Receive an authorization request
    - */
    -static int receiveauthrequest(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 tmp;
    - char *bn, *msg, *tmpstr;
    -
    - /* Read buddy name */
    - tmp = byte_stream_get8(bs);
    - if (!tmp) {
    - purple_debug_warning("oscar", "Dropping auth request SNAC "
    - "because username was empty\n");
    - return 0;
    - }
    - bn = byte_stream_getstr(bs, tmp);
    - if (!g_utf8_validate(bn, -1, NULL)) {
    - purple_debug_warning("oscar", "Dropping auth request SNAC "
    - "because the username was not valid UTF-8\n");
    - g_free(bn);
    - }
    -
    - /* Read message */
    - tmp = byte_stream_get16(bs);
    - if (tmp) {
    - msg = byte_stream_getstr(bs, tmp);
    - if (!g_utf8_validate(msg, -1, NULL)) {
    - /* Ugh, msg isn't UTF8. Let's salvage. */
    - purple_debug_warning("oscar", "Got non-UTF8 message in auth "
    - "request from %s\n", bn);
    - tmpstr = purple_utf8_salvage(msg);
    - g_free(msg);
    - msg = tmpstr;
    - }
    - } else
    - msg = NULL;
    -
    - /* Unknown */
    - tmp = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, bn, msg);
    -
    - g_free(bn);
    - g_free(msg);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x001a - Send authorization reply
    - *
    - * Sends a reply to a request for authorization. The reply can either
    - * grant authorization or deny authorization.
    - *
    - * if reply=0x00 then deny
    - * if reply=0x01 then grant
    - *
    - */
    -int aim_ssi_sendauthreply(OscarData *od, const char *bn, guint8 reply, const char *msg)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 1+strlen(bn) + 1 + 2+(msg ? (strlen(msg)+1) : 0) + 2);
    -
    - /* Username */
    - byte_stream_put8(&bs, strlen(bn));
    - byte_stream_putstr(&bs, bn);
    -
    - /* Grant or deny */
    - byte_stream_put8(&bs, reply);
    -
    - /* Message (null terminated) */
    - byte_stream_put16(&bs, msg ? (strlen(msg)+1) : 0);
    - if (msg) {
    - byte_stream_putstr(&bs, msg);
    - byte_stream_put8(&bs, 0x00);
    - }
    -
    - /* Unknown */
    - byte_stream_put16(&bs, 0x0000);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x001b - Receive an authorization reply
    - *
    - * You get this bad boy when other people respond to the authorization
    - * request that you have previously sent them.
    - */
    -static int receiveauthreply(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 tmp;
    - guint8 reply;
    - char *bn, *msg, *tmpstr;
    -
    - /* Read buddy name */
    - tmp = byte_stream_get8(bs);
    - if (!tmp) {
    - purple_debug_warning("oscar", "Dropping auth reply SNAC "
    - "because username was empty\n");
    - return 0;
    - }
    - bn = byte_stream_getstr(bs, tmp);
    - if (!g_utf8_validate(bn, -1, NULL)) {
    - purple_debug_warning("oscar", "Dropping auth reply SNAC "
    - "because the username was not valid UTF-8\n");
    - g_free(bn);
    - }
    -
    - /* Read reply */
    - reply = byte_stream_get8(bs);
    -
    - /* Read message */
    - tmp = byte_stream_get16(bs);
    - if (tmp) {
    - msg = byte_stream_getstr(bs, tmp);
    - if (!g_utf8_validate(msg, -1, NULL)) {
    - /* Ugh, msg isn't UTF8. Let's salvage. */
    - purple_debug_warning("oscar", "Got non-UTF8 message in auth "
    - "reply from %s\n", bn);
    - tmpstr = purple_utf8_salvage(msg);
    - g_free(msg);
    - msg = tmpstr;
    - }
    - } else
    - msg = NULL;
    -
    - /* Unknown */
    - tmp = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, bn, reply, msg);
    -
    - g_free(bn);
    - g_free(msg);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x001c - Receive a message telling you someone added you to their list.
    - */
    -static int receiveadded(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 tmp;
    - char *bn;
    -
    - /* Read buddy name */
    - tmp = byte_stream_get8(bs);
    - if (!tmp) {
    - purple_debug_warning("oscar", "Dropping 'you were added' SNAC "
    - "because username was empty\n");
    - return 0;
    - }
    - bn = byte_stream_getstr(bs, tmp);
    - if (!g_utf8_validate(bn, -1, NULL)) {
    - purple_debug_warning("oscar", "Dropping 'you were added' SNAC "
    - "because the username was not valid UTF-8\n");
    - g_free(bn);
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, bn);
    -
    - g_free(bn);
    -
    - return ret;
    -}
    -
    -/*
    - * If we're on ICQ, then AIM_SSI_TYPE_DENY is used for the "permanently invisible" list.
    - * AIM_SSI_TYPE_ICQDENY is used for blocking users instead.
    - */
    -guint16
    -aim_ssi_getdenyentrytype(OscarData* od)
    -{
    - return od->icq ? AIM_SSI_TYPE_ICQDENY : AIM_SSI_TYPE_DENY;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO)
    - return parserights(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_LIST)
    - return parsedata(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_ADD)
    - return parseadd(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_MOD)
    - return parsemod(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_DEL)
    - return parsedel(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_SRVACK)
    - return parseack(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_NOLIST)
    - return parsedataunchanged(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTH)
    - return receiveauthgrant(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ)
    - return receiveauthrequest(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP)
    - return receiveauthreply(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_ADDED)
    - return receiveadded(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -static void
    -ssi_shutdown(OscarData *od, aim_module_t *mod)
    -{
    - aim_ssi_freelist(od);
    -}
    -
    -int
    -ssi_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_FEEDBAG;
    - mod->version = 0x0004;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "feedbag", sizeof(mod->name));
    - mod->snachandler = snachandler;
    - mod->shutdown = ssi_shutdown;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_icbm.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2143 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0004 - Routines for sending/receiving Instant Messages.
    - *
    - * Note the term ICBM (Inter-Client Basic Message) which blankets
    - * all types of generically routed through-server messages. Within
    - * the ICBM types (family 4), a channel is defined. Each channel
    - * represents a different type of message. Channel 1 is used for
    - * what would commonly be called an "instant message". Channel 2
    - * is used for negotiating "rendezvous". These transactions end in
    - * something more complex happening, such as a chat invitation, or
    - * a file transfer. Channel 3 is used for chat messages (not in
    - * the same family as these channels). Channel 4 is used for
    - * various ICQ messages. Examples are normal messages, URLs, and
    - * old-style authorization.
    - *
    - * In addition to the channel, every ICBM contains a cookie. For
    - * standard IMs, these are only used for error messages. However,
    - * the more complex rendezvous messages make suitably more complex
    - * use of this field.
    - *
    - * TODO: Split this up into an im.c file an an icbm.c file. It
    - * will be beautiful, you'll see.
    - *
    - * Make sure flap_connection_findbygroup is used by all functions.
    - */
    -
    -#include "encoding.h"
    -#include "oscar.h"
    -#include "peer.h"
    -
    -#ifdef _WIN32
    -#include "win32dep.h"
    -#endif
    -
    -#include "util.h"
    -
    -static const char * const errcodereason[] = {
    - N_("Invalid error"),
    - N_("Not logged in"),
    - N_("Cannot receive IM due to parental controls"),
    - N_("Cannot send SMS without accepting terms"),
    - N_("Cannot send SMS"), /* SMS_WITHOUT_DISCLAIMER is weird */
    - N_("Cannot send SMS to this country"),
    - N_("Unknown error"), /* Undocumented */
    - N_("Unknown error"), /* Undocumented */
    - N_("Cannot send SMS to unknown country"),
    - N_("Bot accounts cannot initiate IMs"),
    - N_("Bot account cannot IM this user"),
    - N_("Bot account reached IM limit"),
    - N_("Bot account reached daily IM limit"),
    - N_("Bot account reached monthly IM limit"),
    - N_("Unable to receive offline messages"),
    - N_("Offline message store full")
    -};
    -static const int errcodereasonlen = G_N_ELEMENTS(errcodereason);
    -
    -/**
    - * Add a standard ICBM header to the given bstream with the given
    - * information.
    - *
    - * @param bs The bstream to write the ICBM header to.
    - * @param c c is for cookie, and cookie is for me.
    - * @param channel The ICBM channel (1 through 4).
    - * @param bn Null-terminated scrizeen nizame.
    - * @return The number of bytes written. It's really not useful.
    - */
    -static int aim_im_puticbm(ByteStream *bs, const guchar *c, guint16 channel, const char *bn)
    -{
    - byte_stream_putraw(bs, c, 8);
    - byte_stream_put16(bs, channel);
    - byte_stream_put8(bs, strlen(bn));
    - byte_stream_putstr(bs, bn);
    - return 8+2+1+strlen(bn);
    -}
    -
    -/**
    - * Generates a random ICBM cookie in a character array of length 8
    - * and copies it into the variable passed as cookie
    - * TODO: Maybe we should stop limiting our characters to the visible range?
    - */
    -void aim_icbm_makecookie(guchar *cookie)
    -{
    - int i;
    -
    - /* Should be like "21CBF95" and null terminated */
    - for (i = 0; i < 7; i++)
    - cookie[i] = 0x30 + ((guchar)rand() % 10);
    - cookie[7] = '\0';
    -}
    -
    -/*
    - * Subtype 0x0001 - Error
    - */
    -static int
    -error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_snac_t *snac2;
    - guint16 reason, errcode = 0;
    - const char *bn;
    - GSList *tlvlist;
    - PurpleConnection *gc = od->gc;
    -#ifdef TODOFT
    - PurpleXfer *xfer;
    -#endif
    - const char *reason_str;
    - char *buf;
    -
    - snac2 = aim_remsnac(od, snac->id);
    - if (!snac2) {
    - purple_debug_misc("oscar", "icbm error: received response from unknown request!\n");
    - return 1;
    - }
    -
    - if (snac2->family != SNAC_FAMILY_ICBM) {
    - purple_debug_misc("oscar", "icbm error: received response from invalid request! %d\n", snac2->family);
    - g_free(snac2->data);
    - g_free(snac2);
    - return 1;
    - }
    -
    - /* Data is assumed to be the destination bn */
    - bn = snac2->data;
    - if (!bn || bn[0] == '\0') {
    - purple_debug_misc("oscar", "icbm error: received response from request without a buddy name!\n");
    - g_free(snac2->data);
    - g_free(snac2);
    - return 1;
    - }
    -
    - reason = byte_stream_get16(bs);
    -
    - tlvlist = aim_tlvlist_read(bs);
    - if (aim_tlv_gettlv(tlvlist, 0x0008, 1))
    - errcode = aim_tlv_get16(tlvlist, 0x0008, 1);
    - aim_tlvlist_free(tlvlist);
    -
    - purple_debug_error("oscar",
    - "Message error with bn %s and reason %hu and errcode %hu\n",
    - bn, reason, errcode);
    -
    -#ifdef TODOFT
    - /* If this was a file transfer request, bn is a cookie */
    - if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, bn))) {
    - purple_xfer_cancel_remote(xfer);
    - return 1;
    - }
    -#endif
    -
    - /* Notify the user that the message wasn't delivered */
    - reason_str = oscar_get_msgerr_reason(reason);
    - if (errcode != 0 && errcode < errcodereasonlen)
    - buf = g_strdup_printf(_("Unable to send message: %s (%s)"), reason_str,
    - _(errcodereason[errcode]));
    - else
    - buf = g_strdup_printf(_("Unable to send message: %s"), reason_str);
    -
    - if (!purple_conv_present_error(bn, purple_connection_get_account(gc), buf)) {
    - g_free(buf);
    - if (errcode != 0 && errcode < errcodereasonlen)
    - buf = g_strdup_printf(_("Unable to send message to %s: %s (%s)"),
    - bn ? bn : "(unknown)", reason_str,
    - _(errcodereason[errcode]));
    - else
    - buf = g_strdup_printf(_("Unable to send message to %s: %s"),
    - bn ? bn : "(unknown)", reason_str);
    - purple_notify_error(od->gc, NULL, buf, reason_str);
    - }
    - g_free(buf);
    -
    - g_free(snac2->data);
    - g_free(snac2);
    -
    - return 1;
    -}
    -
    -/**
    - * Subtype 0x0002 - Set ICBM parameters.
    - *
    - * I definitely recommend sending this. If you don't, you'll be stuck
    - * with the rather unreasonable defaults.
    - *
    - */
    -int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - if (!params)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 16);
    -
    - /* This is read-only (see Parameter Reply). Must be set to zero here. */
    - byte_stream_put16(&bs, 0x0000);
    -
    - /* These are all read-write */
    - byte_stream_put32(&bs, params->flags);
    - byte_stream_put16(&bs, params->maxmsglen);
    - byte_stream_put16(&bs, params->maxsenderwarn);
    - byte_stream_put16(&bs, params->maxrecverwarn);
    - byte_stream_put32(&bs, params->minmsginterval);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0002, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/**
    - * Subtype 0x0004 - Request ICBM parameter information.
    - *
    - */
    -int aim_im_reqparams(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_ICBM, 0x0004);
    -
    - return 0;
    -}
    -
    -/**
    - * Subtype 0x0005 - Receive parameter information.
    - *
    - */
    -static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - struct aim_icbmparameters params;
    -
    - params.maxchan = byte_stream_get16(bs);
    - params.flags = byte_stream_get32(bs);
    - params.maxmsglen = byte_stream_get16(bs);
    - params.maxsenderwarn = byte_stream_get16(bs);
    - params.maxrecverwarn = byte_stream_get16(bs);
    - params.minmsginterval = byte_stream_get32(bs);
    -
    - params.flags = AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED
    - | AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED
    - | AIM_IMPARAM_FLAG_EVENTS_ALLOWED
    - | AIM_IMPARAM_FLAG_SMS_SUPPORTED
    - | AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED
    - | AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ;
    - params.maxmsglen = 8000;
    - params.minmsginterval = 0;
    -
    - aim_im_setparams(od, &params);
    -
    - return 0;
    -}
    -
    -/**
    - * Subtype 0x0006 - Send an ICBM (instant message).
    - *
    - *
    - * Possible flags:
    - * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse
    - * AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
    - * online (probably ICQ only).
    - *
    - * Implementation note: Since this is one of the most-used functions
    - * in all of libfaim, it is written with performance in mind. As such,
    - * it is not as clear as it could be in respect to how this message is
    - * supposed to be layed out. Most obviously, tlvlists should be used
    - * instead of writing out the bytes manually.
    - */
    -int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
    -{
    - FlapConnection *conn;
    - aim_snacid_t snacid;
    - ByteStream data;
    - guchar cookie[8];
    - int msgtlvlen;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - if (!args)
    - return -EINVAL;
    -
    - if (!args->msg || (args->msglen <= 0))
    - return -EINVAL;
    -
    - if (args->msglen > MAXMSGLEN)
    - return -E2BIG;
    -
    - /* Painfully calculate the size of the message TLV */
    - msgtlvlen = 1 + 1; /* 0501 */
    - msgtlvlen += 2 + args->featureslen;
    - msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
    - msgtlvlen += 4 /* charset */ + args->msglen;
    -
    - byte_stream_new(&data, msgtlvlen + 128);
    -
    - /* Generate an ICBM cookie */
    - aim_icbm_makecookie(cookie);
    -
    - /* ICBM header */
    - aim_im_puticbm(&data, cookie, 0x0001, args->destbn);
    -
    - /* Message TLV (type 0x0002) */
    - byte_stream_put16(&data, 0x0002);
    - byte_stream_put16(&data, msgtlvlen);
    -
    - /* Features TLV (type 0x0501) */
    - byte_stream_put16(&data, 0x0501);
    - byte_stream_put16(&data, args->featureslen);
    - byte_stream_putraw(&data, args->features, args->featureslen);
    -
    - /* Insert message text in a TLV (type 0x0101) */
    - byte_stream_put16(&data, 0x0101);
    -
    - /* Message block length */
    - byte_stream_put16(&data, args->msglen + 0x04);
    -
    - /* Character set */
    - byte_stream_put16(&data, args->charset);
    - /* Character subset -- we always use 0 here */
    - byte_stream_put16(&data, 0x0);
    -
    - /* Message. Not terminated */
    - byte_stream_putraw(&data, (guchar *)args->msg, args->msglen);
    -
    - /* Set the Autoresponse flag */
    - if (args->flags & AIM_IMFLAGS_AWAY) {
    - byte_stream_put16(&data, 0x0004);
    - byte_stream_put16(&data, 0x0000);
    - } else {
    - /* Set the Request Acknowledge flag */
    - byte_stream_put16(&data, 0x0003);
    - byte_stream_put16(&data, 0x0000);
    -
    - if (args->flags & AIM_IMFLAGS_OFFLINE) {
    - /* Allow this message to be queued as an offline message */
    - byte_stream_put16(&data, 0x0006);
    - byte_stream_put16(&data, 0x0000);
    - }
    - }
    -
    - /*
    - * Set the I HAVE A REALLY PURTY ICON flag.
    - * XXX - This should really only be sent on initial
    - * IMs and when you change your icon.
    - */
    - if (args->flags & AIM_IMFLAGS_HASICON) {
    - byte_stream_put16(&data, 0x0008);
    - byte_stream_put16(&data, 0x000c);
    - byte_stream_put32(&data, args->iconlen);
    - byte_stream_put16(&data, 0x0001);
    - byte_stream_put16(&data, args->iconsum);
    - byte_stream_put32(&data, args->iconstamp);
    - }
    -
    - /*
    - * Set the Buddy Icon Requested flag.
    - * XXX - Every time? Surely not...
    - */
    - if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
    - byte_stream_put16(&data, 0x0009);
    - byte_stream_put16(&data, 0x0000);
    - }
    -
    - /* XXX - should be optional */
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, args->destbn, strlen(args->destbn)+1);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &data);
    - byte_stream_destroy(&data);
    -
    - /* clean out SNACs over 60sec old */
    - aim_cleansnacs(od, 60);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0006 - Send a chat invitation.
    - */
    -int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - IcbmCookie *msgcookie;
    - struct aim_invite_priv *priv;
    - guchar cookie[8];
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - if (!bn || !msg || !roomname)
    - return -EINVAL;
    -
    - aim_icbm_makecookie(cookie);
    -
    - byte_stream_new(&bs, 1142+strlen(bn)+strlen(roomname)+strlen(msg));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, bn, strlen(bn)+1);
    -
    - /* XXX should be uncached by an unwritten 'invite accept' handler */
    - priv = g_malloc(sizeof(struct aim_invite_priv));
    - priv->bn = g_strdup(bn);
    - priv->roomname = g_strdup(roomname);
    - priv->exchange = exchange;
    - priv->instance = instance;
    -
    - if ((msgcookie = aim_mkcookie(cookie, AIM_COOKIETYPE_INVITE, priv)))
    - aim_cachecookie(od, msgcookie);
    - else
    - g_free(priv);
    -
    - /* ICBM Header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - /*
    - * TLV t(0005)
    - *
    - * Everything else is inside this TLV.
    - *
    - * Sigh. AOL was rather inconsistent right here. So we have
    - * to play some minor tricks. Right inside the type 5 is some
    - * raw data, followed by a series of TLVs.
    - *
    - */
    - byte_stream_new(&hdrbs, 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2);
    -
    - byte_stream_put16(&hdrbs, 0x0000); /* Unknown! */
    - byte_stream_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */
    - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_CHAT);
    -
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    - aim_tlvlist_add_str(&inner_tlvlist, 0x000c, msg);
    - aim_tlvlist_add_chatroom(&inner_tlvlist, 0x2711, exchange, roomname, instance);
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/**
    - * Subtype 0x0006 - Send your icon to a given user.
    - *
    - * This is also performance sensitive. (If you can believe it...)
    - *
    - */
    -int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - guchar cookie[8];
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - if (!bn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
    - return -EINVAL;
    -
    - aim_icbm_makecookie(cookie);
    -
    - byte_stream_new(&bs, 8+2+1+strlen(bn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - /*
    - * TLV t(0005)
    - *
    - * Encompasses everything below.
    - */
    - byte_stream_put16(&bs, 0x0005);
    - byte_stream_put16(&bs, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
    -
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_putraw(&bs, cookie, 8);
    - byte_stream_putcaps(&bs, OSCAR_CAPABILITY_BUDDYICON);
    -
    - /* TLV t(000a) */
    - byte_stream_put16(&bs, 0x000a);
    - byte_stream_put16(&bs, 0x0002);
    - byte_stream_put16(&bs, 0x0001);
    -
    - /* TLV t(000f) */
    - byte_stream_put16(&bs, 0x000f);
    - byte_stream_put16(&bs, 0x0000);
    -
    - /* TLV t(2711) */
    - byte_stream_put16(&bs, 0x2711);
    - byte_stream_put16(&bs, 4+4+4+iconlen+strlen(AIM_ICONIDENT));
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, iconsum);
    - byte_stream_put32(&bs, iconlen);
    - byte_stream_put32(&bs, stamp);
    - byte_stream_putraw(&bs, icon, iconlen);
    - byte_stream_putstr(&bs, AIM_ICONIDENT);
    -
    - /* TLV t(0003) */
    - byte_stream_put16(&bs, 0x0003);
    - byte_stream_put16(&bs, 0x0000);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/**
    - * Cancel a rendezvous invitation. It could be an invitation to
    - * establish a direct connection, or a file-send, or a chat invite.
    - */
    -void
    -aim_im_sendch2_cancel(PeerConnection *peer_conn)
    -{
    - OscarData *od;
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    -
    - od = peer_conn->od;
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 118+strlen(peer_conn->bn));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn);
    -
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
    -
    - byte_stream_new(&hdrbs, 64);
    -
    - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_CANCEL);
    - byte_stream_putraw(&hdrbs, peer_conn->cookie, 8);
    - byte_stream_putcaps(&hdrbs, peer_conn->type);
    -
    - /* This TLV means "cancel!" */
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000b, 0x0001);
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0006 - Send an "I accept and I've connected to
    - * you" message.
    - */
    -void
    -aim_im_sendch2_connected(PeerConnection *peer_conn)
    -{
    - OscarData *od;
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - od = peer_conn->od;
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 11+strlen(peer_conn->bn) + 4+2+8+16);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn);
    -
    - byte_stream_put16(&bs, 0x0005);
    - byte_stream_put16(&bs, 0x001a);
    - byte_stream_put16(&bs, AIM_RENDEZVOUS_CONNECTED);
    - byte_stream_putraw(&bs, peer_conn->cookie, 8);
    - byte_stream_putcaps(&bs, peer_conn->type);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0006 - Send a direct connect rendezvous ICBM. This
    - * could have a number of meanings, depending on the content:
    - * "I want you to connect to me"
    - * "I want to connect to you"
    - * "I want to connect through a proxy server"
    - */
    -void
    -aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 246+strlen(bn));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
    -
    - byte_stream_new(&hdrbs, 128);
    -
    - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
    - byte_stream_putraw(&hdrbs, cookie, 8);
    - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, port);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0006 - Send a direct connect rendezvous ICBM asking the
    - * remote user to connect to us via a proxy server.
    - */
    -void
    -aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    - guint8 ip_comp[4];
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 246+strlen(bn));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
    -
    - byte_stream_new(&hdrbs, 128);
    -
    - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
    - byte_stream_putraw(&hdrbs, cookie, 8);
    - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, pin);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x0010);
    -
    - /* Send the bitwise complement of the port and ip. As a check? */
    - ip_comp[0] = ~ip[0];
    - ip_comp[1] = ~ip[1];
    - ip_comp[2] = ~ip[2];
    - ip_comp[3] = ~ip[3];
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
    -
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0006 - Send an "I want to send you this file" message
    - *
    - */
    -void
    -aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    -
    - g_return_if_fail(bn != NULL);
    - g_return_if_fail(ip != NULL);
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 1014);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
    -
    - byte_stream_new(&hdrbs, 512);
    -
    - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
    - byte_stream_putraw(&hdrbs, cookie, 8);
    - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, port);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    - /* TODO: Send 0x0016 and 0x0017 */
    -
    - if (filename != NULL)
    - {
    - ByteStream inner_bs;
    -
    - /* Begin TLV t(2711) */
    - byte_stream_new(&inner_bs, 2+2+4+strlen(filename)+1);
    - byte_stream_put16(&inner_bs, (numfiles > 1) ? 0x0002 : 0x0001);
    - byte_stream_put16(&inner_bs, numfiles);
    - byte_stream_put32(&inner_bs, size);
    -
    - /* Filename - NULL terminated, for some odd reason */
    - byte_stream_putstr(&inner_bs, filename);
    - byte_stream_put8(&inner_bs, 0x00);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, inner_bs.len, inner_bs.data);
    - byte_stream_destroy(&inner_bs);
    - /* End TLV t(2711) */
    - }
    -
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0006 - Send a sendfile connect rendezvous ICBM asking the
    - * remote user to connect to us via a proxy server.
    - */
    -void
    -aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    - guint8 ip_comp[4];
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 1014);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
    -
    - byte_stream_new(&hdrbs, 512);
    -
    - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
    - byte_stream_putraw(&hdrbs, cookie, 8);
    - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, pin);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x0010);
    -
    - /* Send the bitwise complement of the port and ip. As a check? */
    - ip_comp[0] = ~ip[0];
    - ip_comp[1] = ~ip[1];
    - ip_comp[2] = ~ip[2];
    - ip_comp[3] = ~ip[3];
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
    -
    - if (filename != NULL)
    - {
    - ByteStream filename_bs;
    -
    - /* Begin TLV t(2711) */
    - byte_stream_new(&filename_bs, 2+2+4+strlen(filename)+1);
    - byte_stream_put16(&filename_bs, (numfiles > 1) ? 0x0002 : 0x0001);
    - byte_stream_put16(&filename_bs, numfiles);
    - byte_stream_put32(&filename_bs, size);
    -
    - /* Filename - NULL terminated, for some odd reason */
    - byte_stream_putstr(&filename_bs, filename);
    - byte_stream_put8(&filename_bs, 0x00);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, filename_bs.len, filename_bs.data);
    - byte_stream_destroy(&filename_bs);
    - /* End TLV t(2711) */
    - }
    -
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -static void
    -incomingim_ch1_parsemsg(OscarData *od, aim_userinfo_t *userinfo, ByteStream *message, struct aim_incomingim_ch1_args *args)
    -{
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - /*
    - * We're interested in the inner TLV 0x101, which contains precious, precious message.
    - */
    - while (byte_stream_bytes_left(message) >= 4) {
    - guint16 type = byte_stream_get16(message);
    - guint16 length = byte_stream_get16(message);
    - if (type == 0x101) {
    - gchar *msg;
    - guint16 msglen = length - 4; /* charset + charsubset */
    - guint16 charset = byte_stream_get16(message);
    - byte_stream_advance(message, 2); /* charsubset */
    -
    - msg = byte_stream_getstr(message, msglen);
    - args->msg = oscar_decode_im(account, userinfo->bn, charset, msg, msglen);
    - g_free(msg);
    - } else {
    - byte_stream_advance(message, length);
    - }
    - }
    -}
    -
    -static int
    -incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie)
    -{
    - guint16 type, length;
    - aim_rxcallback_t userfunc;
    - int ret = 0;
    - struct aim_incomingim_ch1_args args;
    - unsigned int endpos;
    -
    - memset(&args, 0, sizeof(args));
    -
    - /*
    - * This used to be done using tlvchains. For performance reasons,
    - * I've changed it to process the TLVs in-place. This avoids lots
    - * of per-IM memory allocations.
    - */
    - while (byte_stream_bytes_left(bs) >= 4)
    - {
    - type = byte_stream_get16(bs);
    - length = byte_stream_get16(bs);
    -
    - if (length > byte_stream_bytes_left(bs))
    - {
    - purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->bn);
    - break;
    - }
    -
    - endpos = byte_stream_curpos(bs) + length;
    -
    - if (type == 0x0002) { /* Message Block */
    - ByteStream tlv02;
    - byte_stream_init(&tlv02, bs->data + bs->offset, length);
    - incomingim_ch1_parsemsg(od, userinfo, &tlv02, &args);
    - } else if (type == 0x0003) { /* Server Ack Requested */
    - args.icbmflags |= AIM_IMFLAGS_ACK;
    - } else if (type == 0x0004) { /* Message is Auto Response */
    - args.icbmflags |= AIM_IMFLAGS_AWAY;
    - } else if (type == 0x0006) { /* Message was received offline. */
    - /*
    - * This flag is set on incoming offline messages for both
    - * AIM and ICQ accounts.
    - */
    - args.icbmflags |= AIM_IMFLAGS_OFFLINE;
    - } else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
    - args.iconlen = byte_stream_get32(bs);
    - byte_stream_get16(bs); /* 0x0001 */
    - args.iconsum = byte_stream_get16(bs);
    - args.iconstamp = byte_stream_get32(bs);
    -
    - /*
    - * This looks to be a client bug. MacAIM 4.3 will
    - * send this tag, but with all zero values, in the
    - * first message of a conversation. This makes no
    - * sense whatsoever, so I'm going to say its a bug.
    - *
    - * You really shouldn't advertise a zero-length icon
    - * anyway.
    - *
    - */
    - if (args.iconlen)
    - args.icbmflags |= AIM_IMFLAGS_HASICON;
    - } else if (type == 0x0009) {
    - args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
    - } else if (type == 0x000b) { /* Non-direct connect typing notification */
    - args.icbmflags |= AIM_IMFLAGS_TYPINGNOT;
    - } else if (type == 0x0016) {
    - /*
    - * UTC timestamp for when the message was sent. Only
    - * provided for offline messages.
    - */
    - args.timestamp = byte_stream_get32(bs);
    - }
    -
    - /*
    - * This is here to protect ourselves from ourselves. That
    - * is, if something above doesn't completely parse its value
    - * section, or, worse, overparses it, this will set the
    - * stream where it needs to be in order to land on the next
    - * TLV when the loop continues.
    - *
    - */
    - byte_stream_setpos(bs, endpos);
    - }
    -
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, userinfo, &args);
    -
    - g_free(args.msg);
    - return ret;
    -}
    -
    -static void
    -incomingim_ch2_buddylist(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
    -{
    - /*
    - * This goes like this...
    - *
    - * group name length
    - * group name
    - * num of buddies in group
    - * buddy name length
    - * buddy name
    - * buddy name length
    - * buddy name
    - * ...
    - * group name length
    - * group name
    - * num of buddies in group
    - * buddy name length
    - * buddy name
    - * ...
    - * ...
    - */
    - while (byte_stream_bytes_left(servdata))
    - {
    - guint16 gnlen, numb;
    - int i;
    - char *gn;
    -
    - gnlen = byte_stream_get16(servdata);
    - gn = byte_stream_getstr(servdata, gnlen);
    - numb = byte_stream_get16(servdata);
    -
    - for (i = 0; i < numb; i++) {
    - guint16 bnlen;
    - char *bn;
    -
    - bnlen = byte_stream_get16(servdata);
    - bn = byte_stream_getstr(servdata, bnlen);
    -
    - purple_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->bn, gn, bn);
    -
    - g_free(bn);
    - }
    -
    - g_free(gn);
    - }
    -
    - return;
    -}
    -
    -static void
    -incomingim_ch2_buddyicon_free(OscarData *od, IcbmArgsCh2 *args)
    -{
    - g_free(args->info.icon.icon);
    -
    - return;
    -}
    -
    -static void
    -incomingim_ch2_buddyicon(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
    -{
    - args->info.icon.checksum = byte_stream_get32(servdata);
    - args->info.icon.length = byte_stream_get32(servdata);
    - args->info.icon.timestamp = byte_stream_get32(servdata);
    - args->info.icon.icon = byte_stream_getraw(servdata, args->info.icon.length);
    -
    - args->destructor = (void *)incomingim_ch2_buddyicon_free;
    -
    - return;
    -}
    -
    -static void
    -incomingim_ch2_chat_free(OscarData *od, IcbmArgsCh2 *args)
    -{
    - /* XXX - aim_chat_roominfo_free() */
    - g_free(args->info.chat.roominfo.name);
    -
    - return;
    -}
    -
    -static void
    -incomingim_ch2_chat(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
    -{
    - /*
    - * Chat room info.
    - */
    - aim_chat_readroominfo(servdata, &args->info.chat.roominfo);
    -
    - args->destructor = (void *)incomingim_ch2_chat_free;
    -}
    -
    -static void
    -incomingim_ch2_icqserverrelay_free(OscarData *od, IcbmArgsCh2 *args)
    -{
    - g_free((char *)args->info.rtfmsg.msg);
    -}
    -
    -/*
    - * The relationship between OSCAR_CAPABILITY_ICQSERVERRELAY and OSCAR_CAPABILITY_ICQRTF is
    - * kind of odd. This sends the client ICQRTF since that is all that I've seen
    - * SERVERRELAY used for.
    - *
    - * Note that this is all little-endian. Cringe.
    - *
    - */
    -static void
    -incomingim_ch2_icqserverrelay(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
    -{
    - guint16 hdrlen, msglen;
    -
    - args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
    -
    -#define SKIP_HEADER(expected_hdrlen) \
    - hdrlen = byte_stream_getle16(servdata); \
    - if (hdrlen != expected_hdrlen) { \
    - purple_debug_warning("oscar", "Expected to find a header with length " #expected_hdrlen "; ignoring message"); \
    - return; \
    - } \
    - byte_stream_advance(servdata, hdrlen);
    -
    - SKIP_HEADER(0x001b);
    - SKIP_HEADER(0x000e);
    -
    - args->info.rtfmsg.msgtype = byte_stream_get8(servdata);
    - /*
    - * Copied from http://iserverd.khstu.ru/oscar/message.html:
    - * xx byte message flags
    - * xx xx word (LE) status code
    - * xx xx word (LE) priority code
    - *
    - * We don't need any of these, so just skip them.
    - */
    - byte_stream_advance(servdata, 1 + 2 + 2);
    -
    - msglen = byte_stream_getle16(servdata);
    - args->info.rtfmsg.msg = byte_stream_getstr(servdata, msglen);
    -}
    -
    -static void
    -incomingim_ch2_sendfile_free(OscarData *od, IcbmArgsCh2 *args)
    -{
    - g_free(args->info.sendfile.filename);
    -}
    -
    -/* Someone is sending us a file */
    -static void
    -incomingim_ch2_sendfile(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
    -{
    - int flen;
    -
    - args->destructor = (void *)incomingim_ch2_sendfile_free;
    -
    - /* Maybe there is a better way to tell what kind of sendfile
    - * this is? Maybe TLV t(000a)? */
    -
    - /* subtype is one of AIM_OFT_SUBTYPE_* */
    - args->info.sendfile.subtype = byte_stream_get16(servdata);
    - args->info.sendfile.totfiles = byte_stream_get16(servdata);
    - args->info.sendfile.totsize = byte_stream_get32(servdata);
    -
    - /*
    - * I hope to God I'm right when I guess that there is a
    - * 32 char max filename length for single files. I think
    - * OFT tends to do that. Gotta love inconsistency. I saw
    - * a 26 byte filename?
    - */
    - /* AAA - create an byte_stream_getnullstr function (don't anymore)(maybe) */
    - /* Use an inelegant way of getting the null-terminated filename,
    - * since there's no easy bstream routine. */
    - for (flen = 0; byte_stream_get8(servdata); flen++);
    - byte_stream_advance(servdata, -flen -1);
    - args->info.sendfile.filename = byte_stream_getstr(servdata, flen);
    -
    - /* There is sometimes more after the null-terminated filename,
    - * but I'm unsure of its format. */
    - /* I don't believe him. */
    - /* There is sometimes a null byte inside a unicode filename,
    - * but as far as I can tell the filename is the last
    - * piece of data that will be in this message. --Jonathan */
    -}
    -
    -typedef void (*ch2_args_destructor_t)(OscarData *od, IcbmArgsCh2 *args);
    -
    -static int incomingim_ch2(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie)
    -{
    - aim_rxcallback_t userfunc;
    - aim_tlv_t *block1, *servdatatlv;
    - GSList *list2;
    - aim_tlv_t *tlv;
    - IcbmArgsCh2 args;
    - ByteStream bbs, sdbs, *sdbsptr = NULL;
    - guint8 *cookie2;
    - int ret = 0;
    -
    - char proxyip[30] = {""};
    - char clientip[30] = {""};
    - char verifiedip[30] = {""};
    -
    - memset(&args, 0, sizeof(args));
    -
    - /*
    - * There's another block of TLVs embedded in the type 5 here.
    - */
    - block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1);
    - if (block1 == NULL)
    - {
    - /* The server sent us ch2 ICBM without ch2 info? Weird. */
    - return 1;
    - }
    - byte_stream_init(&bbs, block1->value, block1->length);
    -
    - /*
    - * First two bytes represent the status of the connection.
    - * One of the AIM_RENDEZVOUS_ defines.
    - *
    - * 0 is a request, 1 is a cancel, 2 is an accept
    - */
    - args.status = byte_stream_get16(&bbs);
    -
    - /*
    - * Next comes the cookie. Should match the ICBM cookie.
    - */
    - cookie2 = byte_stream_getraw(&bbs, 8);
    - if (memcmp(cookie, cookie2, 8) != 0)
    - {
    - purple_debug_warning("oscar",
    - "Cookies don't match in rendezvous ICBM, bailing out.\n");
    - g_free(cookie2);
    - return 1;
    - }
    - memcpy(args.cookie, cookie2, 8);
    - g_free(cookie2);
    -
    - /*
    - * The next 16bytes are a capability block so we can
    - * identify what type of rendezvous this is.
    - */
    - args.type = aim_locate_getcaps(od, &bbs, 0x10);
    -
    - /*
    - * What follows may be TLVs or nothing, depending on the
    - * purpose of the message.
    - *
    - * Ack packets for instance have nothing more to them.
    - */
    - list2 = aim_tlvlist_read(&bbs);
    -
    - /*
    - * IP address to proxy the file transfer through.
    - *
    - * TODO: I don't like this. Maybe just read in an int? Or inet_ntoa...
    - */
    - tlv = aim_tlv_gettlv(list2, 0x0002, 1);
    - if ((tlv != NULL) && (tlv->length == 4))
    - snprintf(proxyip, sizeof(proxyip), "%hhu.%hhu.%hhu.%hhu",
    - tlv->value[0], tlv->value[1],
    - tlv->value[2], tlv->value[3]);
    -
    - /*
    - * IP address from the perspective of the client.
    - */
    - tlv = aim_tlv_gettlv(list2, 0x0003, 1);
    - if ((tlv != NULL) && (tlv->length == 4))
    - snprintf(clientip, sizeof(clientip), "%hhu.%hhu.%hhu.%hhu",
    - tlv->value[0], tlv->value[1],
    - tlv->value[2], tlv->value[3]);
    -
    - /*
    - * Verified IP address (from the perspective of Oscar).
    - *
    - * This is added by the server.
    - */
    - tlv = aim_tlv_gettlv(list2, 0x0004, 1);
    - if ((tlv != NULL) && (tlv->length == 4))
    - snprintf(verifiedip, sizeof(verifiedip), "%hhu.%hhu.%hhu.%hhu",
    - tlv->value[0], tlv->value[1],
    - tlv->value[2], tlv->value[3]);
    -
    - /*
    - * Port number for something.
    - */
    - if (aim_tlv_gettlv(list2, 0x0005, 1))
    - args.port = aim_tlv_get16(list2, 0x0005, 1);
    -
    - /*
    - * File transfer "request number":
    - * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy
    - * 0x0002 - "Reply request" for a stage 2 proxy (receiver wants to use proxy)
    - * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers
    - */
    - if (aim_tlv_gettlv(list2, 0x000a, 1))
    - args.requestnumber = aim_tlv_get16(list2, 0x000a, 1);
    -
    - /*
    - * Terminate connection/error code. 0x0001 means the other user
    - * cancelled the connection.
    - */
    - if (aim_tlv_gettlv(list2, 0x000b, 1))
    - args.errorcode = aim_tlv_get16(list2, 0x000b, 1);
    -
    - /*
    - * Invitation message / chat description.
    - */
    - if (aim_tlv_gettlv(list2, 0x000c, 1)) {
    - args.msg = aim_tlv_getstr(list2, 0x000c, 1);
    - args.msglen = aim_tlv_getlength(list2, 0x000c, 1);
    - }
    -
    - /*
    - * Character set.
    - */
    - if (aim_tlv_gettlv(list2, 0x000d, 1))
    - args.encoding = aim_tlv_getstr(list2, 0x000d, 1);
    -
    - /*
    - * Language.
    - */
    - if (aim_tlv_gettlv(list2, 0x000e, 1))
    - args.language = aim_tlv_getstr(list2, 0x000e, 1);
    -
    - /*
    - * Flag meaning we should proxy the file transfer through an AIM server
    - */
    - if (aim_tlv_gettlv(list2, 0x0010, 1))
    - args.use_proxy = TRUE;
    -
    - if (strlen(proxyip))
    - args.proxyip = (char *)proxyip;
    - if (strlen(clientip))
    - args.clientip = (char *)clientip;
    - if (strlen(verifiedip))
    - args.verifiedip = (char *)verifiedip;
    -
    - /*
    - * This must be present in PROPOSALs, but will probably not
    - * exist in CANCELs and ACCEPTs. Also exists in ICQ Lite
    - * Beta 4.0 URLs (OSCAR_CAPABILITY_ICQSERVERRELAY).
    - *
    - * Service Data blocks are module-specific in format.
    - */
    - if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) {
    -
    - byte_stream_init(&sdbs, servdatatlv->value, servdatatlv->length);
    - sdbsptr = &sdbs;
    -
    - /*
    - * The rest of the handling depends on what type it is.
    - *
    - * Not all of them have special handling (yet).
    - */
    - if (args.type & OSCAR_CAPABILITY_BUDDYICON)
    - incomingim_ch2_buddyicon(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
    - else if (args.type & OSCAR_CAPABILITY_SENDBUDDYLIST)
    - incomingim_ch2_buddylist(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
    - else if (args.type & OSCAR_CAPABILITY_CHAT)
    - incomingim_ch2_chat(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
    - else if (args.type & OSCAR_CAPABILITY_ICQSERVERRELAY)
    - incomingim_ch2_icqserverrelay(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
    - else if (args.type & OSCAR_CAPABILITY_SENDFILE)
    - incomingim_ch2_sendfile(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, userinfo, &args);
    -
    -
    - if (args.destructor)
    - ((ch2_args_destructor_t)args.destructor)(od, &args);
    -
    - g_free((char *)args.msg);
    - g_free((char *)args.encoding);
    - g_free((char *)args.language);
    -
    - aim_tlvlist_free(list2);
    -
    - return ret;
    -}
    -
    -static int incomingim_ch4(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie)
    -{
    - ByteStream meat;
    - aim_rxcallback_t userfunc;
    - aim_tlv_t *block;
    - struct aim_incomingim_ch4_args args;
    - int ret = 0;
    -
    - /*
    - * Make a bstream for the meaty part. Yum. Meat.
    - */
    - if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1)))
    - return -1;
    - byte_stream_init(&meat, block->value, block->length);
    -
    - args.uin = byte_stream_getle32(&meat);
    - args.type = byte_stream_getle8(&meat);
    - args.flags = byte_stream_getle8(&meat);
    - if (args.type == 0x1a)
    - /* There seems to be a problem with the length in SMS msgs from server, this fixed it */
    - args.msglen = block->length - 6;
    - else
    - args.msglen = byte_stream_getle16(&meat);
    - args.msg = (gchar *)byte_stream_getraw(&meat, args.msglen);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, userinfo, &args);
    -
    - g_free(args.msg);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0007
    - *
    - * It can easily be said that parsing ICBMs is THE single
    - * most difficult thing to do in the in AIM protocol. In
    - * fact, I think I just did say that.
    - *
    - * Below is the best damned solution I've come up with
    - * over the past sixteen months of battling with it. This
    - * can parse both away and normal messages from every client
    - * I have access to. Its not fast, its not clean. But it works.
    - *
    - */
    -static int incomingim(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - guchar *cookie;
    - guint16 channel;
    - aim_userinfo_t userinfo;
    -
    - memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
    -
    - /*
    - * Read ICBM Cookie.
    - */
    - cookie = byte_stream_getraw(bs, 8);
    -
    - /*
    - * Channel ID.
    - *
    - * Channel 0x0001 is the message channel. It is
    - * used to send basic ICBMs.
    - *
    - * Channel 0x0002 is the Rendezvous channel, which
    - * is where Chat Invitiations and various client-client
    - * connection negotiations come from.
    - *
    - * Channel 0x0003 is used for chat messages.
    - *
    - * Channel 0x0004 is used for ICQ authorization, or
    - * possibly any system notice.
    - *
    - */
    - channel = byte_stream_get16(bs);
    -
    - /*
    - * Extract the standard user info block.
    - *
    - * Note that although this contains TLVs that appear contiguous
    - * with the TLVs read below, they are two different pieces. The
    - * userinfo block contains the number of TLVs that contain user
    - * information, the rest are not even though there is no separation.
    - * You can start reading the message TLVs after aim_info_extract()
    - * parses out the standard userinfo block.
    - *
    - * That also means that TLV types can be duplicated between the
    - * userinfo block and the rest of the message, however there should
    - * never be two TLVs of the same type in one block.
    - *
    - */
    - aim_info_extract(od, bs, &userinfo);
    -
    - /*
    - * From here on, its depends on what channel we're on.
    - *
    - * Technically all channels have a TLV list have this, however,
    - * for the common channel 1 case, in-place parsing is used for
    - * performance reasons (less memory allocation).
    - */
    - if (channel == 1) {
    -
    - ret = incomingim_ch1(od, conn, mod, frame, snac, channel, &userinfo, bs, cookie);
    -
    - } else if (channel == 2) {
    - GSList *tlvlist;
    -
    - /*
    - * Read block of TLVs (not including the userinfo data). All
    - * further data is derived from what is parsed here.
    - */
    - tlvlist = aim_tlvlist_read(bs);
    -
    - ret = incomingim_ch2(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - } else if (channel == 4) {
    - GSList *tlvlist;
    -
    - tlvlist = aim_tlvlist_read(bs);
    - ret = incomingim_ch4(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
    - aim_tlvlist_free(tlvlist);
    -
    - } else {
    - purple_debug_misc("oscar", "icbm: ICBM received on an unsupported channel. Ignoring. (chan = %04x)\n", channel);
    - }
    -
    - aim_info_free(&userinfo);
    - g_free(cookie);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x000a */
    -static int missedcall(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 channel, nummissed, reason;
    - aim_userinfo_t userinfo;
    -
    - while (byte_stream_bytes_left(bs)) {
    -
    - channel = byte_stream_get16(bs);
    - aim_info_extract(od, bs, &userinfo);
    - nummissed = byte_stream_get16(bs);
    - reason = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, &userinfo, nummissed, reason);
    -
    - aim_info_free(&userinfo);
    - }
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x000b
    - *
    - * Possible codes:
    - * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
    - *
    - */
    -int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 8+2+1+strlen(bn)+6);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
    -
    - byte_stream_putraw(&bs, cookie, 8);
    -
    - byte_stream_put16(&bs, 0x0002); /* channel */
    - byte_stream_put8(&bs, strlen(bn));
    - byte_stream_putstr(&bs, bn);
    -
    - aim_tlvlist_add_16(&tlvlist, 0x0003, code);
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x000b.
    - * Send confirmation for a channel 2 message (Miranda wants it by default).
    - */
    -void
    -aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - guint32 header_size, data_size;
    - guint16 cookie2 = (guint16)g_random_int();
    -
    - purple_debug_misc("oscar", "Sending message ack to %s\n", bn);
    -
    - header_size = 8 + 2 + 1 + strlen(bn) + 2;
    - data_size = 2 + 1 + 16 + 4*2 + 2*3 + 4*3 + 1*2 + 2*3 + 1;
    - byte_stream_new(&bs, header_size + data_size);
    -
    - /* The message header. */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    - byte_stream_put16(&bs, 0x0003); /* reason */
    -
    - /* The actual message. */
    - byte_stream_putle16(&bs, 0x1b); /* subheader #1 length */
    - byte_stream_put8(&bs, 0x08); /* protocol version */
    - byte_stream_putcaps(&bs, OSCAR_CAPABILITY_EMPTY);
    - byte_stream_put32(&bs, 0x3); /* client features */
    - byte_stream_put32(&bs, 0x0004); /* DC type */
    - byte_stream_put16(&bs, cookie2); /* a cookie, chosen by fair dice roll */
    - byte_stream_putle16(&bs, 0x0e); /* header #2 len? */
    - byte_stream_put16(&bs, cookie2); /* the same cookie again */
    - byte_stream_put32(&bs, 0); /* unknown */
    - byte_stream_put32(&bs, 0); /* unknown */
    - byte_stream_put32(&bs, 0); /* unknown */
    - byte_stream_put8(&bs, 0x01); /* plain text message */
    - byte_stream_put8(&bs, 0x00); /* no message flags */
    - byte_stream_put16(&bs, 0x0000); /* no icq status */
    - byte_stream_put16(&bs, 0x0100); /* priority */
    - byte_stream_putle16(&bs, 1); /* query message len */
    - byte_stream_put8(&bs, 0x00); /* empty query message */
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - g_warn_if_fail(conn);
    - if (conn) {
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b,
    - snacid, &bs);
    - }
    - byte_stream_destroy(&bs);
    -}
    -
    -/*
    - * Subtype 0x000b - Receive the response from an ICQ status message
    - * request (in which case this contains the ICQ status message) or
    - * a file transfer or direct IM request was declined.
    - */
    -static int clientautoresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 channel, reason;
    - char *bn;
    - guchar *cookie;
    - guint8 bnlen;
    - char *xml = NULL;
    - guint16 hdrlen;
    - int curpos;
    - guint16 num1, num2;
    - PurpleAccount *account;
    - PurpleBuddy *buddy;
    - PurplePresence *presence;
    - PurpleStatus *status;
    -
    - cookie = byte_stream_getraw(bs, 8);
    - channel = byte_stream_get16(bs);
    - bnlen = byte_stream_get8(bs);
    - bn = byte_stream_getstr(bs, bnlen);
    - reason = byte_stream_get16(bs);
    -
    - if (channel == 0x0002)
    - {
    - hdrlen = byte_stream_getle16(bs);
    - if (hdrlen == 27 && bs->len > (27 + 51)) {
    - byte_stream_advance(bs, 51);
    - num1 = byte_stream_getle16(bs);
    - num2 = byte_stream_getle16(bs);
    - purple_debug_misc("oscar", "X-Status: num1 %hu, num2 %hu\n", num1, num2);
    -
    - if (num1 == 0x4f00 && num2 == 0x3b00) {
    - byte_stream_advance(bs, 86);
    - curpos = byte_stream_curpos(bs);
    - xml = byte_stream_getstr(bs, bs->len - curpos);
    - purple_debug_misc("oscar", "X-Status: Received XML reply\n");
    - if (xml) {
    - GString *xstatus;
    - char *tmp1, *tmp2, *unescaped_xstatus;
    -
    - /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", xml); */
    -
    - xstatus = g_string_new(NULL);
    -
    - tmp1 = strstr(xml, "&lt;title&gt;");
    - if (tmp1 != NULL) {
    - tmp1 += 13;
    - tmp2 = strstr(tmp1, "&lt;/title&gt;");
    - if (tmp2 != NULL)
    - g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
    - }
    - tmp1 = strstr(xml, "&lt;desc&gt;");
    - if (tmp1 != NULL) {
    - tmp1 += 12;
    - tmp2 = strstr(tmp1, "&lt;/desc&gt;");
    - if (tmp2 != NULL) {
    - if (xstatus->len > 0 && tmp2 > tmp1)
    - g_string_append(xstatus, " - ");
    - g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
    - }
    - }
    - unescaped_xstatus = purple_unescape_text(xstatus->str);
    - g_string_free(xstatus, TRUE);
    - if (*unescaped_xstatus) {
    - purple_debug_misc("oscar", "X-Status reply: %s\n", unescaped_xstatus);
    - account = purple_connection_get_account(od->gc);
    - buddy = purple_find_buddy(account, bn);
    - presence = purple_buddy_get_presence(buddy);
    - status = purple_presence_get_status(presence, "mood");
    - if (status) {
    - purple_prpl_got_user_status(account, bn,
    - "mood",
    - PURPLE_MOOD_NAME, purple_status_get_attr_string(status, PURPLE_MOOD_NAME),
    - PURPLE_MOOD_COMMENT, unescaped_xstatus, NULL);
    - }
    - }
    - g_free(unescaped_xstatus);
    - } else {
    - purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n");
    - }
    - } else {
    - purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n");
    - }
    -
    - }
    -
    - } else if (channel == 0x0004) { /* ICQ message */
    - switch (reason) {
    - case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */
    - guint8 statusmsgtype, *msg;
    - guint16 len;
    - guint32 state;
    -
    - len = byte_stream_getle16(bs); /* Should be 0x001b */
    - byte_stream_advance(bs, len); /* Unknown */
    -
    - len = byte_stream_getle16(bs); /* Should be 0x000e */
    - byte_stream_advance(bs, len); /* Unknown */
    -
    - statusmsgtype = byte_stream_getle8(bs);
    - switch (statusmsgtype) {
    - case 0xe8:
    - state = AIM_ICQ_STATE_AWAY;
    - break;
    - case 0xe9:
    - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
    - break;
    - case 0xea:
    - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
    - break;
    - case 0xeb:
    - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
    - break;
    - case 0xec:
    - state = AIM_ICQ_STATE_CHAT;
    - break;
    - default:
    - state = 0;
    - break;
    - }
    -
    - byte_stream_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
    - byte_stream_getle16(bs); /* Unknown - 0x0000 */
    - byte_stream_getle16(bs); /* Unknown - 0x0000 */
    -
    - len = byte_stream_getle16(bs);
    - msg = byte_stream_getraw(bs, len);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, bn, reason, state, msg);
    -
    - g_free(msg);
    - } break;
    -
    - default: {
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, bn, reason);
    - } break;
    - } /* end switch */
    - }
    -
    - g_free(cookie);
    - g_free(bn);
    - g_free(xml);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x000c - Receive an ack after sending an ICBM. The ack contains the ICBM header of the message you sent.
    - */
    -static int msgack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - guchar *cookie;
    - char *bn;
    - int ret = 0;
    -
    - cookie = byte_stream_getraw(bs, 8);
    - byte_stream_get16(bs); /* ch */
    - bn = byte_stream_getstr(bs, byte_stream_get8(bs));
    -
    - purple_debug_info("oscar", "Sent message to %s.\n", bn);
    -
    - g_free(bn);
    - g_free(cookie);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0010 - Request any offline messages that are waiting for
    - * us. This is the "new" way of handling offline messages which is
    - * used for both AIM and ICQ. The old way is to use the ugly
    - * aim_icq_reqofflinemsgs() function, but that is no longer necessary.
    - *
    - * We set the 0x00000100 flag on the ICBM message parameters, which
    - * tells the oscar servers that we support offline messages. When we
    - * set that flag the servers do not automatically send us offline
    - * messages. Instead we must request them using this function. This
    - * should happen after sending the 0x0001/0x0002 "client online" SNAC.
    - */
    -int aim_im_reqofflinemsgs(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
    - return -EINVAL;
    -
    - aim_genericreq_n(od, conn, SNAC_FAMILY_ICBM, 0x0010);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0014 - Send a mini typing notification (mtn) packet.
    - *
    - * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
    - * and Purple 0.60 and newer.
    - *
    - */
    -int aim_im_sendmtn(OscarData *od, guint16 channel, const char *bn, guint16 event)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
    - return -EINVAL;
    -
    - if (!bn)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 11 + strlen(bn) + 2);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0014, 0x0000, NULL, 0);
    -
    - /* ICBM cookie */
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    -
    - /*
    - * Channel (should be 0x0001 for mtn)
    - */
    - byte_stream_put16(&bs, channel);
    -
    - /*
    - * Dest buddy name
    - */
    - byte_stream_put8(&bs, strlen(bn));
    - byte_stream_putstr(&bs, bn);
    -
    - /*
    - * Event (should be 0x0000, 0x0001, or 0x0002 for mtn)
    - */
    - byte_stream_put16(&bs, event);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0014, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0006 - Send eXtra Status request
    - */
    -int icq_im_xstatus_request(OscarData *od, const char *sn)
    -{
    - FlapConnection *conn;
    - aim_snacid_t snacid;
    - guchar cookie[8];
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream bs, header, plugindata;
    - PurpleAccount *account;
    - const char *fmt;
    - char *statxml;
    - int xmllen;
    -
    - static const guint8 pluginid[] = {
    - 0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00
    - };
    -
    - static const guint8 c_plugindata[] = {
    - 0x1B, 0x00, 0x0A,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00,
    - 0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C,
    - 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70,
    - 0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
    - 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41,
    - 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00
    - };
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
    - return -EINVAL;
    -
    - if (!sn)
    - return -EINVAL;
    -
    - fmt = "<N><QUERY>&lt;Q&gt;&lt;PluginID&gt;srvMng&lt;/PluginID&gt;&lt;/Q&gt;</QUERY><NOTIFY>&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;req&gt;&lt;id&gt;AwayStat&lt;/id&gt;&lt;trans&gt;2&lt;/trans&gt;&lt;senderId&gt;%s&lt;/senderId&gt;&lt;/req&gt;&lt;/srv&gt;</NOTIFY></N>\r\n";
    -
    - account = purple_connection_get_account(od->gc);
    -
    - statxml = g_strdup_printf(fmt, account->username);
    - xmllen = strlen(statxml);
    -
    - aim_icbm_makecookie(cookie);
    -
    - byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2
    - + 2 + 2 + 8 + 16 + 2 + 2 + 2 + 2 + 2
    - + 2 + 2 + sizeof(c_plugindata) + xmllen
    - + 2 + 2);
    -
    - snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
    - aim_im_puticbm(&bs, cookie, 0x0002, sn);
    -
    - byte_stream_new(&header, (7*2) + 16 + 8 + 2 + sizeof(c_plugindata) + xmllen); /* TLV 0x0005 Stream + Size */
    - byte_stream_put16(&header, 0x0000); /* Message Type: Request */
    - byte_stream_putraw(&header, cookie, sizeof(cookie)); /* Message ID */
    - byte_stream_putraw(&header, pluginid, sizeof(pluginid)); /* Plugin ID */
    -
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    -
    - /* Add Plugin Specific Data */
    - byte_stream_new(&plugindata, (sizeof(c_plugindata) + xmllen));
    - byte_stream_putraw(&plugindata, c_plugindata, sizeof(c_plugindata)); /* Content of TLV 0x2711 */
    - byte_stream_putraw(&plugindata, (const guint8*)statxml, xmllen);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, (sizeof(c_plugindata) + xmllen), plugindata.data);
    -
    - aim_tlvlist_write(&header, &inner_tlvlist);
    - aim_tlvlist_free(inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&header), header.data);
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); /* Empty TLV 0x0003 */
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - purple_debug_misc("oscar", "X-Status Request\n");
    - flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, snacid, &bs, TRUE);
    -
    - aim_tlvlist_free(outer_tlvlist);
    - byte_stream_destroy(&header);
    - byte_stream_destroy(&plugindata);
    - byte_stream_destroy(&bs);
    - g_free(statxml);
    -
    - return 0;
    -}
    -
    -int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - PurpleAccount *account;
    - PurpleStatus *status;
    - const char *fmt;
    - const char *formatted_msg;
    - char *msg;
    - char *statxml;
    - const char *title;
    - int len;
    -
    - static const guint8 plugindata[] = {
    - 0x1B, 0x00,
    - 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F,
    - 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0,
    - 0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00,
    - 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75,
    - 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
    - 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
    - 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00,
    - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00
    - };
    -
    - fmt = "<NR><RES>&lt;ret event='OnRemoteNotification'&gt;&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;val srv_id='cAwaySrv'&gt;&lt;Root&gt;&lt;CASXtraSetAwayMessage&gt;&lt;/CASXtraSetAwayMessage&gt;&l t;uin&gt;%s&lt;/uin&gt;&lt;index&gt;1&lt;/index&gt;&lt;title&gt;%s&lt;/title&gt;&lt;desc&gt;%s&lt;/desc&gt;&lt;/Root&gt;&lt;/val&gt;&lt;/srv&gt;&lt;srv&gt;&lt;id&gt;cRandomizerSrv&lt;/id&gt;&lt;val srv_id='cRandomizerSrv'&gt;undefined&lt;/val&gt;&lt;/srv&gt;&lt;/ret&gt;</RES></NR>\r\n";
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
    - return -EINVAL;
    -
    - if (!sn)
    - return -EINVAL;
    -
    - account = purple_connection_get_account(od->gc);
    - if (!account)
    - return -EINVAL;
    -
    - /* if (purple_strequal(account->username, sn))
    - icq_im_xstatus_request(od, sn); */
    -
    - status = purple_presence_get_active_status(account->presence);
    - if (!status)
    - return -EINVAL;
    -
    - title = purple_status_get_name(status);
    - if (!title)
    - return -EINVAL;
    -
    - formatted_msg = purple_status_get_attr_string(status, "message");
    - if (!formatted_msg)
    - return -EINVAL;
    -
    - msg = purple_markup_strip_html(formatted_msg);
    - if (!msg)
    - return -EINVAL;
    -
    - statxml = g_strdup_printf(fmt, account->username, title, msg);
    - len = strlen(statxml);
    -
    - purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg);
    -
    - byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + sizeof(plugindata) + len); /* 16 extra */
    -
    - snacid = aim_cachesnac(od, 0x0004, 0x000b, 0x0000, NULL, 0);
    - aim_im_puticbm(&bs, cookie, 0x0002, sn);
    - byte_stream_put16(&bs, 0x0003);
    - byte_stream_putraw(&bs, plugindata, sizeof(plugindata));
    - byte_stream_putraw(&bs, (const guint8*)statxml, len);
    -
    - flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, snacid, &bs, TRUE);
    -
    - g_free(statxml);
    - g_free(msg);
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
    - *
    - * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
    - * and Purple 0.60 and newer.
    - *
    - */
    -static int mtn_receive(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - char *bn;
    - guint8 bnlen;
    - guint16 channel, event;
    -
    - byte_stream_advance(bs, 8); /* ICBM cookie */
    - channel = byte_stream_get16(bs);
    - bnlen = byte_stream_get8(bs);
    - bn = byte_stream_getstr(bs, bnlen);
    - event = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, bn, event);
    -
    - g_free(bn);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return error(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0005)
    - return aim_im_paraminfo(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0007)
    - return incomingim(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000a)
    - return missedcall(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000b)
    - return clientautoresp(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000c)
    - return msgack(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0014)
    - return mtn_receive(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -msg_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_ICBM;
    - mod->version = 0x0001;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "messaging", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_icq.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,793 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0015 - Encapsulated ICQ.
    - *
    - */
    -
    -#include "encoding.h"
    -#include "oscar.h"
    -
    -#define AIM_ICQ_INFO_REQUEST 0x04b2
    -#define AIM_ICQ_ALIAS_REQUEST 0x04ba
    -
    -static
    -int compare_icq_infos(gconstpointer a, gconstpointer b)
    -{
    - const struct aim_icq_info* aa = a;
    - const guint16* bb = b;
    - return aa->reqid - *bb;
    -}
    -
    -static void aim_icq_freeinfo(struct aim_icq_info *info) {
    - int i;
    -
    - if (!info)
    - return;
    - g_free(info->nick);
    - g_free(info->first);
    - g_free(info->last);
    - g_free(info->email);
    - g_free(info->homecity);
    - g_free(info->homestate);
    - g_free(info->homephone);
    - g_free(info->homefax);
    - g_free(info->homeaddr);
    - g_free(info->mobile);
    - g_free(info->homezip);
    - g_free(info->personalwebpage);
    - if (info->email2)
    - for (i = 0; i < info->numaddresses; i++)
    - g_free(info->email2[i]);
    - g_free(info->email2);
    - g_free(info->workcity);
    - g_free(info->workstate);
    - g_free(info->workphone);
    - g_free(info->workfax);
    - g_free(info->workaddr);
    - g_free(info->workzip);
    - g_free(info->workcompany);
    - g_free(info->workdivision);
    - g_free(info->workposition);
    - g_free(info->workwebpage);
    - g_free(info->info);
    - g_free(info->status_note_title);
    - g_free(info->auth_request_reason);
    -}
    -
    -static
    -int error(OscarData *od, aim_modsnac_t *error_snac, ByteStream *bs)
    -{
    - aim_snac_t *original_snac = aim_remsnac(od, error_snac->id);
    - guint16 *request_type;
    - GSList *original_info_ptr;
    - struct aim_icq_info *original_info;
    - guint16 reason;
    - gchar *uin;
    -
    - if (!original_snac || (original_snac->family != SNAC_FAMILY_ICQ) || !original_snac->data) {
    - purple_debug_misc("oscar", "icq: the original snac for the error packet was not found");
    - g_free(original_snac);
    - return 0;
    - }
    -
    - request_type = original_snac->data;
    - original_info_ptr = g_slist_find_custom(od->icq_info, &original_snac->id, compare_icq_infos);
    -
    - if (!original_info_ptr) {
    - purple_debug_misc("oscar", "icq: the request info for the error packet was not found");
    - g_free(original_snac);
    - return 0;
    - }
    -
    - original_info = original_info_ptr->data;
    -
    - reason = byte_stream_get16(bs);
    - uin = g_strdup_printf("%u", original_info->uin);
    - switch (*request_type) {
    - case AIM_ICQ_INFO_REQUEST:
    - oscar_user_info_display_error(od, reason, uin);
    - break;
    - case AIM_ICQ_ALIAS_REQUEST:
    - /* Couldn't retrieve an alias for the buddy requesting authorization; have to make do with UIN only. */
    - if (original_info->for_auth_request)
    - oscar_auth_recvrequest(od->gc, uin, NULL, original_info->auth_request_reason);
    - break;
    - default:
    - purple_debug_misc("oscar", "icq: got an error packet with unknown request type %u", *request_type);
    - break;
    - }
    -
    - aim_icq_freeinfo(original_info);
    - od->icq_info = g_slist_remove(od->icq_info, original_info_ptr);
    - g_free(original_snac->data);
    - g_free(original_snac);
    - return 1;
    -}
    -
    -int
    -aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
    - return -EINVAL;
    -
    - bslen = 2+4+2+2+2+2+2+1+1+1+1+1+1;
    -
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
    -
    - /* For simplicity, don't bother using a tlvlist */
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, bslen);
    -
    - byte_stream_putle16(&bs, bslen - 2);
    - byte_stream_putuid(&bs, od);
    - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
    - byte_stream_putle16(&bs, snacid); /* eh. */
    - byte_stream_putle16(&bs, 0x0c3a); /* shrug. */
    - byte_stream_putle16(&bs, 0x030c);
    - byte_stream_putle16(&bs, 0x0001);
    - byte_stream_putle8(&bs, webaware);
    - byte_stream_putle8(&bs, 0xf8);
    - byte_stream_putle8(&bs, 0x02);
    - byte_stream_putle8(&bs, 0x01);
    - byte_stream_putle8(&bs, 0x00);
    - byte_stream_putle8(&bs, !auth_required);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/**
    - * Change your ICQ password.
    - *
    - * @param od The oscar session
    - * @param passwd The new password. If this is longer than 8 characters it
    - * will be truncated.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_icq_changepasswd(OscarData *od, const char *passwd)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen, passwdlen;
    -
    - if (!passwd)
    - return -EINVAL;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
    - return -EINVAL;
    -
    - passwdlen = strlen(passwd);
    - if (passwdlen > MAXICQPASSLEN)
    - passwdlen = MAXICQPASSLEN;
    - bslen = 2+4+2+2+2+2+passwdlen+1;
    -
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
    -
    - /* For simplicity, don't bother using a tlvlist */
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, bslen);
    -
    - byte_stream_putle16(&bs, bslen - 2);
    - byte_stream_putuid(&bs, od);
    - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
    - byte_stream_putle16(&bs, snacid); /* eh. */
    - byte_stream_putle16(&bs, 0x042e); /* shrug. */
    - byte_stream_putle16(&bs, passwdlen+1);
    - byte_stream_putraw(&bs, (const guint8 *)passwd, passwdlen);
    - byte_stream_putle8(&bs, '\0');
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -int aim_icq_getallinfo(OscarData *od, const char *uin)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen;
    - struct aim_icq_info *info;
    - guint16 request_type = AIM_ICQ_INFO_REQUEST;
    -
    - if (!uin || uin[0] < '0' || uin[0] > '9')
    - return -EINVAL;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
    - return -EINVAL;
    -
    - bslen = 2 + 4 + 2 + 2 + 2 + 4;
    -
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type));
    -
    - /* For simplicity, don't bother using a tlvlist */
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, bslen);
    -
    - byte_stream_putle16(&bs, bslen - 2);
    - byte_stream_putuid(&bs, od);
    - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
    - byte_stream_putle16(&bs, snacid); /* eh. */
    - byte_stream_putle16(&bs, request_type); /* shrug. */
    - byte_stream_putle32(&bs, atoi(uin));
    -
    - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs, FALSE);
    -
    - byte_stream_destroy(&bs);
    -
    - /* Keep track of this request and the ICQ number and request ID */
    - info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
    - info->reqid = snacid;
    - info->uin = atoi(uin);
    - od->icq_info = g_slist_prepend(od->icq_info, info);
    -
    - return 0;
    -}
    -
    -int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen;
    - struct aim_icq_info *info;
    - guint16 request_type = AIM_ICQ_ALIAS_REQUEST;
    -
    - if (!uin || uin[0] < '0' || uin[0] > '9')
    - return -EINVAL;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
    - return -EINVAL;
    -
    - purple_debug_info("oscar", "Requesting ICQ alias for %s\n", uin);
    -
    - bslen = 2 + 4 + 2 + 2 + 2 + 4;
    -
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type));
    -
    - /* For simplicity, don't bother using a tlvlist */
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, bslen);
    -
    - byte_stream_putle16(&bs, bslen - 2);
    - byte_stream_putuid(&bs, od);
    - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
    - byte_stream_putle16(&bs, snacid); /* eh. */
    - byte_stream_putle16(&bs, request_type); /* shrug. */
    - byte_stream_putle32(&bs, atoi(uin));
    -
    - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs, FALSE);
    -
    - byte_stream_destroy(&bs);
    -
    - /* Keep track of this request and the ICQ number and request ID */
    - info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
    - info->reqid = snacid;
    - info->uin = atoi(uin);
    - info->for_auth_request = for_auth_request;
    - info->auth_request_reason = g_strdup(auth_request_reason);
    - od->icq_info = g_slist_prepend(od->icq_info, info);
    -
    - return 0;
    -}
    -
    -/*
    - * Send an SMS message. This is the non-US way. The US-way is to IM
    - * their cell phone number (+19195551234).
    - *
    - * We basically construct and send an XML message. The format is:
    - * <icq_sms_message>
    - * <destination>full_phone_without_leading_+</destination>
    - * <text>message</text>
    - * <codepage>1252</codepage>
    - * <senders_UIN>self_uin</senders_UIN>
    - * <senders_name>self_name</senders_name>
    - * <delivery_receipt>Yes|No</delivery_receipt>
    - * <time>Wkd, DD Mmm YYYY HH:MM:SS TMZ</time>
    - * </icq_sms_message>
    - *
    - * Yeah hi Peter, whaaaat's happening. If there's any way to use
    - * a codepage other than 1252 that would be great. Thaaaanks.
    - */
    -int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias)
    -{
    - FlapConnection *conn;
    - PurpleAccount *account;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen, xmllen;
    - char *xml;
    - const char *timestr, *username;
    - time_t t;
    - struct tm *tm;
    - gchar *stripped;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
    - return -EINVAL;
    -
    - if (!name || !msg || !alias)
    - return -EINVAL;
    -
    - account = purple_connection_get_account(od->gc);
    - username = purple_account_get_username(account);
    -
    - time(&t);
    - tm = gmtime(&t);
    - timestr = purple_utf8_strftime("%a, %d %b %Y %T %Z", tm);
    -
    - stripped = purple_markup_strip_html(msg);
    -
    - /* The length of xml included the null terminating character */
    - xmllen = 209 + strlen(name) + strlen(stripped) + strlen(username) + strlen(alias) + strlen(timestr) + 1;
    -
    - xml = g_new(char, xmllen);
    - snprintf(xml, xmllen, "<icq_sms_message>"
    - "<destination>%s</destination>"
    - "<text>%s</text>"
    - "<codepage>1252</codepage>"
    - "<senders_UIN>%s</senders_UIN>"
    - "<senders_name>%s</senders_name>"
    - "<delivery_receipt>Yes</delivery_receipt>"
    - "<time>%s</time>"
    - "</icq_sms_message>",
    - name, stripped, username, alias, timestr);
    -
    - bslen = 36 + xmllen;
    -
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
    -
    - /* For simplicity, don't bother using a tlvlist */
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, bslen);
    -
    - byte_stream_putle16(&bs, bslen - 2);
    - byte_stream_putuid(&bs, od);
    - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
    - byte_stream_putle16(&bs, snacid); /* eh. */
    -
    - /* From libicq200-0.3.2/src/SNAC-SRV.cpp */
    - byte_stream_putle16(&bs, 0x1482);
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, 0x0016);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    -
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, xmllen);
    - byte_stream_putstr(&bs, xml);
    - byte_stream_put8(&bs, 0x00);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - g_free(xml);
    - g_free(stripped);
    -
    - return 0;
    -}
    -
    -static void
    -gotalias(OscarData *od, struct aim_icq_info *info)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleBuddy *b;
    - gchar *utf8 = oscar_utf8_try_convert(account, od, info->nick);
    -
    - if (info->for_auth_request) {
    - oscar_auth_recvrequest(gc, g_strdup_printf("%u", info->uin), utf8, info->auth_request_reason);
    - } else {
    - if (utf8 && *utf8) {
    - gchar who[16];
    - g_snprintf(who, sizeof(who), "%u", info->uin);
    - serv_got_alias(gc, who, utf8);
    - if ((b = purple_find_buddy(account, who))) {
    - purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", utf8);
    - }
    - }
    - g_free(utf8);
    - }
    -}
    -
    -/**
    - * Subtype 0x0003 - Response to SNAC_FAMILY_ICQ/0x002, contains an ICQesque packet.
    - */
    -static int
    -icqresponse(OscarData *od, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - GSList *tlvlist;
    - aim_tlv_t *datatlv;
    - ByteStream qbs;
    - guint32 ouruin;
    - guint16 cmdlen, cmd, reqid;
    -
    - if (!(tlvlist = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tlvlist, 0x0001, 1))) {
    - aim_tlvlist_free(tlvlist);
    - purple_debug_misc("oscar", "corrupt ICQ response\n");
    - return 0;
    - }
    -
    - byte_stream_init(&qbs, datatlv->value, datatlv->length);
    -
    - cmdlen = byte_stream_getle16(&qbs);
    - ouruin = byte_stream_getle32(&qbs);
    - cmd = byte_stream_getle16(&qbs);
    - reqid = byte_stream_getle16(&qbs);
    -
    - purple_debug_misc("oscar", "icq response: %d bytes, %u, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid);
    -
    - if (cmd == 0x07da) { /* information */
    - guint16 subtype;
    - GSList *info_ptr;
    - struct aim_icq_info *info;
    -
    - subtype = byte_stream_getle16(&qbs);
    - byte_stream_advance(&qbs, 1); /* 0x0a */
    -
    - /* find other data from the same request */
    - info_ptr = g_slist_find_custom(od->icq_info, &reqid, compare_icq_infos);
    - if (!info_ptr) {
    - struct aim_icq_info *new_info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
    - new_info->reqid = reqid;
    - info_ptr = od->icq_info = g_slist_prepend(od->icq_info, new_info);
    - }
    -
    - info = info_ptr->data;
    - switch (subtype) {
    - case 0x00a0: { /* hide ip status */
    - /* nothing */
    - } break;
    -
    - case 0x00aa: { /* password change status */
    - /* nothing */
    - } break;
    -
    - case 0x00c8: { /* general and "home" information */
    - info->nick = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->first = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->last = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->email = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homecity = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homestate = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homephone = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homefax = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homeaddr = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->mobile = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homezip = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homecountry = byte_stream_getle16(&qbs);
    - /* 0x0a 00 02 00 */
    - /* 1 byte timezone? */
    - /* 1 byte hide email flag? */
    - } break;
    -
    - case 0x00dc: { /* personal information */
    - info->age = byte_stream_getle8(&qbs);
    - info->unknown = byte_stream_getle8(&qbs);
    - info->gender = byte_stream_getle8(&qbs); /* Not specified=0x00, Female=0x01, Male=0x02 */
    - info->personalwebpage = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->birthyear = byte_stream_getle16(&qbs);
    - info->birthmonth = byte_stream_getle8(&qbs);
    - info->birthday = byte_stream_getle8(&qbs);
    - info->language1 = byte_stream_getle8(&qbs);
    - info->language2 = byte_stream_getle8(&qbs);
    - info->language3 = byte_stream_getle8(&qbs);
    - /* 0x00 00 01 00 00 01 00 00 00 00 00 */
    - } break;
    -
    - case 0x00d2: { /* work information */
    - info->workcity = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workstate = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workphone = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workfax = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workaddr = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workzip = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workcountry = byte_stream_getle16(&qbs);
    - info->workcompany = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workdivision = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workposition = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - byte_stream_advance(&qbs, 2); /* 0x01 00 */
    - info->workwebpage = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - } break;
    -
    - case 0x00e6: { /* additional personal information */
    - info->info = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)-1);
    - } break;
    -
    - case 0x00eb: { /* email address(es) */
    - int i;
    - info->numaddresses = byte_stream_getle16(&qbs);
    - info->email2 = (char **)g_new0(char *, info->numaddresses);
    - for (i = 0; i < info->numaddresses; i++) {
    - info->email2[i] = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - if (i+1 != info->numaddresses)
    - byte_stream_advance(&qbs, 1); /* 0x00 */
    - }
    - } break;
    -
    - case 0x00f0: { /* personal interests */
    - } break;
    -
    - case 0x00fa: { /* past background and current organizations */
    - } break;
    -
    - case 0x0104: { /* alias info */
    - info->nick = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->first = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->last = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - byte_stream_advance(&qbs, byte_stream_getle16(&qbs)); /* email address? */
    - /* Then 0x00 02 00 */
    - } break;
    -
    - case 0x010e: { /* unknown */
    - /* 0x00 00 */
    - } break;
    -
    - case 0x019a: { /* simple info */
    - byte_stream_advance(&qbs, 2);
    - info->uin = byte_stream_getle32(&qbs);
    - info->nick = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->first = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->last = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->email = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - /* Then 0x00 02 00 00 00 00 00 */
    - } break;
    -
    - /* status note title and send request for status note text */
    - case 0x0fb4: {
    - GSList *tlvlist;
    - aim_tlv_t *tlv;
    - FlapConnection *conn;
    - char *uin = NULL;
    - char *status_note_title = NULL;
    -
    - conn = flap_connection_findbygroup(od, 0x0004);
    - if (conn == NULL)
    - {
    - purple_debug_misc("oscar", "icq/0x0fb4: flap connection was not found.\n");
    - break;
    - }
    -
    - byte_stream_advance(&qbs, 0x02); /* length */
    - byte_stream_advance(&qbs, 0x2f); /* unknown stuff */
    -
    - tlvlist = aim_tlvlist_read(&qbs);
    -
    - tlv = aim_tlv_gettlv(tlvlist, 0x0032, 1);
    - if (tlv != NULL)
    - /* Get user number */
    - uin = aim_tlv_getvalue_as_string(tlv);
    -
    - tlv = aim_tlv_gettlv(tlvlist, 0x0226, 1);
    - if (tlv != NULL)
    - /* Get status note title */
    - status_note_title = aim_tlv_getvalue_as_string(tlv);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - if (uin == NULL || status_note_title == NULL)
    - {
    - purple_debug_misc("oscar", "icq/0x0fb4: uin or "
    - "status_note_title was not found\n");
    - g_free(uin);
    - g_free(status_note_title);
    - break;
    - }
    -
    - if (status_note_title[0] == '\0')
    - {
    - PurpleAccount *account;
    - PurpleBuddy *buddy;
    - PurplePresence *presence;
    - PurpleStatus *status;
    -
    - account = purple_connection_get_account(od->gc);
    - buddy = purple_find_buddy(account, uin);
    - presence = purple_buddy_get_presence(buddy);
    - status = purple_presence_get_active_status(presence);
    -
    - purple_prpl_got_user_status(account, uin,
    - purple_status_get_id(status),
    - "message", NULL, NULL);
    -
    - g_free(status_note_title);
    - }
    - else
    - {
    - struct aim_icq_info *info;
    - ByteStream bs;
    - guint32 bslen;
    - aim_snacid_t snacid;
    - guchar cookie[8];
    -
    - info = g_new0(struct aim_icq_info, 1);
    -
    - bslen = 13 + strlen(uin) + 30 + 6 + 4 + 55 + 85 + 4;
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
    -
    - aim_icbm_makecookie(cookie);
    -
    - byte_stream_putraw(&bs, cookie, 8); /* ICBM cookie */
    - byte_stream_put16(&bs, 0x0002); /* message channel */
    - byte_stream_put8(&bs, strlen(uin)); /* uin */
    - byte_stream_putstr(&bs, uin);
    -
    - byte_stream_put16(&bs, 0x0005); /* rendez vous data */
    - byte_stream_put16(&bs, 0x00b2);
    - byte_stream_put16(&bs, 0x0000); /* request */
    - byte_stream_putraw(&bs, cookie, 8); /* ICBM cookie */
    - byte_stream_put32(&bs, 0x09461349); /* ICQ server relaying */
    - byte_stream_put16(&bs, 0x4c7f);
    - byte_stream_put16(&bs, 0x11d1);
    - byte_stream_put32(&bs, 0x82224445);
    - byte_stream_put32(&bs, 0x53540000);
    -
    - byte_stream_put16(&bs, 0x000a); /* unknown TLV */
    - byte_stream_put16(&bs, 0x0002);
    - byte_stream_put16(&bs, 0x0001);
    -
    - byte_stream_put16(&bs, 0x000f); /* unknown TLV */
    - byte_stream_put16(&bs, 0x0000);
    -
    - byte_stream_put16(&bs, 0x2711); /* extended data */
    - byte_stream_put16(&bs, 0x008a);
    - byte_stream_putle16(&bs, 0x001b); /* length */
    - byte_stream_putle16(&bs, 0x0009); /* version */
    - byte_stream_putle32(&bs, 0x00000000); /* plugin: none */
    - byte_stream_putle32(&bs, 0x00000000);
    - byte_stream_putle32(&bs, 0x00000000);
    - byte_stream_putle32(&bs, 0x00000000);
    - byte_stream_putle16(&bs, 0x0000); /* unknown */
    - byte_stream_putle32(&bs, 0x00000000); /* client capabilities flags */
    - byte_stream_put8(&bs, 0x00); /* unknown */
    - byte_stream_putle16(&bs, 0x0064); /* downcounter? */
    - byte_stream_putle16(&bs, 0x000e); /* length */
    - byte_stream_putle16(&bs, 0x0064); /* downcounter? */
    - byte_stream_putle32(&bs, 0x00000000); /* unknown */
    - byte_stream_putle32(&bs, 0x00000000);
    - byte_stream_putle32(&bs, 0x00000000);
    - byte_stream_put8(&bs, 0x1a); /* message type: plugin message descibed by text string */
    - byte_stream_put8(&bs, 0x00); /* message flags */
    - byte_stream_putle16(&bs, 0x0000); /* status code */
    - byte_stream_putle16(&bs, 0x0001); /* priority code */
    - byte_stream_putle16(&bs, 0x0000); /* text length */
    -
    - byte_stream_put8(&bs, 0x3a); /* message dump */
    - byte_stream_put32(&bs, 0x00811a18);
    - byte_stream_put32(&bs, 0xbc0e6c18);
    - byte_stream_put32(&bs, 0x47a5916f);
    - byte_stream_put32(&bs, 0x18dcc76f);
    - byte_stream_put32(&bs, 0x1a010013);
    - byte_stream_put32(&bs, 0x00000041);
    - byte_stream_put32(&bs, 0x77617920);
    - byte_stream_put32(&bs, 0x53746174);
    - byte_stream_put32(&bs, 0x7573204d);
    - byte_stream_put32(&bs, 0x65737361);
    - byte_stream_put32(&bs, 0x67650100);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000015);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x0000000d);
    - byte_stream_put32(&bs, 0x00000074);
    - byte_stream_put32(&bs, 0x6578742f);
    - byte_stream_put32(&bs, 0x782d616f);
    - byte_stream_put32(&bs, 0x6c727466);
    -
    - byte_stream_put16(&bs, 0x0003); /* server ACK requested */
    - byte_stream_put16(&bs, 0x0000);
    -
    - info->uin = atoi(uin);
    - info->status_note_title = status_note_title;
    -
    - memcpy(&info->icbm_cookie, cookie, 8);
    -
    - od->icq_info = g_slist_prepend(od->icq_info, info);
    -
    - flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, snacid, &bs, FALSE);
    -
    - byte_stream_destroy(&bs);
    - }
    -
    - g_free(uin);
    -
    - } break;
    -
    - } /* End switch statement */
    -
    - if (!(snac->flags & 0x0001)) {
    - if (subtype != 0x0104)
    - oscar_user_info_display_icq(od, info);
    -
    - if (info->uin && info->nick)
    - gotalias(od, info);
    -
    - aim_icq_freeinfo(info);
    - od->icq_info = g_slist_remove(od->icq_info, info);
    - }
    - }
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return 1;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return error(od, snac, bs);
    - else if (snac->subtype == 0x0003)
    - return icqresponse(od, snac, bs);
    -
    - return 0;
    -}
    -
    -static void
    -icq_shutdown(OscarData *od, aim_module_t *mod)
    -{
    - GSList *cur;
    - for (cur = od->icq_info; cur; cur = cur->next)
    - aim_icq_freeinfo(cur->data);
    - g_slist_free(od->icq_info);
    -}
    -
    -int
    -icq_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_ICQ;
    - mod->version = 0x0001;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x047c;
    - mod->flags = 0;
    - strncpy(mod->name, "icq", sizeof(mod->name));
    - mod->snachandler = snachandler;
    - mod->shutdown = icq_shutdown;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_locate.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1556 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0002 - Locate.
    - *
    - * The functions here are responsible for requesting and parsing information-
    - * gathering SNACs. Or something like that. This family contains the SNACs
    - * for getting and setting info, away messages, directory profile thingy, etc.
    - */
    -
    -#include "oscar.h"
    -#ifdef _WIN32
    -#include "win32dep.h"
    -#endif
    -
    -/* Define to log unknown TLVs */
    -/* #define LOG_UNKNOWN_TLV */
    -
    -/*
    - * Capability blocks.
    - *
    - * These are CLSIDs. They should actually be of the form:
    - *
    - * {0x0946134b, 0x4c7f, 0x11d1,
    - * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}},
    - *
    - * But, eh.
    - */
    -static const struct {
    - guint64 flag;
    - guint8 data[16];
    -} aim_caps[] = {
    -
    - /*
    - * These are in ascending numerical order.
    - */
    -
    - /* Client understands short caps, a UUID of the form
    - * 0946XXYY-4C7F-11D1-8222-444553540000 where XXYY is the short cap. */
    - {OSCAR_CAPABILITY_SHORTCAPS,
    - {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_SECUREIM,
    - {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* OSCAR_CAPABILITY_XHTML_IM */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x00, 0x02, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_VIDEO,
    - {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* "Live Video" (SIP/RTC Video) support in Windows AIM 5.5.3501 and newer */
    - {OSCAR_CAPABILITY_LIVEVIDEO,
    - {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* "Camera" support in Windows AIM 5.5.3501 and newer */
    - {OSCAR_CAPABILITY_CAMERA,
    - {0x09, 0x46, 0x01, 0x02, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* "Microphone" support in Windows AIM 5.5.3501 and newer */
    - /* OSCAR_CAPABILITY_MICROPHONE */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x01, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* Supports RTC Audio */
    - /* OSCAR_CAPABILITY_RTCAUDIO */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x01, 0x04, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* In iChatAV (version numbers...?) */
    - {OSCAR_CAPABILITY_ICHATAV,
    - {0x09, 0x46, 0x01, 0x05, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}},
    -
    - /* Supports "new status message features" (Who advertises this one?) */
    - /* OSCAR_CAPABILITY_HOST_STATUS_TEXT_AWARE */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x01, 0x0a, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* Supports "see as I type" (Who advertises this one?) */
    - /* OSCAR_CAPABILITY_SEE_AS_I_TYPE */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x01, 0x0b, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* Client only asserts caps for services in which it is participating */
    - /* OSCAR_CAPABILITY_SMARTCAPS */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_HIPTOP,
    - {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_TALK,
    - {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_SENDFILE,
    - {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_ICQ_DIRECT,
    - {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_DIRECTIM,
    - {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_BUDDYICON,
    - {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_ADDINS,
    - {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_GETFILE,
    - {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_ICQSERVERRELAY,
    - {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /*
    - * Indeed, there are two of these. The former appears to be correct,
    - * but in some versions of winaim, the second one is set. Either they
    - * forgot to fix endianness, or they made a typo. It really doesn't
    - * matter which.
    - */
    - {OSCAR_CAPABILITY_GAMES,
    - {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    - {OSCAR_CAPABILITY_GAMES2,
    - {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* New format of caps (xtraz icons) */
    - {OSCAR_CAPABILITY_NEWCAPS,
    - {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* Support xtraz statuses */
    - {OSCAR_CAPABILITY_XTRAZ,
    - {0x1a, 0x09, 0x3c, 0x6c, 0xd7, 0xFD, 0x4e, 0xc5,
    - 0x9d, 0x51, 0xa6, 0x47, 0x4e, 0x34, 0xf5, 0xa0}},
    -
    - {OSCAR_CAPABILITY_SENDBUDDYLIST,
    - {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /*
    - * Setting this lets AIM users receive messages from ICQ users, and ICQ
    - * users receive messages from AIM users. It also lets ICQ users show
    - * up in buddy lists for AIM users, and AIM users show up in buddy lists
    - * for ICQ users. And ICQ privacy/invisibility acts like AIM privacy,
    - * in that if you add a user to your deny list, you will not be able to
    - * see them as online (previous you could still see them, but they
    - * couldn't see you.
    - */
    - {OSCAR_CAPABILITY_INTEROPERATE,
    - {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_UNICODE,
    - {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_ICHAT_SCREENSHARE,
    - {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0xf0, 0x05, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_UNICODEOLD,
    - {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
    - 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
    -
    - {OSCAR_CAPABILITY_TYPING,
    - {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd,
    - 0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}},
    -
    - /*
    - * Chat is oddball.
    - */
    - {OSCAR_CAPABILITY_CHAT,
    - {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* This is added by the servers and it only shows up for ourselves... */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
    - 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}},
    -
    - {OSCAR_CAPABILITY_ICQRTF,
    - {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
    - 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}},
    -
    - {OSCAR_CAPABILITY_APINFO,
    - {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6,
    - 0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}},
    -
    - {OSCAR_CAPABILITY_TRILLIANCRYPT,
    - {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb,
    - 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_EMPTY,
    - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_HTML_MSGS,
    - {0x01, 0x38, 0xca, 0x7b, 0x76, 0x9a, 0x49, 0x15,
    - 0x88, 0xf2, 0x13, 0xfc, 0x00, 0x97, 0x9e, 0xa8}},
    -
    - {OSCAR_CAPABILITY_LAST,
    - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
    -};
    -
    -/* Keep this array synchronized with icq_purple_moods. */
    -static const struct {
    - const char *mood;
    - guint8 data[16];
    -} icq_custom_icons[] = {
    -
    - {"thinking",
    - {0x3f, 0xb0, 0xbd, 0x36, 0xaf, 0x3b, 0x4a, 0x60,
    - 0x9e, 0xef, 0xcf, 0x19, 0x0f, 0x6a, 0x5a, 0x7f}},
    -
    - {"busy",
    - {0x48, 0x8e, 0x14, 0x89, 0x8a, 0xca, 0x4a, 0x08,
    - 0x82, 0xaa, 0x77, 0xce, 0x7a, 0x16, 0x52, 0x08}},
    -
    - {"shopping",
    - {0x63, 0x62, 0x73, 0x37, 0xa0, 0x3f, 0x49, 0xff,
    - 0x80, 0xe5, 0xf7, 0x09, 0xcd, 0xe0, 0xa4, 0xee}},
    -
    - /* This was in the original patch, but isn't what the official client
    - * (ICQ 6) sets when you choose its typewriter icon. */
    - {"typing",
    - {0x63, 0x4f, 0x6b, 0xd8 ,0xad, 0xd2, 0x4a, 0xa1,
    - 0xaa, 0xb9, 0x11, 0x5b, 0xc2, 0x6d, 0x05, 0xa1}},
    -
    - {"question",
    - {0x63, 0x14, 0x36, 0xff, 0x3f, 0x8a, 0x40, 0xd0,
    - 0xa5, 0xcb, 0x7b, 0x66, 0xe0, 0x51, 0xb3, 0x64}},
    -
    - {"angry",
    - {0x01, 0xd8, 0xd7, 0xee, 0xac, 0x3b, 0x49, 0x2a,
    - 0xa5, 0x8d, 0xd3, 0xd8, 0x77, 0xe6, 0x6b, 0x92}},
    -
    - {"plate",
    - {0xf8, 0xe8, 0xd7, 0xb2, 0x82, 0xc4, 0x41, 0x42,
    - 0x90, 0xf8, 0x10, 0xc6, 0xce, 0x0a, 0x89, 0xa6}},
    -
    - {"cinema",
    - {0x10, 0x7a, 0x9a, 0x18, 0x12, 0x32, 0x4d, 0xa4,
    - 0xb6, 0xcd, 0x08, 0x79, 0xdb, 0x78, 0x0f, 0x09}},
    -
    - {"sick",
    - {0x1f, 0x7a, 0x40, 0x71, 0xbf, 0x3b, 0x4e, 0x60,
    - 0xbc, 0x32, 0x4c, 0x57, 0x87, 0xb0, 0x4c, 0xf1}},
    -
    - {"typing",
    - {0x2c, 0xe0, 0xe4, 0xe5, 0x7c, 0x64, 0x43, 0x70,
    - 0x9c, 0x3a, 0x7a, 0x1c, 0xe8, 0x78, 0xa7, 0xdc}},
    -
    - {"suit",
    - {0xb7, 0x08, 0x67, 0xf5, 0x38, 0x25, 0x43, 0x27,
    - 0xa1, 0xff, 0xcf, 0x4c, 0xc1, 0x93, 0x97, 0x97}},
    -
    - {"bathing",
    - {0x5a, 0x58, 0x1e, 0xa1, 0xe5, 0x80, 0x43, 0x0c,
    - 0xa0, 0x6f, 0x61, 0x22, 0x98, 0xb7, 0xe4, 0xc7}},
    -
    - {"tv",
    - {0x80, 0x53, 0x7d, 0xe2, 0xa4, 0x67, 0x4a, 0x76,
    - 0xb3, 0x54, 0x6d, 0xfd, 0x07, 0x5f, 0x5e, 0xc6}},
    -
    - {"excited",
    - {0x6f, 0x49, 0x30, 0x98, 0x4f, 0x7c, 0x4a, 0xff,
    - 0xa2, 0x76, 0x34, 0xa0, 0x3b, 0xce, 0xae, 0xa7}},
    -
    - {"sleeping",
    - {0x78, 0x5e, 0x8c, 0x48, 0x40, 0xd3, 0x4c, 0x65,
    - 0x88, 0x6f, 0x04, 0xcf, 0x3f, 0x3f, 0x43, 0xdf}},
    -
    - {"hiptop",
    - {0x10, 0x11, 0x17, 0xc9, 0xa3, 0xb0, 0x40, 0xf9,
    - 0x81, 0xac, 0x49, 0xe1, 0x59, 0xfb, 0xd5, 0xd4}},
    -
    - {"in_love",
    - {0xdd, 0xcf, 0x0e, 0xa9, 0x71, 0x95, 0x40, 0x48,
    - 0xa9, 0xc6, 0x41, 0x32, 0x06, 0xd6, 0xf2, 0x80}},
    -
    - {"sleepy",
    - {0x83, 0xc9, 0xb7, 0x8e, 0x77, 0xe7, 0x43, 0x78,
    - 0xb2, 0xc5, 0xfb, 0x6c, 0xfc, 0xc3, 0x5b, 0xec}},
    -
    - {"meeting",
    - {0xf1, 0x8a, 0xb5, 0x2e, 0xdc, 0x57, 0x49, 0x1d,
    - 0x99, 0xdc, 0x64, 0x44, 0x50, 0x24, 0x57, 0xaf}},
    -
    - {"phone",
    - {0x12, 0x92, 0xe5, 0x50, 0x1b, 0x64, 0x4f, 0x66,
    - 0xb2, 0x06, 0xb2, 0x9a, 0xf3, 0x78, 0xe4, 0x8d}},
    -
    - {"surfing",
    - {0xa6, 0xed, 0x55, 0x7e, 0x6b, 0xf7, 0x44, 0xd4,
    - 0xa5, 0xd4, 0xd2, 0xe7, 0xd9, 0x5c, 0xe8, 0x1f}},
    -
    - {"mobile",
    - {0x16, 0x0c, 0x60, 0xbb, 0xdd, 0x44, 0x43, 0xf3,
    - 0x91, 0x40, 0x05, 0x0f, 0x00, 0xe6, 0xc0, 0x09}},
    -
    - {"search",
    - {0xd4, 0xe2, 0xb0, 0xba, 0x33, 0x4e, 0x4f, 0xa5,
    - 0x98, 0xd0, 0x11, 0x7d, 0xbf, 0x4d, 0x3c, 0xc8}},
    -
    - {"party",
    - {0xe6, 0x01, 0xe4, 0x1c, 0x33, 0x73, 0x4b, 0xd1,
    - 0xbc, 0x06, 0x81, 0x1d, 0x6c, 0x32, 0x3d, 0x81}},
    -
    - {"coffee",
    - {0x1b, 0x78, 0xae, 0x31, 0xfa, 0x0b, 0x4d, 0x38,
    - 0x93, 0xd1, 0x99, 0x7e, 0xee, 0xaf, 0xb2, 0x18}},
    -
    - {"console",
    - {0xd4, 0xa6, 0x11, 0xd0, 0x8f, 0x01, 0x4e, 0xc0,
    - 0x92, 0x23, 0xc5, 0xb6, 0xbe, 0xc6, 0xcc, 0xf0}},
    -
    - {"internet",
    - {0x12, 0xd0, 0x7e, 0x3e, 0xf8, 0x85, 0x48, 0x9e,
    - 0x8e, 0x97, 0xa7, 0x2a, 0x65, 0x51, 0xe5, 0x8d}},
    -
    - {"cigarette",
    - {0x64, 0x43, 0xc6, 0xaf, 0x22, 0x60, 0x45, 0x17,
    - 0xb5, 0x8c, 0xd7, 0xdf, 0x8e, 0x29, 0x03, 0x52}},
    -
    - {"writing",
    - {0x00, 0x72, 0xd9, 0x08, 0x4a, 0xd1, 0x43, 0xdd,
    - 0x91, 0x99, 0x6f, 0x02, 0x69, 0x66, 0x02, 0x6f}},
    -
    - {"beer",
    - {0x8c, 0x50, 0xdb, 0xae, 0x81, 0xed, 0x47, 0x86,
    - 0xac, 0xca, 0x16, 0xcc, 0x32, 0x13, 0xc7, 0xb7}},
    -
    - {"music",
    - {0x61, 0xbe, 0xe0, 0xdd, 0x8b, 0xdd, 0x47, 0x5d,
    - 0x8d, 0xee, 0x5f, 0x4b, 0xaa, 0xcf, 0x19, 0xa7}},
    -
    - {"studying",
    - {0x60, 0x9d, 0x52, 0xf8, 0xa2, 0x9a, 0x49, 0xa6,
    - 0xb2, 0xa0, 0x25, 0x24, 0xc5, 0xe9, 0xd2, 0x60}},
    -
    - {"working",
    - {0xba, 0x74, 0xdb, 0x3e, 0x9e, 0x24, 0x43, 0x4b,
    - 0x87, 0xb6, 0x2f, 0x6b, 0x8d, 0xfe, 0xe5, 0x0f}},
    -
    - {"restroom",
    - {0x16, 0xf5, 0xb7, 0x6f, 0xa9, 0xd2, 0x40, 0x35,
    - 0x8c, 0xc5, 0xc0, 0x84, 0x70, 0x3c, 0x98, 0xfa}},
    -
    - {NULL,
    - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}
    -};
    -
    -/* Keep this array synchronized with icq_custom_icons. */
    -static PurpleMood icq_purple_moods[] = {
    - {"thinking", N_("Thinking"), NULL},
    - {"busy", N_("Busy"), NULL},
    - {"shopping", N_("Shopping"), NULL},
    - /* This was in the original patch, but isn't what the official client
    - * (ICQ 6) sets when you choose its typewriter icon. */
    - {"typing", NULL, NULL},
    - {"question", N_("Questioning"), NULL},
    - {"angry", N_("Angry"), NULL},
    - {"plate", N_("Eating"), NULL},
    - {"cinema", N_("Watching a movie"), NULL},
    - {"sick", N_("Sick"), NULL},
    - {"typing", N_("Typing"), NULL},
    - {"suit", N_("At the office"), NULL},
    - {"bathing", N_("Taking a bath"), NULL},
    - {"tv", N_("Watching TV"), NULL},
    - {"excited", N_("Having fun"), NULL},
    - {"sleeping", N_("Sleeping"), NULL},
    - {"hiptop", N_("Using a PDA"), NULL},
    - {"in_love", N_("In love"), NULL},
    - /* Sleepy / Tired */
    - {"sleepy", N_("Sleepy"), NULL},
    - {"meeting", N_("Meeting friends"), NULL},
    - {"phone", N_("On the phone"), NULL},
    - {"surfing", N_("Surfing"), NULL},
    - /* "I am mobile." / "John is mobile." */
    - {"mobile", N_("Mobile"), NULL},
    - {"search", N_("Searching the web"), NULL},
    - {"party", N_("At a party"), NULL},
    - {"coffee", N_("Having Coffee"), NULL},
    - /* Playing video games */
    - {"console", N_("Gaming"), NULL},
    - {"internet", N_("Browsing the web"), NULL},
    - {"cigarette", N_("Smoking"), NULL},
    - {"writing", N_("Writing"), NULL},
    - /* Drinking [Alcohol] */
    - {"beer", N_("Drinking"), NULL},
    - {"music", N_("Listening to music"), NULL},
    - {"studying", N_("Studying"), NULL},
    - {"working", N_("Working"), NULL},
    - {"restroom", N_("In the restroom"), NULL},
    - /* Mark the last record. */
    - {NULL, NULL, NULL},
    -};
    -
    -
    -/*
    - * Add the userinfo to our linked list. If we already have userinfo
    - * for this buddy, then just overwrite parts of the old data.
    - *
    - * @param userinfo Contains the new information for the buddy.
    - */
    -static void
    -aim_locate_adduserinfo(OscarData *od, aim_userinfo_t *userinfo)
    -{
    - aim_userinfo_t *cur;
    -
    - cur = aim_locate_finduserinfo(od, userinfo->bn);
    -
    - if (cur == NULL) {
    - cur = (aim_userinfo_t *)g_new0(aim_userinfo_t, 1);
    - cur->bn = g_strdup(userinfo->bn);
    - cur->next = od->locate.userinfo;
    - od->locate.userinfo = cur;
    - }
    -
    - cur->warnlevel = userinfo->warnlevel;
    - cur->idletime = userinfo->idletime;
    - if (userinfo->flags != 0)
    - cur->flags = userinfo->flags;
    - if (userinfo->createtime != 0)
    - cur->createtime = userinfo->createtime;
    - if (userinfo->membersince != 0)
    - cur->membersince = userinfo->membersince;
    - if (userinfo->onlinesince != 0)
    - cur->onlinesince = userinfo->onlinesince;
    - if (userinfo->sessionlen != 0)
    - cur->sessionlen = userinfo->sessionlen;
    - if (userinfo->capabilities != 0)
    - cur->capabilities = userinfo->capabilities;
    -
    - cur->present |= userinfo->present;
    -
    - if (userinfo->iconcsumlen > 0) {
    - g_free(cur->iconcsum);
    - cur->iconcsum = (guint8 *)g_malloc(userinfo->iconcsumlen);
    - memcpy(cur->iconcsum, userinfo->iconcsum, userinfo->iconcsumlen);
    - cur->iconcsumlen = userinfo->iconcsumlen;
    - }
    -
    - if (userinfo->info != NULL) {
    - g_free(cur->info);
    - g_free(cur->info_encoding);
    - if (userinfo->info_len > 0) {
    - cur->info = (char *)g_malloc(userinfo->info_len);
    - memcpy(cur->info, userinfo->info, userinfo->info_len);
    - } else
    - cur->info = NULL;
    - cur->info_encoding = g_strdup(userinfo->info_encoding);
    - cur->info_len = userinfo->info_len;
    - }
    -
    - if (userinfo->status != NULL) {
    - g_free(cur->status);
    - g_free(cur->status_encoding);
    - if (userinfo->status_len > 0) {
    - cur->status = (char *)g_malloc(userinfo->status_len);
    - memcpy(cur->status, userinfo->status, userinfo->status_len);
    - } else
    - cur->status = NULL;
    - if (userinfo->status_encoding != NULL)
    - cur->status_encoding = g_strdup(userinfo->status_encoding);
    - else
    - cur->status_encoding = NULL;
    - cur->status_len = userinfo->status_len;
    - }
    -
    - if (userinfo->itmsurl != NULL) {
    - g_free(cur->itmsurl);
    - g_free(cur->itmsurl_encoding);
    - if (userinfo->itmsurl_len > 0) {
    - cur->itmsurl = (char *)g_malloc(userinfo->itmsurl_len);
    - memcpy(cur->itmsurl, userinfo->itmsurl, userinfo->itmsurl_len);
    - } else
    - cur->itmsurl = NULL;
    - if (userinfo->itmsurl_encoding != NULL)
    - cur->itmsurl_encoding = g_strdup(userinfo->itmsurl_encoding);
    - else
    - cur->itmsurl_encoding = NULL;
    - cur->itmsurl_len = userinfo->itmsurl_len;
    - }
    -
    - if (userinfo->away != NULL) {
    - g_free(cur->away);
    - g_free(cur->away_encoding);
    - if (userinfo->away_len > 0) {
    - cur->away = (char *)g_malloc(userinfo->away_len);
    - memcpy(cur->away, userinfo->away, userinfo->away_len);
    - } else
    - cur->away = NULL;
    - cur->away_encoding = g_strdup(userinfo->away_encoding);
    - cur->away_len = userinfo->away_len;
    -
    - } else {
    - /*
    - * We don't have an away message specified in this user_info
    - * block, so clear any cached away message now.
    - */
    - if (cur->away) {
    - g_free(cur->away);
    - cur->away = NULL;
    - }
    - if (cur->away_encoding) {
    - g_free(cur->away_encoding);
    - cur->away_encoding = NULL;
    - }
    - cur->away_len = 0;
    - }
    -}
    -
    -aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn) {
    - aim_userinfo_t *cur = NULL;
    -
    - if (bn == NULL)
    - return NULL;
    -
    - cur = od->locate.userinfo;
    -
    - while (cur != NULL) {
    - if (oscar_util_name_compare(cur->bn, bn) == 0)
    - return cur;
    - cur = cur->next;
    - }
    -
    - return NULL;
    -}
    -
    -guint64
    -aim_locate_getcaps(OscarData *od, ByteStream *bs, int len)
    -{
    - guint64 flags = 0;
    - int offset;
    -
    - for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) {
    - guint8 *cap;
    - int i, identified;
    -
    - cap = byte_stream_getraw(bs, 0x10);
    -
    - for (i = 0, identified = 0; !(aim_caps[i].flag & OSCAR_CAPABILITY_LAST); i++) {
    - if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) {
    - flags |= aim_caps[i].flag;
    - identified++;
    - break; /* should only match once... */
    - }
    - }
    -
    - if (!identified)
    - purple_debug_misc("oscar", "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
    - cap[0], cap[1], cap[2], cap[3],
    - cap[4], cap[5],
    - cap[6], cap[7],
    - cap[8], cap[9],
    - cap[10], cap[11], cap[12], cap[13],
    - cap[14], cap[15]);
    - g_free(cap);
    - }
    -
    - return flags;
    -}
    -
    -static const char *
    -aim_receive_custom_icon(OscarData *od, ByteStream *bs, int len)
    -{
    - int offset;
    - const char *result = NULL;
    -
    - for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) {
    - /* check wheather this capability is a custom user icon */
    - guint8 *cap;
    - int i;
    -
    - cap = byte_stream_getraw(bs, 0x10);
    -
    - for (i = 0; icq_custom_icons[i].mood; i++) {
    - if (memcmp(&icq_custom_icons[i].data, cap, 0x10) == 0) {
    - purple_debug_misc("oscar", "Custom status icon: %s\n", icq_purple_moods[i].description);
    - result = icq_custom_icons[i].mood;
    - break; /* should only match once... */
    - }
    - }
    - g_free(cap);
    - }
    -
    - return result;
    -}
    -
    -guint64
    -aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len)
    -{
    - guint64 flags = 0;
    - int offset;
    -
    - for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x02) {
    - guint8 *cap;
    - int i, identified;
    -
    - cap = byte_stream_getraw(bs, 0x02);
    -
    - for (i = 0, identified = 0; !(aim_caps[i].flag & OSCAR_CAPABILITY_LAST); i++) {
    - if (memcmp(&aim_caps[i].data[2], cap, 0x02) == 0) {
    - flags |= aim_caps[i].flag;
    - identified++;
    - break; /* should only match once... */
    - }
    - }
    -
    - if (!identified)
    - purple_debug_misc("oscar", "unknown short capability: {%02x%02x}\n", cap[0], cap[1]);
    -
    - g_free(cap);
    - }
    -
    - return flags;
    -}
    -
    -int
    -byte_stream_putcaps(ByteStream *bs, guint64 caps)
    -{
    - int i;
    -
    - if (!bs)
    - return -EINVAL;
    -
    - for (i = 0; byte_stream_bytes_left(bs); i++) {
    - if (aim_caps[i].flag == OSCAR_CAPABILITY_LAST)
    - break;
    -
    - if (caps & aim_caps[i].flag)
    - byte_stream_putraw(bs, aim_caps[i].data, 0x10);
    - }
    - return 0;
    -}
    -
    -#ifdef LOG_UNKNOWN_TLV
    -static void
    -dumptlv(OscarData *od, guint16 type, ByteStream *bs, guint8 len)
    -{
    - int i;
    -
    - if (!od || !bs || !len)
    - return;
    -
    - purple_debug_misc("oscar", "userinfo: type =0x%04x\n", type);
    - purple_debug_misc("oscar", "userinfo: length=0x%04x\n", len);
    - purple_debug_misc("oscar", "userinfo: value:\n");
    -
    - for (i = 0; i < len; i++) {
    - if ((i % 8) == 0)
    - purple_debug_misc("oscar", "\nuserinfo: ");
    - purple_debug_misc("oscar", "0x%2x ", byte_stream_get8(bs));
    - }
    -
    - purple_debug_misc("oscar", "\n");
    -
    - return;
    -}
    -#endif
    -
    -void
    -aim_info_free(aim_userinfo_t *info)
    -{
    - g_free(info->bn);
    - g_free(info->iconcsum);
    - g_free(info->info);
    - g_free(info->info_encoding);
    - g_free(info->status);
    - g_free(info->status_encoding);
    - g_free(info->itmsurl);
    - g_free(info->itmsurl_encoding);
    - g_free(info->away);
    - g_free(info->away_encoding);
    -}
    -
    -static const struct {
    - char *icqmood;
    - const char *mood;
    -} icqmoods[] = {
    - {"icqmood0", "shopping"},
    - {"icqmood1", "bathing"},
    - {"icqmood2", "sleepy"},
    - {"icqmood3", "party"},
    - {"icqmood4", "beer"},
    - {"icqmood5", "thinking"},
    - {"icqmood6", "plate"},
    - {"icqmood7", "tv"},
    - {"icqmood8", "meeting"},
    - {"icqmood9", "coffee"},
    - {"icqmood10", "music"},
    - {"icqmood11", "suit"},
    - {"icqmood12", "cinema"},
    - {"icqmood13", "smile-big"},
    - {"icqmood14", "phone"},
    - {"icqmood15", "console"},
    - {"icqmood16", "studying"},
    - {"icqmood17", "sick"},
    - {"icqmood18", "sleeping"},
    - {"icqmood19", "surfing"},
    - {"icqmood20", "internet"},
    - {"icqmood21", "working"},
    - {"icqmood22", "typing"},
    - {"icqmood23", "angry"},
    - {NULL, 0}
    -
    -};
    -
    -/*
    - * AIM is fairly regular about providing user info. This is a generic
    - * routine to extract it in its standard form.
    - */
    -int
    -aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *outinfo)
    -{
    - int curtlv, tlvcnt;
    - guint8 bnlen;
    -
    - if (!bs || !outinfo)
    - return -EINVAL;
    -
    - /* Clear out old data first */
    - memset(outinfo, 0x00, sizeof(aim_userinfo_t));
    -
    - /*
    - * Username. Stored as an unterminated string prepended with a
    - * byte containing its length.
    - */
    - bnlen = byte_stream_get8(bs);
    - outinfo->bn = byte_stream_getstr(bs, bnlen);
    -
    - /*
    - * Warning Level. Stored as an unsigned short.
    - */
    - outinfo->warnlevel = byte_stream_get16(bs);
    -
    - /*
    - * TLV Count. Unsigned short representing the number of
    - * Type-Length-Value triples that follow.
    - */
    - tlvcnt = byte_stream_get16(bs);
    -
    - /*
    - * Parse out the Type-Length-Value triples as they're found.
    - */
    - for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
    - guint16 type, length;
    - int endpos;
    - int curpos;
    -
    - type = byte_stream_get16(bs);
    - length = byte_stream_get16(bs);
    - curpos = byte_stream_curpos(bs);
    - endpos = curpos + MIN(length, byte_stream_bytes_left(bs));
    -
    - if (type == 0x0001) {
    - /*
    - * User flags
    - *
    - * Specified as any of the following ORed together:
    - * 0x0001 Unconfirmed account
    - * 0x0002 Unknown bit 2
    - * 0x0004 AOL Main Service user
    - * 0x0008 Unknown bit 4
    - * 0x0010 Free (AIM) user
    - * 0x0020 Away
    - * 0x0040 ICQ user (AIM bit also set)
    - * 0x0080 Mobile device
    - * 0x0400 Bot (like ActiveBuddy)
    - */
    - outinfo->flags = byte_stream_get16(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_FLAGS;
    -
    - } else if (type == 0x0002) {
    - /*
    - * Account creation time
    - *
    - * The time/date that the user originally registered for
    - * the service, stored in time_t format.
    - *
    - * I'm not sure how this differs from type 5 ("member
    - * since").
    - *
    - * Note: This is the field formerly known as "member
    - * since". All these years and I finally found out
    - * that I got the name wrong.
    - */
    - outinfo->createtime = byte_stream_get32(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME;
    -
    - } else if (type == 0x0003) {
    - /*
    - * On-Since date
    - *
    - * The time/date that the user started their current
    - * session, stored in time_t format.
    - */
    - outinfo->onlinesince = byte_stream_get32(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE;
    -
    - } else if (type == 0x0004) {
    - /*
    - * Idle time
    - *
    - * Number of minutes since the user actively used the
    - * service.
    - *
    - * Note that the client tells the server when to start
    - * counting idle times, so this may or may not be
    - * related to reality.
    - */
    - outinfo->idletime = byte_stream_get16(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_IDLE;
    -
    - } else if (type == 0x0005) {
    - /*
    - * Member since date
    - *
    - * The time/date that the user originally registered for
    - * the service, stored in time_t format.
    - *
    - * This is sometimes sent instead of type 2 ("account
    - * creation time"), particularly in the self-info.
    - * And particularly for ICQ?
    - */
    - outinfo->membersince = byte_stream_get32(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE;
    -
    - } else if (type == 0x0006) {
    - /*
    - * ICQ Online Status
    - *
    - * ICQ's Away/DND/etc "enriched" status. Some decoding
    - * of values done by Scott <darkagl@pcnet.com>
    - */
    - byte_stream_get16(bs);
    - outinfo->icqinfo.status = byte_stream_get16(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS;
    -
    - } else if (type == 0x0008) {
    - /*
    - * Client type, or some such.
    - */
    -
    - } else if (type == 0x000a) {
    - /*
    - * ICQ User IP Address
    - *
    - * Ahh, the joy of ICQ security.
    - */
    - outinfo->icqinfo.ipaddr = byte_stream_get32(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR;
    -
    - } else if (type == 0x000c) {
    - /*
    - * Random crap containing the IP address,
    - * apparently a port number, and some Other Stuff.
    - *
    - * Format is:
    - * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43
    - */
    - byte_stream_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
    - outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA;
    -
    - } else if (type == 0x000d) {
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - const char *mood;
    -
    - /*
    - * OSCAR Capability information
    - */
    - outinfo->capabilities |= aim_locate_getcaps(od, bs, length);
    - outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
    - byte_stream_setpos(bs, curpos);
    -
    - mood = aim_receive_custom_icon(od, bs, length);
    - if (mood)
    - purple_prpl_got_user_status(account, outinfo->bn, "mood",
    - PURPLE_MOOD_NAME, mood,
    - NULL);
    - else
    - purple_prpl_got_user_status_deactive(account, outinfo->bn, "mood");
    -
    - } else if (type == 0x000e) {
    - /*
    - * AOL capability information
    - */
    -
    - } else if ((type == 0x000f) || (type == 0x0010)) {
    - /*
    - * Type = 0x000f: Session Length. (AIM)
    - * Type = 0x0010: Session Length. (AOL)
    - *
    - * The duration, in seconds, of the user's current
    - * session.
    - *
    - * Which TLV type this comes in depends on the
    - * service the user is using (AIM or AOL).
    - */
    - outinfo->sessionlen = byte_stream_get32(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN;
    -
    - } else if (type == 0x0014) {
    - /*
    - * My instance number.
    - */
    - byte_stream_get8(bs);
    -
    - } else if (type == 0x0019) {
    - /*
    - * OSCAR short capability information. A shortened
    - * form of the normal capabilities.
    - */
    - outinfo->capabilities |= aim_locate_getcaps_short(od, bs, length);
    - outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
    -
    - } else if (type == 0x001a) {
    - /*
    - * Type = 0x001a
    - *
    - * AOL short capability information. A shortened
    - * form of the normal capabilities.
    - */
    -
    - } else if (type == 0x001b) {
    - /*
    - * Encryption certification MD5 checksum.
    - */
    -
    - } else if (type == 0x001d) {
    - /*
    - * Buddy icon information and status/available messages.
    - *
    - * This almost seems like the AIM protocol guys gave
    - * the iChat guys a Type, and the iChat guys tried to
    - * cram as much cool shit into it as possible. Then
    - * the Windows AIM guys were like, "hey, that's
    - * pretty neat, let's copy those prawns."
    - *
    - * In that spirit, this can contain a custom message,
    - * kind of like an away message, but you're not away
    - * (it's called an "available" message). Or it can
    - * contain information about the buddy icon the user
    - * has stored on the server.
    - */
    - guint16 type2;
    - guint8 number2, length2;
    - int endpos2;
    -
    - /*
    - * Continue looping as long as we're able to read type2,
    - * number2, and length2.
    - */
    - while (byte_stream_curpos(bs) + 4 <= endpos) {
    - type2 = byte_stream_get16(bs);
    - number2 = byte_stream_get8(bs);
    - length2 = byte_stream_get8(bs);
    -
    - endpos2 = byte_stream_curpos(bs) + MIN(length2, byte_stream_bytes_left(bs));
    -
    - switch (type2) {
    - case 0x0000: { /* This is an official buddy icon? */
    - /* This is always 5 bytes of "0x02 01 d2 04 72"? */
    - } break;
    -
    - case 0x0001: { /* A buddy icon checksum */
    - if ((length2 > 0) && ((number2 == 0x00) || (number2 == 0x01))) {
    - g_free(outinfo->iconcsum);
    - outinfo->iconcsumtype = number2;
    - outinfo->iconcsum = byte_stream_getraw(bs, length2);
    - outinfo->iconcsumlen = length2;
    - }
    - } break;
    -
    - case 0x0002: { /* A status/available message */
    - g_free(outinfo->status);
    - g_free(outinfo->status_encoding);
    - if (length2 >= 4) {
    - outinfo->status_len = byte_stream_get16(bs);
    - outinfo->status = byte_stream_getstr(bs, outinfo->status_len);
    - if (byte_stream_get16(bs) == 0x0001) { /* We have an encoding */
    - byte_stream_get16(bs);
    - outinfo->status_encoding = byte_stream_getstr(bs, byte_stream_get16(bs));
    - } else {
    - /* No explicit encoding, client should use UTF-8 */
    - outinfo->status_encoding = NULL;
    - }
    - } else {
    - byte_stream_advance(bs, length2);
    - outinfo->status_len = 0;
    - outinfo->status = g_strdup("");
    - outinfo->status_encoding = NULL;
    - }
    - } break;
    -
    - case 0x0009: { /* An iTunes Music Store link */
    - g_free(outinfo->itmsurl);
    - g_free(outinfo->itmsurl_encoding);
    - if (length2 >= 4) {
    - outinfo->itmsurl_len = byte_stream_get16(bs);
    - outinfo->itmsurl = byte_stream_getstr(bs, outinfo->itmsurl_len);
    - if (byte_stream_get16(bs) == 0x0001) {
    - /* We have an encoding */
    - byte_stream_get16(bs);
    - outinfo->itmsurl_encoding = byte_stream_getstr(bs, byte_stream_get16(bs));
    - } else {
    - /* No explicit encoding, client should use UTF-8 */
    - outinfo->itmsurl_encoding = NULL;
    - }
    - } else {
    - byte_stream_advance(bs, length2);
    - outinfo->itmsurl_len = 0;
    - outinfo->itmsurl = g_strdup("");
    - outinfo->itmsurl_encoding = NULL;
    - }
    - } break;
    -
    - case 0x000e: { /* ICQ mood */
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - char *icqmood;
    - gint32 i;
    - const char *mood = NULL;
    -
    - icqmood = byte_stream_getstr(bs, length2);
    -
    - /* icqmood = "" means X-Status
    - * with no mood icon. */
    - if (*icqmood) {
    - for (i = 0; icqmoods[i].icqmood; i++) {
    - if (purple_strequal(icqmood, icqmoods[i].icqmood)) {
    - mood = icqmoods[i].mood;
    - break; /* should only match once... */
    - }
    - }
    -
    - if (!mood)
    - purple_debug_warning("oscar", "Unknown icqmood: %s\n", icqmood);
    - }
    - g_free(icqmood);
    -
    - if (mood)
    - purple_prpl_got_user_status(account, outinfo->bn, "mood",
    - PURPLE_MOOD_NAME, mood,
    - NULL);
    - else
    - purple_prpl_got_user_status_deactive(account, outinfo->bn, "mood");
    - } break;
    - }
    -
    - /* Save ourselves. */
    - byte_stream_setpos(bs, endpos2);
    - }
    -
    - } else if (type == 0x001e) {
    - /*
    - * Always four bytes, but it doesn't look like an int.
    - */
    -
    - } else if (type == 0x001f) {
    - /*
    - * Upper bytes of user flags. Can be any size
    - *
    - * Seen on a buddy using DeadAIM. Data was 4 bytes:
    - * 0x00 00 00 10
    - */
    -
    - } else if (type == 0x0023) {
    - /*
    - * Last Buddy Feed update time, in seconds since the epoch.
    - */
    -
    - } else if (type == 0x0026) {
    - /*
    - * Time that the profile was set, in seconds since the epoch.
    - */
    -
    - } else if (type == 0x0027) {
    - /*
    - * Time that the away message was set, in seconds since the epoch.
    - */
    -
    - } else if (type == 0x002a) {
    - /*
    - * Country code based on GeoIP data.
    - */
    -
    - } else {
    -
    - /*
    - * Reaching here indicates that either AOL has
    - * added yet another TLV for us to deal with,
    - * or the parsing has gone Terribly Wrong.
    - *
    - * Either way, inform the owner and attempt
    - * recovery.
    - *
    - */
    -#ifdef LOG_UNKNOWN_TLV
    - purple_debug_misc("oscar", "userinfo: **warning: unexpected TLV:\n");
    - purple_debug_misc("oscar", "userinfo: bn =%s\n", outinfo->bn);
    - dumptlv(od, type, bs, length);
    -#endif
    - }
    -
    - /* Save ourselves. */
    - byte_stream_setpos(bs, endpos);
    - }
    -
    - aim_locate_adduserinfo(od, outinfo);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0001
    - */
    -static int
    -error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_snac_t *snac2;
    - guint16 reason;
    - char *bn;
    -
    - snac2 = aim_remsnac(od, snac->id);
    - if (!snac2) {
    - purple_debug_misc("oscar", "locate error: received response from unknown request!\n");
    - return 0;
    - }
    -
    - if ((snac2->family != SNAC_FAMILY_LOCATE) && (snac2->type != 0x0015)) {
    - purple_debug_misc("oscar", "locate error: received response from invalid request! %d\n", snac2->family);
    - g_free(snac2->data);
    - g_free(snac2);
    - return 0;
    - }
    -
    - bn = snac2->data;
    - if (!bn) {
    - purple_debug_misc("oscar", "locate error: received response from request without a buddy name!\n");
    - g_free(snac2);
    - return 0;
    - }
    -
    - reason = byte_stream_get16(bs);
    -
    - oscar_user_info_display_error(od, reason, bn);
    -
    - g_free(snac2->data);
    - g_free(snac2);
    -
    - return 1;
    -}
    -
    -/*
    - * Subtype 0x0002
    - *
    - * Request Location services rights.
    - *
    - */
    -int
    -aim_locate_reqrights(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
    - return -EINVAL;
    -
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_REQRIGHTS);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0003
    - *
    - * Normally contains:
    - * t(0001) - short containing max profile length (value = 1024)
    - * t(0002) - short - unknown (value = 16) [max MIME type length?]
    - * t(0003) - short - unknown (value = 10)
    - * t(0004) - short - unknown (value = 2048) [ICQ only?]
    - */
    -static int
    -rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - GSList *tlvlist;
    - aim_rxcallback_t userfunc;
    - int ret = 0;
    - guint16 maxsiglen = 0;
    -
    - tlvlist = aim_tlvlist_read(bs);
    -
    - if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
    - maxsiglen = aim_tlv_get16(tlvlist, 0x0001, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, maxsiglen);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0004
    - *
    - * Gives BOS your profile.
    - *
    - * profile_encoding and awaymsg_encoding MUST be set if profile or
    - * away are set, respectively, and their value may or may not be
    - * restricted to a few choices. I am currently aware of:
    - *
    - * us-ascii Just that
    - * unicode-2-0 UTF-16BE
    - *
    - * profile_len and awaymsg_len MUST be set similarly, and they MUST
    - * be the length of their respective strings in bytes.
    - *
    - * To get the previous behavior of awaymsg == "" un-setting the away
    - * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the
    - * obvious equivalent).
    - *
    - */
    -int
    -aim_locate_setprofile(OscarData *od,
    - const char *profile_encoding, const gchar *profile, const int profile_len,
    - const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    - char *encoding;
    - static const char defencoding[] = {"text/aolrtf; charset=\"%s\""};
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
    - return -EINVAL;
    -
    - if (!profile && !awaymsg)
    - return -EINVAL;
    -
    - if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) {
    - return -EINVAL;
    - }
    -
    - /* Build the packet first to get real length */
    - if (profile) {
    - /* no + 1 here because of %s */
    - encoding = g_malloc(strlen(defencoding) + strlen(profile_encoding));
    - snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding);
    - aim_tlvlist_add_str(&tlvlist, 0x0001, encoding);
    - aim_tlvlist_add_raw(&tlvlist, 0x0002, profile_len, (const guchar *)profile);
    - g_free(encoding);
    - }
    -
    - /*
    - * So here's how this works:
    - * - You are away when you have a non-zero-length type 4 TLV stored.
    - * - You become unaway when you clear the TLV with a zero-length
    - * type 4 TLV.
    - * - If you do not send the type 4 TLV, your status does not change
    - * (that is, if you were away, you'll remain away).
    - */
    - if (awaymsg) {
    - if (awaymsg_len) {
    - encoding = g_malloc(strlen(defencoding) + strlen(awaymsg_encoding));
    - snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding);
    - aim_tlvlist_add_str(&tlvlist, 0x0003, encoding);
    - aim_tlvlist_add_raw(&tlvlist, 0x0004, awaymsg_len, (const guchar *)awaymsg);
    - g_free(encoding);
    - } else
    - aim_tlvlist_add_noval(&tlvlist, 0x0004);
    - }
    -
    - byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0004 - Set your client's capabilities.
    - */
    -int
    -aim_locate_setcaps(OscarData *od, guint64 caps)
    -{
    - FlapConnection *conn;
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - PurplePresence *presence = purple_account_get_presence(account);
    - PurpleStatus *status = purple_presence_get_status(presence, "mood");
    - const char *mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - if (!(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
    - return -EINVAL;
    -
    - aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, mood);
    -
    - byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/* Subtype 0x0006 */
    -static int
    -userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_userinfo_t *userinfo, *userinfo2;
    - GSList *tlvlist;
    - aim_tlv_t *tlv = NULL;
    -
    - userinfo = (aim_userinfo_t *)g_malloc(sizeof(aim_userinfo_t));
    - aim_info_extract(od, bs, userinfo);
    - tlvlist = aim_tlvlist_read(bs);
    -
    - /* Profile will be 1 and 2 */
    - userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1);
    - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) {
    - userinfo->info = (char *)g_malloc(tlv->length);
    - memcpy(userinfo->info, tlv->value, tlv->length);
    - userinfo->info_len = tlv->length;
    - }
    -
    - /* Away message will be 3 and 4 */
    - userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1);
    - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
    - userinfo->away = (char *)g_malloc(tlv->length);
    - memcpy(userinfo->away, tlv->value, tlv->length);
    - userinfo->away_len = tlv->length;
    - }
    -
    - /* Caps will be 5 */
    - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) {
    - ByteStream cbs;
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - const char *mood;
    -
    - byte_stream_init(&cbs, tlv->value, tlv->length);
    - userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length);
    - byte_stream_rewind(&cbs);
    - userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES;
    -
    - mood = aim_receive_custom_icon(od, &cbs, tlv->length);
    - if (mood)
    - purple_prpl_got_user_status(account, userinfo->bn, "mood",
    - PURPLE_MOOD_NAME, mood,
    - NULL);
    - else
    - purple_prpl_got_user_status_deactive(account, userinfo->bn, "mood");
    - }
    - aim_tlvlist_free(tlvlist);
    -
    - aim_locate_adduserinfo(od, userinfo);
    - userinfo2 = aim_locate_finduserinfo(od, userinfo->bn);
    - aim_info_free(userinfo);
    - g_free(userinfo);
    -
    - /* Show the info to the user */
    - oscar_user_info_display_aim(od, userinfo2);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0015 - Request the info of a user using the short method. This is
    - * what iChat uses. It normally is VERY leniently rate limited.
    - *
    - * @param bn The buddy name whose info you wish to request.
    - * @param flags The bitmask which specifies the type of info you wish to request.
    - * 0x00000001 - Info/profile.
    - * 0x00000002 - Away message.
    - * 0x00000004 - Capabilities.
    - * 0x00000008 - Certification.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int
    -aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 4 + 1 + strlen(bn));
    - byte_stream_put32(&bs, flags);
    - byte_stream_put8(&bs, strlen(bn));
    - byte_stream_putstr(&bs, bn);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, bn, strlen(bn)+1);
    - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_LOCATE, 0x0015, snacid, &bs, FALSE);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return error(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0003)
    - return rights(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0006)
    - return userinfo(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -static void
    -locate_shutdown(OscarData *od, aim_module_t *mod)
    -{
    - aim_userinfo_t *del;
    -
    - while (od->locate.userinfo) {
    - del = od->locate.userinfo;
    - od->locate.userinfo = od->locate.userinfo->next;
    - aim_info_free(del);
    - g_free(del);
    - }
    -}
    -
    -int
    -locate_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_LOCATE;
    - mod->version = 0x0001;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "locate", sizeof(mod->name));
    - mod->snachandler = snachandler;
    - mod->shutdown = locate_shutdown;
    -
    - return 0;
    -}
    -
    -const char*
    -icq_get_custom_icon_description(const char *mood)
    -{
    - int i;
    -
    - if (!(mood && *mood))
    - return NULL;
    -
    - for (i = 0; icq_custom_icons[i].mood; i++) {
    - /* We check that description is not NULL to exclude
    - * duplicates, like the typing duplicate. */
    - if (icq_purple_moods[i].description &&
    - purple_strequal(mood, icq_custom_icons[i].mood)) {
    - return icq_purple_moods[i].description;
    - }
    - }
    -
    - return NULL;
    -}
    -
    -guint8*
    -icq_get_custom_icon_data(const char *mood)
    -{
    - int i;
    -
    - if (!(mood && *mood))
    - return NULL;
    -
    - for (i = 0; icq_custom_icons[i].mood; i++) {
    - /* We check that description is not NULL to exclude
    - * duplicates, like the typing duplicate. */
    - if (icq_purple_moods[i].description &&
    - purple_strequal(mood, icq_custom_icons[i].mood)) {
    - return (guint8 *)icq_custom_icons[i].data;
    - }
    - }
    - return NULL;
    -}
    -
    -PurpleMood*
    -icq_get_purple_moods(PurpleAccount *account)
    -{
    - return icq_purple_moods;
    -}
    --- a/libpurple/protocols/oscar/family_oservice.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1142 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0001 - This is a very special group. All connections support
    - * this group, as it does some particularly good things (like rate limiting).
    - */
    -
    -#include "oscar.h"
    -
    -#include "cipher.h"
    -
    -/*
    - * Each time we make a FLAP connection to an oscar server the server gives
    - * us a list of rate classes. Each rate class has different properties for
    - * how frequently we can send SNACs in that rate class before we become
    - * throttled or disconnected.
    - *
    - * The server also gives us a list of every available SNAC and tells us which
    - * rate class it's in. There are a lot of different SNACs, so this list can be
    - * fairly large. One important characteristic of these rate classes is that
    - * currently (and since at least 2004) most SNACs are in the same rate class.
    - *
    - * One optimization we can do to save memory is to only keep track of SNACs
    - * that are in classes other than this default rate class. So if we try to
    - * look up a SNAC and it's not in our hash table then we can assume that it's
    - * in the default rate class.
    - */
    -#define OSCAR_DEFAULT_RATECLASS 1
    -
    -/* Subtype 0x0002 - Client Online */
    -void
    -aim_srv_clientready(OscarData *od, FlapConnection *conn)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *cur;
    -
    - byte_stream_new(&bs, 1142);
    -
    - /*
    - * Send only the tool versions that the server cares about (that it
    - * marked as supporting in the server ready SNAC).
    - */
    - for (cur = conn->groups; cur != NULL; cur = cur->next)
    - {
    - aim_module_t *mod;
    -
    - if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data))))
    - {
    - byte_stream_put16(&bs, mod->family);
    - byte_stream_put16(&bs, mod->version);
    - byte_stream_put16(&bs, mod->toolid);
    - byte_stream_put16(&bs, mod->toolversion);
    - }
    - }
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/*
    - * Subtype 0x0003 - Host Online
    - *
    - * See comments in conn.c about how the group associations are supposed
    - * to work, and how they really work.
    - *
    - * This info probably doesn't even need to make it to the client.
    - *
    - * We don't actually call the client here. This starts off the connection
    - * initialization routine required by all AIM connections. The next time
    - * the client is called is the CONNINITDONE callback, which should be
    - * shortly after the rate information is acknowledged.
    - *
    - */
    -static int
    -hostonline(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int group;
    -
    - while (byte_stream_bytes_left(bs))
    - {
    - group = byte_stream_get16(bs);
    - conn->groups = g_slist_prepend(conn->groups, GUINT_TO_POINTER(group));
    - }
    -
    - /*
    - * Next step is in the Host Versions handler.
    - *
    - * Note that we must send this before we request rates, since
    - * the format of the rate information depends on the versions we
    - * give it.
    - *
    - */
    - aim_srv_setversions(od, conn);
    -
    - return 1;
    -}
    -
    -/* Subtype 0x0004 - Service request */
    -void
    -aim_srv_requestnew(OscarData *od, guint16 serviceid)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
    - if(!conn)
    - return;
    -
    - byte_stream_new(&bs, 6);
    -
    - byte_stream_put16(&bs, serviceid);
    -
    - if (od->use_ssl)
    - /* Request SSL Connection */
    - aim_tlvlist_add_noval(&tlvlist, 0x008c);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/*
    - * Join a room of name roomname. This is the first step to joining an
    - * already created room. It's basically a Service Request for
    - * family 0x000e, with a little added on to specify the exchange and room
    - * name.
    - */
    -int
    -aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    - struct chatsnacinfo csi;
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
    - if (!conn || !roomname || roomname[0] == '\0')
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 506);
    -
    - memset(&csi, 0, sizeof(csi));
    - csi.exchange = exchange;
    - g_strlcpy(csi.name, roomname, sizeof(csi.name));
    - csi.instance = instance;
    -
    - /*
    - * Requesting service chat (0x000e)
    - */
    - byte_stream_put16(&bs, 0x000e);
    -
    - aim_tlvlist_add_chatroom(&tlvlist, 0x0001, exchange, roomname, instance);
    -
    - if (od->use_ssl)
    - /* Request SSL Connection */
    - aim_tlvlist_add_noval(&tlvlist, 0x008c);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, &csi, sizeof(csi));
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/* Subtype 0x0005 - Redirect */
    -static int
    -redirect(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - struct aim_redirect_data redir;
    - aim_rxcallback_t userfunc;
    - GSList *tlvlist;
    - aim_snac_t *origsnac = NULL;
    - int ret = 0;
    -
    - memset(&redir, 0, sizeof(redir));
    -
    - tlvlist = aim_tlvlist_read(bs);
    -
    - if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) ||
    - !aim_tlv_gettlv(tlvlist, 0x0005, 1) ||
    - !aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
    - aim_tlvlist_free(tlvlist);
    - return 0;
    - }
    -
    - redir.group = aim_tlv_get16(tlvlist, 0x000d, 1);
    - redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
    - redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length;
    - redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1);
    - redir.ssl_cert_cn = aim_tlv_getstr(tlvlist, 0x008d, 1);
    - redir.use_ssl = aim_tlv_get8(tlvlist, 0x008e, 1);
    -
    - /* Fetch original SNAC so we can get csi if needed */
    - origsnac = aim_remsnac(od, snac->id);
    -
    - if ((redir.group == SNAC_FAMILY_CHAT) && origsnac) {
    - struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
    -
    - redir.chat.exchange = csi->exchange;
    - redir.chat.room = csi->name;
    - redir.chat.instance = csi->instance;
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, &redir);
    -
    - g_free((void *)redir.ip);
    - g_free((void *)redir.cookie);
    - g_free((void *)redir.ssl_cert_cn);
    -
    - if (origsnac)
    - g_free(origsnac->data);
    - g_free(origsnac);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x0006 - Request Rate Information. */
    -void
    -aim_srv_reqrates(OscarData *od, FlapConnection *conn)
    -{
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x0006);
    -}
    -
    -/*
    - * OSCAR defines several 'rate classes'. Each class has separate
    - * rate limiting properties (limit level, alert level, disconnect
    - * level, etc), and a set of SNAC family/type pairs associated with
    - * it. The rate classes, their limiting properties, and the definitions
    - * of which SNACs belong to which class are defined in the
    - * Rate Response packet at login to each host.
    - *
    - * Logically, all rate offenses within one class count against further
    - * offenses for other SNACs in the same class (ie, sending messages
    - * too fast will limit the number of user info requests you can send,
    - * since those two SNACs are in the same rate class).
    - *
    - * Since the rate classes are defined dynamically at login, the values
    - * below may change. But they seem to be fairly constant.
    - *
    - * Currently, BOS defines five rate classes, with the commonly used
    - * members as follows...
    - *
    - * Rate class 0x0001:
    - * - Everything thats not in any of the other classes
    - *
    - * Rate class 0x0002:
    - * - Buddy list add/remove
    - * - Permit list add/remove
    - * - Deny list add/remove
    - *
    - * Rate class 0x0003:
    - * - User information requests
    - * - Outgoing ICBMs
    - *
    - * Rate class 0x0004:
    - * - A few unknowns: 2/9, 2/b, and f/2
    - *
    - * Rate class 0x0005:
    - * - Chat room create
    - * - Outgoing chat ICBMs
    - *
    - * The only other thing of note is that class 5 (chat) has slightly looser
    - * limiting properties than class 3 (normal messages). But thats just a
    - * small bit of trivia for you.
    - *
    - * The last thing that needs to be learned about the rate limiting
    - * system is how the actual numbers relate to the passing of time. This
    - * seems to be a big mystery.
    - *
    - * See joscar's javadoc for the RateClassInfo class for a great
    - * explanation. You might be able to find it at
    - * http://dscoder.com/RateClassInfo.html
    - */
    -
    -static struct rateclass *
    -rateclass_find(GSList *rateclasses, guint16 id)
    -{
    - GSList *tmp;
    -
    - for (tmp = rateclasses; tmp != NULL; tmp = tmp->next)
    - {
    - struct rateclass *rateclass;
    - rateclass = tmp->data;
    - if (rateclass->classid == id)
    - return rateclass;
    - }
    -
    - return NULL;
    -}
    -
    -/* Subtype 0x0007 - Rate Parameters */
    -static int
    -rateresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - guint16 numclasses, i;
    - aim_rxcallback_t userfunc;
    -
    - /*
    - * First are the parameters for each rate class.
    - */
    - numclasses = byte_stream_get16(bs);
    - for (i = 0; i < numclasses; i++)
    - {
    - struct rateclass *rateclass;
    - guint32 delta;
    - struct timeval now;
    -
    - gettimeofday(&now, NULL);
    - rateclass = g_new(struct rateclass, 1);
    -
    - rateclass->classid = byte_stream_get16(bs);
    - rateclass->windowsize = byte_stream_get32(bs);
    - rateclass->clear = byte_stream_get32(bs);
    - rateclass->alert = byte_stream_get32(bs);
    - rateclass->limit = byte_stream_get32(bs);
    - rateclass->disconnect = byte_stream_get32(bs);
    - rateclass->current = byte_stream_get32(bs);
    - rateclass->max = byte_stream_get32(bs);
    - if (mod->version >= 3) {
    - delta = byte_stream_get32(bs);
    - rateclass->dropping_snacs = byte_stream_get8(bs);
    - } else {
    - delta = 0;
    - rateclass->dropping_snacs = 0;
    - }
    -
    - rateclass->last.tv_sec = now.tv_sec - delta / 1000;
    - rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
    -
    - conn->rateclasses = g_slist_prepend(conn->rateclasses, rateclass);
    -
    - if (rateclass->classid == OSCAR_DEFAULT_RATECLASS)
    - conn->default_rateclass = rateclass;
    - }
    - conn->rateclasses = g_slist_reverse(conn->rateclasses);
    -
    - /*
    - * Then the members of each class.
    - */
    - for (i = 0; i < numclasses; i++)
    - {
    - guint16 classid, count;
    - struct rateclass *rateclass;
    - int j;
    -
    - classid = byte_stream_get16(bs);
    - count = byte_stream_get16(bs);
    -
    - if (classid == OSCAR_DEFAULT_RATECLASS) {
    - /*
    - * Don't bother adding these SNACs to the hash table. See the
    - * comment for OSCAR_DEFAULT_RATECLASS at the top of this file.
    - */
    - byte_stream_advance(bs, 4 * count);
    - continue;
    - }
    -
    - rateclass = rateclass_find(conn->rateclasses, classid);
    -
    - for (j = 0; j < count; j++)
    - {
    - guint16 group, subtype;
    -
    - group = byte_stream_get16(bs);
    - subtype = byte_stream_get16(bs);
    -
    - if (rateclass != NULL)
    - g_hash_table_insert(conn->rateclass_members,
    - GUINT_TO_POINTER((group << 16) + subtype),
    - rateclass);
    - }
    - }
    -
    - /*
    - * We don't pass the rate information up to the client, as it really
    - * doesn't care. The information is stored in the connection, however
    - * so that we can do rate limiting management when sending SNACs.
    - */
    -
    - /*
    - * Subscribe to rate change information for all rate classes.
    - */
    - aim_srv_rates_addparam(od, conn);
    -
    - /*
    - * Finally, tell the client it's ready to go...
    - */
    - if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE)))
    - userfunc(od, conn, frame);
    -
    - return 1;
    -}
    -
    -/* Subtype 0x0008 - Add Rate Parameter */
    -void
    -aim_srv_rates_addparam(OscarData *od, FlapConnection *conn)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tmp;
    -
    - byte_stream_new(&bs, 502);
    -
    - for (tmp = conn->rateclasses; tmp != NULL; tmp = tmp->next)
    - {
    - struct rateclass *rateclass;
    - rateclass = tmp->data;
    - byte_stream_put16(&bs, rateclass->classid);
    - }
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0008, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/* Subtype 0x000a - Rate Change */
    -static int
    -ratechange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - guint16 code, classid;
    - struct rateclass *rateclass;
    - guint32 delta;
    - struct timeval now;
    - static const char *codes[5] = {
    - "invalid",
    - "change",
    - "warning",
    - "limit",
    - "limit cleared",
    - };
    -
    - gettimeofday(&now, NULL);
    - code = byte_stream_get16(bs);
    - classid = byte_stream_get16(bs);
    -
    - rateclass = rateclass_find(conn->rateclasses, classid);
    - if (rateclass == NULL)
    - /* This should never really happen */
    - return 0;
    -
    - rateclass->windowsize = byte_stream_get32(bs);
    - rateclass->clear = byte_stream_get32(bs);
    - rateclass->alert = byte_stream_get32(bs);
    - rateclass->limit = byte_stream_get32(bs);
    - rateclass->disconnect = byte_stream_get32(bs);
    - rateclass->current = byte_stream_get32(bs);
    - rateclass->max = byte_stream_get32(bs);
    - if (mod->version >= 3) {
    - delta = byte_stream_get32(bs);
    - rateclass->dropping_snacs = byte_stream_get8(bs);
    - } else {
    - delta = 0;
    - rateclass->dropping_snacs = 0;
    - }
    -
    - rateclass->last.tv_sec = now.tv_sec - delta / 1000;
    - rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
    -
    - purple_debug_misc("oscar", "rate %s (param ID 0x%04hx): curavg = %u, "
    - "maxavg = %u, alert at %u, clear warning at %u, limit at %u, "
    - "disconnect at %u, delta is %u, dropping is %u (window size = %u)\n",
    - (code < 5) ? codes[code] : codes[0], rateclass->classid,
    - rateclass->current, rateclass->max, rateclass->alert,
    - rateclass->clear, rateclass->limit, rateclass->disconnect,
    - delta, rateclass->dropping_snacs, rateclass->windowsize);
    -
    - if (code == AIM_RATE_CODE_LIMIT) {
    - purple_debug_warning("oscar", "The last action you attempted "
    - "could not be performed because you are over the rate "
    - "limit. Please wait 10 seconds and try again.\n");
    - }
    -
    - return 1;
    -}
    -
    -/*
    - * How Migrations work.
    - *
    - * The server sends a Server Pause message, which the client should respond to
    - * with a Server Pause Ack, which contains the families it needs on this
    - * connection. The server will send a Migration Notice with an IP address, and
    - * then disconnect. Next the client should open the connection and send the
    - * cookie. Repeat the normal login process and pretend this never happened.
    - *
    - * The Server Pause contains no data.
    - *
    - */
    -
    -/* Subtype 0x000b - Service Pause */
    -static int
    -serverpause(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x000d - Service Resume */
    -static int
    -serverresume(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x000e - Request self-info */
    -void
    -aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn)
    -{
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x000e);
    -}
    -
    -/* Subtype 0x000f - Self User Info */
    -static int
    -selfinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - aim_userinfo_t userinfo;
    -
    - aim_info_extract(od, bs, &userinfo);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, &userinfo);
    -
    - aim_info_free(&userinfo);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x0010 - Evil Notification */
    -static int
    -evilnotify(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 newevil;
    - aim_userinfo_t userinfo;
    -
    - memset(&userinfo, 0, sizeof(aim_userinfo_t));
    -
    - newevil = byte_stream_get16(bs);
    -
    - if (byte_stream_bytes_left(bs))
    - aim_info_extract(od, bs, &userinfo);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, newevil, &userinfo);
    -
    - aim_info_free(&userinfo);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0011 - Idle Notification
    - *
    - * Should set your current idle time in seconds. Note that this should
    - * never be called consecutively with a non-zero idle time. That makes
    - * OSCAR do funny things. Instead, just set it once you go idle, and then
    - * call it again with zero when you're back.
    - *
    - */
    -void
    -aim_srv_setidle(OscarData *od, guint32 idletime)
    -{
    - FlapConnection *conn;
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
    - if(!conn)
    - return;
    -
    - aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0011, &idletime);
    -}
    -
    -/*
    - * Subtype 0x0012 - Service Migrate
    - *
    - * This is the final SNAC sent on the original connection during a migration.
    - * It contains the IP and cookie used to connect to the new server, and
    - * optionally a list of the SNAC groups being migrated.
    - *
    - */
    -static int
    -migrate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_rxcallback_t userfunc;
    - int ret = 0;
    - guint16 groupcount, i;
    - GSList *tlvlist;
    - char *ip = NULL;
    - aim_tlv_t *cktlv;
    -
    - /*
    - * Apparently there's some fun stuff that can happen right here. The
    - * migration can actually be quite selective about what groups it
    - * moves to the new server. When not all the groups for a connection
    - * are migrated, or they are all migrated but some groups are moved
    - * to a different server than others, it is called a bifurcated
    - * migration.
    - *
    - * Let's play dumb and not support that.
    - *
    - */
    - groupcount = byte_stream_get16(bs);
    - for (i = 0; i < groupcount; i++) {
    - guint16 group;
    -
    - group = byte_stream_get16(bs);
    -
    - purple_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group);
    - }
    -
    - tlvlist = aim_tlvlist_read(bs);
    -
    - if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
    - ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
    -
    - cktlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, ip, cktlv ? cktlv->value : NULL);
    -
    - aim_tlvlist_free(tlvlist);
    - g_free(ip);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x0013 - Message of the Day */
    -static int
    -motd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_rxcallback_t userfunc;
    - char *msg = NULL;
    - int ret = 0;
    - GSList *tlvlist;
    - guint16 id;
    -
    - /*
    - * Code.
    - *
    - * Valid values:
    - * 1 Mandatory upgrade
    - * 2 Advisory upgrade
    - * 3 System bulletin
    - * 4 Nothing's wrong ("top o the world" -- normal)
    - * 5 Lets-break-something.
    - *
    - */
    - id = byte_stream_get16(bs);
    -
    - /*
    - * TLVs follow
    - */
    - tlvlist = aim_tlvlist_read(bs);
    -
    - msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, id, msg);
    -
    - g_free(msg);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0017 - Set client versions
    - *
    - * If you've seen the clientonline/clientready SNAC you're probably
    - * wondering what the point of this one is. And that point seems to be
    - * that the versions in the client online SNAC are sent too late for the
    - * server to be able to use them to change the protocol for the earlier
    - * login packets (client versions are sent right after Host Online is
    - * received, but client online versions aren't sent until quite a bit later).
    - * We can see them already making use of this by changing the format of
    - * the rate information based on what version of group 1 we advertise here.
    - *
    - */
    -void
    -aim_srv_setversions(OscarData *od, FlapConnection *conn)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *cur;
    -
    - byte_stream_new(&bs, 1142);
    -
    - /*
    - * Send only the versions that the server cares about (that it
    - * marked as supporting in the server ready SNAC).
    - */
    - for (cur = conn->groups; cur != NULL; cur = cur->next)
    - {
    - aim_module_t *mod;
    -
    - if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data))))
    - {
    - byte_stream_put16(&bs, mod->family);
    - byte_stream_put16(&bs, mod->version);
    - }
    - }
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0017, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/* Subtype 0x0018 - Host versions */
    -static int
    -hostversions(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int vercount;
    - guint8 *versions;
    -
    - /* This is frivolous. (Thank you SmarterChild.) */
    - vercount = byte_stream_bytes_left(bs)/4;
    -
    - /* XXX: vercount probably should be used for reading versions. */
    - (void)vercount;
    - versions = byte_stream_getraw(bs, byte_stream_bytes_left(bs));
    - g_free(versions);
    -
    - /*
    - * Now request rates.
    - */
    - aim_srv_reqrates(od, conn);
    -
    - return 1;
    -}
    -
    -/**
    - * Subtype 0x001e - Extended Status/Extra Info.
    - *
    - * These settings are transient, not server-stored (i.e. they only
    - * apply to this session, and must be re-set the next time you sign
    - * on).
    - *
    - * You can set your ICQ status (available, away, do not disturb,
    - * etc.), or whether your IP address should be hidden or not, or
    - * if your status is visible on ICQ web sites, and you can set
    - * your IP address info and what not.
    - *
    - * You can also set your "available" message. This is currently
    - * only supported by iChat, Purple and other 3rd party clients.
    - *
    - * These are the same TLVs seen in user info. You can
    - * also set 0x0008 and 0x000c.
    - */
    -int
    -aim_srv_setextrainfo(OscarData *od,
    - gboolean seticqstatus, guint32 icqstatus,
    - gboolean setstatusmsg, const char *statusmsg, const char *itmsurl)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - if (seticqstatus)
    - {
    - aim_tlvlist_add_32(&tlvlist, 0x0006, icqstatus |
    - AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH);
    - }
    -
    - if (setstatusmsg)
    - {
    - size_t statusmsglen, itmsurllen;
    - ByteStream tmpbs;
    -
    - statusmsglen = (statusmsg != NULL) ? strlen(statusmsg) : 0;
    - itmsurllen = (itmsurl != NULL) ? strlen(itmsurl) : 0;
    -
    - byte_stream_new(&tmpbs, statusmsglen + 8 + itmsurllen + 8);
    - byte_stream_put_bart_asset_str(&tmpbs, 0x0002, statusmsg);
    - byte_stream_put_bart_asset_str(&tmpbs, 0x0009, itmsurl);
    -
    - aim_tlvlist_add_raw(&tlvlist, 0x001d,
    - byte_stream_curpos(&tmpbs), tmpbs.data);
    - byte_stream_destroy(&tmpbs);
    - }
    -
    - byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x001e, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/* Send dummy DC (direct connect) information to the server.
    - * Direct connect is ICQ's counterpart for AIM's DirectIM,
    - * as far as I can tell. Anyway, we don't support it;
    - * the reason to send this packet is that some clients
    - * (Miranda, QIP) won't send us channel 2 ICBM messages
    - * unless we specify DC version >= 8.
    - *
    - * See #12044 for more information.
    - */
    -void
    -aim_srv_set_dc_info(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - ByteStream bs, tlv0c;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - /* http://iserverd.khstu.ru/oscar/snac_01_1e.html has a nice analysis of what goes in 0xc tlv.
    - * Kopete sends a dummy DC info, too, so I just copied the values from them.
    - */
    - byte_stream_new(&tlv0c, 4*2 + 1 + 2 + 4*6 + 2);
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put8(&tlv0c, 0x0); /* We don't support DC */
    - byte_stream_put16(&tlv0c, 8); /* DC version */
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put32(&tlv0c, 0x50);
    - byte_stream_put32(&tlv0c, 0x3);
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put16(&tlv0c, 0x0);
    - aim_tlvlist_add_raw(&tlvlist, 0x000c, byte_stream_curpos(&tlv0c), tlv0c.data);
    - byte_stream_destroy(&tlv0c);
    -
    - byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - g_warn_if_fail(conn != NULL);
    - if (conn) {
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE,
    - 0x001e, snacid, &bs);
    - }
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Starting this past week (26 Mar 2001, say), AOL has started sending
    - * this nice little extra SNAC. AFAIK, it has never been used until now.
    - *
    - * The request contains eight bytes. The first four are an offset, the
    - * second four are a length.
    - *
    - * The offset is an offset into aim.exe when it is mapped during execution
    - * on Win32. So far, AOL has only been requesting bytes in static regions
    - * of memory. (I won't put it past them to start requesting data in
    - * less static regions -- regions that are initialized at run time, but still
    - * before the client receives this request.)
    - *
    - * When the client receives the request, it adds it to the current ds
    - * (0x00400000) and dereferences it, copying the data into a buffer which
    - * it then runs directly through the MD5 hasher. The 16 byte output of
    - * the hash is then sent back to the server.
    - *
    - * If the client does not send any data back, or the data does not match
    - * the data that the specific client should have, the client will get the
    - * following message from "AOL Instant Messenger":
    - * "You have been disconnected from the AOL Instant Message Service (SM)
    - * for accessing the AOL network using unauthorized software. You can
    - * download a FREE, fully featured, and authorized client, here
    - * http://www.aol.com/aim/download2.html"
    - * The connection is then closed, receiving disconnect code 1, URL
    - * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html.
    - *
    - * Note, however, that numerous inconsistencies can cause the above error,
    - * not just sending back a bad hash. Do not immediatly suspect this code
    - * if you get disconnected. AOL and the open/free software community have
    - * played this game for a couple years now, generating the above message
    - * on numerous ocassions.
    - *
    - * Anyway, neener. We win again.
    - *
    - */
    -/* Subtype 0x001f - Client verification */
    -static int
    -memrequest(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint32 offset, len;
    - GSList *tlvlist;
    - char *modname;
    -
    - offset = byte_stream_get32(bs);
    - len = byte_stream_get32(bs);
    - tlvlist = aim_tlvlist_read(bs);
    -
    - modname = aim_tlv_getstr(tlvlist, 0x0001, 1);
    -
    - purple_debug_info("oscar", "Got memory request for data at 0x%08x (%u bytes) of requested %s\n", offset, len, modname ? modname : "aim.exe");
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, offset, len, modname);
    -
    - g_free(modname);
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x0020 - Client verification reply */
    -int
    -aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !conn)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 2+16);
    -
    - byte_stream_put16(&bs, 0x0010); /* md5 is always 16 bytes */
    -
    - if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */
    -
    - byte_stream_putraw(&bs, buf, 0x10);
    -
    - } else if (buf && (len > 0)) { /* use input buffer */
    - PurpleCipherContext *context;
    - guchar digest[16];
    -
    - context = purple_cipher_context_new_by_name("md5", NULL);
    - purple_cipher_context_append(context, buf, len);
    - purple_cipher_context_digest(context, 16, digest, NULL);
    - purple_cipher_context_destroy(context);
    -
    - byte_stream_putraw(&bs, digest, 0x10);
    -
    - } else if (len == 0) { /* no length, just hash NULL (buf is optional) */
    - PurpleCipherContext *context;
    - guchar digest[16];
    - guint8 nil = '\0';
    -
    - /*
    - * I'm not sure if we really need the empty append with the
    - * new MD5 functions, so I'll leave it in, just in case.
    - */
    - context = purple_cipher_context_new_by_name("md5", NULL);
    - purple_cipher_context_append(context, &nil, 0);
    - purple_cipher_context_digest(context, 16, digest, NULL);
    - purple_cipher_context_destroy(context);
    -
    - byte_stream_putraw(&bs, digest, 0x10);
    -
    - } else {
    -
    - /*
    - * This data is correct for AIM 3.5.1670.
    - *
    - * Using these blocks is as close to "legal" as you can get
    - * without using an AIM binary.
    - *
    - */
    - if ((offset == 0x03ffffff) && (len == 0x03ffffff)) {
    -
    -#if 1 /* with "AnrbnrAqhfzcd" */
    - byte_stream_put32(&bs, 0x44a95d26);
    - byte_stream_put32(&bs, 0xd2490423);
    - byte_stream_put32(&bs, 0x93b8821f);
    - byte_stream_put32(&bs, 0x51c54b01);
    -#else /* no filename */
    - byte_stream_put32(&bs, 0x1df8cbae);
    - byte_stream_put32(&bs, 0x5523b839);
    - byte_stream_put32(&bs, 0xa0e10db3);
    - byte_stream_put32(&bs, 0xa46d3b39);
    -#endif
    -
    - } else
    - purple_debug_warning("oscar", "sendmemblock: unknown hash request\n");
    -
    - }
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0020, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0020, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0021 - Receive our extended status
    - *
    - * This is used for iChat's "available" messages, and maybe ICQ extended
    - * status messages? It's also used to tell the client whether or not it
    - * needs to upload an SSI buddy icon... who engineers this stuff, anyway?
    - */
    -static int
    -aim_parse_extstatus(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - guint16 type = byte_stream_get16(bs);
    - if (type == 0x0000 || type == 0x0001) {
    - /* buddy icon checksum */
    - /* not sure what the difference between 1 and 0 is */
    - guint8 flags = byte_stream_get8(bs);
    - guint8 length = byte_stream_get8(bs);
    - guint8 *md5 = byte_stream_getraw(bs, length);
    -
    - if ((flags == 0x00) || (flags == 0x41)) {
    - if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) {
    - od->iconconnecting = TRUE;
    - od->set_icon = TRUE;
    - aim_srv_requestnew(od, SNAC_FAMILY_BART);
    - } else {
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
    - if (img == NULL) {
    - aim_ssi_delicon(od);
    - } else {
    -
    - purple_debug_info("oscar",
    - "Uploading icon to icon server\n");
    - aim_bart_upload(od, purple_imgstore_get_data(img),
    - purple_imgstore_get_size(img));
    - purple_imgstore_unref(img);
    - }
    - }
    - } else if (flags == 0x81) {
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
    - if (img == NULL)
    - aim_ssi_delicon(od);
    - else {
    - aim_ssi_seticon(od, md5, length);
    - purple_imgstore_unref(img);
    - }
    - }
    -
    - g_free(md5);
    - }
    -
    - return 0;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0003)
    - return hostonline(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0005)
    - return redirect(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0007)
    - return rateresp(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000a)
    - return ratechange(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000b)
    - return serverpause(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000d)
    - return serverresume(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000f)
    - return selfinfo(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0010)
    - return evilnotify(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0012)
    - return migrate(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0013)
    - return motd(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0018)
    - return hostversions(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x001f)
    - return memrequest(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0021)
    - return aim_parse_extstatus(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int service_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_OSERVICE;
    - mod->version = 0x0003;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "oservice", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_popup.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,84 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0008 - Popups.
    - *
    - * Popups are just what it sounds like. They're a way for the server to
    - * open up an informative box on the client's screen.
    - */
    -
    -#include <oscar.h>
    -
    -/*
    - * This is all there is to it.
    - *
    - * The message is probably HTML.
    - *
    - */
    -static int
    -parsepopup(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_rxcallback_t userfunc;
    - GSList *tlvlist;
    - int ret = 0;
    - char *msg, *url;
    - guint16 width, height, delay;
    -
    - tlvlist = aim_tlvlist_read(bs);
    -
    - msg = aim_tlv_getstr(tlvlist, 0x0001, 1);
    - url = aim_tlv_getstr(tlvlist, 0x0002, 1);
    - width = aim_tlv_get16(tlvlist, 0x0003, 1);
    - height = aim_tlv_get16(tlvlist, 0x0004, 1);
    - delay = aim_tlv_get16(tlvlist, 0x0005, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, msg, url, width, height, delay);
    -
    - aim_tlvlist_free(tlvlist);
    - g_free(msg);
    - g_free(url);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0002)
    - return parsepopup(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -popups_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_POPUP;
    - mod->version = 0x0001;
    - mod->toolid = 0x0104;
    - mod->toolversion = 0x0001;
    - mod->flags = 0;
    - strncpy(mod->name, "popup", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_stats.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,64 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x000b - Statistics.
    - *
    - */
    -
    -#include <oscar.h>
    -
    -static int
    -reportinterval(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 interval;
    -
    - interval = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, interval);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0002)
    - return reportinterval(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -stats_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_STATS;
    - mod->version = 0x0001;
    - mod->toolid = 0x0104;
    - mod->toolversion = 0x0001;
    - mod->flags = 0;
    - strncpy(mod->name, "stats", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_userlookup.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,157 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x000a - User Search.
    - *
    - * TODO: Add aim_usersearch_name()
    - *
    - */
    -
    -#include "oscar.h"
    -
    -/*
    - * Subtype 0x0001
    - *
    - * XXX can this be integrated with the rest of the error handling?
    - */
    -static int error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - aim_snac_t *snac2;
    -
    - /* XXX the modules interface should have already retrieved this for us */
    - if (!(snac2 = aim_remsnac(od, snac->id))) {
    - purple_debug_misc("oscar", "search error: couldn't get a snac for 0x%08x\n", snac->id);
    - return 0;
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, snac2->data /* address */);
    -
    - /* XXX freesnac()? */
    - if (snac2)
    - g_free(snac2->data);
    - g_free(snac2);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0002
    - *
    - */
    -int aim_search_address(OscarData *od, const char *address)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_USERLOOKUP);
    -
    - if (!conn || !address)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, strlen(address));
    -
    - byte_stream_putstr(&bs, address);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_USERLOOKUP, 0x0002, 0x0000, address, strlen(address)+1);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_USERLOOKUP, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0003
    - *
    - */
    -static int reply(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int j = 0, m, ret = 0;
    - GSList *tlvlist;
    - char *cur = NULL, *buf = NULL;
    - aim_rxcallback_t userfunc;
    - aim_snac_t *snac2;
    - const char *searchaddr = NULL;
    -
    - if ((snac2 = aim_remsnac(od, snac->id)))
    - searchaddr = (const char *)snac2->data;
    -
    - tlvlist = aim_tlvlist_read(bs);
    - m = aim_tlvlist_count(tlvlist);
    -
    - /* XXX uhm.
    - * This is the only place that uses something other than 1 for the 3rd
    - * parameter to aim_tlv_gettlv_whatever().
    - */
    - while ((cur = aim_tlv_getstr(tlvlist, 0x0001, j+1)) && j < m)
    - {
    - buf = g_realloc(buf, (j+1) * (MAXSNLEN+1));
    -
    - strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN);
    - g_free(cur);
    -
    - j++;
    - }
    - g_free(cur);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, searchaddr, j, buf);
    -
    - /* XXX freesnac()? */
    - if (snac2)
    - g_free(snac2->data);
    - g_free(snac2);
    -
    - g_free(buf);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return error(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0003)
    - return reply(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -search_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_USERLOOKUP;
    - mod->version = 0x0001;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "userlookup", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/flap_connection.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1127 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "oscar.h"
    -
    -#include "eventloop.h"
    -#include "proxy.h"
    -
    -#ifndef _WIN32
    -#include <netdb.h>
    -#include <sys/socket.h>
    -#include <netinet/in.h>
    -#endif
    -
    -#ifdef _WIN32
    -#include "win32dep.h"
    -#endif
    -
    -/**
    - * This sends a channel 1 SNAC containing the FLAP version.
    - * The FLAP version is sent by itself at the beginning of every
    - * connection to a FLAP server. It is always the very first
    - * packet sent by both the server and the client after the SYN,
    - * SYN/ACK, ACK handshake.
    - */
    -void
    -flap_connection_send_version(OscarData *od, FlapConnection *conn)
    -{
    - FlapFrame *frame;
    -
    - frame = flap_frame_new(od, 0x01, 4);
    - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
    - flap_connection_send(conn, frame);
    -}
    -
    -/**
    - * This sends a channel 1 FLAP containing the FLAP version and
    - * the authentication cookie. This is sent when connecting to
    - * any FLAP server after the initial connection to the auth
    - * server. It is always the very first packet sent by both the
    - * server and the client after the SYN, SYN/ACK, ACK handshake.
    - */
    -void
    -flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy)
    -{
    - FlapFrame *frame;
    - GSList *tlvlist = NULL;
    -
    - frame = flap_frame_new(od, 0x01, 4 + 2 + 2 + length);
    - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
    - aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy);
    - aim_tlvlist_write(&frame->data, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send(conn, frame);
    -}
    -
    -void
    -flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci, gboolean allow_multiple_logins)
    -{
    - FlapFrame *frame;
    - GSList *tlvlist = NULL;
    -
    - frame = flap_frame_new(od, 0x01, 1152 + length);
    -
    - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
    - aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy);
    -
    - if (ci->clientstring != NULL)
    - aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring);
    - else {
    - gchar *clientstring = oscar_get_clientstring();
    - aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring);
    - g_free(clientstring);
    - }
    - aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major);
    - aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor);
    - aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point);
    - aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build);
    - aim_tlvlist_add_8(&tlvlist, 0x004a, (allow_multiple_logins ? 0x01 : 0x03));
    -
    - aim_tlvlist_write(&frame->data, &tlvlist);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send(conn, frame);
    -}
    -
    -static struct rateclass *
    -flap_connection_get_rateclass(FlapConnection *conn, guint16 family, guint16 subtype)
    -{
    - gconstpointer key;
    - gpointer rateclass;
    -
    - key = GUINT_TO_POINTER((family << 16) + subtype);
    - rateclass = g_hash_table_lookup(conn->rateclass_members, key);
    - if (rateclass != NULL)
    - return rateclass;
    -
    - return conn->default_rateclass;
    -}
    -
    -/*
    - * Attempt to calculate what our new current average would be if we
    - * were to send a SNAC in this rateclass at the given time.
    - */
    -static guint32
    -rateclass_get_new_current(FlapConnection *conn, struct rateclass *rateclass, struct timeval *now)
    -{
    - unsigned long timediff; /* In milliseconds */
    - guint32 current;
    -
    - /* This formula is documented at http://dev.aol.com/aim/oscar/#RATELIMIT */
    - timediff = (now->tv_sec - rateclass->last.tv_sec) * 1000 + (now->tv_usec - rateclass->last.tv_usec) / 1000;
    - current = ((rateclass->current * (rateclass->windowsize - 1)) + timediff) / rateclass->windowsize;
    -
    - return MIN(current, rateclass->max);
    -}
    -
    -/*
    - * Attempt to send the contents of a given queue
    - *
    - * @return TRUE if the queue was completely emptied or was initially
    - * empty; FALSE if rate limiting prevented it from being
    - * emptied.
    - */
    -static gboolean flap_connection_send_snac_queue(FlapConnection *conn, struct timeval now, GQueue *queue)
    -{
    - while (!g_queue_is_empty(queue))
    - {
    - QueuedSnac *queued_snac;
    - struct rateclass *rateclass;
    -
    - queued_snac = g_queue_peek_head(queue);
    -
    - rateclass = flap_connection_get_rateclass(conn, queued_snac->family, queued_snac->subtype);
    - if (rateclass != NULL)
    - {
    - guint32 new_current;
    -
    - new_current = rateclass_get_new_current(conn, rateclass, &now);
    -
    - if (rateclass->dropping_snacs || new_current <= rateclass->alert)
    - /* Not ready to send this SNAC yet--keep waiting. */
    - return FALSE;
    -
    - rateclass->current = new_current;
    - rateclass->last.tv_sec = now.tv_sec;
    - rateclass->last.tv_usec = now.tv_usec;
    - }
    -
    - flap_connection_send(conn, queued_snac->frame);
    - g_free(queued_snac);
    - g_queue_pop_head(queue);
    - }
    -
    - /* We emptied the queue */
    - return TRUE;
    -}
    -
    -static gboolean flap_connection_send_queued(gpointer data)
    -{
    - FlapConnection *conn;
    - struct timeval now;
    -
    - conn = data;
    - gettimeofday(&now, NULL);
    -
    - purple_debug_info("oscar", "Attempting to send %u queued SNACs and %u queued low-priority SNACs for %p\n",
    - (conn->queued_snacs ? conn->queued_snacs->length : 0),
    - (conn->queued_lowpriority_snacs ? conn->queued_lowpriority_snacs->length : 0),
    - conn);
    - if (!conn->queued_snacs || flap_connection_send_snac_queue(conn, now, conn->queued_snacs)) {
    - if (!conn->queued_lowpriority_snacs || flap_connection_send_snac_queue(conn, now, conn->queued_lowpriority_snacs)) {
    - /* Both queues emptied. */
    - conn->queued_timeout = 0;
    - return FALSE;
    - }
    - }
    -
    - /* We couldn't send all our SNACs. Keep trying */
    - return TRUE;
    -}
    -
    -/**
    - * This sends a channel 2 FLAP containing a SNAC. The SNAC family and
    - * subtype are looked up in the rate info for this connection, and if
    - * sending this SNAC will induce rate limiting then we delay sending
    - * of the SNAC by putting it into an outgoing holding queue.
    - *
    - * @param data The optional bytestream that makes up the data portion
    - * of this SNAC. For empty SNACs this should be NULL.
    - * @param high_priority If TRUE, the SNAC will be queued normally if
    - * needed. If FALSE, it will be queued separately, to be sent
    - * only if all high priority SNACs have been sent.
    - */
    -void
    -flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority)
    -{
    - FlapFrame *frame;
    - guint32 length;
    - gboolean enqueue = FALSE;
    - struct rateclass *rateclass;
    -
    - length = data != NULL ? data->offset : 0;
    -
    - frame = flap_frame_new(od, 0x02, 10 + length);
    - aim_putsnac(&frame->data, family, subtype, snacid);
    -
    - if (length > 0)
    - {
    - byte_stream_rewind(data);
    - byte_stream_putbs(&frame->data, data, length);
    - }
    -
    - if (conn->queued_timeout != 0)
    - enqueue = TRUE;
    - else if ((rateclass = flap_connection_get_rateclass(conn, family, subtype)) != NULL)
    - {
    - struct timeval now;
    - guint32 new_current;
    -
    - gettimeofday(&now, NULL);
    - new_current = rateclass_get_new_current(conn, rateclass, &now);
    -
    - if (rateclass->dropping_snacs || new_current <= rateclass->alert)
    - {
    - purple_debug_info("oscar", "Current rate for conn %p would be %u, but we alert at %u; enqueueing\n", conn, new_current, rateclass->alert);
    -
    - enqueue = TRUE;
    - }
    - else
    - {
    - rateclass->current = new_current;
    - rateclass->last.tv_sec = now.tv_sec;
    - rateclass->last.tv_usec = now.tv_usec;
    - }
    - }
    -
    - if (enqueue)
    - {
    - /* We've been sending too fast, so delay this message */
    - QueuedSnac *queued_snac;
    -
    - queued_snac = g_new(QueuedSnac, 1);
    - queued_snac->family = family;
    - queued_snac->subtype = subtype;
    - queued_snac->frame = frame;
    -
    - if (high_priority) {
    - if (!conn->queued_snacs)
    - conn->queued_snacs = g_queue_new();
    - g_queue_push_tail(conn->queued_snacs, queued_snac);
    - } else {
    - if (!conn->queued_lowpriority_snacs)
    - conn->queued_lowpriority_snacs = g_queue_new();
    - g_queue_push_tail(conn->queued_lowpriority_snacs, queued_snac);
    - }
    -
    - if (conn->queued_timeout == 0)
    - conn->queued_timeout = purple_timeout_add(500, flap_connection_send_queued, conn);
    -
    - return;
    - }
    -
    - flap_connection_send(conn, frame);
    -}
    -
    -void
    -flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data)
    -{
    - flap_connection_send_snac_with_priority(od, conn, family, subtype, snacid, data, TRUE);
    -}
    -
    -/**
    - * This sends an empty channel 4 FLAP. This is sent to signify
    - * that we're logging off. This shouldn't really be necessary--
    - * usually the AIM server will detect that the TCP connection has
    - * been destroyed--but it's good practice.
    - */
    -static void
    -flap_connection_send_close(OscarData *od, FlapConnection *conn)
    -{
    - FlapFrame *frame;
    -
    - frame = flap_frame_new(od, 0x04, 0);
    - flap_connection_send(conn, frame);
    -}
    -
    -/**
    - * This sends an empty channel 5 FLAP. This is used as a keepalive
    - * packet in FLAP connections. WinAIM 4.x and higher send these
    - * _every minute_ to keep the connection alive.
    - */
    -void
    -flap_connection_send_keepalive(OscarData *od, FlapConnection *conn)
    -{
    - FlapFrame *frame;
    -
    - frame = flap_frame_new(od, 0x05, 0);
    - flap_connection_send(conn, frame);
    -
    - /* clean out SNACs over 60sec old */
    - aim_cleansnacs(od, 60);
    -}
    -
    -/**
    - * Allocate a new empty connection structure.
    - *
    - * @param od The oscar session associated with this connection.
    - * @param type Type of connection to create
    - *
    - * @return Returns the new connection structure.
    - */
    -FlapConnection *
    -flap_connection_new(OscarData *od, int type)
    -{
    - FlapConnection *conn;
    -
    - conn = g_new0(FlapConnection, 1);
    - conn->od = od;
    - conn->buffer_outgoing = purple_circ_buffer_new(0);
    - conn->fd = -1;
    - conn->subtype = -1;
    - conn->type = type;
    - conn->rateclass_members = g_hash_table_new(g_direct_hash, g_direct_equal);
    -
    - od->oscar_connections = g_slist_prepend(od->oscar_connections, conn);
    -
    - return conn;
    -}
    -
    -/**
    - * Close (but not free) a connection.
    - *
    - * This cancels any currently pending connection attempt,
    - * closes any open fd and frees the auth cookie.
    - *
    - * @param conn The connection to close.
    - */
    -void
    -flap_connection_close(OscarData *od, FlapConnection *conn)
    -{
    - if (conn->connect_data != NULL)
    - {
    - purple_proxy_connect_cancel(conn->connect_data);
    - conn->connect_data = NULL;
    - }
    -
    - if (conn->gsc != NULL && conn->gsc->connect_data != NULL)
    - {
    - purple_ssl_close(conn->gsc);
    - conn->gsc = NULL;
    - }
    -
    - if (conn->new_conn_data != NULL)
    - {
    - if (conn->type == SNAC_FAMILY_CHAT)
    - {
    - oscar_chat_destroy(conn->new_conn_data);
    - conn->new_conn_data = NULL;
    - }
    - }
    -
    - if ((conn->fd >= 0 || conn->gsc != NULL)
    - && conn->type == SNAC_FAMILY_LOCATE)
    - flap_connection_send_close(od, conn);
    -
    - if (conn->watcher_incoming != 0)
    - {
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    - }
    -
    - if (conn->watcher_outgoing != 0)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - }
    -
    - if (conn->fd >= 0)
    - {
    - close(conn->fd);
    - conn->fd = -1;
    - }
    -
    - if (conn->gsc != NULL)
    - {
    - purple_ssl_close(conn->gsc);
    - conn->gsc = NULL;
    - }
    -
    - g_free(conn->buffer_incoming.data.data);
    - conn->buffer_incoming.data.data = NULL;
    -
    - purple_circ_buffer_destroy(conn->buffer_outgoing);
    - conn->buffer_outgoing = NULL;
    -}
    -
    -/**
    - * Free a FlapFrame
    - *
    - * @param frame The frame to free.
    - */
    -static void
    -flap_frame_destroy(FlapFrame *frame)
    -{
    - g_free(frame->data.data);
    - g_free(frame);
    -}
    -
    -static gboolean
    -flap_connection_destroy_cb(gpointer data)
    -{
    - FlapConnection *conn;
    - OscarData *od;
    - PurpleAccount *account;
    - aim_rxcallback_t userfunc;
    -
    - conn = data;
    - /* Explicitly added for debugging #5927. Don't re-order this, only
    - * consider removing it.
    - */
    - purple_debug_info("oscar", "Destroying FLAP connection %p\n", conn);
    -
    - od = conn->od;
    - account = purple_connection_get_account(od->gc);
    -
    - purple_debug_info("oscar", "Destroying oscar connection (%p) of "
    - "type 0x%04hx. Disconnect reason is %d\n", conn,
    - conn->type, conn->disconnect_reason);
    -
    - od->oscar_connections = g_slist_remove(od->oscar_connections, conn);
    -
    - if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
    - userfunc(od, conn, NULL, conn->disconnect_code, conn->error_message);
    -
    - /*
    - * TODO: If we don't have a SNAC_FAMILY_LOCATE connection then
    - * we should try to request one instead of disconnecting.
    - */
    - if (!account->disconnecting && ((od->oscar_connections == NULL)
    - || (!flap_connection_getbytype(od, SNAC_FAMILY_LOCATE))))
    - {
    - /* No more FLAP connections! Sign off this PurpleConnection! */
    - gchar *tmp;
    - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
    -
    - if (conn->disconnect_code == 0x0001) {
    - reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
    - tmp = g_strdup(_("You have signed on from another location"));
    - if (!purple_account_get_remember_password(account))
    - purple_account_set_password(account, NULL);
    - } else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED)
    - tmp = g_strdup(_("Server closed the connection"));
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
    - tmp = g_strdup_printf(_("Lost connection with server: %s"),
    - conn->error_message);
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_INVALID_DATA)
    - tmp = g_strdup(_("Received invalid data on connection with server"));
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_COULD_NOT_CONNECT)
    - tmp = g_strdup_printf(_("Unable to connect: %s"),
    - conn->error_message);
    - else
    - /*
    - * We shouldn't print a message for some disconnect_reasons.
    - * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
    - */
    - tmp = NULL;
    -
    - if (tmp != NULL)
    - {
    - purple_connection_error_reason(od->gc, reason, tmp);
    - g_free(tmp);
    - }
    - }
    -
    - flap_connection_close(od, conn);
    -
    - g_free(conn->error_message);
    - g_free(conn->cookie);
    -
    - /*
    - * Free conn->internal, if necessary
    - */
    - if (conn->type == SNAC_FAMILY_CHAT)
    - flap_connection_destroy_chat(od, conn);
    -
    - g_slist_free(conn->groups);
    - while (conn->rateclasses != NULL)
    - {
    - g_free(conn->rateclasses->data);
    - conn->rateclasses = g_slist_delete_link(conn->rateclasses, conn->rateclasses);
    - }
    -
    - g_hash_table_destroy(conn->rateclass_members);
    -
    - if (conn->queued_snacs) {
    - while (!g_queue_is_empty(conn->queued_snacs))
    - {
    - QueuedSnac *queued_snac;
    - queued_snac = g_queue_pop_head(conn->queued_snacs);
    - flap_frame_destroy(queued_snac->frame);
    - g_free(queued_snac);
    - }
    - g_queue_free(conn->queued_snacs);
    - }
    -
    - if (conn->queued_lowpriority_snacs) {
    - while (!g_queue_is_empty(conn->queued_lowpriority_snacs))
    - {
    - QueuedSnac *queued_snac;
    - queued_snac = g_queue_pop_head(conn->queued_lowpriority_snacs);
    - flap_frame_destroy(queued_snac->frame);
    - g_free(queued_snac);
    - }
    - g_queue_free(conn->queued_lowpriority_snacs);
    - }
    -
    - if (conn->queued_timeout > 0)
    - purple_timeout_remove(conn->queued_timeout);
    -
    - g_free(conn);
    -
    - return FALSE;
    -}
    -
    -/**
    - * See the comments for the parameters of
    - * flap_connection_schedule_destroy().
    - */
    -void
    -flap_connection_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
    -{
    - if (conn->destroy_timeout != 0)
    - purple_timeout_remove(conn->destroy_timeout);
    - conn->disconnect_reason = reason;
    - g_free(conn->error_message);
    - conn->error_message = g_strdup(error_message);
    - flap_connection_destroy_cb(conn);
    -}
    -
    -/**
    - * Schedule Purple to destroy the given FlapConnection as soon as we
    - * return control back to the program's main loop. We must do this
    - * if we want to destroy the connection but we are still using it
    - * for some reason.
    - *
    - * @param reason The reason for the disconnection.
    - * @param error_message A brief error message that gives more detail
    - * regarding the reason for the disconnecting. This should
    - * be NULL for everything except OSCAR_DISCONNECT_LOST_CONNECTION,
    - * in which case it should contain the value of g_strerror(errno),
    - * and OSCAR_DISCONNECT_COULD_NOT_CONNECT, in which case it
    - * should contain the error_message passed back from the call
    - * to purple_proxy_connect().
    - */
    -void
    -flap_connection_schedule_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
    -{
    - if (conn->destroy_timeout != 0)
    - /* Already taken care of */
    - return;
    -
    - purple_debug_info("oscar", "Scheduling destruction of FLAP "
    - "connection %p of type 0x%04hx\n", conn, conn->type);
    - conn->disconnect_reason = reason;
    - g_free(conn->error_message);
    - conn->error_message = g_strdup(error_message);
    - conn->destroy_timeout = purple_timeout_add(0, flap_connection_destroy_cb, conn);
    -}
    -
    -/**
    - * In OSCAR, every connection has a set of SNAC groups associated
    - * with it. These are the groups that you can send over this connection
    - * without being guaranteed a "Not supported" SNAC error.
    - *
    - * The grand theory of things says that these associations transcend
    - * what libfaim calls "connection types" (conn->type). You can probably
    - * see the elegance here, but since I want to revel in it for a bit, you
    - * get to hear it all spelled out.
    - *
    - * So let us say that you have your core BOS connection running. One
    - * of your modules has just given you a SNAC of the group 0x0004 to send
    - * you. Maybe an IM destined for some twit in Greenland. So you start
    - * at the top of your connection list, looking for a connection that
    - * claims to support group 0x0004. You find one. Why, that neat BOS
    - * connection of yours can do that. So you send it on its way.
    - *
    - * Now, say, that fellow from Greenland has friends and they all want to
    - * meet up with you in a lame chat room. This has landed you a SNAC
    - * in the family 0x000e and you have to admit you're a bit lost. You've
    - * searched your connection list for someone who wants to make your life
    - * easy and deliver this SNAC for you, but there isn't one there.
    - *
    - * Here comes the good bit. Without even letting anyone know, particularly
    - * the module that decided to send this SNAC, and definitely not that twit
    - * in Greenland, you send out a service request. In this request, you have
    - * marked the need for a connection supporting group 0x000e. A few seconds
    - * later, you receive a service redirect with an IP address and a cookie in
    - * it. Great, you say. Now I have something to do. Off you go, making
    - * that connection. One of the first things you get from this new server
    - * is a message saying that indeed it does support the group you were looking
    - * for. So you continue and send rate confirmation and all that.
    - *
    - * Then you remember you had that SNAC to send, and now you have a means to
    - * do it, and you do, and everyone is happy. Except the Greenlander, who is
    - * still stuck in the bitter cold.
    - *
    - * Oh, and this is useful for building the Migration SNACs, too. In the
    - * future, this may help convince me to implement rate limit mitigation
    - * for real. We'll see.
    - *
    - * Just to make me look better, I'll say that I've known about this great
    - * scheme for quite some time now. But I still haven't convinced myself
    - * to make libfaim work that way. It would take a fair amount of effort,
    - * and probably some client API changes as well. (Whenever I don't want
    - * to do something, I just say it would change the client API. Then I
    - * instantly have a couple of supporters of not doing it.)
    - *
    - * Generally, addgroup is only called by the internal handling of the
    - * server ready SNAC. So if you want to do something before that, you'll
    - * have to be more creative. That is done rather early, though, so I don't
    - * think you have to worry about it. Unless you're me. I care deeply
    - * about such inane things.
    - *
    - */
    -
    -/**
    - * Find a FlapConnection that supports the given oscar
    - * family.
    - */
    -FlapConnection *
    -flap_connection_findbygroup(OscarData *od, guint16 group)
    -{
    - GSList *cur;
    -
    - for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
    - {
    - FlapConnection *conn;
    - GSList *l;
    -
    - conn = cur->data;
    -
    - for (l = conn->groups; l != NULL; l = l->next)
    - {
    - if (GPOINTER_TO_UINT(l->data) == group)
    - return conn;
    - }
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * Locates a connection of the specified type in the
    - * specified session.
    - *
    - * TODO: Use flap_connection_findbygroup everywhere and get rid of this.
    - *
    - * @param od The session to search.
    - * @param type The type of connection to look for.
    - *
    - * @return Returns the first connection found of the given target type,
    - * or NULL if none could be found.
    - */
    -FlapConnection *
    -flap_connection_getbytype(OscarData *od, int type)
    -{
    - GSList *cur;
    -
    - for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
    - {
    - FlapConnection *conn;
    - conn = cur->data;
    - if ((conn->type == type) && (conn->connected))
    - return conn;
    - }
    -
    - return NULL;
    -}
    -
    -FlapConnection *
    -flap_connection_getbytype_all(OscarData *od, int type)
    -{
    - GSList *cur;
    -
    - for (cur = od->oscar_connections; cur; cur = cur->next)
    - {
    - FlapConnection *conn;
    - conn = cur->data;
    - if (conn->type == type)
    - return conn;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * Allocate a new FLAP frame.
    - *
    - * @param channel The FLAP channel. This is almost always 2.
    - */
    -FlapFrame *
    -flap_frame_new(OscarData *od, guint16 channel, int datalen)
    -{
    - FlapFrame *frame;
    -
    - frame = g_new0(FlapFrame, 1);
    - frame->channel = channel;
    -
    - if (datalen > 0)
    - byte_stream_new(&frame->data, datalen);
    -
    - return frame;
    -}
    -
    -static void
    -parse_snac(OscarData *od, FlapConnection *conn, FlapFrame *frame)
    -{
    - aim_module_t *cur;
    - aim_modsnac_t snac;
    -
    - if (byte_stream_bytes_left(&frame->data) < 10)
    - return;
    -
    - snac.family = byte_stream_get16(&frame->data);
    - snac.subtype = byte_stream_get16(&frame->data);
    - snac.flags = byte_stream_get16(&frame->data);
    - snac.id = byte_stream_get32(&frame->data);
    -
    - /* SNAC flags are apparently uniform across all SNACs, so we handle them here */
    - if (snac.flags & 0x0001) {
    - /*
    - * This means the SNAC will be followed by another SNAC with
    - * related information. We don't need to do anything about
    - * this here.
    - */
    - }
    - if (snac.flags & 0x8000) {
    - /*
    - * This packet contains the version of the family that this SNAC is
    - * in. You get this when your SSI module is version 2 or higher.
    - * For now we have no need for this, but you could always save
    - * it as a part of aim_modnsac_t, or something. The format is...
    - * 2 byte length of total mini-header (which is 6 bytes), then TLV
    - * of type 0x0001, length 0x0002, value is the 2 byte version
    - * number
    - */
    - byte_stream_advance(&frame->data, byte_stream_get16(&frame->data));
    - }
    -
    - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
    -
    - if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
    - (cur->family != snac.family))
    - continue;
    -
    - if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
    - return;
    - }
    -}
    -
    -static void
    -parse_fakesnac(OscarData *od, FlapConnection *conn, FlapFrame *frame, guint16 family, guint16 subtype)
    -{
    - aim_module_t *cur;
    - aim_modsnac_t snac;
    -
    - snac.family = family;
    - snac.subtype = subtype;
    - snac.flags = snac.id = 0;
    -
    - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
    -
    - if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
    - (cur->family != snac.family))
    - continue;
    -
    - if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
    - return;
    - }
    -}
    -
    -static void
    -parse_flap_ch4(OscarData *od, FlapConnection *conn, FlapFrame *frame)
    -{
    - GSList *tlvlist;
    - char *msg = NULL;
    -
    - if (byte_stream_bytes_left(&frame->data) == 0) {
    - /* XXX should do something with this */
    - return;
    - }
    -
    - /* An ICQ account is logging in */
    - if (conn->type == SNAC_FAMILY_AUTH)
    - {
    - parse_fakesnac(od, conn, frame, 0x0017, 0x0003);
    - return;
    - }
    -
    - tlvlist = aim_tlvlist_read(&frame->data);
    -
    - if (aim_tlv_gettlv(tlvlist, 0x0009, 1))
    - conn->disconnect_code = aim_tlv_get16(tlvlist, 0x0009, 1);
    -
    - if (aim_tlv_gettlv(tlvlist, 0x000b, 1))
    - msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
    -
    - /*
    - * The server ended this FLAP connnection, so let's be nice and
    - * close the physical TCP connection
    - */
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_REMOTE_CLOSED, msg);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - g_free(msg);
    -}
    -
    -/**
    - * Takes a new incoming FLAP frame and sends it to the appropriate
    - * handler function to be parsed.
    - */
    -static void
    -parse_flap(OscarData *od, FlapConnection *conn, FlapFrame *frame)
    -{
    - if (frame->channel == 0x01) {
    - guint32 flap_version = byte_stream_get32(&frame->data);
    - if (flap_version != 0x00000001)
    - {
    - /* Error! */
    - purple_debug_warning("oscar", "Expecting FLAP version "
    - "0x00000001 but received FLAP version %08x. Closing connection.\n",
    - flap_version);
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - }
    - else
    - conn->connected = TRUE;
    -
    - } else if (frame->channel == 0x02) {
    - parse_snac(od, conn, frame);
    -
    - } else if (frame->channel == 0x04) {
    - parse_flap_ch4(od, conn, frame);
    -
    - } else if (frame->channel == 0x05) {
    - /* TODO: Reset our keepalive watchdog? */
    -
    - }
    -}
    -
    -/**
    - * Read in all available data on the socket for a given connection.
    - * All complete FLAPs handled immedate after they're received.
    - * Incomplete FLAP data is stored locally and appended to the next
    - * time this callback is triggered.
    - *
    - * This is called by flap_connection_recv_cb and
    - * flap_connection_recv_cb_ssl for unencrypted/encrypted connections.
    - */
    -static void
    -flap_connection_recv(FlapConnection *conn)
    -{
    - gpointer buf;
    - gsize buflen;
    - gssize read;
    -
    - /* Read data until we run out of data and break out of the loop */
    - while (TRUE)
    - {
    - /* Start reading a new FLAP */
    - if (conn->buffer_incoming.data.data == NULL)
    - {
    - buf = conn->header + conn->header_received;
    - buflen = 6 - conn->header_received;
    -
    - /* Read the first 6 bytes (the FLAP header) */
    - if (conn->gsc)
    - read = purple_ssl_read(conn->gsc, buf, buflen);
    - else
    - read = recv(conn->fd, buf, buflen, 0);
    -
    - /* Check if the FLAP server closed the connection */
    - if (read == 0)
    - {
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - break;
    - }
    -
    - /* If there was an error then close the connection */
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - break;
    -
    - /* Error! */
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - break;
    - }
    - conn->od->gc->last_received = time(NULL);
    -
    - /* If we don't even have a complete FLAP header then do nothing */
    - conn->header_received += read;
    - if (conn->header_received < 6)
    - break;
    -
    - /* All FLAP frames must start with the byte 0x2a */
    - if (aimutil_get8(&conn->header[0]) != 0x2a)
    - {
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - break;
    - }
    -
    - /* Initialize a new temporary FlapFrame for incoming data */
    - conn->buffer_incoming.channel = aimutil_get8(&conn->header[1]);
    - conn->buffer_incoming.seqnum = aimutil_get16(&conn->header[2]);
    - conn->buffer_incoming.data.len = aimutil_get16(&conn->header[4]);
    - conn->buffer_incoming.data.data = g_new(guint8, conn->buffer_incoming.data.len);
    - conn->buffer_incoming.data.offset = 0;
    - }
    -
    - buflen = conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset;
    - if (buflen)
    - {
    - buf = &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset];
    - /* Read data into the temporary FlapFrame until it is complete */
    - if (conn->gsc)
    - read = purple_ssl_read(conn->gsc, buf, buflen);
    - else
    - read = recv(conn->fd, buf, buflen, 0);
    -
    - /* Check if the FLAP server closed the connection */
    - if (read == 0)
    - {
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - break;
    - }
    -
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - break;
    -
    - /* Error! */
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - break;
    - }
    -
    - conn->buffer_incoming.data.offset += read;
    - if (conn->buffer_incoming.data.offset < conn->buffer_incoming.data.len)
    - /* Waiting for more data to arrive */
    - break;
    - }
    -
    - /* We have a complete FLAP! Handle it and continue reading */
    - byte_stream_rewind(&conn->buffer_incoming.data);
    - parse_flap(conn->od, conn, &conn->buffer_incoming);
    - conn->lastactivity = time(NULL);
    -
    - g_free(conn->buffer_incoming.data.data);
    - conn->buffer_incoming.data.data = NULL;
    -
    - conn->header_received = 0;
    - }
    -}
    -
    -void
    -flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - FlapConnection *conn = data;
    -
    - flap_connection_recv(conn);
    -}
    -
    -void
    -flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
    -{
    - FlapConnection *conn = data;
    -
    - flap_connection_recv(conn);
    -}
    -
    -/**
    - * @param source When this function is called as a callback source is
    - * set to the fd that triggered the callback. But this function
    - * is also called directly from flap_connection_send_byte_stream(),
    - * in which case source will be -1. So don't use source--use
    - * conn->gsc or conn->fd instead.
    - */
    -static void
    -send_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - FlapConnection *conn;
    - int writelen, ret;
    -
    - conn = data;
    - writelen = purple_circ_buffer_get_max_read(conn->buffer_outgoing);
    -
    - if (writelen == 0)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - return;
    - }
    -
    - if (conn->gsc)
    - ret = purple_ssl_write(conn->gsc, conn->buffer_outgoing->outptr,
    - writelen);
    - else
    - ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
    - if (ret <= 0)
    - {
    - if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - /* Error! */
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - if (conn->gsc) {
    - purple_ssl_close(conn->gsc);
    - conn->gsc = NULL;
    - } else {
    - close(conn->fd);
    - conn->fd = -1;
    - }
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - return;
    - }
    -
    - purple_circ_buffer_mark_read(conn->buffer_outgoing, ret);
    -}
    -
    -static void
    -flap_connection_send_byte_stream(ByteStream *bs, FlapConnection *conn, size_t count)
    -{
    - if (conn == NULL)
    - return;
    -
    - /* Make sure we don't send past the end of the bs */
    - if (count > byte_stream_bytes_left(bs))
    - count = byte_stream_bytes_left(bs); /* truncate to remaining space */
    -
    - if (count == 0)
    - return;
    -
    - /* Add everything to our outgoing buffer */
    - purple_circ_buffer_append(conn->buffer_outgoing, bs->data, count);
    -
    - /* If we haven't already started writing stuff, then start the cycle */
    - if (conn->watcher_outgoing == 0)
    - {
    - if (conn->gsc) {
    - conn->watcher_outgoing = purple_input_add(conn->gsc->fd,
    - PURPLE_INPUT_WRITE, send_cb, conn);
    - send_cb(conn, -1, 0);
    - } else if (conn->fd >= 0) {
    - conn->watcher_outgoing = purple_input_add(conn->fd,
    - PURPLE_INPUT_WRITE, send_cb, conn);
    - send_cb(conn, -1, 0);
    - }
    - }
    -}
    -
    -static void
    -sendframe_flap(FlapConnection *conn, FlapFrame *frame)
    -{
    - ByteStream bs;
    - int payloadlen, bslen;
    -
    - payloadlen = byte_stream_curpos(&frame->data);
    -
    - byte_stream_new(&bs, 6 + payloadlen);
    -
    - /* FLAP header */
    - byte_stream_put8(&bs, 0x2a);
    - byte_stream_put8(&bs, frame->channel);
    - byte_stream_put16(&bs, frame->seqnum);
    - byte_stream_put16(&bs, payloadlen);
    -
    - /* Payload */
    - byte_stream_rewind(&frame->data);
    - byte_stream_putbs(&bs, &frame->data, payloadlen);
    -
    - bslen = byte_stream_curpos(&bs);
    - byte_stream_rewind(&bs);
    - flap_connection_send_byte_stream(&bs, conn, bslen);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -void
    -flap_connection_send(FlapConnection *conn, FlapFrame *frame)
    -{
    - frame->seqnum = ++(conn->seqnum_out);
    - sendframe_flap(conn, frame);
    - flap_frame_destroy(frame);
    -}
    --- a/libpurple/protocols/oscar/kerberos.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,431 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/**
    - * This file implements AIM's kerberos procedure for authenticating
    - * users. This replaces the older MD5-based and XOR-based
    - * authentication methods that use SNAC family 0x0017.
    - *
    - * This doesn't use SNACs or FLAPs at all. It makes https
    - * POSTs to AOL KDC server to validate the user based on the password they
    - * provided to us. Upon successful authentication we receive two tokens
    - * in the response. One is assumed to be the kerberos ticket for authentication
    - * on the various AOL websites, while the other contains BOSS information, such
    - * as the hostname and port number to use, the TLS certificate name as well as
    - * the cookie to use to authenticate to the BOS server.
    - * And then everything else is the same as with BUCP.
    - *
    - */
    -
    -#include "oscar.h"
    -#include "oscarcommon.h"
    -#include "core.h"
    -
    -#define MAXAIMPASSLEN 16
    -
    -/*
    - * Incomplete X-SNAC format taken from reverse engineering doen by digsby:
    - * https://github.com/ifwe/digsby/blob/master/digsby/src/oscar/login2.py
    - */
    -typedef struct {
    - aim_tlv_t *main_tlv;
    - gchar *principal1;
    - gchar *service;
    - gchar *principal1_again;
    - gchar *principal2;
    - gchar unknown;
    - guint8 *footer;
    - struct {
    - guint32 unknown1;
    - guint32 unknown2;
    - guint32 epoch_now;
    - guint32 epoch_valid;
    - guint32 epoch_renew;
    - guint32 epoch_expire;
    - guint32 unknown3;
    - guint32 unknown4;
    - guint32 unknown5;
    - } dates;
    - GSList *tlvlist;
    -} aim_xsnac_token_t;
    -
    -typedef struct {
    - guint16 family;
    - guint16 subtype;
    - guint8 flags[8];
    - guint16 request_id;
    - guint32 epoch;
    - guint32 unknown;
    - gchar *principal1;
    - gchar *principal2;
    - guint16 num_tokens;
    - aim_xsnac_token_t *tokens;
    - GSList *tlvlist;
    -} aim_xsnac_t;
    -
    -static gchar *get_kdc_url(OscarData *od)
    -{
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - const gchar *server;
    - gchar *url;
    - gchar *port_str = NULL;
    - gint port;
    -
    - server = purple_account_get_string(account, "server", AIM_DEFAULT_KDC_SERVER);
    - port = purple_account_get_int(account, "port", AIM_DEFAULT_KDC_PORT);
    - if (port != 443)
    - port_str = g_strdup_printf(":%d", port);
    - url = g_strdup_printf("https://%s%s/", server, port_str ? port_str : "");
    - g_free(port_str);
    -
    - return url;
    -}
    -
    -static const char *get_client_key(OscarData *od)
    -{
    - return oscar_get_ui_info_string(
    - od->icq ? "prpl-icq-clientkey" : "prpl-aim-clientkey",
    - od->icq ? ICQ_DEFAULT_CLIENT_KEY : AIM_DEFAULT_CLIENT_KEY);
    -}
    -
    -static void
    -aim_encode_password(const char *password, gchar *encoded)
    -{
    - guint8 encoding_table[] = {
    - 0x76, 0x91, 0xc5, 0xe7,
    - 0xd0, 0xd9, 0x95, 0xdd,
    - 0x9e, 0x2F, 0xea, 0xd8,
    - 0x6B, 0x21, 0xc2, 0xbc,
    -
    - };
    - guint i;
    -
    - /*
    - * We truncate AIM passwords to 16 characters since that's what
    - * the official client does as well.
    - */
    - for (i = 0; i < strlen(password) && i < MAXAIMPASSLEN; i++)
    - encoded[i] = (password[i] ^ encoding_table[i]);
    -}
    -
    -static void
    -aim_xsnac_free(aim_xsnac_t *xsnac)
    -{
    - gint i;
    -
    - g_free(xsnac->principal1);
    - g_free(xsnac->principal2);
    - aim_tlvlist_free(xsnac->tlvlist);
    -
    - for (i = 0; i < xsnac->num_tokens; i++) {
    - g_free(xsnac->tokens[i].main_tlv->value);
    - g_free(xsnac->tokens[i].main_tlv);
    - g_free(xsnac->tokens[i].principal1);
    - g_free(xsnac->tokens[i].service);
    - g_free(xsnac->tokens[i].principal1_again);
    - g_free(xsnac->tokens[i].principal2);
    - g_free(xsnac->tokens[i].footer);
    - aim_tlvlist_free(xsnac->tokens[i].tlvlist);
    - }
    - g_free(xsnac->tokens);
    -}
    -
    -static void
    -kerberos_login_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
    - const gchar *got_data, gsize got_len, const gchar *error_message)
    -{
    - OscarData *od = user_data;
    - PurpleConnection *gc;
    - ByteStream bs;
    - aim_xsnac_t xsnac = {0};
    - guint16 len;
    - gchar *bosip = NULL;
    - gchar *tlsCertName = NULL;
    - guint8 *cookie = NULL;
    - guint32 cookie_len = 0;
    - char *host; int port;
    - gsize i;
    -
    - gc = od->gc;
    -
    - od->url_data = NULL;
    -
    - if (error_message != NULL || got_len == 0) {
    - gchar *tmp;
    - gchar *url;
    -
    - url = get_kdc_url(od);
    - tmp = g_strdup_printf(_("Error requesting %s: %s"),
    - url, error_message ?
    - error_message : _("The server returned an empty response"));
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - g_free(url);
    - return;
    - }
    -
    - purple_debug_info("oscar", "Received kerberos login HTTP response %lu : ", got_len);
    -
    - byte_stream_init(&bs, (guint8 *)got_data, got_len);
    -
    - xsnac.family = byte_stream_get16(&bs);
    - xsnac.subtype = byte_stream_get16(&bs);
    - byte_stream_getrawbuf(&bs, (guint8 *) xsnac.flags, 8);
    -
    - if (xsnac.family == 0x50C && xsnac.subtype == 0x0005) {
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
    - _("Incorrect password"));
    - return;
    - }
    - if (xsnac.family != 0x50C || xsnac.subtype != 0x0003) {
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Error parsing response from authentication server"));
    - return;
    - }
    - xsnac.request_id = byte_stream_get16(&bs);
    - xsnac.epoch = byte_stream_get32(&bs);
    - xsnac.unknown = byte_stream_get32(&bs);
    - len = byte_stream_get16(&bs);
    - xsnac.principal1 = byte_stream_getstr(&bs, len);
    - len = byte_stream_get16(&bs);
    - xsnac.principal2 = byte_stream_getstr(&bs, len);
    - xsnac.num_tokens = byte_stream_get16(&bs);
    -
    - purple_debug_info("oscar", "KDC: %d tokens between '%s' and '%s'\n",
    - xsnac.num_tokens, xsnac.principal1, xsnac.principal2);
    - xsnac.tokens = g_new0(aim_xsnac_token_t, xsnac.num_tokens);
    - for (i = 0; i < xsnac.num_tokens; i++) {
    - GSList *tlv;
    -
    - tlv = aim_tlvlist_readnum(&bs, 1);
    - if (tlv)
    - xsnac.tokens[i].main_tlv = tlv->data;
    - g_slist_free(tlv);
    -
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].principal1 = byte_stream_getstr(&bs, len);
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].service = byte_stream_getstr(&bs, len);
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].principal1_again = byte_stream_getstr(&bs, len);
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].principal2 = byte_stream_getstr(&bs, len);
    - xsnac.tokens[i].unknown = byte_stream_get8(&bs);
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].footer = byte_stream_getraw(&bs, len);
    -
    - xsnac.tokens[i].dates.unknown1 = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.unknown2 = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.epoch_now = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.epoch_valid = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.epoch_renew = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.epoch_expire = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.unknown3 = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.unknown4 = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.unknown5 = byte_stream_get32(&bs);
    -
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].tlvlist = aim_tlvlist_readnum(&bs, len);
    -
    - purple_debug_info("oscar", "Token %lu has %d TLVs for service '%s'\n",
    - i, len, xsnac.tokens[i].service);
    - }
    - len = byte_stream_get16(&bs);
    - xsnac.tlvlist = aim_tlvlist_readnum(&bs, len);
    -
    - for (i = 0; i < xsnac.num_tokens; i++) {
    - if (purple_strequal(xsnac.tokens[i].service, "im/boss")) {
    - aim_tlv_t *tlv;
    - GSList *tlvlist;
    - ByteStream tbs;
    -
    - tlv = aim_tlv_gettlv(xsnac.tokens[i].tlvlist, 0x0003, 1);
    - if (tlv != NULL) {
    - byte_stream_init(&tbs, tlv->value, tlv->length);
    - byte_stream_get32(&tbs);
    - tlvlist = aim_tlvlist_read(&tbs);
    - if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
    - bosip = aim_tlv_getstr(tlvlist, 0x0005, 1);
    - if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
    - tlsCertName = aim_tlv_getstr(tlvlist, 0x008D, 1);
    - tlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
    - if (tlv) {
    - cookie_len = tlv->length;
    - cookie = tlv->value;
    - }
    - }
    - break;
    - }
    - }
    - if (bosip && cookie) {
    - port = AIM_DEFAULT_KDC_PORT;
    - for (i = 0; i < strlen(bosip); i++) {
    - if (bosip[i] == ':') {
    - port = atoi(&(bosip[i+1]));
    - break;
    - }
    - }
    - host = g_strndup(bosip, i);
    - oscar_connect_to_bos(gc, od, host, port, cookie, cookie_len, tlsCertName);
    - g_free(host);
    - } else {
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unknown error during authentication"));
    - }
    - aim_xsnac_free(&xsnac);
    - g_free(tlsCertName);
    - g_free(bosip);
    -}
    -
    -/**
    - * This function sends a binary blob request to the Kerberos KDC server
    - * https://kdc.uas.aol.com with the user's username and password and
    - * receives the IM cookie, which is used to request a connection to the
    - * BOSS server.
    - * The binary data below is what AIM 8.0.8.1 sends in order to authenticate
    - * to the KDC server. It is an 'X-SNAC' packet, which is relatively similar
    - * to SNAC packets but somehow different.
    - * The header starts with the 0x50C family follow by 0x0002 subtype, then
    - * some fixed length data and TLVs. The string "COOL" appears in there for
    - * some reason followed by the 'US' and 'en' strings.
    - * Then the 'imApp key=<client key>' comes after that, and then the username
    - * and the string "im/boss" which seems to represent the service we are
    - * requesting the authentication for. Changing that will lead to a
    - * 'unknown service' error. The client key is then added again (without the
    - * 'imApp key' string prepended to it) then a XOR-ed version of the password.
    - * The meaning of the header/footer/in-between bytes is not known but never
    - * seems to change so there is no need to reverse engineer their meaning at
    - * this point.
    - */
    -void send_kerberos_login(OscarData *od, const char *username)
    -{
    - PurpleConnection *gc;
    - GString *request;
    - gchar *url;
    - const gchar *password;
    - gchar password_xored[MAXAIMPASSLEN];
    - const gchar *client_key;
    - gchar *imapp_key;
    - GString *body;
    - guint16 len_be;
    - guint16 reqid;
    - const gchar header[] = {
    - 0x05, 0x0C, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
    - 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05,
    - 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
    - 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x18, 0x99,
    - 0x00, 0x05, 0x00, 0x04, 0x43, 0x4F, 0x4F, 0x4C,
    - 0x00, 0x0A, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0B,
    - 0x00, 0x04, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00,
    - 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
    - 0x55, 0x53, 0x00, 0x02, 0x65, 0x6E, 0x00, 0x04,
    - 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D,
    - 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04,
    - 0x00, 0x05};
    - const gchar pre_username[] = {
    - 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x01, 0x8B,
    - 0x01, 0x00, 0x00, 0x00, 0x00};
    - const gchar post_username[] = {
    - 0x00, 0x07, 0x69, 0x6D, 0x2F, 0x62, 0x6F, 0x73,
    - 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x04, 0x00, 0x02};
    - const gchar pre_password[] = {
    - 0x40, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01,
    - 0x00, 0x00};
    - const gchar post_password[] = {0x00, 0x00, 0x00, 0x1D};
    - const gchar footer[] = {
    - 0x00, 0x21, 0x00, 0x32, 0x00, 0x01, 0x10, 0x03,
    - 0x00, 0x2C, 0x00, 0x07, 0x00, 0x14, 0x00, 0x04,
    - 0x00, 0x00, 0x01, 0x8B, 0x00, 0x16, 0x00, 0x02,
    - 0x00, 0x26, 0x00, 0x17, 0x00, 0x02, 0x00, 0x07,
    - 0x00, 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19,
    - 0x00, 0x02, 0x00, 0x0D, 0x00, 0x1A, 0x00, 0x02,
    - 0x00, 0x04, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x28,
    - 0x00, 0x00};
    -
    - gc = od->gc;
    -
    - password = purple_connection_get_password(gc);
    - aim_encode_password(password, password_xored);
    -
    - client_key = get_client_key(od);
    - imapp_key = g_strdup_printf("imApp key=%s", client_key);
    -
    - /* Construct the body of the HTTP POST request */
    - body = g_string_new(NULL);
    - g_string_append_len(body, header, sizeof(header));
    - reqid = (guint16) g_random_int();
    - g_string_overwrite_len(body, 0xC, (void *)&reqid, sizeof(guint16));
    -
    - len_be = GUINT16_TO_BE(strlen(imapp_key));
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - g_string_append(body, imapp_key);
    -
    - len_be = GUINT16_TO_BE(strlen(username));
    - g_string_append_len(body, pre_username, sizeof(pre_username));
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - g_string_append(body, username);
    - g_string_append_len(body, post_username, sizeof(post_username));
    -
    - len_be = GUINT16_TO_BE(strlen(password) + 0x10);
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - g_string_append_len(body, pre_password, sizeof(pre_password));
    - len_be = GUINT16_TO_BE(strlen(password) + 4);
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - len_be = GUINT16_TO_BE(strlen(password));
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - g_string_append_len(body, password_xored, strlen(password));
    - g_string_append_len(body, post_password, sizeof(post_password));
    -
    - len_be = GUINT16_TO_BE(strlen(client_key));
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - g_string_append(body, client_key);
    - g_string_append_len(body, footer, sizeof(footer));
    -
    - g_free(imapp_key);
    -
    - url = get_kdc_url(od);
    -
    - /* Construct an HTTP POST request */
    - request = g_string_new("POST / HTTP/1.1\n"
    - "Connection: close\n"
    - "Accept: application/x-snac\n");
    -
    - /* Tack on the body */
    - g_string_append_printf(request, "Content-Type: application/x-snac\n");
    - g_string_append_printf(request, "Content-Length: %" G_GSIZE_FORMAT "\n\n", body->len);
    - g_string_append_len(request, body->str, body->len);
    -
    - /* Send the POST request */
    - od->url_data = purple_util_fetch_url_request_data_len_with_account(
    - purple_connection_get_account(gc), url,
    - TRUE, NULL, TRUE, request->str, request->len, FALSE, -1,
    - kerberos_login_cb, od);
    - g_string_free(request, TRUE);
    -
    - g_string_free(body, TRUE);
    - g_free(url);
    -}
    --- a/libpurple/protocols/oscar/libaim.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,154 +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
    - *
    - */
    -
    -/* libaim is the AIM protocol plugin. It is linked against liboscar,
    - * which contains all the shared implementation code with libicq
    - */
    -
    -#include "oscarcommon.h"
    -#include "oscar.h"
    -
    -static PurplePluginProtocolInfo prpl_info =
    -{
    - OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE,
    - NULL, /* user_splits */
    - NULL, /* protocol_options */
    - {"gif,jpeg,bmp,ico", 0, 0, 64, 64, 7168, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
    - oscar_list_icon_aim, /* list_icon */
    - oscar_list_emblem, /* list_emblems */
    - oscar_status_text, /* status_text */
    - oscar_tooltip_text, /* tooltip_text */
    - oscar_status_types, /* status_types */
    - oscar_blist_node_menu, /* blist_node_menu */
    - oscar_chat_info, /* chat_info */
    - oscar_chat_info_defaults, /* chat_info_defaults */
    - oscar_login, /* login */
    - oscar_close, /* close */
    - oscar_send_im, /* send_im */
    - oscar_set_info, /* set_info */
    - oscar_send_typing, /* send_typing */
    - oscar_get_info, /* get_info */
    - oscar_set_status, /* set_status */
    - oscar_set_idle, /* set_idle */
    - oscar_change_passwd, /* change_passwd */
    - NULL, /* add_buddy */
    - NULL, /* add_buddies */
    - oscar_remove_buddy, /* remove_buddy */
    - NULL, /* remove_buddies */
    - oscar_add_permit, /* add_permit */
    - oscar_add_deny, /* add_deny */
    - oscar_rem_permit, /* rem_permit */
    - oscar_rem_deny, /* rem_deny */
    - oscar_set_aim_permdeny, /* set_permit_deny */
    - oscar_join_chat, /* join_chat */
    - NULL, /* reject_chat */
    - oscar_get_chat_name, /* get_chat_name */
    - oscar_chat_invite, /* chat_invite */
    - oscar_chat_leave, /* chat_leave */
    - NULL, /* chat_whisper */
    - oscar_send_chat, /* chat_send */
    - oscar_keepalive, /* keepalive */
    - NULL, /* register_user */
    - NULL, /* get_cb_info */
    - NULL, /* get_cb_away */
    - oscar_alias_buddy, /* alias_buddy */
    - oscar_move_buddy, /* group_buddy */
    - oscar_rename_group, /* rename_group */
    - NULL, /* buddy_free */
    - oscar_convo_closed, /* convo_closed */
    - oscar_normalize, /* normalize */
    - oscar_set_icon, /* set_buddy_icon */
    - oscar_remove_group, /* remove_group */
    - NULL, /* get_cb_real_name */
    - NULL, /* set_chat_topic */
    - NULL, /* find_blist_chat */
    - NULL, /* roomlist_get_list */
    - NULL, /* roomlist_cancel */
    - NULL, /* roomlist_expand_category */
    - oscar_can_receive_file, /* can_receive_file */
    - oscar_send_file, /* send_file */
    - oscar_new_xfer, /* new_xfer */
    - oscar_offline_message, /* offline_message */
    - NULL, /* whiteboard_prpl_ops */
    - NULL, /* send_raw */
    - NULL, /* roomlist_room_serialize */
    - NULL, /* unregister_user */
    - NULL, /* send_attention */
    - NULL, /* get_attention_types */
    - sizeof(PurplePluginProtocolInfo), /* struct_size */
    - NULL, /* get_account_text_table */
    - NULL, /* initiate_media */
    - NULL, /* get_media_caps */
    - NULL, /* get_moods */
    - NULL, /* set_public_alias */
    - NULL, /* get_public_alias */
    - oscar_add_buddy, /* add_buddy_with_invite */
    - NULL, /* add_buddies_with_invite */
    - NULL, /* get_cb_alias */
    - NULL, /* chat_can_receive_file */
    - NULL, /* chat_send_file */
    -};
    -
    -static PurplePluginInfo info =
    -{
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_PROTOCOL, /**< type */
    - NULL, /**< ui_requirement */
    - 0, /**< flags */
    - NULL, /**< dependencies */
    - PURPLE_PRIORITY_DEFAULT, /**< priority */
    -
    - "prpl-aim", /**< id */
    - "AIM", /**< name */
    - DISPLAY_VERSION, /**< version */
    - /** summary */
    - N_("AIM Protocol Plugin"),
    - /** description */
    - N_("AIM Protocol Plugin"),
    - NULL, /**< author */
    - PURPLE_WEBSITE, /**< homepage */
    -
    - NULL, /**< load */
    - NULL, /**< unload */
    - NULL, /**< destroy */
    -
    - NULL, /**< ui_info */
    - &prpl_info, /**< extra_info */
    - NULL,
    - oscar_actions,
    -
    - /* padding */
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin)
    -{
    - oscar_init(plugin, FALSE);
    -}
    -
    -PURPLE_INIT_PLUGIN(aim, init_plugin, info);
    --- a/libpurple/protocols/oscar/libicq.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,169 +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
    - *
    - */
    -
    -/* libicq is the ICQ protocol plugin. It is linked against liboscar,
    - * which contains all the shared implementation code with libaim
    - */
    -
    -
    -#include "oscarcommon.h"
    -
    -static GHashTable *
    -icq_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)_("ICQ UIN..."));
    - return table;
    -}
    -
    -static PurplePluginProtocolInfo prpl_info =
    -{
    - OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE,
    - NULL, /* user_splits */
    - NULL, /* protocol_options */
    - {"gif,jpeg,bmp,ico", 0, 0, 64, 64, 7168, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
    - oscar_list_icon_icq, /* list_icon */
    - oscar_list_emblem, /* list_emblems */
    - oscar_status_text, /* status_text */
    - oscar_tooltip_text, /* tooltip_text */
    - oscar_status_types, /* status_types */
    - oscar_blist_node_menu, /* blist_node_menu */
    - oscar_chat_info, /* chat_info */
    - oscar_chat_info_defaults, /* chat_info_defaults */
    - oscar_login, /* login */
    - oscar_close, /* close */
    - oscar_send_im, /* send_im */
    - oscar_set_info, /* set_info */
    - oscar_send_typing, /* send_typing */
    - oscar_get_info, /* get_info */
    - oscar_set_status, /* set_status */
    - oscar_set_idle, /* set_idle */
    - oscar_change_passwd, /* change_passwd */
    - NULL, /* add_buddy */
    - NULL, /* add_buddies */
    - oscar_remove_buddy, /* remove_buddy */
    - NULL, /* remove_buddies */
    - NULL, /* add_permit */
    - oscar_add_deny, /* add_deny */
    - NULL, /* rem_permit */
    - oscar_rem_deny, /* rem_deny */
    - NULL, /* set_permit_deny */
    - oscar_join_chat, /* join_chat */
    - NULL, /* reject_chat */
    - oscar_get_chat_name, /* get_chat_name */
    - oscar_chat_invite, /* chat_invite */
    - oscar_chat_leave, /* chat_leave */
    - NULL, /* chat_whisper */
    - oscar_send_chat, /* chat_send */
    - oscar_keepalive, /* keepalive */
    - NULL, /* register_user */
    - NULL, /* get_cb_info */
    - NULL, /* get_cb_away */
    - oscar_alias_buddy, /* alias_buddy */
    - oscar_move_buddy, /* group_buddy */
    - oscar_rename_group, /* rename_group */
    - NULL, /* buddy_free */
    - oscar_convo_closed, /* convo_closed */
    - oscar_normalize, /* normalize */
    - oscar_set_icon, /* set_buddy_icon */
    - oscar_remove_group, /* remove_group */
    - NULL, /* get_cb_real_name */
    - NULL, /* set_chat_topic */
    - NULL, /* find_blist_chat */
    - NULL, /* roomlist_get_list */
    - NULL, /* roomlist_cancel */
    - NULL, /* roomlist_expand_category */
    - oscar_can_receive_file, /* can_receive_file */
    - oscar_send_file, /* send_file */
    - oscar_new_xfer, /* new_xfer */
    - oscar_offline_message, /* offline_message */
    - NULL, /* whiteboard_prpl_ops */
    - NULL, /* send_raw */
    - NULL, /* roomlist_room_serialize */
    - NULL, /* unregister_user */
    - NULL, /* send_attention */
    - NULL, /* get_attention_types */
    -
    - sizeof(PurplePluginProtocolInfo), /* struct_size */
    - icq_get_account_text_table, /* get_account_text_table */
    - NULL, /* initiate_media */
    - NULL, /* can_do_media */
    - oscar_get_purple_moods, /* get_moods */
    - NULL, /* set_public_alias */
    - NULL, /* get_public_alias */
    - oscar_add_buddy, /* add_buddy_with_invite */
    - NULL, /* add_buddies_with_invite */
    - NULL, /* get_cb_alias */
    - NULL, /* chat_can_receive_file */
    - NULL, /* chat_send_file */
    -};
    -
    -static PurplePluginInfo info =
    -{
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_PROTOCOL, /**< type */
    - NULL, /**< ui_requirement */
    - 0, /**< flags */
    - NULL, /**< dependencies */
    - PURPLE_PRIORITY_DEFAULT, /**< priority */
    -
    - "prpl-icq", /**< id */
    - "ICQ", /**< name */
    - DISPLAY_VERSION, /**< version */
    - /** summary */
    - N_("ICQ Protocol Plugin"),
    - /** description */
    - N_("ICQ Protocol Plugin"),
    - NULL, /**< author */
    - PURPLE_WEBSITE, /**< homepage */
    -
    - NULL, /**< load */
    - NULL, /**< unload */
    - NULL, /**< destroy */
    -
    - NULL, /**< ui_info */
    - &prpl_info, /**< extra_info */
    - NULL,
    - oscar_actions,
    -
    - /* padding */
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin)
    -{
    - PurpleAccountOption *option;
    -
    - oscar_init(plugin, TRUE);
    -
    - option = purple_account_option_string_new(_("Encoding"), "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
    - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
    -}
    -
    -PURPLE_INIT_PLUGIN(icq, init_plugin, info);
    --- a/libpurple/protocols/oscar/misc.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,133 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Random stuff. Basically just a few functions for sending
    - * simple SNACs, and then the generic error handler.
    - */
    -
    -#include "oscar.h"
    -
    -/*
    - * Generic routine for sending commands.
    - *
    - * I know I can do this in a smarter way...but I'm not thinking straight
    - * right now...
    - *
    - * I had one big function that handled all three cases, but then it broke
    - * and I split it up into three. But then I fixed it. I just never went
    - * back to the single. I don't see any advantage to doing it either way.
    - *
    - */
    -void
    -aim_genericreq_n(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype)
    -{
    - aim_snacid_t snacid = 0x00000000;
    -
    - flap_connection_send_snac(od, conn, family, subtype, snacid, NULL);
    -}
    -
    -void
    -aim_genericreq_n_snacid(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype)
    -{
    - aim_snacid_t snacid;
    -
    - snacid = aim_cachesnac(od, family, subtype, 0x0000, NULL, 0);
    -
    - flap_connection_send_snac(od, conn, family, subtype, snacid, NULL);
    -}
    -
    -void
    -aim_genericreq_l(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint32 *longdata)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!longdata)
    - {
    - aim_genericreq_n(od, conn, family, subtype);
    - return;
    - }
    -
    - byte_stream_new(&bs, 4);
    -
    - snacid = aim_cachesnac(od, family, subtype, 0x0000, NULL, 0);
    -
    - byte_stream_put32(&bs, *longdata);
    -
    - flap_connection_send_snac(od, conn, family, subtype, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/*
    - * Should be generic enough to handle the errors for all groups.
    - *
    - */
    -static int
    -generror(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - int error = 0;
    - aim_rxcallback_t userfunc;
    - aim_snac_t *snac2;
    -
    - snac2 = aim_remsnac(od, snac->id);
    -
    - if (byte_stream_bytes_left(bs))
    - error = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, error, snac2 ? snac2->data : NULL);
    -
    - if (snac2) {
    - g_free(snac2->data);
    - g_free(snac2);
    - }
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return generror(od, conn, mod, frame, snac, bs);
    - else if ((snac->family == 0xffff) && (snac->subtype == 0xffff)) {
    - aim_rxcallback_t userfunc;
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - return userfunc(od, conn, frame);
    - }
    -
    - return 0;
    -}
    -
    -int
    -misc_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = 0xffff;
    - mod->version = 0x0000;
    - mod->flags = AIM_MODFLAG_MULTIFAMILY;
    - strncpy(mod->name, "misc", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/msgcookie.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,179 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Cookie Caching stuff. Adam wrote this, apparently just some
    - * derivatives of n's SNAC work. I cleaned it up, added comments.
    - *
    - */
    -
    -/*
    - * I'm assuming that cookies are type-specific. that is, we can have
    - * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we
    - * lose some error checking. if we assume cookies are not type-specific and are
    - * wrong, we get quirky behavior when cookies step on each others' toes.
    - */
    -
    -#include "oscar.h"
    -
    -/**
    - * aim_cachecookie - appends a cookie to the cookie list
    - *
    - * if cookie->cookie for type cookie->type is found, updates the
    - * ->addtime of the found structure; otherwise adds the given cookie
    - * to the cache
    - *
    - * @param od session to add to
    - * @param cookie pointer to struct to append
    - * @return returns -1 on error, 0 on append, 1 on update. the cookie you pass
    - * in may be free'd, so don't count on its value after calling this!
    - */
    -int aim_cachecookie(OscarData *od, IcbmCookie *cookie)
    -{
    - IcbmCookie *newcook;
    -
    - if (!od || !cookie)
    - return -EINVAL;
    -
    - newcook = aim_checkcookie(od, cookie->cookie, cookie->type);
    -
    - if (newcook == cookie) {
    - newcook->addtime = time(NULL);
    - return 1;
    - } else if (newcook)
    - aim_cookie_free(od, newcook);
    -
    - cookie->addtime = time(NULL);
    -
    - cookie->next = od->msgcookies;
    - od->msgcookies = cookie;
    -
    - return 0;
    -}
    -
    -/**
    - * aim_uncachecookie - grabs a cookie from the cookie cache (removes it from the list)
    - *
    - * takes a cookie string and a cookie type and finds the cookie struct associated with that duple, removing it from the cookie list ikn the process.
    - *
    - * @param od session to grab cookie from
    - * @param cookie cookie string to look for
    - * @param type cookie type to look for
    - * @return if found, returns the struct; if none found (or on error), returns NULL:
    - */
    -IcbmCookie *aim_uncachecookie(OscarData *od, guint8 *cookie, int type)
    -{
    - IcbmCookie *cur, **prev;
    -
    - if (!cookie || !od->msgcookies)
    - return NULL;
    -
    - for (prev = &od->msgcookies; (cur = *prev); ) {
    - if ((cur->type == type) &&
    - (memcmp(cur->cookie, cookie, 8) == 0)) {
    - *prev = cur->next;
    - return cur;
    - }
    - prev = &cur->next;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * aim_mkcookie - generate an IcbmCookie *struct from a cookie string, a type, and a data pointer.
    - *
    - * @param c pointer to the cookie string array
    - * @param type cookie type to use
    - * @param data data to be cached with the cookie
    - * @return returns NULL on error, a pointer to the newly-allocated
    - * cookie on success.
    - */
    -IcbmCookie *aim_mkcookie(guint8 *c, int type, void *data)
    -{
    - IcbmCookie *cookie;
    -
    - if (!c)
    - return NULL;
    -
    - cookie = g_new0(IcbmCookie, 1);
    -
    - cookie->data = data;
    - cookie->type = type;
    - memcpy(cookie->cookie, c, 8);
    -
    - return cookie;
    -}
    -
    -/**
    - * aim_checkcookie - check to see if a cookietuple has been cached
    - *
    - * @param od session to check for the cookie in
    - * @param cookie pointer to the cookie string array
    - * @param type type of the cookie to look for
    - * @return returns a pointer to the cookie struct (still in the list)
    - * on success; returns NULL on error/not found
    - */
    -
    -IcbmCookie *aim_checkcookie(OscarData *od, const guint8 *cookie, const int type)
    -{
    - IcbmCookie *cur;
    -
    - for (cur = od->msgcookies; cur; cur = cur->next) {
    - if ((cur->type == type) &&
    - (memcmp(cur->cookie, cookie, 8) == 0))
    - return cur;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * aim_cookie_free - free an IcbmCookie struct
    - *
    - * this function removes the cookie *cookie from the list of cookies
    - * in od, and then frees all memory associated with it. including
    - * its data! if you want to use the private data after calling this,
    - * make sure you copy it first.
    - *
    - * @param od session to remove the cookie from
    - * @param cookie the address of a pointer to the cookie struct to remove
    - * @return returns -1 on error, 0 on success.
    - *
    - */
    -int aim_cookie_free(OscarData *od, IcbmCookie *cookie)
    -{
    - IcbmCookie *cur, **prev;
    -
    - if (!od || !cookie)
    - return -EINVAL;
    -
    - for (prev = &od->msgcookies; (cur = *prev); ) {
    - if (cur == cookie)
    - *prev = cur->next;
    - else
    - prev = &cur->next;
    - }
    -
    - g_free(cookie->data);
    - g_free(cookie);
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/odc.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,628 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/* From the oscar PRPL */
    -#include "encoding.h"
    -#include "oscar.h"
    -#include "peer.h"
    -
    -/* From Purple */
    -#include "conversation.h"
    -#include "imgstore.h"
    -#include "glibcompat.h"
    -#include "util.h"
    -
    -#define DIRECTIM_MAX_FILESIZE 52428800
    -
    -/**
    - * Free any ODC related data and print a message to the conversation
    - * window based on conn->disconnect_reason.
    - */
    -void
    -peer_odc_close(PeerConnection *conn)
    -{
    - gchar *tmp;
    -
    - if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED)
    - tmp = g_strdup(_("The remote user has closed the connection."));
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED)
    - tmp = g_strdup(_("The remote user has declined your request."));
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
    - tmp = g_strdup_printf(_("Lost connection with the remote user:<br>%s"),
    - conn->error_message);
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_INVALID_DATA)
    - tmp = g_strdup(_("Received invalid data on connection with remote user."));
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_COULD_NOT_CONNECT)
    - tmp = g_strdup(_("Unable to establish a connection with the remote user."));
    - else
    - /*
    - * We shouldn't print a message for some disconnect_reasons.
    - * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
    - */
    - tmp = NULL;
    -
    - if (tmp != NULL)
    - {
    - PurpleAccount *account;
    - PurpleConversation *conv;
    -
    - account = purple_connection_get_account(conn->od->gc);
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
    - purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
    - g_free(tmp);
    - }
    -
    - if (conn->frame != NULL)
    - {
    - OdcFrame *frame;
    - frame = conn->frame;
    - g_free(frame->payload.data);
    - g_free(frame);
    - }
    -}
    -
    -/**
    - * Write the given OdcFrame to a ByteStream and send it out
    - * on the established PeerConnection.
    - */
    -static void
    -peer_odc_send(PeerConnection *conn, OdcFrame *frame)
    -{
    - PurpleAccount *account;
    - const char *username;
    - size_t length;
    - ByteStream bs;
    -
    - purple_debug_info("oscar", "Outgoing ODC frame to %s with "
    - "type=0x%04x, flags=0x%04x, payload length=%" G_GSIZE_FORMAT "\n",
    - conn->bn, frame->type, frame->flags, frame->payload.len);
    -
    - account = purple_connection_get_account(conn->od->gc);
    - username = purple_account_get_username(account);
    - memcpy(frame->bn, username, strlen(username));
    - memcpy(frame->cookie, conn->cookie, 8);
    -
    - length = 76;
    - byte_stream_new(&bs, length + frame->payload.len);
    - byte_stream_putraw(&bs, conn->magic, 4);
    - byte_stream_put16(&bs, length);
    - byte_stream_put16(&bs, frame->type);
    - byte_stream_put16(&bs, frame->subtype);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_putraw(&bs, frame->cookie, 8);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put32(&bs, frame->payload.len);
    - byte_stream_put16(&bs, frame->encoding);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, frame->flags);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_putraw(&bs, frame->bn, 32);
    - byte_stream_putraw(&bs, frame->payload.data, frame->payload.len);
    -
    - peer_connection_send(conn, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Send a very basic ODC frame (which contains the cookie) so that the
    - * remote user can verify that we are the person they were expecting.
    - * If we made an outgoing connection to then remote user, then we send
    - * this immediately. If the remote user connected to us, then we wait
    - * for the other person to send this to us, then we send one to them.
    - */
    -void
    -peer_odc_send_cookie(PeerConnection *conn)
    -{
    - OdcFrame frame;
    -
    - memset(&frame, 0, sizeof(OdcFrame));
    - frame.type = 0x0001;
    - frame.subtype = 0x0006;
    - frame.flags = 0x0060; /* Maybe this means "we're sending the cookie"? */
    -
    - peer_odc_send(conn, &frame);
    -}
    -
    -/**
    - * Send client-to-client typing notification over an established direct connection.
    - */
    -void
    -peer_odc_send_typing(PeerConnection *conn, PurpleTypingState typing)
    -{
    - OdcFrame frame;
    -
    - memset(&frame, 0, sizeof(OdcFrame));
    - frame.type = 0x0001;
    - frame.subtype = 0x0006;
    - if (typing == PURPLE_TYPING)
    - frame.flags = 0x0002 | 0x0008;
    - else if (typing == PURPLE_TYPED)
    - frame.flags = 0x0002 | 0x0004;
    - else
    - frame.flags = 0x0002;
    -
    - peer_odc_send(conn, &frame);
    -}
    -
    -/**
    - * Send client-to-client IM over an established direct connection.
    - * To send a direct IM, call this just like you would aim_send_im.
    - *
    - * @param conn The already-connected ODC connection.
    - * @param msg Null-terminated string to send.
    - * @param len The length of the message to send, including binary data.
    - * @param encoding See the AIM_CHARSET_* defines in oscar.h
    - * @param autoreply TRUE if this is any auto-reply.
    - */
    -void
    -peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply)
    -{
    - OdcFrame frame;
    -
    - g_return_if_fail(msg != NULL);
    - g_return_if_fail(len > 0);
    -
    - memset(&frame, 0, sizeof(OdcFrame));
    - frame.type = 0x0001;
    - frame.subtype = 0x0006;
    - frame.payload.len = len;
    - frame.encoding = encoding;
    - frame.flags = autoreply;
    - byte_stream_new(&frame.payload, len);
    - byte_stream_putraw(&frame.payload, (guint8 *)msg, len);
    -
    - peer_odc_send(conn, &frame);
    -
    - g_free(frame.payload.data);
    -}
    -
    -struct embedded_data
    -{
    - size_t size;
    - const guint8 *data;
    -};
    -
    -/**
    - * This is called after a direct IM has been received in its entirety. This
    - * function is passed a long chunk of data which contains the IM with any
    - * data chunks (images) appended to it.
    - *
    - * This function rips out all the data chunks and creates an imgstore for
    - * each one. In order to do this, it first goes through the IM and takes
    - * out all the IMG tags. When doing so, it rewrites the original IMG tag
    - * with one compatible with the imgstore Purple core code. For each one, we
    - * then read in chunks of data from the end of the message and actually
    - * create the img store using the given data.
    - *
    - * For somewhat easy reference, here's a sample message
    - * (with added whitespace):
    - *
    - * <HTML><BODY BGCOLOR="#ffffff">
    - * <FONT LANG="0">
    - * This is a really stupid picture:<BR>
    - * <IMG SRC="Sample.jpg" ID="1" WIDTH="283" HEIGHT="212" DATASIZE="9894"><BR>
    - * Yeah it is<BR>
    - * Here is another one:<BR>
    - * <IMG SRC="Soap Bubbles.bmp" ID="2" WIDTH="256" HEIGHT="256" DATASIZE="65978">
    - * </FONT>
    - * </BODY></HTML>
    - * <BINARY>
    - * <DATA ID="1" SIZE="9894">datadatadatadata</DATA>
    - * <DATA ID="2" SIZE="65978">datadatadatadata</DATA>
    - * </BINARY>
    - */
    -static void
    -peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int encoding, gboolean autoreply)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - const char *msgend, *binary_start, *dataend;
    - const char *tmp, *start, *end, *idstr, *src, *sizestr;
    - GData *attributes;
    - GHashTable *embedded_datas;
    - struct embedded_data *embedded_data;
    - GSList *images;
    - gchar *utf8;
    - GString *newmsg;
    - PurpleMessageFlags imflags;
    -
    - gc = conn->od->gc;
    - account = purple_connection_get_account(gc);
    -
    - dataend = msg + len;
    -
    - /*
    - * Create a hash table containing references to each embedded
    - * data chunk. The key is the "ID" and the value is an
    - * embedded_data struct.
    - */
    - embedded_datas = g_hash_table_new_full(g_direct_hash,
    - g_direct_equal, NULL, g_free);
    -
    - /*
    - * Create an index of any binary chunks. If we run into any
    - * problems while parsing the binary data section then we stop
    - * parsing it, and the local user will see broken image icons.
    - */
    - binary_start = purple_strcasestr(msg, "<binary>");
    - if (binary_start == NULL)
    - msgend = dataend;
    - else
    - {
    - msgend = binary_start;
    -
    - /* Move our pointer to immediately after the <binary> tag */
    - tmp = binary_start + 8;
    -
    - /* The embedded binary markup has a mimimum length of 29 bytes */
    - while ((tmp + 29 <= dataend) &&
    - purple_markup_find_tag("data", tmp, &start, &tmp, &attributes))
    - {
    - unsigned int id;
    - size_t size;
    -
    - /* Move the binary pointer from ">" to the start of the data */
    - tmp++;
    -
    - /* Get the ID */
    - idstr = g_datalist_get_data(&attributes, "id");
    - if (idstr == NULL)
    - {
    - g_datalist_clear(&attributes);
    - break;
    - }
    - id = atoi(idstr);
    -
    - /* Get the size */
    - sizestr = g_datalist_get_data(&attributes, "size");
    - if (sizestr == NULL)
    - {
    - g_datalist_clear(&attributes);
    - break;
    - }
    - size = atol(sizestr);
    -
    - g_datalist_clear(&attributes);
    -
    - if ((size > 0) && (tmp + size > dataend))
    - break;
    -
    - embedded_data = g_new(struct embedded_data, 1);
    - embedded_data->size = size;
    - embedded_data->data = (const guint8 *)tmp;
    - tmp += size;
    -
    - /* Skip past the closing </data> tag */
    - if (g_ascii_strncasecmp(tmp, "</data>", 7))
    - {
    - g_free(embedded_data);
    - break;
    - }
    - tmp += 7;
    -
    - g_hash_table_insert(embedded_datas,
    - GINT_TO_POINTER(id), embedded_data);
    - }
    - }
    -
    - /*
    - * Loop through the message, replacing OSCAR img tags with the
    - * equivalent Purple img tag.
    - */
    - images = NULL;
    - newmsg = g_string_new("");
    - tmp = msg;
    - while (purple_markup_find_tag("img", tmp, &start, &end, &attributes))
    - {
    - int imgid = 0;
    -
    - idstr = g_datalist_get_data(&attributes, "id");
    - src = g_datalist_get_data(&attributes, "src");
    - sizestr = g_datalist_get_data(&attributes, "datasize");
    -
    - if ((idstr != NULL) && (src != NULL) && (sizestr!= NULL))
    - {
    - unsigned int id;
    - size_t size;
    -
    - id = atoi(idstr);
    - size = atol(sizestr);
    - embedded_data = g_hash_table_lookup(embedded_datas,
    - GINT_TO_POINTER(id));
    -
    - if ((embedded_data != NULL) && (embedded_data->size == size))
    - {
    - imgid = purple_imgstore_add_with_id(g_memdup2(embedded_data->data, size), size, src);
    -
    - /* Record the image number */
    - images = g_slist_append(images, GINT_TO_POINTER(imgid));
    - }
    - }
    -
    - /* Delete the attribute list */
    - g_datalist_clear(&attributes);
    -
    - /* Append the message up to the tag */
    - utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, start - tmp);
    - if (utf8 != NULL) {
    - g_string_append(newmsg, utf8);
    - g_free(utf8);
    - }
    -
    - if (imgid != 0)
    - {
    - /* Write the new image tag */
    - g_string_append_printf(newmsg, "<IMG ID=\"%d\">", imgid);
    - }
    -
    - /* Continue from the end of the tag */
    - tmp = end + 1;
    - }
    -
    - /* Append any remaining message data */
    - if (tmp <= msgend)
    - {
    - utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, msgend - tmp);
    - if (utf8 != NULL) {
    - g_string_append(newmsg, utf8);
    - g_free(utf8);
    - }
    - }
    -
    - /* Display the message we received */
    - imflags = 0;
    - if (images != NULL)
    - imflags |= PURPLE_MESSAGE_IMAGES;
    - if (autoreply)
    - imflags |= PURPLE_MESSAGE_AUTO_RESP;
    - serv_got_im(gc, conn->bn, newmsg->str, imflags, time(NULL));
    - g_string_free(newmsg, TRUE);
    -
    - /* unref any images we allocated */
    - if (images)
    - {
    - GSList *l;
    - for (l = images; l != NULL; l = l->next)
    - purple_imgstore_unref_by_id(GPOINTER_TO_INT(l->data));
    - g_slist_free(images);
    - }
    -
    - /* Delete our list of pointers to embedded images */
    - g_hash_table_destroy(embedded_datas);
    -}
    -
    -/**
    - * This is a purple_input_add() watcher callback function for reading
    - * direct IM payload data. "Payload data" is always an IM and
    - * maybe some embedded images or files or something. The actual
    - * ODC frame is read using peer_connection_recv_cb(). We temporarily
    - * switch to this watcher callback ONLY to read the payload, and we
    - * switch back once we're done.
    - */
    -static void
    -peer_odc_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PeerConnection *conn;
    - OdcFrame *frame;
    - ByteStream *bs;
    - gssize read;
    -
    - conn = data;
    - frame = conn->frame;
    - bs = &frame->payload;
    -
    - /* Read data into the temporary buffer until it is complete */
    - read = recv(conn->fd,
    - &bs->data[bs->offset],
    - bs->len - bs->offset,
    - 0);
    -
    - /* Check if the remote user closed the connection */
    - if (read == 0)
    - {
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - return;
    - }
    -
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - peer_connection_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - return;
    - }
    -
    - bs->offset += read;
    - if (bs->offset < bs->len)
    - /* Waiting for more data to arrive */
    - return;
    - /* TODO: Instead of null-terminating this, it would be better if we just
    - respected the length of the buffer when parsing it. But it doesn't
    - really matter and this is easy. */
    - bs->data[bs->len] = '\0';
    -
    - /* We have a complete ODC/OFT frame! Handle it and continue reading */
    - byte_stream_rewind(bs);
    - peer_odc_handle_payload(conn, (const char *)bs->data,
    - bs->len, frame->encoding, frame->flags & 0x0001);
    - g_free(bs->data);
    - bs->data = NULL;
    - g_free(frame);
    - conn->frame = NULL;
    -
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
    -}
    -
    -/**
    - * Handle an incoming OdcFrame. If there is a payload associated
    - * with this frame, then we remove the old watcher and add the
    - * ODC watcher to read in the payload.
    - */
    -void
    -peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
    -{
    - PurpleConnection *gc;
    - OdcFrame *frame;
    -
    - gc = conn->od->gc;
    -
    - frame = g_new0(OdcFrame, 1);
    - frame->type = byte_stream_get16(bs);
    - frame->subtype = byte_stream_get16(bs);
    - byte_stream_advance(bs, 2);
    - byte_stream_getrawbuf(bs, frame->cookie, 8);
    - byte_stream_advance(bs, 8);
    - frame->payload.len = byte_stream_get32(bs);
    - frame->encoding = byte_stream_get16(bs);
    - byte_stream_advance(bs, 4);
    - frame->flags = byte_stream_get16(bs);
    - byte_stream_advance(bs, 4);
    - byte_stream_getrawbuf(bs, frame->bn, 32);
    -
    - purple_debug_info("oscar", "Incoming ODC frame from %s with "
    - "type=0x%04x, flags=0x%04x, payload length=%" G_GSIZE_FORMAT "\n",
    - frame->bn, frame->type, frame->flags, frame->payload.len);
    -
    - if (!conn->ready)
    - {
    - /*
    - * We need to verify the cookie so that we know we are
    - * connected to our friend and not a malicious middle man.
    - */
    -
    - PurpleAccount *account;
    - PurpleConversation *conv;
    -
    - if (conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING)
    - {
    - if (memcmp(conn->cookie, frame->cookie, 8))
    - {
    - /*
    - * Oh no! The user that connected to us did not send
    - * the correct cookie! They are not our friend. Go try
    - * to accept another connection?
    - */
    - purple_debug_info("oscar", "Received an incorrect cookie. "
    - "Closing connection.\n");
    - peer_connection_destroy(conn,
    - OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - g_free(frame);
    - return;
    - }
    -
    - /*
    - * Ok, we know they are legit. Now be courteous and
    - * send them our cookie. Note: This doesn't seem
    - * to be necessary, but it also doesn't seem to hurt.
    - */
    - peer_odc_send_cookie(conn);
    - }
    -
    - conn->ready = TRUE;
    -
    - /*
    - * If they connected to us then close the listener socket
    - * and send them our cookie.
    - */
    - if (conn->listenerfd != -1)
    - {
    - close(conn->listenerfd);
    - conn->listenerfd = -1;
    - }
    -
    - /* Tell the local user that we are connected */
    - account = purple_connection_get_account(gc);
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
    - purple_conversation_write(conv, NULL, _("Direct IM established"),
    - PURPLE_MESSAGE_SYSTEM, time(NULL));
    - }
    -
    - if ((frame->type != 0x0001) && (frame->subtype != 0x0006))
    - {
    - purple_debug_info("oscar", "Unknown ODC frame type 0x%04hx, "
    - "subtype 0x%04hx.\n", frame->type, frame->subtype);
    - g_free(frame);
    - return;
    - }
    -
    - if (frame->flags & 0x0008)
    - {
    - /* I had to leave this. It's just too funny. It reminds me of my sister. */
    - purple_debug_info("oscar", "ohmigod! %s has started typing "
    - "(DirectIM). He's going to send you a message! "
    - "*squeal*\n", conn->bn);
    - serv_got_typing(gc, conn->bn, 0, PURPLE_TYPING);
    - }
    - else if (frame->flags & 0x0004)
    - {
    - serv_got_typing(gc, conn->bn, 0, PURPLE_TYPED);
    - }
    - else
    - {
    - serv_got_typing_stopped(gc, conn->bn);
    - }
    -
    - if (frame->payload.len > 0)
    - {
    - if (frame->payload.len > DIRECTIM_MAX_FILESIZE)
    - {
    - gchar *tmp, *size1, *size2;
    - PurpleAccount *account;
    - PurpleConversation *conv;
    -
    - size1 = purple_str_size_to_units(frame->payload.len);
    - size2 = purple_str_size_to_units(DIRECTIM_MAX_FILESIZE);
    - tmp = g_strdup_printf(_("%s tried to send you a %s file, but we only allow files up to %s over Direct IM. Try using file transfer instead.\n"), conn->bn, size1, size2);
    - g_free(size1);
    - g_free(size2);
    -
    - account = purple_connection_get_account(conn->od->gc);
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
    - purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
    - g_free(tmp);
    -
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    - g_free(frame);
    - return;
    - }
    -
    - /* We have payload data! Switch to the ODC watcher to read it. */
    - frame->payload.data = g_new(guint8, frame->payload.len + 1);
    - frame->payload.offset = 0;
    - conn->frame = frame;
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, peer_odc_recv_cb, conn);
    - return;
    - }
    -
    - g_free(frame);
    -}
    --- a/libpurple/protocols/oscar/oft.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,824 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * I feel like this is a good place to explain OFT, so I'm going to
    - * do just that. Each OFT packet has a header type. I guess this
    - * is pretty similar to the subtype of a SNAC packet. The type
    - * basically tells the other client the meaning of the OFT packet.
    - * There are two distinct types of file transfer, which I usually
    - * call "sendfile" and "getfile." Sendfile is when you send a file
    - * to another AIM user. Getfile is when you share a group of files,
    - * and other users request that you send them the files.
    - *
    - * A typical sendfile file transfer goes like this:
    - * 1) Sender sends a channel 2 ICBM telling the other user that
    - * we want to send them a file. At the same time, we open a
    - * listener socket (this should be done before sending the
    - * ICBM) on some port, and wait for them to connect to us.
    - * The ICBM we sent should contain our IP address and the port
    - * number that we're listening on.
    - * 2) The receiver connects to the sender on the given IP address
    - * and port. After the connection is established, the receiver
    - * sends an ICBM signifying that we are ready and waiting.
    - * 3) The sender sends an OFT PROMPT message over the OFT
    - * connection.
    - * 4) The receiver of the file sends back an exact copy of this
    - * OFT packet, except the cookie is filled in with the cookie
    - * from the ICBM. I think this might be an attempt to verify
    - * that the user that is connected is actually the guy that
    - * we sent the ICBM to. Oh, I've been calling this the ACK.
    - * 5) The sender starts sending raw data across the connection
    - * until the entire file has been sent.
    - * 6) The receiver knows the file is finished because the sender
    - * sent the file size in an earlier OFT packet. So then the
    - * receiver sends the DONE thingy (after filling in the
    - * "received" checksum and size) and closes the connection.
    - */
    -
    -#include "oscar.h"
    -#include "peer.h"
    -
    -#include "glibcompat.h"
    -#include "util.h"
    -
    -#define CHECKSUM_BUFFER_SIZE 256 * 1024
    -
    -struct _ChecksumData
    -{
    - PeerConnection *conn;
    - PurpleXfer *xfer;
    - GSourceFunc callback;
    - size_t size;
    - guint32 checksum;
    - size_t total;
    - FILE *file;
    - guint8 buffer[CHECKSUM_BUFFER_SIZE];
    - guint timer;
    -};
    -
    -void
    -peer_oft_checksum_destroy(ChecksumData *checksum_data)
    -{
    - checksum_data->conn->checksum_data = NULL;
    - fclose(checksum_data->file);
    - if (checksum_data->timer > 0)
    - purple_timeout_remove(checksum_data->timer);
    - g_free(checksum_data);
    -}
    -
    -/**
    - * Calculate oft checksum of buffer
    - *
    - * Prevcheck should be 0xFFFF0000 when starting a checksum of a file. The
    - * checksum is kind of a rolling checksum thing, so each time you get bytes
    - * of a file you just call this puppy and it updates the checksum. You can
    - * calculate the checksum of an entire file by calling this in a while or a
    - * for loop, or something.
    - *
    - * Thanks to Graham Booker for providing this improved checksum routine,
    - * which is simpler and should be more accurate than Josh Myer's original
    - * code. -- wtm
    - *
    - * This algorithm works every time I have tried it. The other fails
    - * sometimes. So, AOL who thought this up? It has got to be the weirdest
    - * checksum I have ever seen.
    - *
    - * @param buffer Buffer of data to checksum. Man I'd like to buff her...
    - * @param bufsize Size of buffer.
    - * @param prevchecksum Previous checksum.
    - * @param odd Whether an odd number of bytes have been processed before this call
    - */
    -static guint32
    -peer_oft_checksum_chunk(const guint8 *buffer, int bufferlen, guint32 prevchecksum, int odd)
    -{
    - guint32 checksum, oldchecksum;
    - int i = 0;
    - unsigned short val;
    -
    - checksum = (prevchecksum >> 16) & 0xffff;
    - if (odd)
    - {
    - /*
    - * This is one hell of a hack, but it should always work.
    - * Essentially, I am reindexing the array so that index 1
    - * is the first element. Since the odd and even bytes are
    - * detected by the index number.
    - */
    - i = 1;
    - bufferlen++;
    - buffer--;
    - }
    - for (; i < bufferlen; i++)
    - {
    - oldchecksum = checksum;
    - if (i & 1)
    - val = buffer[i];
    - else
    - val = buffer[i] << 8;
    - checksum -= val;
    - /*
    - * The following appears to be necessary.... It happens
    - * every once in a while and the checksum doesn't fail.
    - */
    - if (checksum > oldchecksum)
    - checksum--;
    - }
    - checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
    - checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
    - return checksum << 16;
    -}
    -
    -static gboolean
    -peer_oft_checksum_file_piece(gpointer data)
    -{
    - ChecksumData *checksum_data;
    - gboolean repeat;
    -
    - checksum_data = data;
    - repeat = FALSE;
    -
    - if (checksum_data->total < checksum_data->size)
    - {
    - size_t bytes = MIN(CHECKSUM_BUFFER_SIZE,
    - checksum_data->size - checksum_data->total);
    -
    - bytes = fread(checksum_data->buffer, 1, bytes, checksum_data->file);
    - if (bytes != 0)
    - {
    - checksum_data->checksum = peer_oft_checksum_chunk(checksum_data->buffer, bytes, checksum_data->checksum, checksum_data->total & 1);
    - checksum_data->total += bytes;
    - repeat = TRUE;
    - }
    - }
    -
    - if (!repeat)
    - {
    - purple_debug_info("oscar", "Checksum of %s calculated\n",
    - purple_xfer_get_local_filename(checksum_data->xfer));
    - if (checksum_data->callback != NULL)
    - checksum_data->callback(checksum_data);
    - peer_oft_checksum_destroy(checksum_data);
    - }
    -
    - return repeat;
    -}
    -
    -/**
    - * Calculate oft checksum of a file in a series of calls to
    - * peer_oft_checksum_file_piece(). We do it this way because
    - * calculating the checksum on large files can take a long time,
    - * and we want to return control to the UI so that the application
    - * doesn't appear completely frozen.
    - *
    - * @param conn The connection used for this file transfer.
    - * @param xfer The file transfer needing this checksum.
    - * @param callback The function to call upon calculation of the checksum.
    - * @param size The maximum size to check.
    - */
    -
    -static void
    -peer_oft_checksum_file(PeerConnection *conn, PurpleXfer *xfer, GSourceFunc callback, size_t size)
    -{
    - ChecksumData *checksum_data;
    -
    - purple_debug_info("oscar", "Calculating checksum of %s\n",
    - purple_xfer_get_local_filename(xfer));
    -
    - checksum_data = g_malloc0(sizeof(ChecksumData));
    - checksum_data->conn = conn;
    - checksum_data->xfer = xfer;
    - checksum_data->callback = callback;
    - checksum_data->size = size;
    - checksum_data->checksum = 0xffff0000;
    - checksum_data->file = g_fopen(purple_xfer_get_local_filename(xfer), "rb");
    -
    - if (checksum_data->file == NULL)
    - {
    - purple_debug_error("oscar", "Unable to open %s for checksumming: %s\n",
    - purple_xfer_get_local_filename(xfer), g_strerror(errno));
    - callback(checksum_data);
    - g_free(checksum_data);
    - }
    - else
    - {
    - checksum_data->timer = purple_timeout_add(10,
    - peer_oft_checksum_file_piece, checksum_data);
    - conn->checksum_data = checksum_data;
    - }
    -}
    -
    -static void
    -peer_oft_copy_xfer_data(PeerConnection *conn, OftFrame *frame)
    -{
    - g_free(conn->xferdata.name);
    -
    - memcpy(&(conn->xferdata), frame, sizeof(OftFrame));
    - conn->xferdata.name = g_memdup2(frame->name, frame->name_length);
    -}
    -
    -/**
    - * Free any OFT related data.
    - */
    -void
    -peer_oft_close(PeerConnection *conn)
    -{
    - /*
    - * If cancelled by local user, and we're receiving a file, and
    - * we're not connected/ready then send an ICBM cancel message.
    - */
    - if ((purple_xfer_get_status(conn->xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) &&
    - !conn->ready)
    - {
    - aim_im_sendch2_cancel(conn);
    - }
    -
    - if (conn->sending_data_timer != 0)
    - {
    - purple_timeout_remove(conn->sending_data_timer);
    - conn->sending_data_timer = 0;
    - }
    -}
    -
    -/**
    - * Write the given OftFrame to a ByteStream and send it out
    - * on the established PeerConnection.
    - */
    -static void
    -peer_oft_send(PeerConnection *conn, OftFrame *frame)
    -{
    - size_t length;
    - ByteStream bs;
    -
    - length = 192 + frame->name_length;
    - byte_stream_new(&bs, length);
    - byte_stream_putraw(&bs, conn->magic, 4);
    - byte_stream_put16(&bs, length);
    - byte_stream_put16(&bs, frame->type);
    - byte_stream_putraw(&bs, frame->cookie, 8);
    - byte_stream_put16(&bs, frame->encrypt);
    - byte_stream_put16(&bs, frame->compress);
    - byte_stream_put16(&bs, frame->totfiles);
    - byte_stream_put16(&bs, frame->filesleft);
    - byte_stream_put16(&bs, frame->totparts);
    - byte_stream_put16(&bs, frame->partsleft);
    - byte_stream_put32(&bs, frame->totsize);
    - byte_stream_put32(&bs, frame->size);
    - byte_stream_put32(&bs, frame->modtime);
    - byte_stream_put32(&bs, frame->checksum);
    - byte_stream_put32(&bs, frame->rfrcsum);
    - byte_stream_put32(&bs, frame->rfsize);
    - byte_stream_put32(&bs, frame->cretime);
    - byte_stream_put32(&bs, frame->rfcsum);
    - byte_stream_put32(&bs, frame->nrecvd);
    - byte_stream_put32(&bs, frame->recvcsum);
    - byte_stream_putraw(&bs, frame->idstring, 32);
    - byte_stream_put8(&bs, frame->flags);
    - byte_stream_put8(&bs, frame->lnameoffset);
    - byte_stream_put8(&bs, frame->lsizeoffset);
    - byte_stream_putraw(&bs, frame->dummy, 69);
    - byte_stream_putraw(&bs, frame->macfileinfo, 16);
    - byte_stream_put16(&bs, frame->nencode);
    - byte_stream_put16(&bs, frame->nlanguage);
    - /*
    - * The name can be more than 64 characters, but if it is less than
    - * 64 characters it is padded with NULLs.
    - */
    - byte_stream_putraw(&bs, frame->name, frame->name_length);
    -
    - peer_connection_send(conn, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -void
    -peer_oft_send_prompt(PeerConnection *conn)
    -{
    - conn->xferdata.type = PEER_TYPE_PROMPT;
    - peer_oft_send(conn, &conn->xferdata);
    -}
    -
    -static void
    -peer_oft_send_ack(PeerConnection *conn)
    -{
    - conn->xferdata.type = PEER_TYPE_ACK;
    -
    - /* Fill in the cookie */
    - memcpy(conn->xferdata.cookie, conn->cookie, 8);
    -
    - peer_oft_send(conn, &conn->xferdata);
    -}
    -
    -static void
    -peer_oft_send_resume_accept(PeerConnection *conn)
    -{
    - conn->xferdata.type = PEER_TYPE_RESUMEACCEPT;
    -
    - /* Fill in the cookie */
    - memcpy(conn->xferdata.cookie, conn->cookie, 8);
    -
    - peer_oft_send(conn, &conn->xferdata);
    -}
    -
    -static void
    -peer_oft_send_done(PeerConnection *conn)
    -{
    - conn->xferdata.type = PEER_TYPE_DONE;
    - conn->xferdata.rfrcsum = 0xffff0000;
    - conn->xferdata.nrecvd = purple_xfer_get_bytes_sent(conn->xfer);
    - peer_oft_send(conn, &conn->xferdata);
    -}
    -
    -/**
    - * This function exists so that we don't remove the outgoing
    - * data watcher while we're still sending data. In most cases
    - * any data we're sending will be instantly wisked away to a TCP
    - * buffer maintained by our operating system... but we want to
    - * make sure the core doesn't start sending file data while
    - * we're still sending OFT frame data. That would be bad.
    - */
    -static gboolean
    -start_transfer_when_done_sending_data(gpointer data)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - if (purple_circ_buffer_get_max_read(conn->buffer_outgoing) == 0)
    - {
    - conn->sending_data_timer = 0;
    - conn->xfer->fd = conn->fd;
    - conn->fd = -1;
    - purple_xfer_start(conn->xfer, conn->xfer->fd, NULL, 0);
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/**
    - * This function is similar to the above function, except instead
    - * of starting the xfer it will destroy the connection. This is
    - * used when you want to send one final message across the peer
    - * connection, and then close everything.
    - */
    -static gboolean
    -destroy_connection_when_done_sending_data(gpointer data)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - if (purple_circ_buffer_get_max_read(conn->buffer_outgoing) == 0)
    - {
    - conn->sending_data_timer = 0;
    - peer_connection_destroy(conn, conn->disconnect_reason, NULL);
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/*
    - * This is called when a buddy sends us some file info. This happens when they
    - * are sending a file to you, and you have just established a connection to them.
    - * You should send them the exact same info except use the real cookie. We also
    - * get like totally ready to like, receive the file, kay?
    - */
    -static void
    -peer_oft_recv_frame_prompt(PeerConnection *conn, OftFrame *frame)
    -{
    - /* Record the file information and send an ack */
    - peer_oft_copy_xfer_data(conn, frame);
    - peer_oft_send_ack(conn);
    -
    - /* Remove our watchers and use the file transfer watchers in the core */
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    - conn->sending_data_timer = purple_timeout_add(100,
    - start_transfer_when_done_sending_data, conn);
    -}
    -
    -/**
    - * We are sending a file to someone else. They have just acknowledged our
    - * prompt, so we want to start sending data like there's no tomorrow.
    - */
    -static void
    -peer_oft_recv_frame_ack(PeerConnection *conn, OftFrame *frame)
    -{
    - if (memcmp(conn->cookie, frame->cookie, 8) != 0)
    - {
    - purple_debug_info("oscar", "Received an incorrect cookie. "
    - "Closing connection.\n");
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - return;
    - }
    -
    - /* Remove our watchers and use the file transfer watchers in the core */
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    - conn->sending_data_timer = purple_timeout_add(100,
    - start_transfer_when_done_sending_data, conn);
    -}
    -
    -static gboolean
    -peer_oft_recv_frame_resume_checksum_calculated_cb(gpointer data)
    -{
    - ChecksumData *checksum_data;
    - PeerConnection *conn;
    -
    - checksum_data = data;
    - conn = checksum_data->conn;
    -
    - /* Check the checksums here. If not match, don't allow resume */
    - if (checksum_data->checksum != conn->xferdata.recvcsum || checksum_data->total != conn->xferdata.nrecvd)
    - {
    - /* Reset internal structure */
    - conn->xferdata.recvcsum = 0xffff0000;
    - conn->xferdata.rfrcsum = 0xffff0000;
    - conn->xferdata.nrecvd = 0;
    - }
    - else
    - /* Accept the change */
    - purple_xfer_set_bytes_sent(checksum_data->xfer, conn->xferdata.nrecvd);
    -
    - peer_oft_send_resume_accept(conn);
    -
    - return FALSE;
    -}
    -
    -/**
    - * We are sending a file to someone else. They have just acknowledged our
    - * prompt and are asking to resume, so we accept their resume and await
    - * a resume ack.
    - */
    -static void
    -peer_oft_recv_frame_resume(PeerConnection *conn, OftFrame *frame)
    -{
    - if (memcmp(conn->cookie, frame->cookie, 8) != 0)
    - {
    - purple_debug_info("oscar", "Received an incorrect cookie. "
    - "Closing connection.\n");
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - return;
    - }
    -
    - /* Copy resume data into internal structure */
    - conn->xferdata.recvcsum = frame->recvcsum;
    - conn->xferdata.rfrcsum = frame->rfrcsum;
    - conn->xferdata.nrecvd = frame->nrecvd;
    -
    - peer_oft_checksum_file(conn, conn->xfer,
    - peer_oft_recv_frame_resume_checksum_calculated_cb,
    - frame->nrecvd);
    -}
    -
    -/*
    - * We just sent a file to someone. They said they got it and everything,
    - * so we can close our direct connection and what not.
    - */
    -static void
    -peer_oft_recv_frame_done(PeerConnection *conn, OftFrame *frame)
    -{
    - /*
    - * The core ft code sets the xfer to completed automatically if we've
    - * sent all bytes to the other user. But this function can be called
    - * even if we haven't sent all bytes to the other user (in the case
    - * where the user already has this file on their computer and the
    - * checksum matches).
    - */
    - if (!purple_xfer_is_completed(conn->xfer))
    - purple_xfer_set_completed(conn->xfer, TRUE);
    -
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    - conn->xfer->fd = conn->fd;
    - conn->fd = -1;
    - conn->disconnect_reason = OSCAR_DISCONNECT_DONE;
    - peer_connection_schedule_destroy(conn, conn->disconnect_reason, NULL);
    -}
    -
    -/**
    - * Handle an incoming OftFrame. If there is a payload associated
    - * with this frame, then we remove the old watcher and add the
    - * OFT watcher to read in the payload.
    - */
    -void
    -peer_oft_recv_frame(PeerConnection *conn, ByteStream *bs)
    -{
    - OftFrame frame;
    -
    - frame.type = byte_stream_get16(bs);
    - byte_stream_getrawbuf(bs, frame.cookie, 8);
    - frame.encrypt = byte_stream_get16(bs);
    - frame.compress = byte_stream_get16(bs);
    - frame.totfiles = byte_stream_get16(bs);
    - frame.filesleft = byte_stream_get16(bs);
    - frame.totparts = byte_stream_get16(bs);
    - frame.partsleft = byte_stream_get16(bs);
    - frame.totsize = byte_stream_get32(bs);
    - frame.size = byte_stream_get32(bs);
    - frame.modtime = byte_stream_get32(bs);
    - frame.checksum = byte_stream_get32(bs);
    - frame.rfrcsum = byte_stream_get32(bs);
    - frame.rfsize = byte_stream_get32(bs);
    - frame.cretime = byte_stream_get32(bs);
    - frame.rfcsum = byte_stream_get32(bs);
    - frame.nrecvd = byte_stream_get32(bs);
    - frame.recvcsum = byte_stream_get32(bs);
    - byte_stream_getrawbuf(bs, frame.idstring, 32);
    - frame.flags = byte_stream_get8(bs);
    - frame.lnameoffset = byte_stream_get8(bs);
    - frame.lsizeoffset = byte_stream_get8(bs);
    - byte_stream_getrawbuf(bs, frame.dummy, 69);
    - byte_stream_getrawbuf(bs, frame.macfileinfo, 16);
    - frame.nencode = byte_stream_get16(bs);
    - frame.nlanguage = byte_stream_get16(bs);
    - frame.name_length = bs->len - 186;
    - frame.name = byte_stream_getraw(bs, frame.name_length);
    -
    - purple_debug_info("oscar", "Incoming OFT frame from %s with "
    - "type=0x%04x\n", conn->bn, frame.type);
    -
    - /* TODOFT: peer_oft_dirconvert_fromstupid(frame->name); */
    -
    - switch(frame.type)
    - {
    - case PEER_TYPE_PROMPT:
    - peer_oft_recv_frame_prompt(conn, &frame);
    - break;
    - case PEER_TYPE_ACK:
    - case PEER_TYPE_RESUMEACK:
    - peer_oft_recv_frame_ack(conn, &frame);
    - break;
    - case PEER_TYPE_RESUME:
    - peer_oft_recv_frame_resume(conn, &frame);
    - break;
    - case PEER_TYPE_DONE:
    - peer_oft_recv_frame_done(conn, &frame);
    - break;
    - default:
    - break;
    - }
    -
    - g_free(frame.name);
    -}
    -
    -/*******************************************************************/
    -/* Begin PurpleXfer callbacks for use when receiving a file */
    -/*******************************************************************/
    -
    -void
    -peer_oft_recvcb_init(PurpleXfer *xfer)
    -{
    - PeerConnection *conn;
    -
    - conn = xfer->data;
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    - peer_connection_trynext(conn);
    -}
    -
    -void
    -peer_oft_recvcb_end(PurpleXfer *xfer)
    -{
    - PeerConnection *conn;
    -
    - conn = xfer->data;
    -
    - /* Tell the other person that we've received everything */
    - conn->fd = conn->xfer->fd;
    - conn->xfer->fd = -1;
    - peer_oft_send_done(conn);
    -
    - conn->disconnect_reason = OSCAR_DISCONNECT_DONE;
    - conn->sending_data_timer = purple_timeout_add(100,
    - destroy_connection_when_done_sending_data, conn);
    -}
    -
    -void
    -peer_oft_recvcb_ack_recv(PurpleXfer *xfer, const guchar *buffer, size_t size)
    -{
    - PeerConnection *conn;
    -
    - /* Update our rolling checksum. Like Walmart, yo. */
    - conn = xfer->data;
    - conn->xferdata.recvcsum = peer_oft_checksum_chunk(buffer,
    - size, conn->xferdata.recvcsum, purple_xfer_get_bytes_sent(xfer) & 1);
    -}
    -
    -/*******************************************************************/
    -/* End PurpleXfer callbacks for use when receiving a file */
    -/*******************************************************************/
    -
    -/*******************************************************************/
    -/* Begin PurpleXfer callbacks for use when sending a file */
    -/*******************************************************************/
    -
    -static gboolean
    -peer_oft_checksum_calculated_cb(gpointer data)
    -{
    - ChecksumData *checksum_data;
    - PeerConnection *conn;
    -
    - checksum_data = data;
    - conn = checksum_data->conn;
    -
    - conn->xferdata.checksum = checksum_data->checksum;
    -
    - /* Start the connection process */
    - peer_connection_trynext(checksum_data->conn);
    -
    - return FALSE;
    -}
    -
    -void
    -peer_oft_sendcb_init(PurpleXfer *xfer)
    -{
    - PeerConnection *conn;
    - size_t size;
    -
    - conn = xfer->data;
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    -
    - /* Make sure the file size can be represented in 32 bits */
    - size = purple_xfer_get_size(xfer);
    - if (size > G_MAXUINT32)
    - {
    - gchar *tmp, *size1, *size2;
    - size1 = purple_str_size_to_units(size);
    - size2 = purple_str_size_to_units(G_MAXUINT32);
    - tmp = g_strdup_printf(_("File %s is %s, which is larger than "
    - "the maximum size of %s."),
    - xfer->local_filename, size1, size2);
    - purple_xfer_error(purple_xfer_get_type(xfer),
    - purple_xfer_get_account(xfer), xfer->who, tmp);
    - g_free(size1);
    - g_free(size2);
    - g_free(tmp);
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    - return;
    - }
    -
    - /* Keep track of file transfer info */
    - conn->xferdata.totfiles = 1;
    - conn->xferdata.filesleft = 1;
    - conn->xferdata.totparts = 1;
    - conn->xferdata.partsleft = 1;
    - conn->xferdata.totsize = size;
    - conn->xferdata.size = size;
    - conn->xferdata.checksum = 0xffff0000;
    - conn->xferdata.rfrcsum = 0xffff0000;
    - conn->xferdata.rfcsum = 0xffff0000;
    - conn->xferdata.recvcsum = 0xffff0000;
    - strncpy((gchar *)conn->xferdata.idstring, "Cool FileXfer", 31);
    - conn->xferdata.modtime = 0;
    - conn->xferdata.cretime = 0;
    - xfer->filename = g_path_get_basename(xfer->local_filename);
    - conn->xferdata.name_length = MAX(64, strlen(xfer->filename) + 1);
    - conn->xferdata.name = (guchar *)g_strndup(xfer->filename, conn->xferdata.name_length - 1);
    -
    - peer_oft_checksum_file(conn, xfer,
    - peer_oft_checksum_calculated_cb, G_MAXUINT32);
    -}
    -
    -/*
    - * AIM file transfers aren't really meant to be thought
    - * of as a transferring just a single file. The rendezvous
    - * establishes a connection between two computers, and then
    - * those computers can use the same connection for transferring
    - * multiple files. So we don't want the Purple core up and closing
    - * the socket all willy-nilly. We want to do that in the oscar
    - * prpl, whenever one side or the other says they're finished
    - * using the connection. There might be a better way to intercept
    - * the socket from the core...
    - */
    -void
    -peer_oft_sendcb_ack(PurpleXfer *xfer, const guchar *buffer, size_t size)
    -{
    - PeerConnection *conn;
    -
    - conn = xfer->data;
    -
    - /*
    - * If we're done sending, intercept the socket from the core ft code
    - * and wait for the other guy to send the "done" OFT packet.
    - */
    - if (purple_xfer_get_bytes_remaining(xfer) <= 0)
    - {
    - purple_input_remove(xfer->watcher);
    - conn->fd = xfer->fd;
    - xfer->fd = -1;
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
    - }
    -}
    -
    -/*******************************************************************/
    -/* End PurpleXfer callbacks for use when sending a file */
    -/*******************************************************************/
    -
    -/*******************************************************************/
    -/* Begin PurpleXfer callbacks for use when sending and receiving */
    -/*******************************************************************/
    -
    -void
    -peer_oft_cb_generic_cancel(PurpleXfer *xfer)
    -{
    - PeerConnection *conn;
    -
    - conn = xfer->data;
    -
    - if (conn == NULL)
    - return;
    -
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    -}
    -
    -/*******************************************************************/
    -/* End PurpleXfer callbacks for use when sending and receiving */
    -/*******************************************************************/
    -
    -#ifdef TODOFT
    -/*
    - * This little area in oscar.c is the nexus of file transfer code,
    - * so I wrote a little explanation of what happens. I am such a
    - * ninja.
    - *
    - * The series of events for a file send is:
    - * -Create xfer and call purple_xfer_request (this happens in oscar_ask_sendfile)
    - * -User chooses a file and oscar_xfer_init is called. It establishes a
    - * listening socket, then asks the remote user to connect to us (and
    - * gives them the file name, port, IP, etc.)
    - * -They connect to us and we send them an PEER_TYPE_PROMPT (this happens
    - * in peer_oft_recv_frame_established)
    - * -They send us an PEER_TYPE_ACK and then we start sending data
    - * -When we finish, they send us an PEER_TYPE_DONE and they close the
    - * connection.
    - * -We get drunk because file transfer kicks ass.
    - *
    - * The series of events for a file receive is:
    - * -Create xfer and call purple_xfer request (this happens in incomingim_chan2)
    - * -Purple user selects file to name and location to save file to and
    - * oscar_xfer_init is called
    - * -It connects to the remote user using the IP they gave us earlier
    - * -After connecting, they send us an PEER_TYPE_PROMPT. In reply, we send
    - * them an PEER_TYPE_ACK.
    - * -They begin to send us lots of raw data.
    - * -When they finish sending data we send an PEER_TYPE_DONE and then close
    - * the connection.
    - *
    - * Update August 2005:
    - * The series of events for transfers has been seriously complicated by the addition
    - * of transfer redirects and proxied connections. I could throw a whole lot of words
    - * at trying to explain things here, but it probably wouldn't do much good. To get
    - * a better idea of what happens, take a look at the diagrams and documentation
    - * from my Summer of Code project. -- Jonathan Clark
    - */
    -
    -/**
    - * Convert the directory separator from / (0x2f) to ^A (0x01)
    - *
    - * @param name The filename to convert.
    - */
    -static void
    -peer_oft_dirconvert_tostupid(char *name)
    -{
    - while (name[0]) {
    - if (name[0] == 0x01)
    - name[0] = G_DIR_SEPARATOR;
    - name++;
    - }
    -}
    -
    -/**
    - * Convert the directory separator from ^A (0x01) to / (0x2f)
    - *
    - * @param name The filename to convert.
    - */
    -static void
    -peer_oft_dirconvert_fromstupid(char *name)
    -{
    - while (name[0]) {
    - if (name[0] == G_DIR_SEPARATOR)
    - name[0] = 0x01;
    - name++;
    - }
    -}
    -#endif
    --- a/libpurple/protocols/oscar/oscar.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,5873 +0,0 @@
    -/*
    - * purple
    - *
    - * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
    - * Some code copyright (C) 1999-2001, Eric Warmenhoven
    - * Some code copyright (C) 2001-2003, Sean Egan
    - * Some code copyright (C) 2001-2007, Mark Doliner <thekingant@users.sourceforge.net>
    - * Some code copyright (C) 2005, Jonathan Clark <ardentlygnarly@users.sourceforge.net>
    - * Some code copyright (C) 2007, ComBOTS Product GmbH (htfv) <foss@combots.com>
    - * Some code copyright (C) 2008, Aman Gupta
    - *
    - * Most libfaim code copyright (C) 1998-2001 Adam Fritzler <afritz@auk.cx>
    - * Some libfaim code copyright (C) 2001-2004 Mark Doliner <thekingant@users.sourceforge.net>
    - *
    - * 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 "accountopt.h"
    -#include "buddyicon.h"
    -#include "cipher.h"
    -#include "conversation.h"
    -#include "core.h"
    -#include "debug.h"
    -#include "encoding.h"
    -#include "glibcompat.h"
    -#include "imgstore.h"
    -#include "network.h"
    -#include "notify.h"
    -#include "privacy.h"
    -#include "prpl.h"
    -#include "proxy.h"
    -#include "request.h"
    -#include "util.h"
    -#include "version.h"
    -#include "visibility.h"
    -
    -#include "oscarcommon.h"
    -#include "oscar.h"
    -#include "peer.h"
    -
    -#define AIMHASHDATA "http://pidgin.im/aim_data.php3"
    -
    -#define OSCAR_CONNECT_STEPS 6
    -
    -static guint64 purple_caps =
    - OSCAR_CAPABILITY_CHAT
    - | OSCAR_CAPABILITY_BUDDYICON
    - | OSCAR_CAPABILITY_DIRECTIM
    - | OSCAR_CAPABILITY_SENDFILE
    - | OSCAR_CAPABILITY_UNICODE
    - | OSCAR_CAPABILITY_INTEROPERATE
    - | OSCAR_CAPABILITY_SHORTCAPS
    - | OSCAR_CAPABILITY_TYPING
    - | OSCAR_CAPABILITY_ICQSERVERRELAY
    - | OSCAR_CAPABILITY_NEWCAPS
    - | OSCAR_CAPABILITY_XTRAZ
    - | OSCAR_CAPABILITY_HTML_MSGS;
    -
    -static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02};
    -static guint8 features_icq[] = {0x01};
    -
    -struct create_room {
    - char *name;
    - int exchange;
    -};
    -
    -struct oscar_ask_directim_data
    -{
    - OscarData *od;
    - char *who;
    -};
    -
    -/* All the libfaim->purple callback functions */
    -
    -/* Only used when connecting with the old-style BUCP login */
    -static int purple_parse_auth_resp (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_login (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_auth_securid_request(OscarData *, FlapConnection *, FlapFrame *, ...);
    -
    -static int purple_handle_redirect (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_info_change (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_account_confirm (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_oncoming (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_offgoing (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_incoming_im(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_misses (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_clientauto (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_motd (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_chatnav_info (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_conv_chat_join (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_conv_chat_leave (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_conv_chat_info_update (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_conv_chat_incoming_msg(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_email_parseupdate(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_icon_parseicon (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_searcherror(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_searchreply(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_bosrights (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_connerr (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_mtn (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_locaterights(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_buddyrights(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_genericerr (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_memrequest (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_selfinfo (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_popup (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_parseerr (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_parserights (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_parselist (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_parseack (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_parseaddmod (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_authgiven (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_authrequest (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_authreply (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_gotadded (OscarData *, FlapConnection *, FlapFrame *, ...);
    -
    -static void purple_icons_fetch(PurpleConnection *gc);
    -
    -void oscar_set_info(PurpleConnection *gc, const char *info);
    -static void oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo, gboolean setstatus, PurpleStatus *status);
    -static void oscar_set_extended_status(PurpleConnection *gc);
    -static gboolean purple_ssi_rerequestdata(gpointer data);
    -
    -void oscar_free_name_data(struct name_data *data) {
    - g_free(data->name);
    - g_free(data->nick);
    - g_free(data);
    -}
    -
    -#ifdef _WIN32
    -const char *oscar_get_locale_charset(void) {
    - static const char *charset = NULL;
    - if (charset == NULL)
    - g_get_charset(&charset);
    - return charset;
    -}
    -#endif
    -
    -static char *oscar_icqstatus(int state) {
    - /* Make a cute little string that shows the status of the dude or dudet */
    - if (state & AIM_ICQ_STATE_CHAT)
    - return g_strdup(_("Free For Chat"));
    - else if (state & AIM_ICQ_STATE_DND)
    - return g_strdup(_("Do Not Disturb"));
    - else if (state & AIM_ICQ_STATE_OUT)
    - return g_strdup(_("Not Available"));
    - else if (state & AIM_ICQ_STATE_BUSY)
    - return g_strdup(_("Occupied"));
    - else if (state & AIM_ICQ_STATE_AWAY)
    - return g_strdup(_("Away"));
    - else if (state & AIM_ICQ_STATE_WEBAWARE)
    - return g_strdup(_("Web Aware"));
    - else if (state & AIM_ICQ_STATE_INVISIBLE)
    - return g_strdup(_("Invisible"));
    - else if (state & AIM_ICQ_STATE_EVIL)
    - return g_strdup(_("Evil"));
    - else if (state & AIM_ICQ_STATE_DEPRESSION)
    - return g_strdup(_("Depression"));
    - else if (state & AIM_ICQ_STATE_ATHOME)
    - return g_strdup(_("At home"));
    - else if (state & AIM_ICQ_STATE_ATWORK)
    - return g_strdup(_("At work"));
    - else if (state & AIM_ICQ_STATE_LUNCH)
    - return g_strdup(_("At lunch"));
    - else
    - return g_strdup(_("Online"));
    -}
    -
    -static char *extract_name(const char *name) {
    - char *tmp, *x;
    - int i, j;
    -
    - if (!name)
    - return NULL;
    -
    - x = strchr(name, '-');
    - if (!x)
    - return NULL;
    -
    - x = strchr(x + 1, '-');
    - if (!x)
    - return NULL;
    -
    - tmp = g_strdup(++x);
    -
    - for (i = 0, j = 0; x[i]; i++) {
    - char hex[3];
    - if (x[i] != '%') {
    - tmp[j++] = x[i];
    - continue;
    - }
    - strncpy(hex, x + ++i, 2);
    - hex[2] = 0;
    - i++;
    - tmp[j++] = strtol(hex, NULL, 16);
    - }
    -
    - tmp[j] = 0;
    - return tmp;
    -}
    -
    -static struct chat_connection *
    -find_oscar_chat(PurpleConnection *gc, int id)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - GSList *cur;
    - struct chat_connection *cc;
    -
    - for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
    - {
    - cc = (struct chat_connection *)cur->data;
    - if (cc->id == id)
    - return cc;
    - }
    -
    - return NULL;
    -}
    -
    -static struct chat_connection *
    -find_oscar_chat_by_conn(PurpleConnection *gc, FlapConnection *conn)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - GSList *cur;
    - struct chat_connection *cc;
    -
    - for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
    - {
    - cc = (struct chat_connection *)cur->data;
    - if (cc->conn == conn)
    - return cc;
    - }
    -
    - return NULL;
    -}
    -
    -static struct chat_connection *
    -find_oscar_chat_by_conv(PurpleConnection *gc, PurpleConversation *conv)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - GSList *cur;
    - struct chat_connection *cc;
    -
    - for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
    - {
    - cc = (struct chat_connection *)cur->data;
    - if (cc->conv == conv)
    - return cc;
    - }
    -
    - return NULL;
    -}
    -
    -void
    -oscar_chat_destroy(struct chat_connection *cc)
    -{
    - g_free(cc->name);
    - g_free(cc->show);
    - g_free(cc);
    -}
    -
    -static void
    -oscar_chat_kill(PurpleConnection *gc, struct chat_connection *cc)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - /* Notify the conversation window that we've left the chat */
    - serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(cc->conv)));
    -
    - /* Destroy the chat_connection */
    - od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
    - oscar_chat_destroy(cc);
    -}
    -
    -/**
    - * This is called from the callback functions for establishing
    - * a TCP connection with an oscar host if an error occurred.
    - */
    -static void
    -connection_common_error_cb(FlapConnection *conn, const gchar *error_message)
    -{
    - OscarData *od;
    - PurpleConnection *gc;
    -
    - od = conn->od;
    - gc = od->gc;
    -
    - purple_debug_error("oscar", "unable to connect to FLAP "
    - "server of type 0x%04hx\n", conn->type);
    -
    - if (conn->type == SNAC_FAMILY_AUTH)
    - {
    - /* This only happens when connecting with the old-style BUCP login */
    - gchar *msg;
    - msg = g_strdup_printf(_("Unable to connect to authentication server: %s"),
    - error_message);
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - }
    - else if (conn->type == SNAC_FAMILY_LOCATE)
    - {
    - gchar *msg;
    - msg = g_strdup_printf(_("Unable to connect to BOS server: %s"),
    - error_message);
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - }
    - else
    - {
    - /* Maybe we should call this for BOS connections, too? */
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_COULD_NOT_CONNECT, error_message);
    - }
    -}
    -
    -/**
    - * This is called from the callback functions for establishing
    - * a TCP connection with an oscar host. Depending on the type
    - * of host, we do a few different things here.
    - */
    -static void
    -connection_common_established_cb(FlapConnection *conn)
    -{
    - OscarData *od;
    - PurpleConnection *gc;
    - PurpleAccount *account;
    -
    - od = conn->od;
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - purple_debug_info("oscar", "connected to FLAP server of type 0x%04hx\n",
    - conn->type);
    -
    - if (conn->cookie == NULL)
    - flap_connection_send_version(od, conn);
    - else
    - {
    - const gchar *login_type = purple_account_get_string(account, "login_type", OSCAR_DEFAULT_LOGIN);
    -
    - if (!purple_strequal(login_type, OSCAR_MD5_LOGIN))
    - {
    - ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM;
    - ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ;
    - flap_connection_send_version_with_cookie_and_clientinfo(od,
    - conn, conn->cookielen, conn->cookie,
    - od->icq ? &icqinfo : &aiminfo,
    - purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS));
    - } else {
    - flap_connection_send_version_with_cookie(od, conn,
    - conn->cookielen, conn->cookie);
    - }
    -
    -
    - g_free(conn->cookie);
    - conn->cookie = NULL;
    - }
    -
    - if (conn->type == SNAC_FAMILY_AUTH)
    - {
    - /* This only happens when connecting with the old-style BUCP login */
    - aim_request_login(od, conn, purple_account_get_username(account));
    - purple_debug_info("oscar", "Username sent, waiting for response\n");
    - purple_connection_update_progress(gc, _("Username sent"), 1, OSCAR_CONNECT_STEPS);
    - }
    - else if (conn->type == SNAC_FAMILY_LOCATE)
    - {
    - purple_connection_update_progress(gc, _("Connection established, cookie sent"), 4, OSCAR_CONNECT_STEPS);
    - }
    - else if (conn->type == SNAC_FAMILY_CHAT)
    - {
    - od->oscar_chats = g_slist_prepend(od->oscar_chats, conn->new_conn_data);
    - conn->new_conn_data = NULL;
    - }
    -}
    -
    -static void
    -connection_established_cb(gpointer data, gint source, const gchar *error_message)
    -{
    - FlapConnection *conn;
    -
    - conn = data;
    -
    - conn->connect_data = NULL;
    - conn->fd = source;
    -
    - if (source < 0)
    - {
    - connection_common_error_cb(conn, error_message);
    - return;
    - }
    -
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, flap_connection_recv_cb, conn);
    - connection_common_established_cb(conn);
    -}
    -
    -static void
    -ssl_connection_established_cb(gpointer data, PurpleSslConnection *gsc,
    - PurpleInputCondition cond)
    -{
    - FlapConnection *conn;
    -
    - conn = data;
    -
    - purple_ssl_input_add(gsc, flap_connection_recv_cb_ssl, conn);
    - connection_common_established_cb(conn);
    -}
    -
    -static void
    -ssl_connection_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error,
    - gpointer data)
    -{
    - FlapConnection *conn;
    -
    - conn = data;
    -
    - if (conn->watcher_outgoing)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - }
    -
    - /* sslconn frees the connection on error */
    - conn->gsc = NULL;
    -
    - connection_common_error_cb(conn, purple_ssl_strerror(error));
    -}
    -
    -static void
    -flap_connection_established_bos(OscarData *od, FlapConnection *conn)
    -{
    - PurpleConnection *gc = od->gc;
    -
    - aim_srv_reqpersonalinfo(od, conn);
    -
    - purple_debug_info("oscar", "ssi: requesting rights and list\n");
    - aim_ssi_reqrights(od);
    - aim_ssi_reqdata(od);
    - if (od->getblisttimer > 0)
    - purple_timeout_remove(od->getblisttimer);
    - od->getblisttimer = purple_timeout_add_seconds(30, purple_ssi_rerequestdata, od);
    -
    - aim_locate_reqrights(od);
    - aim_buddylist_reqrights(od, conn);
    - aim_im_reqparams(od);
    - aim_bos_reqrights(od, conn); /* TODO: Don't call this with ssi */
    -
    - purple_connection_update_progress(gc, _("Finalizing connection"), 5, OSCAR_CONNECT_STEPS);
    -}
    -
    -static void
    -flap_connection_established_admin(OscarData *od, FlapConnection *conn)
    -{
    - aim_srv_clientready(od, conn);
    - purple_debug_info("oscar", "connected to admin\n");
    -
    - if (od->chpass) {
    - purple_debug_info("oscar", "changing password\n");
    - aim_admin_changepasswd(od, conn, od->newp, od->oldp);
    - g_free(od->oldp);
    - od->oldp = NULL;
    - g_free(od->newp);
    - od->newp = NULL;
    - od->chpass = FALSE;
    - }
    - if (od->setnick) {
    - purple_debug_info("oscar", "formatting username\n");
    - aim_admin_setnick(od, conn, od->newformatting);
    - g_free(od->newformatting);
    - od->newformatting = NULL;
    - od->setnick = FALSE;
    - }
    - if (od->conf) {
    - purple_debug_info("oscar", "confirming account\n");
    - aim_admin_reqconfirm(od, conn);
    - od->conf = FALSE;
    - }
    - if (od->reqemail) {
    - purple_debug_info("oscar", "requesting email address\n");
    - aim_admin_getinfo(od, conn, 0x0011);
    - od->reqemail = FALSE;
    - }
    - if (od->setemail) {
    - purple_debug_info("oscar", "setting email address\n");
    - aim_admin_setemail(od, conn, od->email);
    - g_free(od->email);
    - od->email = NULL;
    - od->setemail = FALSE;
    - }
    -}
    -
    -static void
    -flap_connection_established_chat(OscarData *od, FlapConnection *conn)
    -{
    - PurpleConnection *gc = od->gc;
    - struct chat_connection *chatcon;
    - static int id = 1;
    -
    - aim_srv_clientready(od, conn);
    -
    - chatcon = find_oscar_chat_by_conn(gc, conn);
    - if (chatcon) {
    - chatcon->id = id;
    - chatcon->conv = serv_got_joined_chat(gc, id++, chatcon->show);
    - }
    -}
    -
    -static void
    -flap_connection_established_chatnav(OscarData *od, FlapConnection *conn)
    -{
    - aim_srv_clientready(od, conn);
    - aim_chatnav_reqrights(od, conn);
    -}
    -
    -static void
    -flap_connection_established_alert(OscarData *od, FlapConnection *conn)
    -{
    - aim_email_sendcookies(od);
    - aim_email_activate(od);
    - aim_srv_clientready(od, conn);
    -}
    -
    -static void
    -flap_connection_established_bart(OscarData *od, FlapConnection *conn)
    -{
    - PurpleConnection *gc = od->gc;
    -
    - aim_srv_clientready(od, conn);
    -
    - od->iconconnecting = FALSE;
    -
    - purple_icons_fetch(gc);
    -}
    -
    -static int
    -flap_connection_established(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - conn->connected = TRUE;
    - purple_debug_info("oscar", "FLAP connection of type 0x%04hx is "
    - "now fully connected\n", conn->type);
    - if (conn->type == SNAC_FAMILY_LOCATE)
    - flap_connection_established_bos(od, conn);
    - else if (conn->type == SNAC_FAMILY_ADMIN)
    - flap_connection_established_admin(od, conn);
    - else if (conn->type == SNAC_FAMILY_CHAT)
    - flap_connection_established_chat(od, conn);
    - else if (conn->type == SNAC_FAMILY_CHATNAV)
    - flap_connection_established_chatnav(od, conn);
    - else if (conn->type == SNAC_FAMILY_ALERT)
    - flap_connection_established_alert(od, conn);
    - else if (conn->type == SNAC_FAMILY_BART)
    - flap_connection_established_bart(od, conn);
    -
    - return 1;
    -}
    -
    -static void
    -idle_reporting_pref_cb(const char *name, PurplePrefType type,
    - gconstpointer value, gpointer data)
    -{
    - PurpleConnection *gc;
    - OscarData *od;
    - gboolean report_idle;
    - guint32 presence;
    -
    - gc = data;
    - od = purple_connection_get_protocol_data(gc);
    - report_idle = !purple_strequal((const char *)value, "none");
    - presence = aim_ssi_getpresence(od->ssi.local);
    -
    - if (report_idle)
    - aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
    - else
    - aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
    -}
    -
    -/**
    - * Should probably make a "Use recent buddies group" account preference
    - * so that this option is surfaced to the user.
    - */
    -static void
    -recent_buddies_pref_cb(const char *name, PurplePrefType type,
    - gconstpointer value, gpointer data)
    -{
    - PurpleConnection *gc;
    - OscarData *od;
    - guint32 presence;
    -
    - gc = data;
    - od = purple_connection_get_protocol_data(gc);
    - presence = aim_ssi_getpresence(od->ssi.local);
    -
    - if (value)
    - aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES);
    - else
    - aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES);
    -}
    -
    -static const gchar *login_servers[] = {
    - AIM_DEFAULT_LOGIN_SERVER,
    - AIM_DEFAULT_SSL_LOGIN_SERVER,
    - ICQ_DEFAULT_LOGIN_SERVER,
    - ICQ_DEFAULT_SSL_LOGIN_SERVER,
    -};
    -
    -static const gchar *
    -get_login_server(gboolean is_icq, gboolean use_ssl)
    -{
    - return login_servers[(is_icq ? 2 : 0) + (use_ssl ? 1 : 0)];
    -}
    -
    -static gint
    -compare_handlers(gconstpointer a, gconstpointer b)
    -{
    - guint aa = GPOINTER_TO_UINT(a);
    - guint bb = GPOINTER_TO_UINT(b);
    - guint family1 = aa >> 16;
    - guint family2 = bb >> 16;
    - guint subtype1 = aa & 0xFFFF;
    - guint subtype2 = bb & 0xFFFF;
    - if (family1 != family2) {
    - return family1 - family2;
    - }
    - return subtype1 - subtype2;
    -}
    -
    -#if !GLIB_CHECK_VERSION(2,14,0)
    -static void hash_table_get_list_of_keys(gpointer key, gpointer value, gpointer user_data)
    -{
    - GList **handlers = (GList **)user_data;
    -
    - *handlers = g_list_prepend(*handlers, key);
    -}
    -#endif /* GLIB < 2.14.0 */
    -
    -void
    -oscar_login(PurpleAccount *account)
    -{
    - PurpleConnection *gc;
    - OscarData *od;
    - const gchar *encryption_type;
    - const gchar *login_type;
    - GList *handlers;
    - GList *sorted_handlers;
    - GList *cur;
    - GString *msg = g_string_new("");
    -
    - gc = purple_account_get_connection(account);
    - od = oscar_data_new();
    - od->gc = gc;
    - purple_connection_set_protocol_data(gc, od);
    -
    - oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, purple_connerr, 0);
    - oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, flap_connection_established, 0);
    -
    - oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0003, purple_info_change, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0005, purple_info_change, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0007, purple_account_confirm, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ALERT, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ALERT, SNAC_SUBTYPE_ALERT_MAILSTATUS, purple_email_parseupdate, 0);
    -
    - /* These are only needed when connecting with the old-style BUCP login */
    - oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0003, purple_parse_auth_resp, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0007, purple_parse_login, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_REQUEST, purple_parse_auth_securid_request, 0);
    -
    - oscar_data_addhandler(od, SNAC_FAMILY_BART, SNAC_SUBTYPE_BART_RESPONSE, purple_icon_parseicon, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0003, purple_bosrights, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_RIGHTSINFO, purple_parse_buddyrights, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_ONCOMING, purple_parse_oncoming, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_OFFGOING, purple_parse_offgoing, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERJOIN, purple_conv_chat_join, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERLEAVE, purple_conv_chat_leave, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE, purple_conv_chat_info_update, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_INCOMINGMSG, purple_conv_chat_incoming_msg, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, SNAC_SUBTYPE_CHATNAV_INFO, purple_chatnav_info, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ERROR, purple_ssi_parseerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO, purple_ssi_parserights, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_LIST, purple_ssi_parselist, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SRVACK, purple_ssi_parseack, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADD, purple_ssi_parseaddmod, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_MOD, purple_ssi_parseaddmod, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTH, purple_ssi_authgiven, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ, purple_ssi_authrequest, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP, purple_ssi_authreply, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADDED, purple_ssi_gotadded, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_INCOMING, purple_parse_incoming_im, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MISSEDCALL, purple_parse_misses, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP, purple_parse_clientauto, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MTN, purple_parse_mtn, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_RIGHTSINFO, purple_parse_locaterights, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x000f, purple_selfinfo, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x001f, purple_memrequest, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_REDIRECT, purple_handle_redirect, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_MOTD, purple_parse_motd, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_POPUP, 0x0002, purple_popup, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, SNAC_SUBTYPE_USERLOOKUP_ERROR, purple_parse_searcherror, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, 0x0003, purple_parse_searchreply, 0);
    -
    - g_string_append(msg, "Registered handlers: ");
    -#if GLIB_CHECK_VERSION(2,14,0)
    - handlers = g_hash_table_get_keys(od->handlerlist);
    -#else
    - handlers = NULL;
    - g_hash_table_foreach(od->handlerlist, hash_table_get_list_of_keys, &handlers);
    -#endif /* GLIB < 2.14.0 */
    - sorted_handlers = g_list_sort(g_list_copy(handlers), compare_handlers);
    - for (cur = sorted_handlers; cur; cur = cur->next) {
    - guint x = GPOINTER_TO_UINT(cur->data);
    - g_string_append_printf(msg, "%04x/%04x, ", x >> 16, x & 0xFFFF);
    - }
    - g_list_free(sorted_handlers);
    - g_list_free(handlers);
    - purple_debug_misc("oscar", "%s\n", msg->str);
    - g_string_free(msg, TRUE);
    -
    - purple_debug_misc("oscar", "oscar_login: gc = %p\n", gc);
    -
    - if (!oscar_util_valid_name(purple_account_get_username(account))) {
    - gchar *buf;
    - buf = g_strdup_printf(_("Unable to sign on as %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), purple_account_get_username(account));
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, buf);
    - g_free(buf);
    - return;
    - }
    -
    - gc->flags |= PURPLE_CONNECTION_HTML;
    - if (purple_strequal(purple_account_get_protocol_id(account), "prpl-icq")) {
    - od->icq = TRUE;
    - } else {
    - gc->flags |= PURPLE_CONNECTION_AUTO_RESP;
    - }
    -
    - /* Set this flag based on the protocol_id rather than the username,
    - because that is what's tied to the get_moods prpl callback. */
    - if (purple_strequal(purple_account_get_protocol_id(account), "prpl-icq"))
    - gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS;
    -
    - od->default_port = purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT);
    -
    - login_type = purple_account_get_string(account, "login_type", OSCAR_DEFAULT_LOGIN);
    - encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION);
    - if (!purple_ssl_is_supported() && purple_strequal(encryption_type, OSCAR_REQUIRE_ENCRYPTION)) {
    - purple_connection_error_reason(
    - gc,
    - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
    - _("You required encryption in your account settings, but encryption is not supported by your system."));
    - return;
    - }
    - od->use_ssl = purple_ssl_is_supported() && !purple_strequal(encryption_type, OSCAR_NO_ENCRYPTION);
    -
    - /* Connect to core Purple signals */
    - purple_prefs_connect_callback(gc, "/purple/away/idle_reporting", idle_reporting_pref_cb, gc);
    - purple_prefs_connect_callback(gc, "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb, gc);
    -
    - /*
    - * On 2008-03-05 AOL released some documentation on the OSCAR protocol
    - * which includes a new login method called clientLogin. It is similar
    - * (though not the same?) as what the AIM 6.0 series uses to
    - * authenticate.
    - *
    - * AIM 5.9 and lower use an MD5-based login procedure called "BUCP".
    - * This authentication method is used for both ICQ and AIM when
    - * clientLogin is not enabled.
    - */
    - if (purple_strequal(login_type, OSCAR_CLIENT_LOGIN)) {
    - /* Note: Actual server/port configuration is ignored here */
    - send_client_login(od, purple_account_get_username(account));
    - } else if (purple_strequal(login_type, OSCAR_KERBEROS_LOGIN)) {
    - const char *server;
    -
    - if (!od->use_ssl) {
    - purple_connection_error_reason(
    - gc,
    - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
    - _("You required Kerberos authentication but encryption is disabled in your account settings."));
    - return;
    - }
    - server = purple_account_get_string(account, "server", AIM_DEFAULT_KDC_SERVER);
    - /*
    - * If the account's server is what the oscar protocol has offered as
    - * the default login server through the vast eons (all two of
    - * said default options, AFAIK) and the user wants KDC, we'll
    - * do what we know is best for them and change the setting out
    - * from under them to the KDC login server.
    - */
    - if (purple_strequal(server, get_login_server(od->icq, FALSE)) ||
    - purple_strequal(server, get_login_server(od->icq, TRUE)) ||
    - purple_strequal(server, AIM_ALT_LOGIN_SERVER) ||
    - purple_strequal(server, "")) {
    - purple_debug_info("oscar", "Account uses Kerberos auth, so changing server to default KDC server\n");
    - purple_account_set_string(account, "server", AIM_DEFAULT_KDC_SERVER);
    - purple_account_set_int(account, "port", AIM_DEFAULT_KDC_PORT);
    - }
    - send_kerberos_login(od, purple_account_get_username(account));
    - } else {
    - FlapConnection *newconn;
    - const char *server;
    -
    - newconn = flap_connection_new(od, SNAC_FAMILY_AUTH);
    -
    - if (od->use_ssl) {
    - server = purple_account_get_string(account, "server", get_login_server(od->icq, TRUE));
    -
    - /*
    - * If the account's server is what the oscar prpl has offered as
    - * the default login server through the vast eons (all two of
    - * said default options, AFAIK) and the user wants SSL, we'll
    - * do what we know is best for them and change the setting out
    - * from under them to the SSL login server.
    - */
    - if (purple_strequal(server, get_login_server(od->icq, FALSE)) ||
    - purple_strequal(server, AIM_ALT_LOGIN_SERVER) ||
    - purple_strequal(server, AIM_DEFAULT_KDC_SERVER) ||
    - purple_strequal(server, "")) {
    - purple_debug_info("oscar", "Account uses SSL, so changing server to default SSL server\n");
    - purple_account_set_string(account, "server", get_login_server(od->icq, TRUE));
    - purple_account_set_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
    - server = get_login_server(od->icq, TRUE);
    - }
    -
    - newconn->gsc = purple_ssl_connect(account, server,
    - purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
    - ssl_connection_established_cb, ssl_connection_error_cb, newconn);
    - } else {
    - server = purple_account_get_string(account, "server", get_login_server(od->icq, FALSE));
    -
    - /*
    - * See the comment above. We do the reverse here. If they don't want
    - * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER,
    - * set it back to the default.
    - */
    - if (purple_strequal(server, get_login_server(od->icq, TRUE)) ||
    - purple_strequal(server, AIM_DEFAULT_KDC_SERVER) ||
    - purple_strequal(server, "")) {
    - purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n");
    - purple_account_set_string(account, "server", get_login_server(od->icq, FALSE));
    - purple_account_set_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
    - server = get_login_server(od->icq, FALSE);
    - }
    -
    - newconn->connect_data = purple_proxy_connect(NULL, account, server,
    - purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
    - connection_established_cb, newconn);
    - }
    -
    - if (newconn->gsc == NULL && newconn->connect_data == NULL) {
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to connect"));
    - return;
    - }
    - }
    -
    - purple_connection_update_progress(gc, _("Connecting"), 0, OSCAR_CONNECT_STEPS);
    -}
    -
    -void
    -oscar_close(PurpleConnection *gc)
    -{
    - OscarData *od;
    -
    - od = purple_connection_get_protocol_data(gc);
    -
    - while (od->oscar_chats)
    - {
    - struct chat_connection *cc = od->oscar_chats->data;
    - od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
    - oscar_chat_destroy(cc);
    - }
    - while (od->create_rooms)
    - {
    - struct create_room *cr = od->create_rooms->data;
    - g_free(cr->name);
    - od->create_rooms = g_slist_remove(od->create_rooms, cr);
    - g_free(cr);
    - }
    - oscar_data_destroy(od);
    - purple_connection_set_protocol_data(gc, NULL);
    -
    - purple_prefs_disconnect_by_handle(gc);
    -
    - purple_debug_info("oscar", "Signed off.\n");
    -}
    -
    -/* XXX - Should use purple_util_fetch_url for the below stuff */
    -struct pieceofcrap {
    - PurpleConnection *gc;
    - unsigned long offset;
    - unsigned long len;
    - char *modname;
    - int fd;
    - FlapConnection *conn;
    - unsigned int inpa;
    -};
    -
    -static void damn_you(gpointer data, gint source, PurpleInputCondition c)
    -{
    - struct pieceofcrap *pos = data;
    - OscarData *od = purple_connection_get_protocol_data(pos->gc);
    - char in = '\0';
    - int x = 0;
    - unsigned char m[17];
    - GString *msg;
    -
    - while (read(pos->fd, &in, 1) == 1) {
    - if (in == '\n')
    - x++;
    - else if (in != '\r')
    - x = 0;
    - if (x == 2)
    - break;
    - in = '\0';
    - }
    - if (in != '\n') {
    - char buf[256];
    - g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. "
    - "If so, check %s for updates."),
    - oscar_get_ui_info_string("website", PURPLE_WEBSITE));
    - purple_notify_warning(pos->gc, NULL,
    - _("Unable to get a valid AIM login hash."),
    - buf);
    - purple_input_remove(pos->inpa);
    - close(pos->fd);
    - g_free(pos);
    - return;
    - }
    - if (read(pos->fd, m, 16) != 16)
    - {
    - purple_debug_warning("oscar", "Could not read full AIM login hash "
    - "from " AIMHASHDATA "--that's bad.\n");
    - }
    - m[16] = '\0';
    -
    - msg = g_string_new("Sending hash: ");
    - for (x = 0; x < 16; x++)
    - g_string_append_printf(msg, "%02hhx ", (unsigned char)m[x]);
    - g_string_append(msg, "\n");
    - purple_debug_misc("oscar", "%s", msg->str);
    - g_string_free(msg, TRUE);
    -
    - purple_input_remove(pos->inpa);
    - close(pos->fd);
    - aim_sendmemblock(od, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
    - g_free(pos);
    -}
    -
    -static void
    -straight_to_hell(gpointer data, gint source, const gchar *error_message)
    -{
    - struct pieceofcrap *pos = data;
    - gchar *buf;
    - gssize result;
    -
    - pos->fd = source;
    -
    - if (source < 0) {
    - buf = g_strdup_printf(_("You may be disconnected shortly. "
    - "If so, check %s for updates."),
    - oscar_get_ui_info_string("website", PURPLE_WEBSITE));
    - purple_notify_warning(pos->gc, NULL,
    - _("Unable to get a valid AIM login hash."),
    - buf);
    - g_free(buf);
    - g_free(pos->modname);
    - g_free(pos);
    - return;
    - }
    -
    - buf = g_strdup_printf("GET " AIMHASHDATA "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
    - pos->offset, pos->len, pos->modname ? pos->modname : "");
    - result = send(pos->fd, buf, strlen(buf), 0);
    - if (result < 0)
    - purple_debug_error("oscar", "Error writing %" G_GSIZE_FORMAT
    - " bytes to fetch AIM hash data: %s\n",
    - strlen(buf), g_strerror(errno));
    - else if ((gsize)result != strlen(buf))
    - purple_debug_error("oscar", "Tried to write %"
    - G_GSIZE_FORMAT " bytes to fetch AIM hash data but "
    - "instead wrote %" G_GSSIZE_FORMAT " bytes\n",
    - strlen(buf), result);
    - g_free(buf);
    - g_free(pos->modname);
    - pos->inpa = purple_input_add(pos->fd, PURPLE_INPUT_READ, damn_you, pos);
    - return;
    -}
    -
    -/* size of icbmui.ocm, the largest module in AIM 3.5 */
    -#define AIM_MAX_FILE_SIZE 98304
    -
    -static int purple_memrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - va_list ap;
    - struct pieceofcrap *pos;
    - guint32 offset, len;
    - char *modname;
    -
    - va_start(ap, fr);
    - offset = va_arg(ap, guint32);
    - len = va_arg(ap, guint32);
    - modname = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_misc("oscar", "offset: %u, len: %u, file: %s\n",
    - offset, len, (modname ? modname : "aim.exe"));
    -
    - if (len == 0) {
    - purple_debug_misc("oscar", "len is 0, hashing NULL\n");
    - aim_sendmemblock(od, conn, offset, len, NULL,
    - AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
    - return 1;
    - }
    -
    - pos = g_new0(struct pieceofcrap, 1);
    - pos->gc = od->gc;
    - pos->conn = conn;
    -
    - pos->offset = offset;
    - pos->len = len;
    - pos->modname = g_strdup(modname);
    -
    - if (purple_proxy_connect(pos->gc, pos->gc->account, "pidgin.im", 80,
    - straight_to_hell, pos) == NULL)
    - {
    - char buf[256];
    - g_free(pos->modname);
    - g_free(pos);
    -
    - g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. "
    - "If so, check %s for updates."),
    - oscar_get_ui_info_string("website", PURPLE_WEBSITE));
    - purple_notify_warning(pos->gc, NULL,
    - _("Unable to get a valid login hash."),
    - buf);
    - }
    -
    - return 1;
    -}
    -
    -int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen, const char *tls_certname)
    -{
    - PurpleAccount *account;
    - FlapConnection *conn;
    -
    - account = purple_connection_get_account(gc);
    -
    - conn = flap_connection_new(od, SNAC_FAMILY_LOCATE);
    - conn->cookielen = cookielen;
    - conn->cookie = g_memdup2(cookie, cookielen);
    -
    - /*
    - * Use TLS only if the server provided us with a tls_certname. The server might not specify a tls_certname even if we requested to use TLS,
    - * and that is something we should be prepared to.
    - */
    - if (tls_certname)
    - {
    - conn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
    - ssl_connection_established_cb, ssl_connection_error_cb,
    - tls_certname, conn);
    - }
    - else
    - {
    - conn->connect_data = purple_proxy_connect(NULL,
    - account, host, port,
    - connection_established_cb, conn);
    - }
    -
    - if (conn->gsc == NULL && conn->connect_data == NULL)
    - {
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
    - return 0;
    - }
    -
    - od->default_port = port;
    -
    - purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS);
    -
    - return 1;
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static int
    -purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - char *host; int port;
    - size_t i;
    - FlapConnection *newconn;
    - va_list ap;
    - struct aim_authresp_info *info;
    -
    - port = purple_account_get_int(account, "port", od->default_port);
    -
    - va_start(ap, fr);
    - info = va_arg(ap, struct aim_authresp_info *);
    - va_end(ap);
    -
    - purple_debug_info("oscar",
    - "inside auth_resp (Username: %s)\n", info->bn);
    -
    - if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) {
    - char buf[256];
    - switch (info->errorcode) {
    - case 0x01:
    - /* Unregistered username */
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Username does not exist"));
    - break;
    - case 0x05:
    - /* Incorrect password */
    - if (!purple_account_get_remember_password(account))
    - purple_account_set_password(account, NULL);
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password"));
    - break;
    - case 0x11:
    - /* Suspended account */
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended"));
    - break;
    - case 0x02:
    - case 0x14:
    - /* service temporarily unavailable */
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable."));
    - break;
    - case 0x18:
    - /* username connecting too frequently */
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your username has been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
    - break;
    - case 0x1c:
    - {
    - /* client too old */
    - g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"),
    - oscar_get_ui_info_string("website", PURPLE_WEBSITE));
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, buf);
    - break;
    - }
    - case 0x1d:
    - /* IP address connecting too frequently */
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your IP address has been connecting and disconnecting too frequently. Wait a minute and try again. If you continue to try, you will need to wait even longer."));
    - break;
    - default:
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Unknown reason"));
    - break;
    - }
    - purple_debug_info("oscar", "Login Error Code 0x%04hx\n", info->errorcode);
    - purple_debug_info("oscar", "Error URL: %s\n", info->errorurl ? info->errorurl : "");
    - return 1;
    - }
    -
    - purple_debug_misc("oscar", "Reg status: %hu\n"
    - "Email: %s\n"
    - "BOSIP: %s\n",
    - info->regstatus,
    - info->email ? info->email : "null",
    - info->bosip ? info->bosip : "null");
    - purple_debug_info("oscar", "Closing auth connection...\n");
    - flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_DONE, NULL);
    -
    - for (i = 0; i < strlen(info->bosip); i++) {
    - if (info->bosip[i] == ':') {
    - port = atoi(&(info->bosip[i+1]));
    - break;
    - }
    - }
    - host = g_strndup(info->bosip, i);
    - newconn = flap_connection_new(od, SNAC_FAMILY_LOCATE);
    - newconn->cookielen = info->cookielen;
    - newconn->cookie = g_memdup2(info->cookie, info->cookielen);
    -
    - if (od->use_ssl)
    - {
    - /*
    - * This shouldn't be hardcoded to "bos.oscar.aol.com" except that
    - * the server isn't sending us a name to use for comparing the
    - * certificate common name.
    - */
    - newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
    - ssl_connection_established_cb, ssl_connection_error_cb,
    - "bos.oscar.aol.com", newconn);
    - }
    - else
    - {
    - newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
    - connection_established_cb, newconn);
    - }
    -
    - g_free(host);
    - if (newconn->gsc == NULL && newconn->connect_data == NULL)
    - {
    - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
    - return 0;
    - }
    -
    - purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS);
    -
    - return 1;
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static void
    -purple_parse_auth_securid_request_yes_cb(gpointer user_data, const char *msg)
    -{
    - PurpleConnection *gc = user_data;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - aim_auth_securid_send(od, msg);
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static void
    -purple_parse_auth_securid_request_no_cb(gpointer user_data, const char *value)
    -{
    - PurpleConnection *gc = user_data;
    -
    - /* Disconnect */
    - purple_connection_error_reason(gc,
    - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
    - _("The SecurID key entered is invalid"));
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static int
    -purple_parse_auth_securid_request(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - gchar *primary;
    -
    - purple_debug_info("oscar", "Got SecurID request\n");
    -
    - primary = g_strdup_printf("Enter the SecurID key for %s.", purple_account_get_username(account));
    - purple_request_input(gc, NULL, _("Enter SecurID"), primary,
    - _("Enter the 6 digit number from the digital display."),
    - FALSE, FALSE, NULL,
    - _("_OK"), G_CALLBACK(purple_parse_auth_securid_request_yes_cb),
    - _("_Cancel"), G_CALLBACK(purple_parse_auth_securid_request_no_cb),
    - account, NULL, NULL,
    - gc);
    - g_free(primary);
    -
    - return 1;
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static int
    -purple_parse_login(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM;
    - ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ;
    - va_list ap;
    - char *key;
    - gboolean truncate_pass;
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - key = va_arg(ap, char *);
    - truncate_pass = va_arg(ap, int);
    - va_end(ap);
    -
    - aim_send_login(od, conn, purple_account_get_username(account),
    - purple_connection_get_password(gc), truncate_pass,
    - od->icq ? &icqinfo : &aiminfo, key,
    - purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS));
    -
    - purple_connection_update_progress(gc, _("Password sent"), 2, OSCAR_CONNECT_STEPS);
    -
    - return 1;
    -}
    -
    -static int
    -purple_handle_redirect(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - char *host, *separator;
    - int port;
    - FlapConnection *newconn;
    - va_list ap;
    - struct aim_redirect_data *redir;
    -
    - va_start(ap, fr);
    - redir = va_arg(ap, struct aim_redirect_data *);
    - va_end(ap);
    -
    - port = od->default_port;
    - separator = strchr(redir->ip, ':');
    - if (separator != NULL)
    - {
    - host = g_strndup(redir->ip, separator - redir->ip);
    - port = atoi(separator + 1);
    - }
    - else
    - host = g_strdup(redir->ip);
    -
    - if (!redir->use_ssl) {
    - const gchar *encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION);
    - if (purple_strequal(encryption_type, OSCAR_OPPORTUNISTIC_ENCRYPTION)) {
    - purple_debug_warning("oscar", "We won't use SSL for FLAP type 0x%04hx.\n", redir->group);
    - } else if (purple_strequal(encryption_type, OSCAR_REQUIRE_ENCRYPTION)) {
    - purple_debug_error("oscar", "FLAP server %s:%d of type 0x%04hx doesn't support encryption.", host, port, redir->group);
    - purple_connection_error_reason(
    - gc,
    - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
    - _("You required encryption in your account settings, but one of the servers doesn't support it."));
    - return 0;
    - }
    - }
    -
    - /*
    - * These FLAP servers advertise SSL (type "0x02"), but SSL connections to these hosts
    - * die a painful death. iChat and Miranda, when using SSL, still do these in plaintext.
    - */
    - if (redir->use_ssl && (redir->group == SNAC_FAMILY_ADMIN ||
    - redir->group == SNAC_FAMILY_BART))
    - {
    - purple_debug_info("oscar", "Ignoring broken SSL for FLAP type 0x%04hx.\n", redir->group);
    - redir->use_ssl = 0;
    - }
    -
    - purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx\n", host, port, redir->group);
    -
    - newconn = flap_connection_new(od, redir->group);
    - newconn->cookielen = redir->cookielen;
    - newconn->cookie = g_memdup2(redir->cookie, redir->cookielen);
    - if (newconn->type == SNAC_FAMILY_CHAT)
    - {
    - struct chat_connection *cc;
    - cc = g_new0(struct chat_connection, 1);
    - cc->conn = newconn;
    - cc->gc = gc;
    - cc->name = g_strdup(redir->chat.room);
    - cc->exchange = redir->chat.exchange;
    - cc->instance = redir->chat.instance;
    - cc->show = extract_name(redir->chat.room);
    - newconn->new_conn_data = cc;
    - purple_debug_info("oscar", "Connecting to chat room %s exchange %hu\n", cc->name, cc->exchange);
    - }
    -
    -
    - if (redir->use_ssl)
    - {
    - newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
    - ssl_connection_established_cb, ssl_connection_error_cb,
    - redir->ssl_cert_cn, newconn);
    - }
    - else
    - {
    - newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
    - connection_established_cb, newconn);
    - }
    -
    - if (newconn->gsc == NULL && newconn->connect_data == NULL)
    - {
    - flap_connection_schedule_destroy(newconn,
    - OSCAR_DISCONNECT_COULD_NOT_CONNECT,
    - _("Unable to initialize connection"));
    - purple_debug_error("oscar", "Unable to connect to FLAP server "
    - "of type 0x%04hx\n", redir->group);
    - }
    - g_free(host);
    -
    - return 1;
    -}
    -
    -
    -static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PurpleBuddy *buddy = NULL;
    - PurpleStatus *previous_status = NULL;
    - struct buddyinfo *bi;
    - time_t time_idle = 0, signon = 0;
    - int type = 0;
    - gboolean buddy_is_away = FALSE;
    - const char *status_id;
    - va_list ap;
    - aim_userinfo_t *info;
    - char *message;
    - char *itmsurl = NULL;
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - info = va_arg(ap, aim_userinfo_t *);
    - va_end(ap);
    -
    - g_return_val_if_fail(info != NULL, 1);
    - g_return_val_if_fail(info->bn != NULL, 1);
    -
    - buddy = purple_find_buddy(account, info->bn);
    - if (buddy) {
    - previous_status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
    - }
    -
    - /*
    - * If this is an AIM buddy and their name has formatting, set their
    - * server alias.
    - */
    - if (!oscar_util_valid_name_icq(info->bn)) {
    - gboolean bn_has_formatting = FALSE;
    - char *c;
    - for (c = info->bn; *c != '\0'; c++) {
    - if (!islower(*c)) {
    - bn_has_formatting = TRUE;
    - break;
    - }
    - }
    - serv_got_alias(gc, info->bn,
    - bn_has_formatting ? info->bn : NULL);
    - }
    -
    - if (info->present & AIM_USERINFO_PRESENT_FLAGS) {
    - if (info->flags & AIM_FLAG_AWAY)
    - buddy_is_away = TRUE;
    - }
    - if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
    - type = info->icqinfo.status;
    - if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
    - (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
    - buddy_is_away = TRUE;
    - }
    - }
    -
    - if (oscar_util_valid_name_icq(info->bn)) {
    - if (type & AIM_ICQ_STATE_CHAT)
    - status_id = OSCAR_STATUS_ID_FREE4CHAT;
    - else if (type & AIM_ICQ_STATE_DND)
    - status_id = OSCAR_STATUS_ID_DND;
    - else if (type & AIM_ICQ_STATE_OUT)
    - status_id = OSCAR_STATUS_ID_NA;
    - else if (type & AIM_ICQ_STATE_BUSY)
    - status_id = OSCAR_STATUS_ID_OCCUPIED;
    - else if (type & AIM_ICQ_STATE_AWAY)
    - status_id = OSCAR_STATUS_ID_AWAY;
    - else if (type & AIM_ICQ_STATE_INVISIBLE)
    - status_id = OSCAR_STATUS_ID_INVISIBLE;
    - else if (type & AIM_ICQ_STATE_EVIL)
    - status_id = OSCAR_STATUS_ID_EVIL;
    - else if (type & AIM_ICQ_STATE_DEPRESSION)
    - status_id = OSCAR_STATUS_ID_DEPRESSION;
    - else if (type & AIM_ICQ_STATE_ATHOME)
    - status_id = OSCAR_STATUS_ID_ATHOME;
    - else if (type & AIM_ICQ_STATE_ATWORK)
    - status_id = OSCAR_STATUS_ID_ATWORK;
    - else if (type & AIM_ICQ_STATE_LUNCH)
    - status_id = OSCAR_STATUS_ID_LUNCH;
    - else
    - status_id = OSCAR_STATUS_ID_AVAILABLE;
    - } else {
    - if (type & AIM_ICQ_STATE_INVISIBLE)
    - status_id = OSCAR_STATUS_ID_INVISIBLE;
    - else if (buddy_is_away)
    - status_id = OSCAR_STATUS_ID_AWAY;
    - else
    - status_id = OSCAR_STATUS_ID_AVAILABLE;
    - }
    -
    - if (info->flags & AIM_FLAG_WIRELESS) {
    - purple_prpl_got_user_status(account, info->bn, OSCAR_STATUS_ID_MOBILE, NULL);
    - } else {
    - purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE);
    - }
    -
    - message = (info->status && info->status_len > 0)
    - ? oscar_encoding_to_utf8(info->status_encoding, info->status, info->status_len)
    - : NULL;
    -
    - if (purple_strequal(status_id, OSCAR_STATUS_ID_AVAILABLE)) {
    - /* TODO: If itmsurl is NULL, does that mean the URL has been
    - cleared? Or does it mean the URL should remain unchanged? */
    - if (info->itmsurl != NULL) {
    - itmsurl = (info->itmsurl_len > 0) ? oscar_encoding_to_utf8(info->itmsurl_encoding, info->itmsurl, info->itmsurl_len) : NULL;
    - } else if (previous_status != NULL && purple_status_is_available(previous_status)) {
    - itmsurl = g_strdup(purple_status_get_attr_string(previous_status, "itmsurl"));
    - }
    - purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s', itmsurl = '%s'\n", status_id, info->bn, message ? message : "(null)", itmsurl ? itmsurl : "(null)");
    - purple_prpl_got_user_status(account, info->bn, status_id, "message", message, "itmsurl", itmsurl, NULL);
    - } else {
    - purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s'\n", status_id, info->bn, message ? message : "(null)");
    - purple_prpl_got_user_status(account, info->bn, status_id, "message", message, NULL);
    - }
    -
    - g_free(message);
    - g_free(itmsurl);
    -
    - /* Login time stuff */
    - if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
    - signon = info->onlinesince;
    - else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
    - signon = time(NULL) - info->sessionlen;
    - purple_prpl_got_user_login_time(account, info->bn, signon);
    -
    - /* Idle time stuff */
    - /* info->idletime is the number of minutes that this user has been idle */
    - if (info->present & AIM_USERINFO_PRESENT_IDLE)
    - time_idle = time(NULL) - info->idletime * 60;
    -
    - if (time_idle > 0)
    - purple_prpl_got_user_idle(account, info->bn, TRUE, time_idle);
    - else
    - purple_prpl_got_user_idle(account, info->bn, FALSE, 0);
    -
    - /* Server stored icon stuff */
    - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, info->bn));
    - if (!bi) {
    - bi = g_new0(struct buddyinfo, 1);
    - g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, info->bn)), bi);
    - }
    - bi->typingnot = FALSE;
    - bi->ico_informed = FALSE;
    - bi->ipaddr = info->icqinfo.ipaddr;
    -
    - if (info->iconcsumlen) {
    - const char *saved_b16 = NULL;
    - char *b16 = NULL;
    - PurpleBuddy *b = NULL;
    -
    - b16 = purple_base16_encode(info->iconcsum, info->iconcsumlen);
    - b = purple_find_buddy(account, info->bn);
    - if (b != NULL)
    - saved_b16 = purple_buddy_icons_get_checksum_for_user(b);
    -
    - if (!b16 || !saved_b16 || !purple_strequal(b16, saved_b16)) {
    - /* Invalidate the old icon for this user */
    - purple_buddy_icons_set_for_user(account, info->bn, NULL, 0, NULL);
    -
    - /* Fetch the new icon (if we're not already doing so) */
    - if (g_slist_find_custom(od->requesticon, info->bn,
    - (GCompareFunc)oscar_util_name_compare) == NULL)
    - {
    - od->requesticon = g_slist_prepend(od->requesticon,
    - g_strdup(purple_normalize(account, info->bn)));
    - purple_icons_fetch(gc);
    - }
    - }
    - g_free(b16);
    - }
    -
    - return 1;
    -}
    -
    -static int purple_parse_offgoing(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - va_list ap;
    - aim_userinfo_t *info;
    -
    - va_start(ap, fr);
    - info = va_arg(ap, aim_userinfo_t *);
    - va_end(ap);
    -
    - purple_prpl_got_user_status(account, info->bn, OSCAR_STATUS_ID_OFFLINE, NULL);
    - purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE);
    - g_hash_table_remove(od->buddyinfo, purple_normalize(gc->account, info->bn));
    -
    - return 1;
    -}
    -
    -static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleMessageFlags flags = 0;
    - struct buddyinfo *bi;
    - PurpleStoredImage *img;
    - gchar *tmp;
    - const char *start, *end;
    - GData *attribs;
    -
    - purple_debug_misc("oscar", "Received IM from %s\n", userinfo->bn);
    -
    - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
    - if (!bi) {
    - bi = g_new0(struct buddyinfo, 1);
    - g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, userinfo->bn)), bi);
    - }
    -
    - if (args->icbmflags & AIM_IMFLAGS_AWAY)
    - flags |= PURPLE_MESSAGE_AUTO_RESP;
    -
    - if (args->icbmflags & AIM_IMFLAGS_TYPINGNOT)
    - bi->typingnot = TRUE;
    - else
    - bi->typingnot = FALSE;
    -
    - if ((args->icbmflags & AIM_IMFLAGS_HASICON) && (args->iconlen) && (args->iconsum) && (args->iconstamp)) {
    - purple_debug_misc("oscar", "%s has an icon\n", userinfo->bn);
    - if ((args->iconlen != bi->ico_len) || (args->iconsum != bi->ico_csum) || (args->iconstamp != bi->ico_time)) {
    - bi->ico_need = TRUE;
    - bi->ico_len = args->iconlen;
    - bi->ico_csum = args->iconsum;
    - bi->ico_time = args->iconstamp;
    - }
    - }
    -
    - img = purple_buddy_icons_find_account_icon(account);
    - if ((img != NULL) &&
    - (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) && !bi->ico_sent && bi->ico_informed) {
    - gconstpointer data = purple_imgstore_get_data(img);
    - size_t len = purple_imgstore_get_size(img);
    - purple_debug_info("oscar",
    - "Sending buddy icon to %s (%" G_GSIZE_FORMAT " bytes)\n",
    - userinfo->bn, len);
    - aim_im_sendch2_icon(od, userinfo->bn, data, len,
    - purple_buddy_icons_get_account_icon_timestamp(account),
    - aimutil_iconsum(data, len));
    - }
    - purple_imgstore_unref(img);
    -
    - tmp = g_strdup(args->msg);
    -
    - /*
    - * Convert iChat color tags to normal font tags.
    - */
    - if (purple_markup_find_tag("body", tmp, &start, &end, &attribs))
    - {
    - int len;
    - char *tmp2, *body;
    - const char *ichattextcolor, *ichatballooncolor;
    - const char *slash_body_start, *slash_body_end = NULL; /* </body> */
    - GData *unused;
    -
    - /*
    - * Find the ending </body> so we can strip off the outer <html/>
    - * and <body/>
    - */
    - if (purple_markup_find_tag("/body", end + 1, &slash_body_start, &slash_body_end, &unused))
    - {
    - body = g_strndup(start, slash_body_end - start + 1);
    - g_datalist_clear(&unused);
    - }
    - else
    - {
    - purple_debug_warning("oscar", "Broken message contains <body> but not </body>!\n");
    - /* Take everything after <body> */
    - body = g_strdup(start);
    - }
    -
    - ichattextcolor = g_datalist_get_data(&attribs, "ichattextcolor");
    - if (ichattextcolor != NULL)
    - {
    - tmp2 = g_strdup_printf("<font color=\"%s\">%s</font>", ichattextcolor, body);
    - g_free(body);
    - body = tmp2;
    - }
    -
    - ichatballooncolor = g_datalist_get_data(&attribs, "ichatballooncolor");
    - if (ichatballooncolor != NULL)
    - {
    - tmp2 = g_strdup_printf("<font back=\"%s\">%s</font>", ichatballooncolor, body);
    - g_free(body);
    - body = tmp2;
    - }
    -
    - g_datalist_clear(&attribs);
    -
    - len = start - tmp;
    - tmp2 = g_strdup_printf("%.*s%s%s", len, tmp, body, slash_body_end ? slash_body_end + 1: "</body>");
    - g_free(tmp);
    - g_free(body);
    -
    - tmp = tmp2;
    - }
    -
    - /*
    - * Are there <html/> surrounding tags? If so, strip them out, too.
    - */
    - if (purple_markup_find_tag("html", tmp, &start, &end, &attribs))
    - {
    - gchar *tmp2;
    - int len;
    -
    - g_datalist_clear(&attribs);
    -
    - len = start - tmp;
    - tmp2 = g_strdup_printf("%.*s%s", len, tmp, end + 1);
    - g_free(tmp);
    - tmp = tmp2;
    - }
    -
    - if (purple_markup_find_tag("/html", tmp, &start, &end, &attribs))
    - {
    - gchar *tmp2;
    - int len;
    -
    - g_datalist_clear(&attribs);
    -
    - len = start - tmp;
    - tmp2 = g_strdup_printf("%.*s%s", len, tmp, end + 1);
    - g_free(tmp);
    - tmp = tmp2;
    - }
    -
    - serv_got_im(gc, userinfo->bn, tmp, flags, (args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL));
    - g_free(tmp);
    -
    - return 1;
    -}
    -
    -static int
    -incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, IcbmArgsCh2 *args)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PurpleMessageFlags flags = 0;
    - char *message = NULL;
    -
    - g_return_val_if_fail(od != NULL, 0);
    - g_return_val_if_fail(od->gc != NULL, 0);
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    - od = purple_connection_get_protocol_data(gc);
    -
    - if (args == NULL)
    - return 0;
    -
    - purple_debug_misc("oscar", "Incoming rendezvous message of type %"
    - G_GUINT64_FORMAT ", user %s, status %hu\n",
    - args->type, userinfo->bn, args->status);
    -
    - if (args->msg != NULL) {
    - message = oscar_encoding_to_utf8(args->encoding, args->msg, args->msglen);
    - }
    -
    - if (args->type & OSCAR_CAPABILITY_CHAT)
    - {
    - char *utf8name, *tmp;
    - GHashTable *components;
    -
    - if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange) {
    - g_free(message);
    - return 1;
    - }
    - utf8name = oscar_encoding_to_utf8(args->encoding, args->info.chat.roominfo.name, args->info.chat.roominfo.namelen);
    -
    - tmp = extract_name(utf8name);
    - if (tmp != NULL)
    - {
    - g_free(utf8name);
    - utf8name = tmp;
    - }
    -
    - components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
    - g_free);
    - g_hash_table_replace(components, g_strdup("room"), utf8name);
    - g_hash_table_replace(components, g_strdup("exchange"),
    - g_strdup_printf("%d", args->info.chat.roominfo.exchange));
    - serv_got_chat_invite(gc,
    - utf8name,
    - userinfo->bn,
    - message,
    - components);
    - }
    -
    - else if ((args->type & OSCAR_CAPABILITY_SENDFILE) || (args->type & OSCAR_CAPABILITY_DIRECTIM))
    - {
    - if (args->status == AIM_RENDEZVOUS_PROPOSE)
    - {
    - peer_connection_got_proposition(od, userinfo->bn, message, args);
    - }
    - else if (args->status == AIM_RENDEZVOUS_CANCEL)
    - {
    - /* The other user cancelled a peer request */
    - PeerConnection *conn;
    -
    - conn = peer_connection_find_by_cookie(od, userinfo->bn, args->cookie);
    - /*
    - * If conn is NULL it means we haven't tried to create
    - * a connection with that user. They may be trying to
    - * do something malicious.
    - */
    - if (conn != NULL)
    - {
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - }
    - }
    - else if (args->status == AIM_RENDEZVOUS_CONNECTED)
    - {
    - /*
    - * Remote user has accepted our peer request. If we
    - * wanted to we could look up the PeerConnection using
    - * args->cookie, but we don't need to do anything here.
    - */
    - }
    - }
    -
    - else if (args->type & OSCAR_CAPABILITY_GETFILE)
    - {
    - }
    -
    - else if (args->type & OSCAR_CAPABILITY_TALK)
    - {
    - }
    -
    - else if (args->type & OSCAR_CAPABILITY_BUDDYICON)
    - {
    - purple_buddy_icons_set_for_user(account, userinfo->bn,
    - g_memdup2(args->info.icon.icon, args->info.icon.length),
    - args->info.icon.length,
    - NULL);
    - }
    -
    - else if (args->type & OSCAR_CAPABILITY_ICQSERVERRELAY)
    - {
    - purple_debug_info("oscar", "Got an ICQ Server Relay message of "
    - "type %d\n", args->info.rtfmsg.msgtype);
    -
    - if (args->info.rtfmsg.msgtype == 1) {
    - if (args->info.rtfmsg.msg != NULL) {
    - char *rtfmsg;
    - const char *encoding = args->encoding;
    - size_t len = strlen(args->info.rtfmsg.msg);
    - char *tmp, *tmp2;
    -
    - if (encoding == NULL && !g_utf8_validate(args->info.rtfmsg.msg, len, NULL)) {
    - /* Yet another wonderful Miranda-related hack. If their user disables the "Send Unicode messages" setting,
    - * Miranda sends us ch2 messages in whatever Windows codepage is set as default on their user's system (instead of UTF-8).
    - * Of course, they don't bother to specify that codepage. Let's just fallback to the encoding OUR users can
    - * specify in account options as a last resort.
    - */
    - encoding = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
    - purple_debug_info("oscar", "Miranda, is that you? Using '%s' as encoding\n", encoding);
    - }
    -
    - rtfmsg = oscar_encoding_to_utf8(encoding, args->info.rtfmsg.msg, len);
    -
    - /* Channel 2 messages are supposed to be plain-text (never mind the name "rtfmsg", even
    - * the official client doesn't parse them as RTF). Therefore, we should escape them before
    - * showing to the user. */
    - tmp = g_markup_escape_text(rtfmsg, -1);
    - g_free(rtfmsg);
    - tmp2 = purple_strreplace(tmp, "\r\n", "<br>");
    - g_free(tmp);
    -
    - serv_got_im(gc, userinfo->bn, tmp2, flags, time(NULL));
    - aim_im_send_icq_confirmation(od, userinfo->bn, args->cookie);
    - g_free(tmp2);
    - }
    - } else if (args->info.rtfmsg.msgtype == 26) {
    - purple_debug_info("oscar", "Sending X-Status Reply\n");
    - icq_relay_xstatus(od, userinfo->bn, args->cookie);
    - }
    - }
    - else
    - {
    - purple_debug_error("oscar", "Unknown request class %"
    - G_GUINT64_FORMAT "\n", args->type);
    - }
    -
    - g_free(message);
    -
    - return 1;
    -}
    -
    -/* When someone sends you buddies */
    -static void
    -purple_icq_buddyadd(struct name_data *data)
    -{
    - PurpleConnection *gc = data->gc;
    -
    - purple_blist_request_add_buddy(purple_connection_get_account(gc), data->name, NULL, data->nick);
    -
    - oscar_free_name_data(data);
    -}
    -
    -static int
    -incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args, time_t t)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - gchar **msg1, **msg2;
    - int i, numtoks;
    -
    - if (!args->type || !args->msg || !args->uin)
    - return 1;
    -
    - purple_debug_info("oscar",
    - "Received a channel 4 message of type 0x%02hx.\n",
    - (guint16)args->type);
    -
    - /*
    - * Split up the message at the delimeter character, then convert each
    - * string to UTF-8. Unless, of course, this is a type 1 message. If
    - * this is a type 1 message, then the delimiter 0xfe could be a valid
    - * character in whatever encoding the message was sent in. Type 1
    - * messages are always made up of only one part, so we can easily account
    - * for this suck-ass part of the protocol by splitting the string into at
    - * most 1 baby string.
    - */
    - msg1 = g_strsplit(args->msg, "\376", (args->type == 0x01 ? 1 : 0));
    - for (numtoks=0; msg1[numtoks]; numtoks++);
    - msg2 = (gchar **)g_malloc((numtoks+1)*sizeof(gchar *));
    - for (i=0; msg1[i]; i++) {
    - gchar *uin = g_strdup_printf("%u", args->uin);
    -
    - purple_str_strip_char(msg1[i], '\r');
    - /* TODO: Should use an encoding other than ASCII? */
    - msg2[i] = oscar_decode_im(account, uin, AIM_CHARSET_ASCII, msg1[i], strlen(msg1[i]));
    - g_free(uin);
    - }
    - msg2[i] = NULL;
    -
    - switch (args->type) {
    - case 0x01: { /* MacICQ message or basic offline message */
    - if (i >= 1) {
    - gchar *uin = g_strdup_printf("%u", args->uin);
    - gchar *tmp;
    -
    - /* If the message came from an ICQ user then escape any HTML */
    - tmp = g_markup_escape_text(msg2[0], -1);
    -
    - if (t) { /* This is an offline message */
    - /* The timestamp is UTC-ish, so we need to get the offset */
    -#ifdef HAVE_TM_GMTOFF
    - time_t now;
    - struct tm *tm;
    - now = time(NULL);
    - tm = localtime(&now);
    - t += tm->tm_gmtoff;
    -#else
    -# ifdef HAVE_TIMEZONE
    - tzset();
    - t -= timezone;
    -# endif
    -#endif
    - serv_got_im(gc, uin, tmp, 0, t);
    - } else { /* This is a message from MacICQ/Miranda */
    - serv_got_im(gc, uin, tmp, 0, time(NULL));
    - }
    - g_free(uin);
    - g_free(tmp);
    - }
    - } break;
    -
    - case 0x04: { /* Someone sent you a URL */
    - if (i >= 2) {
    - if (msg2[1] != NULL) {
    - gchar *uin = g_strdup_printf("%u", args->uin);
    - gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>",
    - msg2[1],
    - (msg2[0] && msg2[0][0]) ? msg2[0] : msg2[1]);
    - serv_got_im(gc, uin, message, 0, time(NULL));
    - g_free(uin);
    - g_free(message);
    - }
    - }
    - } break;
    -
    - case 0x06: { /* Someone requested authorization */
    - if (i >= 6) {
    - gchar *bn = g_strdup_printf("%u", args->uin);
    - gchar *reason = NULL;
    -
    - if (msg2[5] != NULL)
    - reason = oscar_decode_im(account, bn, AIM_CHARSET_LATIN_1, msg2[5], strlen(msg2[5]));
    -
    - purple_debug_info("oscar",
    - "Received an authorization request from UIN %u\n",
    - args->uin);
    - aim_icq_getalias(od, bn, TRUE, reason);
    - g_free(bn);
    - g_free(reason);
    - }
    - } break;
    -
    - case 0x07: { /* Someone has denied you authorization */
    - if (i >= 1) {
    - gchar *dialog_msg = g_strdup_printf(_("The user %u has denied your request to add them to your buddy list for the following reason:\n%s"), args->uin, msg2[0] ? msg2[0] : _("No reason given."));
    - purple_notify_info(gc, NULL, _("ICQ authorization denied."),
    - dialog_msg);
    - g_free(dialog_msg);
    - }
    - } break;
    -
    - case 0x08: { /* Someone has granted you authorization */
    - gchar *dialog_msg = g_strdup_printf(_("The user %u has granted your request to add them to your buddy list."), args->uin);
    - purple_notify_info(gc, NULL, "ICQ authorization accepted.",
    - dialog_msg);
    - g_free(dialog_msg);
    - } break;
    -
    - case 0x09: { /* Message from the Godly ICQ server itself, I think */
    - if (i >= 5) {
    - gchar *dialog_msg = g_strdup_printf(_("You have received a special message\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
    - purple_notify_info(gc, NULL, "ICQ Server Message", dialog_msg);
    - g_free(dialog_msg);
    - }
    - } break;
    -
    - case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */
    - if (i >= 6) {
    - gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ page\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
    - purple_notify_info(gc, NULL, "ICQ Page", dialog_msg);
    - g_free(dialog_msg);
    - }
    - } break;
    -
    - case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */
    - if (i >= 6) {
    - gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ email from %s [%s]\n\nMessage is:\n%s"), msg2[0], msg2[3], msg2[5]);
    - purple_notify_info(gc, NULL, "ICQ Email", dialog_msg);
    - g_free(dialog_msg);
    - }
    - } break;
    -
    - case 0x12: {
    - /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */
    - /* Someone added you to their buddy list? */
    - } break;
    -
    - case 0x13: { /* Someone has sent you some ICQ buddies */
    - guint i, num;
    - gchar **text;
    - text = g_strsplit(args->msg, "\376", 0);
    - if (text) {
    - /* Read the number of contacts that we were sent */
    - errno = 0;
    - num = text[0] ? strtoul(text[0], NULL, 10) : 0;
    -
    - if (num > 0 && errno == 0) {
    - for (i=0; i<num; i++) {
    - struct name_data *data;
    - gchar *message;
    -
    - if (!text[i*2 + 1] || !text[i*2 + 2]) {
    - /* We're missing the contact name or nickname. Bail out. */
    - gchar *tmp = g_strescape(args->msg, NULL);
    - purple_debug_error("oscar", "Unknown syntax parsing "
    - "ICQ buddies. args->msg=%s\n", tmp);
    - g_free(tmp);
    - break;
    - }
    -
    - message = g_strdup_printf(_("ICQ user %u has sent you a buddy: %s (%s)"), args->uin, text[i*2+2], text[i*2+1]);
    -
    - data = g_new(struct name_data, 1);
    - data->gc = gc;
    - data->name = g_strdup(text[i*2+1]);
    - data->nick = g_strdup(text[i*2+2]);
    -
    - purple_request_action(gc, NULL, message,
    - _("Do you want to add this buddy "
    - "to your buddy list?"),
    - PURPLE_DEFAULT_ACTION_NONE,
    - purple_connection_get_account(gc), data->name, NULL,
    - data, 2,
    - _("_Add"), G_CALLBACK(purple_icq_buddyadd),
    - _("_Decline"), G_CALLBACK(oscar_free_name_data));
    - g_free(message);
    - }
    - } else {
    - gchar *tmp = g_strescape(args->msg, NULL);
    - purple_debug_error("oscar", "Unknown syntax parsing "
    - "ICQ buddies. args->msg=%s\n", tmp);
    - g_free(tmp);
    - }
    - g_strfreev(text);
    - }
    - } break;
    -
    - case 0x1a: { /* Handle SMS or someone has sent you a greeting card or requested buddies? */
    - ByteStream qbs;
    - guint16 smstype;
    - guint32 taglen, smslen;
    - char *tagstr = NULL, *smsmsg = NULL;
    - xmlnode *xmlroot = NULL, *xmltmp = NULL;
    - gchar *uin = NULL, *message = NULL;
    -
    - /* From libicq2000-0.3.2/src/ICQ.cpp */
    - byte_stream_init(&qbs, (guint8 *)args->msg, args->msglen);
    - byte_stream_advance(&qbs, 21);
    - /* expected: 01 00 00 20 00 0e 28 f6 00 11 e7 d3 11 bc f3 00 04 ac 96 9d c2 | 00 00 | 06 00 00 00 | 49 43 51 53 43 53 ...*/
    - /* unexpected: 00 00 26 00 81 1a 18 bc 0e 6c 18 47 a5 91 6f 18 dc c7 6f 1a | 00 00 | 0d 00 00 00 | 49 43 51 57 65 62 4d 65 73 73 61 67 65 ... */
    - smstype = byte_stream_getle16(&qbs);
    - if (smstype != 0)
    - break;
    - taglen = byte_stream_getle32(&qbs);
    - if (taglen > 2000) {
    - /* Avoid trying to allocate large amounts of memory, in
    - case we get something unexpected. */
    - break;
    - }
    - tagstr = byte_stream_getstr(&qbs, taglen);
    - if (tagstr == NULL)
    - break;
    - byte_stream_advance(&qbs, 3);
    - byte_stream_advance(&qbs, 4);
    - smslen = byte_stream_getle32(&qbs);
    - if (smslen > 2000) {
    - /* Avoid trying to allocate large amounts of memory, in
    - case we get something unexpected. */
    - g_free(tagstr);
    - break;
    - }
    - smsmsg = byte_stream_getstr(&qbs, smslen);
    -
    - /* Check if this is an SMS being sent from server */
    - if ((smstype == 0) && (purple_strequal(tagstr, "ICQSMS")) && (smsmsg != NULL))
    - {
    - xmlroot = xmlnode_from_str(smsmsg, -1);
    - if (xmlroot != NULL)
    - {
    - xmltmp = xmlnode_get_child(xmlroot, "sender");
    - if (xmltmp != NULL)
    - uin = xmlnode_get_data(xmltmp);
    -
    - xmltmp = xmlnode_get_child(xmlroot, "text");
    - if (xmltmp != NULL)
    - message = xmlnode_get_data(xmltmp);
    -
    - if ((uin != NULL) && (message != NULL))
    - serv_got_im(gc, uin, message, 0, time(NULL));
    -
    - g_free(uin);
    - g_free(message);
    - xmlnode_free(xmlroot);
    - }
    - }
    - g_free(tagstr);
    - g_free(smsmsg);
    - } break;
    -
    - default: {
    - purple_debug_info("oscar",
    - "Received a channel 4 message of unknown type "
    - "(type 0x%02hhx).\n", args->type);
    - } break;
    - }
    -
    - g_strfreev(msg1);
    - g_strfreev(msg2);
    -
    - return 1;
    -}
    -
    -static int purple_parse_incoming_im(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - guint16 channel;
    - int ret = 0;
    - aim_userinfo_t *userinfo;
    - va_list ap;
    -
    - va_start(ap, fr);
    - channel = (guint16)va_arg(ap, unsigned int);
    - userinfo = va_arg(ap, aim_userinfo_t *);
    -
    - switch (channel) {
    - case 1: { /* standard message */
    - struct aim_incomingim_ch1_args *args;
    - args = va_arg(ap, struct aim_incomingim_ch1_args *);
    - ret = incomingim_chan1(od, conn, userinfo, args);
    - } break;
    -
    - case 2: { /* rendezvous */
    - IcbmArgsCh2 *args;
    - args = va_arg(ap, IcbmArgsCh2 *);
    - ret = incomingim_chan2(od, conn, userinfo, args);
    - } break;
    -
    - case 4: { /* ICQ */
    - struct aim_incomingim_ch4_args *args;
    - args = va_arg(ap, struct aim_incomingim_ch4_args *);
    - ret = incomingim_chan4(od, conn, userinfo, args, 0);
    - } break;
    -
    - default: {
    - purple_debug_warning("oscar",
    - "ICBM received on unsupported channel (channel "
    - "0x%04hx).", channel);
    - } break;
    - }
    -
    - va_end(ap);
    -
    - return ret;
    -}
    -
    -static int purple_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - char *buf;
    - va_list ap;
    - guint16 nummissed, reason;
    - aim_userinfo_t *userinfo;
    -
    - va_start(ap, fr);
    - va_arg(ap, unsigned int); /* guint16 chan */
    - userinfo = va_arg(ap, aim_userinfo_t *);
    - nummissed = (guint16)va_arg(ap, unsigned int);
    - reason = (guint16)va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - switch(reason) {
    - case 0: /* Invalid (0) */
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s because it was invalid.",
    - "You missed %hu messages from %s because they were invalid.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - case 1: /* Message too large */
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s because it was too large.",
    - "You missed %hu messages from %s because they were too large.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - case 2: /* Rate exceeded */
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s because the rate limit has been exceeded.",
    - "You missed %hu messages from %s because the rate limit has been exceeded.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - case 3: /* Evil Sender */
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s because his/her warning level is too high.",
    - "You missed %hu messages from %s because his/her warning level is too high.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - case 4: /* Evil Receiver */
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s because your warning level is too high.",
    - "You missed %hu messages from %s because your warning level is too high.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - default:
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s for an unknown reason.",
    - "You missed %hu messages from %s for an unknown reason.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - }
    -
    - if (!purple_conv_present_error(userinfo->bn, account, buf))
    - purple_notify_error(od->gc, NULL, buf, NULL);
    - g_free(buf);
    -
    - return 1;
    -}
    -
    -static int
    -purple_parse_clientauto_ch2(OscarData *od, const char *who, guint16 reason, const guchar *cookie)
    -{
    - if (reason == 0x0003)
    - {
    - /* Rendezvous was refused. */
    - PeerConnection *conn;
    -
    - conn = peer_connection_find_by_cookie(od, who, cookie);
    -
    - if (conn == NULL)
    - {
    - purple_debug_info("oscar", "Received a rendezvous cancel message "
    - "for a nonexistent connection from %s.\n", who);
    - }
    - else
    - {
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_REFUSED, NULL);
    - }
    - }
    - else
    - {
    - purple_debug_warning("oscar", "Received an unknown rendezvous "
    - "message from %s. Type 0x%04hx\n", who, reason);
    - }
    -
    - return 0;
    -}
    -
    -static int purple_parse_clientauto_ch4(OscarData *od, char *who, guint16 reason, guint32 state, char *msg) {
    - PurpleConnection *gc = od->gc;
    -
    - switch(reason) {
    - case 0x0003: { /* Reply from an ICQ status message request */
    - char *statusmsg, **splitmsg;
    - PurpleNotifyUserInfo *user_info;
    -
    - /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
    - statusmsg = oscar_icqstatus(state);
    - splitmsg = g_strsplit(msg, "\r\n", 0);
    -
    - user_info = purple_notify_user_info_new();
    -
    - purple_notify_user_info_add_pair(user_info, _("UIN"), who);
    - purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
    - purple_notify_user_info_add_section_break(user_info);
    - purple_notify_user_info_add_pair(user_info, NULL, g_strjoinv("<BR>", splitmsg));
    -
    - g_free(statusmsg);
    - g_strfreev(splitmsg);
    -
    - purple_notify_userinfo(gc, who, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    -
    - } break;
    -
    - case 0x0006: { /* Reply from an ICQ status message request */
    - char *statusmsg, **splitmsg;
    - PurpleNotifyUserInfo *user_info;
    -
    - /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
    - statusmsg = oscar_icqstatus(state);
    - splitmsg = g_strsplit(msg, "\r\n", 0);
    -
    - user_info = purple_notify_user_info_new();
    -
    - purple_notify_user_info_add_pair(user_info, _("UIN"), who);
    - purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
    - purple_notify_user_info_add_section_break(user_info);
    - purple_notify_user_info_add_pair(user_info, NULL, g_strjoinv("<BR>", splitmsg));
    -
    - g_free(statusmsg);
    - g_strfreev(splitmsg);
    -
    - purple_notify_userinfo(gc, who, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    -
    - } break;
    -
    - default: {
    - purple_debug_warning("oscar",
    - "Received an unknown client auto-response from %s. "
    - "Type 0x%04hx\n", who, reason);
    - } break;
    - } /* end of switch */
    -
    - return 0;
    -}
    -
    -static int purple_parse_clientauto(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - guint16 chan, reason;
    - char *who;
    - int ret = 1;
    -
    - va_start(ap, fr);
    - chan = (guint16)va_arg(ap, unsigned int);
    - who = va_arg(ap, char *);
    - reason = (guint16)va_arg(ap, unsigned int);
    -
    - if (chan == 0x0002) { /* File transfer declined */
    - guchar *cookie = va_arg(ap, guchar *);
    - ret = purple_parse_clientauto_ch2(od, who, reason, cookie);
    - } else if (chan == 0x0004) { /* ICQ message */
    - guint32 state = 0;
    - char *msg = NULL;
    - if (reason == 0x0003) {
    - state = va_arg(ap, guint32);
    - msg = va_arg(ap, char *);
    - }
    - ret = purple_parse_clientauto_ch4(od, who, reason, state, msg);
    - }
    -
    - va_end(ap);
    -
    - return ret;
    -}
    -
    -static int purple_parse_genericerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - guint16 reason;
    -
    - va_start(ap, fr);
    - reason = (guint16) va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - purple_debug_error("oscar", "snac threw error (reason 0x%04hx: %s)\n",
    - reason, oscar_get_msgerr_reason(reason));
    - return 1;
    -}
    -
    -static int purple_parse_mtn(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - guint16 channel, event;
    - char *bn;
    -
    - va_start(ap, fr);
    - channel = (guint16) va_arg(ap, unsigned int);
    - bn = va_arg(ap, char *);
    - event = (guint16) va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - switch (event) {
    - case 0x0000: { /* Text has been cleared */
    - serv_got_typing_stopped(gc, bn);
    - } break;
    -
    - case 0x0001: { /* Paused typing */
    - serv_got_typing(gc, bn, 0, PURPLE_TYPED);
    - } break;
    -
    - case 0x0002: { /* Typing */
    - serv_got_typing(gc, bn, 0, PURPLE_TYPING);
    - } break;
    -
    - case 0x000f: { /* Closed IM window */
    - serv_got_typing_stopped(gc, bn);
    - } break;
    -
    - default: {
    - purple_debug_info("oscar", "Received unknown typing "
    - "notification message from %s. Channel is 0x%04x "
    - "and event is 0x%04hx.\n", bn, channel, event);
    - } break;
    - }
    -
    - return 1;
    -}
    -
    -static int purple_parse_motd(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - char *msg;
    - guint16 id;
    - va_list ap;
    -
    - va_start(ap, fr);
    - id = (guint16) va_arg(ap, unsigned int);
    - msg = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "MOTD: %s (%hu)\n", msg ? msg : "Unknown", id);
    - if (id < 4)
    - purple_notify_warning(od->gc, NULL,
    - _("Your AIM connection may be lost."), NULL);
    -
    - return 1;
    -}
    -
    -static int purple_chatnav_info(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - guint16 type;
    -
    - va_start(ap, fr);
    - type = (guint16) va_arg(ap, unsigned int);
    -
    - switch(type) {
    - case 0x0002: {
    - GString *msg = g_string_new("");
    - guint8 maxrooms;
    - struct aim_chat_exchangeinfo *exchanges;
    - int exchangecount, i;
    -
    - maxrooms = (guint8) va_arg(ap, unsigned int);
    - exchangecount = va_arg(ap, int);
    - exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
    -
    - g_string_append_printf(msg, "chat info: Max Concurrent Rooms: %hhd, Exchange List (%d total): ", maxrooms, exchangecount);
    - for (i = 0; i < exchangecount; i++) {
    - g_string_append_printf(msg, "%hu", exchanges[i].number);
    - if (exchanges[i].name) {
    - g_string_append_printf(msg, " %s", exchanges[i].name);
    - }
    - g_string_append(msg, ", ");
    - }
    - purple_debug_misc("oscar", "%s\n", msg->str);
    - g_string_free(msg, TRUE);
    -
    - while (od->create_rooms) {
    - struct create_room *cr = od->create_rooms->data;
    - purple_debug_info("oscar",
    - "creating room %s\n", cr->name);
    - aim_chatnav_createroom(od, conn, cr->name, cr->exchange);
    - g_free(cr->name);
    - od->create_rooms = g_slist_remove(od->create_rooms, cr);
    - g_free(cr);
    - }
    - }
    - break;
    - case 0x0008: {
    - char *fqcn, *name, *ck;
    - guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
    - guint8 createperms;
    - guint32 createtime;
    -
    - fqcn = va_arg(ap, char *);
    - instance = (guint16)va_arg(ap, unsigned int);
    - exchange = (guint16)va_arg(ap, unsigned int);
    - flags = (guint16)va_arg(ap, unsigned int);
    - createtime = va_arg(ap, guint32);
    - maxmsglen = (guint16)va_arg(ap, unsigned int);
    - maxoccupancy = (guint16)va_arg(ap, unsigned int);
    - createperms = (guint8)va_arg(ap, unsigned int);
    - unknown = (guint16)va_arg(ap, unsigned int);
    - name = va_arg(ap, char *);
    - ck = va_arg(ap, char *);
    -
    - purple_debug_misc("oscar",
    - "created room: %s %hu %hu %hu %u %hu %hu %hhu %hu %s %s\n",
    - fqcn ? fqcn : "(null)", exchange, instance, flags, createtime,
    - maxmsglen, maxoccupancy, createperms, unknown,
    - name ? name : "(null)", ck);
    - aim_chat_join(od, exchange, ck, instance);
    - }
    - break;
    - default:
    - purple_debug_warning("oscar",
    - "chatnav info: unknown type (%04hx)\n", type);
    - break;
    - }
    -
    - va_end(ap);
    -
    - return 1;
    -}
    -
    -static int purple_conv_chat_join(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - int count, i;
    - aim_userinfo_t *info;
    - PurpleConnection *gc = od->gc;
    -
    - struct chat_connection *c = NULL;
    -
    - va_start(ap, fr);
    - count = va_arg(ap, int);
    - info = va_arg(ap, aim_userinfo_t *);
    - va_end(ap);
    -
    - c = find_oscar_chat_by_conn(gc, conn);
    - if (!c)
    - return 1;
    -
    - for (i = 0; i < count; i++)
    - purple_conv_chat_add_user(PURPLE_CONV_CHAT(c->conv), info[i].bn, NULL, PURPLE_CBFLAGS_NONE, TRUE);
    -
    - return 1;
    -}
    -
    -static int purple_conv_chat_leave(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - int count, i;
    - aim_userinfo_t *info;
    - PurpleConnection *gc = od->gc;
    -
    - struct chat_connection *c = NULL;
    -
    - va_start(ap, fr);
    - count = va_arg(ap, int);
    - info = va_arg(ap, aim_userinfo_t *);
    - va_end(ap);
    -
    - c = find_oscar_chat_by_conn(gc, conn);
    - if (!c)
    - return 1;
    -
    - for (i = 0; i < count; i++)
    - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c->conv), info[i].bn, NULL);
    -
    - return 1;
    -}
    -
    -static int purple_conv_chat_info_update(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - guint16 maxmsglen, maxvisiblemsglen;
    - PurpleConnection *gc = od->gc;
    - struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
    -
    - if (!ccon)
    - return 1;
    -
    - va_start(ap, fr);
    - maxmsglen = (guint16)va_arg(ap, unsigned int);
    - maxvisiblemsglen = (guint16)va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "inside chat_info_update (maxmsglen = %hu, maxvislen = %hu)\n",
    - maxmsglen, maxvisiblemsglen);
    -
    - ccon->maxlen = maxmsglen;
    - ccon->maxvis = maxvisiblemsglen;
    -
    - return 1;
    -}
    -
    -static int purple_conv_chat_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
    - gchar *utf8;
    - va_list ap;
    - aim_userinfo_t *info;
    - int len;
    - char *msg;
    - char *charset;
    -
    - if (!ccon)
    - return 1;
    -
    - va_start(ap, fr);
    - info = va_arg(ap, aim_userinfo_t *);
    - len = va_arg(ap, int);
    - msg = va_arg(ap, char *);
    - charset = va_arg(ap, char *);
    - va_end(ap);
    -
    - utf8 = oscar_encoding_to_utf8(charset, msg, len);
    - serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time(NULL));
    - g_free(utf8);
    -
    - return 1;
    -}
    -
    -static int purple_email_parseupdate(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - struct aim_emailinfo *emailinfo;
    - int havenewmail;
    - char *alertitle, *alerturl;
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - emailinfo = va_arg(ap, struct aim_emailinfo *);
    - havenewmail = va_arg(ap, int);
    - alertitle = va_arg(ap, char *);
    - alerturl = va_arg(ap, char *);
    - va_end(ap);
    -
    - if (account != NULL && emailinfo != NULL && purple_account_get_check_mail(account) &&
    - emailinfo->unread && havenewmail) {
    - gchar *to = g_strdup_printf("%s%s%s",
    - purple_account_get_username(account),
    - emailinfo->domain ? "@" : "",
    - emailinfo->domain ? emailinfo->domain : "");
    - const char *tos[2] = { to };
    - const char *urls[2] = { emailinfo->url };
    - purple_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL,
    - tos, urls, NULL, NULL);
    - g_free(to);
    - }
    -
    - if (alertitle)
    - purple_debug_misc("oscar", "Got an alert '%s' %s\n", alertitle, alerturl ? alerturl : "");
    -
    - return 1;
    -}
    -
    -static int purple_icon_parseicon(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - char *bn;
    - guint8 *iconcsum, *icon;
    - guint16 iconcsumlen, iconlen;
    -
    - va_start(ap, fr);
    - bn = va_arg(ap, char *);
    - va_arg(ap, int); /* iconsumtype */
    - iconcsum = va_arg(ap, guint8 *);
    - iconcsumlen = va_arg(ap, int);
    - icon = va_arg(ap, guint8 *);
    - iconlen = va_arg(ap, int);
    - va_end(ap);
    -
    - /*
    - * Some AIM clients will send a blank GIF image with iconlen 90 when
    - * no icon is set. Ignore these.
    - */
    - if ((iconlen > 0) && (iconlen != 90)) {
    - char *b16 = purple_base16_encode(iconcsum, iconcsumlen);
    - purple_buddy_icons_set_for_user(purple_connection_get_account(gc),
    - bn, g_memdup2(icon, iconlen), iconlen, b16);
    - g_free(b16);
    - }
    -
    - return 1;
    -}
    -
    -static void
    -purple_icons_fetch(PurpleConnection *gc)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - aim_userinfo_t *userinfo;
    - FlapConnection *conn;
    -
    - conn = flap_connection_getbytype(od, SNAC_FAMILY_BART);
    - if (!conn) {
    - if (!od->iconconnecting) {
    - aim_srv_requestnew(od, SNAC_FAMILY_BART);
    - od->iconconnecting = TRUE;
    - }
    - return;
    - }
    -
    - if (od->set_icon) {
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
    - if (img == NULL) {
    - aim_ssi_delicon(od);
    - } else {
    - purple_debug_info("oscar",
    - "Uploading icon to icon server\n");
    - aim_bart_upload(od, purple_imgstore_get_data(img),
    - purple_imgstore_get_size(img));
    - purple_imgstore_unref(img);
    - }
    - od->set_icon = FALSE;
    - }
    -
    - while (od->requesticon != NULL)
    - {
    - userinfo = aim_locate_finduserinfo(od, (char *)od->requesticon->data);
    - if ((userinfo != NULL) && (userinfo->iconcsumlen > 0))
    - aim_bart_request(od, od->requesticon->data, userinfo->iconcsumtype, userinfo->iconcsum, userinfo->iconcsumlen);
    -
    - g_free(od->requesticon->data);
    - od->requesticon = g_slist_delete_link(od->requesticon, od->requesticon);
    - }
    -
    - purple_debug_misc("oscar", "no more icons to request\n");
    -}
    -
    -static int purple_selfinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - aim_userinfo_t *info;
    -
    - va_start(ap, fr);
    - info = va_arg(ap, aim_userinfo_t *);
    - va_end(ap);
    -
    - purple_connection_set_display_name(od->gc, info->bn);
    -
    - return 1;
    -}
    -
    -static int purple_connerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - guint16 code;
    - char *msg;
    -
    - va_start(ap, fr);
    - code = (guint16)va_arg(ap, int);
    - msg = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_info("oscar", "Disconnected. Code is 0x%04x and msg is %s\n",
    - code, (msg != NULL ? msg : ""));
    -
    - g_return_val_if_fail(conn != NULL, 1);
    -
    - if (conn->type == SNAC_FAMILY_CHAT) {
    - struct chat_connection *cc;
    - PurpleConversation *conv = NULL;
    -
    - cc = find_oscar_chat_by_conn(gc, conn);
    - if (cc != NULL)
    - {
    - conv = purple_find_chat(gc, cc->id);
    -
    - if (conv != NULL)
    - {
    - /*
    - * TOOD: Have flap_connection_destroy_cb() send us the
    - * error message stored in 'tmp', which should be
    - * human-friendly, and print that to the chat room.
    - */
    - gchar *buf;
    - buf = g_strdup_printf(_("You have been disconnected from chat "
    - "room %s."), cc->name);
    - purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_ERROR, time(NULL));
    - g_free(buf);
    - }
    - oscar_chat_kill(gc, cc);
    - }
    - }
    -
    - return 1;
    -}
    -
    -static int purple_parse_locaterights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - va_list ap;
    - guint16 maxsiglen;
    -
    - va_start(ap, fr);
    - maxsiglen = (guint16) va_arg(ap, int);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "locate rights: max sig len = %d\n", maxsiglen);
    -
    - od->rights.maxsiglen = od->rights.maxawaymsglen = (guint)maxsiglen;
    -
    - aim_locate_setcaps(od, purple_caps);
    - oscar_set_info_and_status(account, TRUE, account->user_info, TRUE,
    - purple_account_get_active_status(account));
    -
    - return 1;
    -}
    -
    -static int purple_parse_buddyrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - guint16 maxbuddies, maxwatchers;
    -
    - va_start(ap, fr);
    - maxbuddies = (guint16) va_arg(ap, unsigned int);
    - maxwatchers = (guint16) va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "buddy list rights: Max buddies = %hu / Max watchers = %hu\n", maxbuddies, maxwatchers);
    -
    - od->rights.maxbuddies = (guint)maxbuddies;
    - od->rights.maxwatchers = (guint)maxwatchers;
    -
    - return 1;
    -}
    -
    -static void oscar_format_username(PurpleConnection *gc, const char *new_display_name)
    -{
    - OscarData *od;
    - const char *old_display_name, *username;
    - char *tmp, *at_sign;
    -
    - old_display_name = purple_connection_get_display_name(gc);
    - if (old_display_name && strchr(old_display_name, '@')) {
    - purple_debug_info("oscar", "Cowardly refusing to attempt to format "
    - "screen name because the current formatting according to "
    - "the server (%s) appears to be an email address\n",
    - old_display_name);
    - return;
    - }
    -
    - username = purple_account_get_username(purple_connection_get_account(gc));
    - if (oscar_util_name_compare(username, new_display_name)) {
    - purple_notify_error(gc, NULL, _("The new formatting is invalid."),
    - _("Username formatting can change only capitalization and whitespace."));
    - return;
    - }
    -
    - tmp = g_strdup(new_display_name);
    -
    - /*
    - * If our local username is an email address then strip off the domain.
    - * This allows formatting to work if the user entered their username as
    - * 'something@aim.com' or possibly other AOL-owned domains.
    - */
    - at_sign = strchr(tmp, '@');
    - if (at_sign)
    - at_sign[0] = '\0';
    -
    - od = purple_connection_get_protocol_data(gc);
    - if (!flap_connection_getbytype(od, SNAC_FAMILY_ADMIN)) {
    - /* We don't have a connection to an "admin" server. Make one. */
    - od->setnick = TRUE;
    - g_free(od->newformatting);
    - od->newformatting = tmp;
    - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
    - } else {
    - aim_admin_setnick(od, flap_connection_getbytype(od, SNAC_FAMILY_ADMIN), tmp);
    - g_free(tmp);
    - }
    -}
    -
    -static int purple_bosrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PurpleStatus *status;
    - gboolean is_available;
    - PurplePresence *presence;
    - const char *username, *message, *itmsurl;
    - char *tmp;
    - va_list ap;
    - guint16 maxpermits, maxdenies;
    -
    - gc = od->gc;
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - maxpermits = (guint16) va_arg(ap, unsigned int);
    - maxdenies = (guint16) va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "BOS rights: Max permit = %hu / Max deny = %hu\n", maxpermits, maxdenies);
    -
    - od->rights.maxpermits = (guint)maxpermits;
    - od->rights.maxdenies = (guint)maxdenies;
    -
    - purple_debug_info("oscar", "buddy list loaded\n");
    -
    - if (purple_account_get_user_info(account) != NULL)
    - serv_set_info(gc, purple_account_get_user_info(account));
    -
    - username = purple_account_get_username(account);
    - if (!od->icq && !purple_strequal(username, purple_connection_get_display_name(gc))) {
    - /*
    - * Format the username for AIM accounts if it's different
    - * than what's currently set.
    - */
    - oscar_format_username(gc, username);
    - }
    -
    - /* Set our available message based on the current status */
    - status = purple_account_get_active_status(account);
    - is_available = purple_status_is_available(status);
    - if (is_available)
    - message = purple_status_get_attr_string(status, "message");
    - else
    - message = NULL;
    - tmp = purple_markup_strip_html(message);
    - itmsurl = purple_status_get_attr_string(status, "itmsurl");
    - aim_srv_setextrainfo(od, FALSE, 0, is_available, tmp, itmsurl);
    - aim_srv_set_dc_info(od);
    - g_free(tmp);
    -
    - presence = purple_status_get_presence(status);
    - aim_srv_setidle(od, !purple_presence_is_idle(presence) ? 0 : time(NULL) - purple_presence_get_idle_time(presence));
    -
    - if (od->icq) {
    - oscar_set_extended_status(gc);
    - aim_icq_setsecurity(od,
    - purple_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION),
    - purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE));
    - }
    -
    - aim_srv_requestnew(od, SNAC_FAMILY_ALERT);
    - aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV);
    -
    - od->bos.have_rights = TRUE;
    -
    - /*
    - * If we've already received our feedbag data then we're not waiting on
    - * anything else, so send the server clientready.
    - *
    - * Normally we get bos rights before we get our feedbag data, so this
    - * rarely (never?) happens. And I'm not sure it actually matters if we
    - * wait for bos rights before calling clientready. But it seems safer
    - * to do it this way.
    - */
    - if (od->ssi.received_data) {
    - aim_srv_clientready(od, conn);
    -
    - /* Request offline messages for AIM and ICQ */
    - aim_im_reqofflinemsgs(od);
    -
    - purple_connection_set_state(gc, PURPLE_CONNECTED);
    - }
    -
    - return 1;
    -}
    -
    -static int purple_popup(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - gchar *text;
    - va_list ap;
    - char *msg, *url;
    -
    - va_start(ap, fr);
    - msg = va_arg(ap, char *);
    - url = va_arg(ap, char *);
    - va_arg(ap, int); /* guint16 wid */
    - va_arg(ap, int); /* guint16 hei */
    - va_arg(ap, int); /* guint16 delay */
    - va_end(ap);
    -
    - text = g_strdup_printf("%s<br><a href=\"%s\">%s</a>", msg, url, url);
    - purple_notify_formatted(gc, NULL, _("Pop-Up Message"), NULL, text, NULL, NULL);
    - g_free(text);
    -
    - return 1;
    -}
    -
    -static void oscar_searchresults_add_buddy_cb(PurpleConnection *gc, GList *row, void *user_data)
    -{
    - purple_blist_request_add_buddy(purple_connection_get_account(gc),
    - g_list_nth_data(row, 0), NULL, NULL);
    -}
    -
    -static int purple_parse_searchreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleNotifySearchResults *results;
    - PurpleNotifySearchColumn *column;
    - gchar *secondary;
    - int i, num;
    - va_list ap;
    - char *email, *usernames;
    -
    - va_start(ap, fr);
    - email = va_arg(ap, char *);
    - num = va_arg(ap, int);
    - usernames = va_arg(ap, char *);
    - va_end(ap);
    -
    - results = purple_notify_searchresults_new();
    -
    - if (results == NULL) {
    - purple_debug_error("oscar", "purple_parse_searchreply: "
    - "Unable to display the search results.\n");
    - purple_notify_error(gc, NULL,
    - _("Unable to display the search results."),
    - NULL);
    - return 1;
    - }
    -
    - secondary = g_strdup_printf(
    - dngettext(PACKAGE, "The following username is associated with %s",
    - "The following usernames are associated with %s",
    - num),
    - email);
    -
    - column = purple_notify_searchresults_column_new(_("Username"));
    - purple_notify_searchresults_column_add(results, column);
    -
    - for (i = 0; i < num; i++) {
    - GList *row;
    - row = g_list_append(NULL, g_strdup(&usernames[i * (MAXSNLEN + 1)]));
    - purple_notify_searchresults_row_add(results, row);
    - }
    - purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD,
    - oscar_searchresults_add_buddy_cb);
    - purple_notify_searchresults(gc, NULL, NULL, secondary, results, NULL, NULL);
    -
    - g_free(secondary);
    -
    - return 1;
    -}
    -
    -static int purple_parse_searcherror(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - char *email;
    - char *buf;
    -
    - va_start(ap, fr);
    - email = va_arg(ap, char *);
    - va_end(ap);
    -
    - buf = g_strdup_printf(_("No results found for email address %s"), email);
    - purple_notify_error(od->gc, NULL, buf, NULL);
    - g_free(buf);
    -
    - return 1;
    -}
    -
    -static int purple_account_confirm(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - guint16 status;
    - va_list ap;
    - char msg[256];
    -
    - va_start(ap, fr);
    - status = (guint16) va_arg(ap, unsigned int); /* status code of confirmation request */
    - va_end(ap);
    -
    - purple_debug_info("oscar",
    - "account confirmation returned status 0x%04x (%s)\n", status,
    - status ? "unknown" : "email sent");
    - if (!status) {
    - g_snprintf(msg, sizeof(msg), _("You should receive an email asking to confirm %s."),
    - purple_account_get_username(purple_connection_get_account(gc)));
    - purple_notify_info(gc, NULL, _("Account Confirmation Requested"), msg);
    - }
    -
    - return 1;
    -}
    -
    -static int purple_info_change(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - guint16 perms, err;
    - char *url, *bn, *email;
    - int change;
    -
    - va_start(ap, fr);
    - change = va_arg(ap, int);
    - perms = (guint16) va_arg(ap, unsigned int);
    - err = (guint16) va_arg(ap, unsigned int);
    - url = va_arg(ap, char *);
    - bn = va_arg(ap, char *);
    - email = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, bn=%s, email=%s\n",
    - change ? "change" : "request", perms, err,
    - (url != NULL) ? url : "(null)",
    - (bn != NULL) ? bn : "(null)",
    - (email != NULL) ? email : "(null)");
    -
    - if ((err > 0) && (url != NULL)) {
    - char *dialog_msg;
    -
    - if (err == 0x0001)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because the requested name differs from the original."), err);
    - else if (err == 0x0006)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because it is invalid."), err);
    - else if (err == 0x00b)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because the requested name is too long."), err);
    - else if (err == 0x001d)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because there is already a request pending for this username."), err);
    - else if (err == 0x0021)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address has too many usernames associated with it."), err);
    - else if (err == 0x0023)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address is invalid."), err);
    - else
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unknown error."), err);
    - purple_notify_error(gc, NULL,
    - _("Error Changing Account Info"), dialog_msg);
    - g_free(dialog_msg);
    - return 1;
    - }
    -
    - if (email != NULL) {
    - char *dialog_msg = g_strdup_printf(_("The email address for %s is %s"),
    - purple_account_get_username(purple_connection_get_account(gc)), email);
    - purple_notify_info(gc, NULL, _("Account Info"), dialog_msg);
    - g_free(dialog_msg);
    - }
    -
    - return 1;
    -}
    -
    -void
    -oscar_keepalive(PurpleConnection *gc)
    -{
    - OscarData *od;
    - GSList *l;
    -
    - od = purple_connection_get_protocol_data(gc);
    - for (l = od->oscar_connections; l; l = l->next) {
    - flap_connection_send_keepalive(od, l->data);
    - }
    -}
    -
    -unsigned int
    -oscar_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state)
    -{
    - OscarData *od;
    - PeerConnection *conn;
    -
    - od = purple_connection_get_protocol_data(gc);
    - conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
    -
    - if ((conn != NULL) && (conn->ready))
    - {
    - peer_odc_send_typing(conn, state);
    - }
    - else {
    - /* Don't send if this turkey is in our deny list */
    - GSList *list;
    - for (list=gc->account->deny; (list && oscar_util_name_compare(name, list->data)); list=list->next);
    - if (!list) {
    - struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(gc->account, name));
    - if (bi && bi->typingnot) {
    - if (state == PURPLE_TYPING)
    - aim_im_sendmtn(od, 0x0001, name, 0x0002);
    - else if (state == PURPLE_TYPED)
    - aim_im_sendmtn(od, 0x0001, name, 0x0001);
    - else
    - aim_im_sendmtn(od, 0x0001, name, 0x0000);
    - }
    - }
    - }
    - return 0;
    -}
    -
    -/* TODO: Move this into odc.c! */
    -static void
    -purple_odc_send_im(PeerConnection *conn, const char *message, PurpleMessageFlags imflags)
    -{
    - GString *msg;
    - GString *data;
    - gchar *tmp;
    - gsize tmplen;
    - guint16 charset;
    - GData *attribs;
    - const char *start, *end, *last;
    - int oscar_id = 0;
    -
    - msg = g_string_new("<HTML><BODY>");
    - data = g_string_new("<BINARY>");
    - last = message;
    -
    - /* for each valid IMG tag... */
    - while (last && *last && purple_markup_find_tag("img", last, &start, &end, &attribs))
    - {
    - PurpleStoredImage *image = NULL;
    - const char *id;
    -
    - if (start - last) {
    - g_string_append_len(msg, last, start - last);
    - }
    -
    - id = g_datalist_get_data(&attribs, "id");
    -
    - /* ... if it refers to a valid purple image ... */
    - if (id && (image = purple_imgstore_find_by_id(atoi(id)))) {
    - /* ... append the message from start to the tag ... */
    - unsigned long size = purple_imgstore_get_size(image);
    - const char *filename = purple_imgstore_get_filename(image);
    - gconstpointer imgdata = purple_imgstore_get_data(image);
    -
    - oscar_id++;
    -
    - /* ... insert a new img tag with the oscar id ... */
    - if (filename)
    - g_string_append_printf(msg,
    - "<IMG SRC=\"%s\" ID=\"%d\" DATASIZE=\"%lu\">",
    - filename, oscar_id, size);
    - else
    - g_string_append_printf(msg,
    - "<IMG ID=\"%d\" DATASIZE=\"%lu\">",
    - oscar_id, size);
    -
    - /* ... and append the data to the binary section ... */
    - g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%lu\">",
    - oscar_id, size);
    - g_string_append_len(data, imgdata, size);
    - g_string_append(data, "</DATA>");
    - }
    - /* If the tag is invalid, skip it, thus no else here */
    -
    - g_datalist_clear(&attribs);
    -
    - /* continue from the end of the tag */
    - last = end + 1;
    - }
    -
    - /* append any remaining message data */
    - if (last && *last)
    - g_string_append(msg, last);
    -
    - g_string_append(msg, "</BODY></HTML>");
    -
    - /* Convert the message to a good encoding */
    - tmp = oscar_encode_im(msg->str, &tmplen, &charset, NULL);
    - g_string_free(msg, TRUE);
    - msg = g_string_new_len(tmp, tmplen);
    - g_free(tmp);
    -
    - /* Append any binary data that we may have */
    - if (oscar_id) {
    - msg = g_string_append_len(msg, data->str, data->len);
    - msg = g_string_append(msg, "</BINARY>");
    - }
    - g_string_free(data, TRUE);
    -
    - purple_debug_info("oscar", "sending direct IM %s using charset %i", msg->str, charset);
    -
    - peer_odc_send_im(conn, msg->str, msg->len, charset,
    - imflags & PURPLE_MESSAGE_AUTO_RESP);
    - g_string_free(msg, TRUE);
    -}
    -
    -int
    -oscar_send_im(PurpleConnection *gc, const char *name, const char *message, PurpleMessageFlags imflags)
    -{
    - OscarData *od;
    - PurpleAccount *account;
    - PeerConnection *conn;
    - int ret;
    - char *tmp1, *tmp2;
    - gboolean is_sms, is_html;
    -
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    - ret = 0;
    -
    - is_sms = oscar_util_valid_name_sms(name);
    -
    - if (od->icq && is_sms) {
    - /*
    - * We're sending to a phone number and this is ICQ,
    - * so send the message as an SMS using aim_icq_sendsms()
    - */
    - int ret;
    - purple_debug_info("oscar", "Sending SMS to %s.\n", name);
    - ret = aim_icq_sendsms(od, name, message, purple_account_get_username(account));
    - return (ret >= 0 ? 1 : ret);
    - }
    -
    - if (imflags & PURPLE_MESSAGE_AUTO_RESP)
    - tmp1 = oscar_util_format_string(message, name);
    - else
    - tmp1 = g_strdup(message);
    -
    - conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
    - if ((conn != NULL) && (conn->ready))
    - {
    - /* If we're directly connected, send a direct IM */
    - purple_debug_info("oscar", "Sending direct IM with flags %i\n", imflags);
    - purple_odc_send_im(conn, tmp1, imflags);
    - } else {
    - struct buddyinfo *bi;
    - struct aim_sendimext_args args;
    - PurpleConversation *conv;
    - PurpleStoredImage *img;
    - PurpleBuddy *buddy;
    -
    - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account);
    -
    - if (strstr(tmp1, "<IMG "))
    - purple_conversation_write(conv, "",
    - _("Your IM Image was not sent. "
    - "You must be Direct Connected to send IM Images."),
    - PURPLE_MESSAGE_ERROR, time(NULL));
    -
    - buddy = purple_find_buddy(account, name);
    -
    - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, name));
    - if (!bi) {
    - bi = g_new0(struct buddyinfo, 1);
    - g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, name)), bi);
    - }
    -
    - args.flags = 0;
    -
    - if (!is_sms && (!buddy || !PURPLE_BUDDY_IS_ONLINE(buddy)))
    - args.flags |= AIM_IMFLAGS_OFFLINE;
    -
    - if (od->icq) {
    - args.features = features_icq;
    - args.featureslen = sizeof(features_icq);
    - } else {
    - args.features = features_aim;
    - args.featureslen = sizeof(features_aim);
    -
    - if (imflags & PURPLE_MESSAGE_AUTO_RESP)
    - args.flags |= AIM_IMFLAGS_AWAY;
    - }
    -
    - if (bi->ico_need) {
    - purple_debug_info("oscar",
    - "Sending buddy icon request with message\n");
    - args.flags |= AIM_IMFLAGS_BUDDYREQ;
    - bi->ico_need = FALSE;
    - }
    -
    - img = purple_buddy_icons_find_account_icon(account);
    - if (img) {
    - gconstpointer data = purple_imgstore_get_data(img);
    - args.iconlen = purple_imgstore_get_size(img);
    - args.iconsum = aimutil_iconsum(data, args.iconlen);
    - args.iconstamp = purple_buddy_icons_get_account_icon_timestamp(account);
    -
    - if ((args.iconlen != bi->ico_me_len) || (args.iconsum != bi->ico_me_csum) || (args.iconstamp != bi->ico_me_time)) {
    - bi->ico_informed = FALSE;
    - bi->ico_sent = FALSE;
    - }
    -
    - /*
    - * TODO:
    - * For some reason sending our icon to people only works
    - * when we're the ones who initiated the conversation. If
    - * the other person sends the first IM then they never get
    - * the icon. We should fix that.
    - */
    - if (!bi->ico_informed) {
    - purple_debug_info("oscar",
    - "Claiming to have a buddy icon\n");
    - args.flags |= AIM_IMFLAGS_HASICON;
    - bi->ico_me_len = args.iconlen;
    - bi->ico_me_csum = args.iconsum;
    - bi->ico_me_time = args.iconstamp;
    - bi->ico_informed = TRUE;
    - }
    -
    - purple_imgstore_unref(img);
    - }
    -
    - args.destbn = name;
    -
    - if (oscar_util_valid_name_sms(name)) {
    - /* Messaging an SMS (mobile) user--strip HTML */
    - tmp2 = purple_markup_strip_html(tmp1);
    - is_html = FALSE;
    - } else {
    - /* ICQ 6 wants its HTML wrapped in these tags. Oblige it. */
    - tmp2 = g_strdup_printf("<HTML><BODY>%s</BODY></HTML>", tmp1);
    - is_html = TRUE;
    - }
    - g_free(tmp1);
    - tmp1 = tmp2;
    -
    - args.msg = oscar_encode_im(tmp1, &args.msglen, &args.charset, NULL);
    - if (is_html && (args.msglen > MAXMSGLEN)) {
    - /* If the length was too long, try stripping the HTML and then running it back through
    - * purple_strdup_withhtml() and the encoding process. The result may be shorter. */
    - g_free((char *)args.msg);
    -
    - tmp2 = purple_markup_strip_html(tmp1);
    - g_free(tmp1);
    -
    - /* re-escape the entities */
    - tmp1 = g_markup_escape_text(tmp2, -1);
    - g_free(tmp2);
    -
    - tmp2 = purple_strdup_withhtml(tmp1);
    - g_free(tmp1);
    - tmp1 = tmp2;
    -
    - args.msg = oscar_encode_im(tmp1, &args.msglen, &args.charset, NULL);
    - purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
    - message, (char *)args.msg);
    - }
    -
    - purple_debug_info("oscar", "Sending IM, charset=0x%04hx, length=%" G_GSIZE_FORMAT "\n", args.charset, args.msglen);
    - ret = aim_im_sendch1_ext(od, &args);
    - g_free((char *)args.msg);
    - }
    -
    - g_free(tmp1);
    -
    - if (ret >= 0)
    - return 1;
    -
    - return ret;
    -}
    -
    -/*
    - * As of 26 June 2006, ICQ users can request AIM info from
    - * everyone, and can request ICQ info from ICQ users, and
    - * AIM users can only request AIM info.
    - */
    -void oscar_get_info(PurpleConnection *gc, const char *name) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->icq && oscar_util_valid_name_icq(name))
    - aim_icq_getallinfo(od, name);
    - else
    - aim_locate_getinfoshort(od, name, 0x00000003);
    -}
    -
    -void oscar_set_idle(PurpleConnection *gc, int time) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - aim_srv_setidle(od, time);
    -}
    -
    -void
    -oscar_set_info(PurpleConnection *gc, const char *rawinfo)
    -{
    - PurpleAccount *account;
    - PurpleStatus *status;
    -
    - account = purple_connection_get_account(gc);
    - status = purple_account_get_active_status(account);
    - oscar_set_info_and_status(account, TRUE, rawinfo, FALSE, status);
    -}
    -
    -static guint32
    -oscar_get_extended_status(PurpleConnection *gc)
    -{
    - PurpleAccount *account;
    - PurpleStatus *status;
    - const gchar *status_id;
    - guint32 data = 0x00000000;
    -
    - account = purple_connection_get_account(gc);
    - status = purple_account_get_active_status(account);
    - status_id = purple_status_get_id(status);
    -
    - data |= AIM_ICQ_STATE_HIDEIP;
    - if (purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE))
    - data |= AIM_ICQ_STATE_WEBAWARE;
    -
    - if (purple_strequal(status_id, OSCAR_STATUS_ID_AVAILABLE))
    - data |= AIM_ICQ_STATE_NORMAL;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_AWAY))
    - data |= AIM_ICQ_STATE_AWAY;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_DND))
    - data |= AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_NA))
    - data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_OCCUPIED))
    - data |= AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_FREE4CHAT))
    - data |= AIM_ICQ_STATE_CHAT;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_INVISIBLE))
    - data |= AIM_ICQ_STATE_INVISIBLE;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_EVIL))
    - data |= AIM_ICQ_STATE_EVIL;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_DEPRESSION))
    - data |= AIM_ICQ_STATE_DEPRESSION;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_ATWORK))
    - data |= AIM_ICQ_STATE_ATWORK;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_ATHOME))
    - data |= AIM_ICQ_STATE_ATHOME;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_LUNCH))
    - data |= AIM_ICQ_STATE_LUNCH;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_CUSTOM))
    - data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
    -
    - return data;
    -}
    -
    -static void
    -oscar_set_extended_status(PurpleConnection *gc)
    -{
    - aim_srv_setextrainfo(purple_connection_get_protocol_data(gc), TRUE, oscar_get_extended_status(gc), FALSE, NULL, NULL);
    -}
    -
    -static void
    -oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo,
    - gboolean setstatus, PurpleStatus *status)
    -{
    - PurpleConnection *gc = purple_account_get_connection(account);
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - PurpleStatusType *status_type;
    - PurpleStatusPrimitive primitive;
    -
    - char *info_encoding = NULL;
    - char *info = NULL;
    - gsize infolen = 0;
    -
    - char *away_encoding = NULL;
    - char *away = NULL;
    - gsize awaylen = 0;
    -
    - char *status_text = NULL;
    - const char *itmsurl = NULL;
    -
    - status_type = purple_status_get_type(status);
    - primitive = purple_status_type_get_primitive(status_type);
    -
    - if (!setinfo)
    - {
    - /* Do nothing! */
    - }
    - else if (od->rights.maxsiglen == 0)
    - {
    - purple_notify_warning(gc, NULL, _("Unable to set AIM profile."),
    - _("You have probably requested to set your "
    - "profile before the login procedure completed. "
    - "Your profile remains unset; try setting it "
    - "again when you are fully connected."));
    - }
    - else if (rawinfo != NULL)
    - {
    - char *htmlinfo = purple_strdup_withhtml(rawinfo);
    - info = oscar_encode_im(htmlinfo, &infolen, NULL, &info_encoding);
    - g_free(htmlinfo);
    -
    - if (infolen > od->rights.maxsiglen)
    - {
    - gchar *errstr;
    - errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum profile length of %d byte "
    - "has been exceeded. It has been truncated for you.",
    - "The maximum profile length of %d bytes "
    - "has been exceeded. It has been truncated for you.",
    - od->rights.maxsiglen), od->rights.maxsiglen);
    - purple_notify_warning(gc, NULL, _("Profile too long."), errstr);
    - g_free(errstr);
    - }
    - }
    -
    - if (setstatus)
    - {
    - const char *status_html;
    -
    - status_html = purple_status_get_attr_string(status, "message");
    -
    - if (status_html == NULL || primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE)
    - {
    - /* This is needed for us to un-set any previous away message. */
    - away = g_strdup("");
    - }
    - else
    - {
    - gchar *linkified;
    -
    - /* We do this for icq too so that they work for old third party clients */
    - linkified = purple_markup_linkify(status_html);
    - away = oscar_encode_im(linkified, &awaylen, NULL, &away_encoding);
    - g_free(linkified);
    -
    - if (awaylen > od->rights.maxawaymsglen)
    - {
    - gchar *errstr;
    -
    - errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum away message length of %d byte "
    - "has been exceeded. It has been truncated for you.",
    - "The maximum away message length of %d bytes "
    - "has been exceeded. It has been truncated for you.",
    - od->rights.maxawaymsglen), od->rights.maxawaymsglen);
    - purple_notify_warning(gc, NULL, _("Away message too long."), errstr);
    - g_free(errstr);
    - }
    - }
    - }
    -
    - aim_locate_setprofile(od,
    - info_encoding, info, MIN(infolen, od->rights.maxsiglen),
    - away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen));
    - g_free(info);
    - g_free(away);
    -
    - if (setstatus)
    - {
    - const char *status_html;
    -
    - status_html = purple_status_get_attr_string(status, "message");
    - if (status_html != NULL)
    - {
    - status_text = purple_markup_strip_html(status_html);
    - /* If the status_text is longer than 251 characters then truncate it */
    - if (strlen(status_text) > MAXAVAILMSGLEN)
    - {
    - char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]);
    - strcpy(tmp, "...");
    - }
    - }
    -
    - itmsurl = purple_status_get_attr_string(status, "itmsurl");
    -
    - aim_srv_setextrainfo(od, TRUE, oscar_get_extended_status(gc), TRUE, status_text, itmsurl);
    - g_free(status_text);
    - }
    -}
    -
    -static void
    -oscar_set_icq_permdeny(PurpleAccount *account)
    -{
    - PurpleConnection *gc = purple_account_get_connection(account);
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - gboolean invisible = purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE);
    -
    - /*
    - * For ICQ the permit/deny setting controls who can see you
    - * online. Mimicking the official client's behavior, we use PURPLE_PRIVACY_ALLOW_USERS
    - * when our status is "invisible" and PURPLE_PRIVACY_DENY_USERS otherwise.
    - * In the former case, we are visible only to buddies on our "permanently visible" list.
    - * In the latter, we are invisible only to buddies on our "permanently invisible" list.
    - */
    - aim_ssi_setpermdeny(od, invisible ? PURPLE_PRIVACY_ALLOW_USERS : PURPLE_PRIVACY_DENY_USERS);
    -}
    -
    -void
    -oscar_set_status(PurpleAccount *account, PurpleStatus *status)
    -{
    - PurpleConnection *pc;
    - OscarData *od;
    -
    - purple_debug_info("oscar", "Set status to %s\n", purple_status_get_name(status));
    -
    - /* Either setting a new status active or setting a status inactive.
    - * (Only possible for independent status (i.e. X-Status moods.) */
    - if (!purple_status_is_active(status) && !purple_status_is_independent(status))
    - return;
    -
    - if (!purple_account_is_connected(account))
    - return;
    -
    - pc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(pc);
    -
    - /* There's no need to do the stuff below for mood updates. */
    - if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) {
    - aim_locate_setcaps(od, purple_caps);
    - return;
    - }
    -
    - if (od->icq) {
    - /* Set visibility */
    - oscar_set_icq_permdeny(account);
    - }
    -
    - /* Set the AIM-style away message for both AIM and ICQ accounts */
    - oscar_set_info_and_status(account, FALSE, NULL, TRUE, status);
    -}
    -
    -void
    -oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *msg)
    -{
    - OscarData *od;
    - PurpleAccount *account;
    - const char *bname, *gname;
    -
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    - bname = purple_buddy_get_name(buddy);
    - gname = purple_group_get_name(group);
    -
    - if (!oscar_util_valid_name(bname)) {
    - gchar *buf;
    - buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), bname);
    - if (!purple_conv_present_error(bname, account, buf))
    - purple_notify_error(gc, NULL, _("Unable to Add"), buf);
    - g_free(buf);
    -
    - /* Remove from local list */
    - purple_blist_remove_buddy(buddy);
    -
    - return;
    - }
    -
    - if (od->ssi.received_data) {
    - if (!aim_ssi_itemlist_finditem(od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY)) {
    - purple_debug_info("oscar",
    - "ssi: adding buddy %s to group %s\n", bname, gname);
    - aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, 0);
    -
    - /* Mobile users should always be online */
    - if (bname[0] == '+') {
    - purple_prpl_got_user_status(account, bname,
    - OSCAR_STATUS_ID_AVAILABLE, NULL);
    - purple_prpl_got_user_status(account, bname,
    - OSCAR_STATUS_ID_MOBILE, NULL);
    - }
    - } else if (aim_ssi_waitingforauth(od->ssi.local,
    - aim_ssi_itemlist_findparentname(od->ssi.local, bname),
    - bname)) {
    - /* Not authorized -- Re-request authorization */
    - oscar_auth_sendrequest(gc, bname, msg);
    - }
    - }
    -
    - /* XXX - Should this be done from AIM accounts, as well? */
    - if (od->icq)
    - aim_icq_getalias(od, bname, FALSE, NULL);
    -}
    -
    -void oscar_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->ssi.received_data) {
    - const char *gname = purple_group_get_name(group);
    - const char *bname = purple_buddy_get_name(buddy);
    - purple_debug_info("oscar",
    - "ssi: deleting buddy %s from group %s\n", bname, gname);
    - aim_ssi_delbuddy(od, bname, gname);
    - }
    -}
    -
    -void oscar_move_buddy(PurpleConnection *gc, const char *name, const char *old_group, const char *new_group) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->ssi.received_data && !purple_strequal(old_group, new_group)) {
    - purple_debug_info("oscar",
    - "ssi: moving buddy %s from group %s to group %s\n", name, old_group, new_group);
    - aim_ssi_movebuddy(od, old_group, new_group, name);
    - }
    -}
    -
    -void oscar_alias_buddy(PurpleConnection *gc, const char *name, const char *alias) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->ssi.received_data) {
    - char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
    - if (gname) {
    - purple_debug_info("oscar",
    - "ssi: changing the alias for buddy %s to %s\n", name, alias ? alias : "(none)");
    - aim_ssi_aliasbuddy(od, gname, name, alias);
    - }
    - }
    -}
    -
    -/*
    - * FYI, the OSCAR SSI code removes empty groups automatically.
    - */
    -void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->ssi.received_data) {
    - const char *gname = purple_group_get_name(group);
    - if (aim_ssi_itemlist_finditem(od->ssi.local, gname, NULL, AIM_SSI_TYPE_GROUP)) {
    - GList *cur, *groups = NULL;
    - PurpleAccount *account = purple_connection_get_account(gc);
    -
    - /* Make a list of what the groups each buddy is in */
    - for (cur = moved_buddies; cur != NULL; cur = cur->next) {
    - PurpleBlistNode *node = cur->data;
    - /* node is PurpleBuddy, parent is a PurpleContact.
    - * We must go two levels up to get the Group */
    - groups = g_list_append(groups,
    - purple_buddy_get_group((PurpleBuddy*)node));
    - }
    -
    - purple_account_remove_buddies(account, moved_buddies, groups);
    - purple_account_add_buddies(account, moved_buddies);
    - g_list_free(groups);
    - purple_debug_info("oscar",
    - "ssi: moved all buddies from group %s to %s\n", old_name, gname);
    - } else {
    - aim_ssi_rename_group(od, old_name, gname);
    - purple_debug_info("oscar",
    - "ssi: renamed group %s to %s\n", old_name, gname);
    - }
    - }
    -}
    -
    -void oscar_remove_group(PurpleConnection *gc, PurpleGroup *group)
    -{
    - aim_ssi_delgroup(purple_connection_get_protocol_data(gc), purple_group_get_name(group));
    -}
    -
    -static gboolean purple_ssi_rerequestdata(gpointer data) {
    - OscarData *od = data;
    -
    - aim_ssi_reqdata(od);
    -
    - return TRUE;
    -}
    -
    -static int purple_ssi_parseerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - guint16 reason;
    -
    - va_start(ap, fr);
    - reason = (guint16)va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - purple_debug_error("oscar", "ssi: SNAC error %hu\n", reason);
    -
    - if (reason == 0x0005) {
    - if (od->getblisttimer > 0)
    - purple_timeout_remove(od->getblisttimer);
    - else
    - /* We only show this error the first time it happens */
    - purple_notify_error(gc, NULL,
    - _("Unable to Retrieve Buddy List"),
    - _("The AIM servers were temporarily unable to send "
    - "your buddy list. Your buddy list is not lost, and "
    - "will probably become available in a few minutes."));
    - od->getblisttimer = purple_timeout_add_seconds(30, purple_ssi_rerequestdata, od);
    - return 1;
    - }
    -
    - return 1;
    -}
    -
    -static int purple_ssi_parserights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - int i;
    - va_list ap;
    - int numtypes;
    - guint16 *maxitems;
    - GString *msg;
    -
    - va_start(ap, fr);
    - numtypes = va_arg(ap, int);
    - maxitems = va_arg(ap, guint16 *);
    - va_end(ap);
    -
    - msg = g_string_new("ssi rights:");
    - for (i=0; i<numtypes; i++)
    - g_string_append_printf(msg, " max type 0x%04x=%hd,", i, maxitems[i]);
    - g_string_append(msg, "\n");
    - purple_debug_misc("oscar", "%s", msg->str);
    - g_string_free(msg, TRUE);
    -
    - if (numtypes >= 0)
    - od->rights.maxbuddies = maxitems[0];
    - if (numtypes >= 1)
    - od->rights.maxgroups = maxitems[1];
    - if (numtypes >= 2)
    - od->rights.maxpermits = maxitems[2];
    - if (numtypes >= 3)
    - od->rights.maxdenies = maxitems[3];
    -
    - return 1;
    -}
    -
    -static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PurpleGroup *g;
    - PurpleBuddy *b;
    - GSList *cur, *next, *buddies;
    - struct aim_ssi_item *curitem;
    - guint32 tmp;
    - PurpleStoredImage *img;
    - va_list ap;
    - guint16 deny_entry_type = aim_ssi_getdenyentrytype(od);
    -
    - gc = od->gc;
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - va_arg(ap, int); /* guint16 fmtver */
    - va_arg(ap, int); /* guint16 numitems */
    - va_arg(ap, guint32); /* timestamp */
    - va_end(ap);
    -
    - /* Don't attempt to re-request our buddy list later */
    - if (od->getblisttimer != 0) {
    - purple_timeout_remove(od->getblisttimer);
    - od->getblisttimer = 0;
    - }
    -
    - purple_debug_info("oscar", "ssi: syncing local list and server list\n");
    -
    - /* Clean the buddy list */
    - aim_ssi_cleanlist(od);
    -
    - /*** Begin code for pruning buddies from local list if they're not in server list ***/
    -
    - /* Buddies */
    - cur = NULL;
    - for (buddies = purple_find_buddies(account, NULL);
    - buddies;
    - buddies = g_slist_delete_link(buddies, buddies))
    - {
    - PurpleGroup *g;
    - const char *gname;
    - const char *bname;
    -
    - b = buddies->data;
    - g = purple_buddy_get_group(b);
    - gname = purple_group_get_name(g);
    - bname = purple_buddy_get_name(b);
    -
    - if (aim_ssi_itemlist_exists(od->ssi.local, bname)) {
    - /* If the buddy is an ICQ user then load his nickname */
    - const char *servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick");
    - char *alias;
    - const char *balias;
    - if (servernick)
    - serv_got_alias(gc, bname, servernick);
    -
    - /* Store local alias on server */
    - alias = aim_ssi_getalias(od->ssi.local, gname, bname);
    - balias = purple_buddy_get_local_buddy_alias(b);
    - if (!alias && balias && *balias)
    - aim_ssi_aliasbuddy(od, gname, bname, balias);
    - g_free(alias);
    - } else {
    - purple_debug_info("oscar",
    - "ssi: removing buddy %s from local list\n", bname);
    - /* Queue the buddy for removal from the local list */
    - cur = g_slist_prepend(cur, b);
    - }
    - }
    - while (cur != NULL) {
    - purple_blist_remove_buddy(cur->data);
    - cur = g_slist_delete_link(cur, cur);
    - }
    -
    - /* Permit list (ICQ doesn't have one) */
    - if (!od->icq) {
    - next = account->permit;
    - while (next != NULL) {
    - cur = next;
    - next = next->next;
    - if (!aim_ssi_itemlist_finditem(od->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) {
    - purple_debug_info("oscar",
    - "ssi: removing permit %s from local list\n", (const char *)cur->data);
    - purple_privacy_permit_remove(account, cur->data, TRUE);
    - }
    - }
    - }
    -
    - /* Deny list */
    - next = account->deny;
    - while (next != NULL) {
    - cur = next;
    - next = next->next;
    - if (!aim_ssi_itemlist_finditem(od->ssi.local, NULL, cur->data, deny_entry_type)) {
    - purple_debug_info("oscar",
    - "ssi: removing deny %s from local list\n", (const char *)cur->data);
    - purple_privacy_deny_remove(account, cur->data, TRUE);
    - }
    - }
    -
    - /* Presence settings (idle time visibility) */
    - tmp = aim_ssi_getpresence(od->ssi.local);
    - if (tmp != 0xFFFFFFFF) {
    - const char *idle_reporting_pref;
    - gboolean report_idle;
    -
    - idle_reporting_pref = purple_prefs_get_string("/purple/away/idle_reporting");
    - report_idle = !purple_strequal(idle_reporting_pref, "none");
    -
    - if (report_idle)
    - aim_ssi_setpresence(od, tmp | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
    - else
    - aim_ssi_setpresence(od, tmp & ~AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
    - }
    -
    - /*** End code for pruning buddies from local list ***/
    -
    - /*** Begin code for adding from server list to local list ***/
    -
    - for (curitem=od->ssi.local; curitem; curitem=curitem->next) {
    - if (curitem->name && !g_utf8_validate(curitem->name, -1, NULL)) {
    - /* Got node with invalid UTF-8 in the name. Skip it. */
    - purple_debug_warning("oscar", "ssi: server list contains item of "
    - "type 0x%04hx with a non-utf8 name\n", curitem->type);
    - continue;
    - }
    -
    - switch (curitem->type) {
    - case AIM_SSI_TYPE_BUDDY: { /* Buddy */
    - if (curitem->name) {
    - struct aim_ssi_item *groupitem;
    - char *gname, *gname_utf8, *alias, *alias_utf8;
    -
    - groupitem = aim_ssi_itemlist_find(od->ssi.local, curitem->gid, 0x0000);
    - gname = groupitem ? groupitem->name : NULL;
    - gname_utf8 = oscar_utf8_try_convert(account, od, gname);
    -
    - g = purple_find_group(gname_utf8 ? gname_utf8 : _("Orphans"));
    - if (g == NULL) {
    - g = purple_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
    - purple_blist_add_group(g, NULL);
    - }
    -
    - alias = aim_ssi_getalias(od->ssi.local, gname, curitem->name);
    - alias_utf8 = oscar_utf8_try_convert(account, od, alias);
    -
    - b = purple_find_buddy_in_group(account, curitem->name, g);
    - if (b) {
    - /* Get server stored alias */
    - purple_blist_alias_buddy(b, alias_utf8);
    - } else {
    - b = purple_buddy_new(account, curitem->name, alias_utf8);
    -
    - purple_debug_info("oscar",
    - "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname);
    - purple_blist_add_buddy(b, NULL, g, NULL);
    - }
    -
    - /* Mobile users should always be online */
    - if (curitem->name[0] == '+') {
    - purple_prpl_got_user_status(account,
    - purple_buddy_get_name(b),
    - OSCAR_STATUS_ID_AVAILABLE, NULL);
    - purple_prpl_got_user_status(account,
    - purple_buddy_get_name(b),
    - OSCAR_STATUS_ID_MOBILE, NULL);
    - }
    -
    - g_free(gname_utf8);
    - g_free(alias);
    - g_free(alias_utf8);
    - }
    - } break;
    -
    - case AIM_SSI_TYPE_GROUP: { /* Group */
    - if (curitem->name != NULL && purple_find_group(curitem->name) == NULL) {
    - g = purple_group_new(curitem->name);
    - purple_blist_add_group(g, NULL);
    - }
    - } break;
    -
    - case AIM_SSI_TYPE_PERMIT: { /* Permit buddy (unless we're on ICQ) */
    - if (!od->icq && curitem->name) {
    - for (cur = account->permit; (cur && oscar_util_name_compare(curitem->name, cur->data)); cur = cur->next);
    - if (!cur) {
    - purple_debug_info("oscar",
    - "ssi: adding permit buddy %s to local list\n", curitem->name);
    - purple_privacy_permit_add(account, curitem->name, TRUE);
    - }
    - }
    - } break;
    -
    - case AIM_SSI_TYPE_ICQDENY:
    - case AIM_SSI_TYPE_DENY: { /* Deny buddy */
    - if (curitem->type == deny_entry_type && curitem->name) {
    - for (cur = account->deny; (cur && oscar_util_name_compare(curitem->name, cur->data)); cur = cur->next);
    - if (!cur) {
    - purple_debug_info("oscar",
    - "ssi: adding deny buddy %s to local list\n", curitem->name);
    - purple_privacy_deny_add(account, curitem->name, TRUE);
    - }
    - }
    - } break;
    -
    - case AIM_SSI_TYPE_PDINFO: { /* Permit/deny setting */
    - /*
    - * We don't inherit the permit/deny setting from the server
    - * for ICQ because, for ICQ, this setting controls who can
    - * see your online status when you are invisible. Thus it is
    - * a part of your status and not really related to blocking.
    - */
    - if (!od->icq && curitem->data) {
    - guint8 perm_deny = aim_ssi_getpermdeny(od->ssi.local);
    - if (perm_deny != 0 && perm_deny != account->perm_deny)
    - {
    - purple_debug_info("oscar",
    - "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, perm_deny);
    - account->perm_deny = perm_deny;
    - }
    - }
    - } break;
    -
    - case AIM_SSI_TYPE_PRESENCEPREFS: { /* Presence setting */
    - /* We don't want to change Purple's setting because it applies to all accounts */
    - } break;
    - } /* End of switch on curitem->type */
    - } /* End of for loop */
    -
    - /*** End code for adding from server list to local list ***/
    -
    - if (od->icq) {
    - oscar_set_icq_permdeny(account);
    - } else {
    - oscar_set_aim_permdeny(gc);
    - }
    -
    - /* Activate SSI */
    - /* Sending the enable causes other people to be able to see you, and you to see them */
    - /* Make sure your privacy setting/invisibility is set how you want it before this! */
    - purple_debug_info("oscar",
    - "ssi: activating server-stored buddy list\n");
    - aim_ssi_enable(od);
    -
    - /*
    - * Make sure our server-stored icon is updated correctly in
    - * the event that the local user set a new icon while this
    - * account was offline.
    - */
    - img = purple_buddy_icons_find_account_icon(account);
    - oscar_set_icon(gc, img);
    - purple_imgstore_unref(img);
    -
    - /*
    - * If we've already received our bos rights then we're not waiting on
    - * anything else, so send the server clientready.
    - */
    - if (od->bos.have_rights) {
    - aim_srv_clientready(od, conn);
    -
    - /* Request offline messages for AIM and ICQ */
    - aim_im_reqofflinemsgs(od);
    -
    - purple_connection_set_state(gc, PURPLE_CONNECTED);
    - }
    -
    - return 1;
    -}
    -
    -static int purple_ssi_parseack(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - struct aim_ssi_tmp *retval;
    -
    - va_start(ap, fr);
    - retval = va_arg(ap, struct aim_ssi_tmp *);
    - va_end(ap);
    -
    - while (retval) {
    - purple_debug_misc("oscar",
    - "ssi: status is 0x%04hx for a 0x%04hx action with name %s\n", retval->ack, retval->action, retval->item ? (retval->item->name ? retval->item->name : "no name") : "no item");
    -
    - if (retval->ack != 0xffff)
    - switch (retval->ack) {
    - case 0x0000: { /* added successfully */
    - } break;
    -
    - case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */
    - gchar *buf;
    - buf = g_strdup_printf(_("Unable to add the buddy %s because you have too many buddies in your buddy list. Please remove one and try again."), (retval->name ? retval->name : _("(no name)")));
    - if ((retval->name != NULL) && !purple_conv_present_error(retval->name, purple_connection_get_account(gc), buf))
    - purple_notify_error(gc, NULL, _("Unable to Add"), buf);
    - g_free(buf);
    - } break;
    -
    - case 0x000e: { /* buddy requires authorization */
    - if ((retval->action == SNAC_SUBTYPE_FEEDBAG_ADD) && (retval->name))
    - oscar_auth_sendrequest(gc, retval->name, NULL);
    - } break;
    -
    - default: { /* La la la */
    - gchar *buf;
    - purple_debug_error("oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval->action, retval->ack);
    - buf = g_strdup_printf(_("Unable to add the buddy %s for an unknown reason."),
    - (retval->name ? retval->name : _("(no name)")));
    - if ((retval->name != NULL) && !purple_conv_present_error(retval->name, purple_connection_get_account(gc), buf))
    - purple_notify_error(gc, NULL, _("Unable to Add"), buf);
    - g_free(buf);
    - } break;
    - }
    -
    - retval = retval->next;
    - }
    -
    - return 1;
    -}
    -
    -static int
    -purple_ssi_parseaddmod(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - char *gname, *gname_utf8, *alias, *alias_utf8;
    - PurpleBuddy *b;
    - PurpleGroup *g;
    - struct aim_ssi_item *ssi_item;
    - va_list ap;
    - guint16 snac_subtype, type;
    - const char *name;
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - snac_subtype = (guint16)va_arg(ap, int);
    - type = (guint16)va_arg(ap, int);
    - name = va_arg(ap, char *);
    - va_end(ap);
    -
    - if ((type != 0x0000) || (name == NULL))
    - return 1;
    -
    - gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
    - gname_utf8 = gname ? oscar_utf8_try_convert(account, od, gname) : NULL;
    -
    - alias = aim_ssi_getalias(od->ssi.local, gname, name);
    - alias_utf8 = oscar_utf8_try_convert(account, od, alias);
    - g_free(alias);
    -
    - b = purple_find_buddy(account, name);
    - if (b) {
    - /*
    - * You're logged in somewhere else and you aliased one
    - * of your buddies, so update our local buddy list with
    - * the person's new alias.
    - */
    - purple_blist_alias_buddy(b, alias_utf8);
    - } else if (snac_subtype == 0x0008) {
    - /*
    - * You're logged in somewhere else and you added a buddy to
    - * your server list, so add them to your local buddy list.
    - */
    - b = purple_buddy_new(account, name, alias_utf8);
    -
    - if (!(g = purple_find_group(gname_utf8 ? gname_utf8 : _("Orphans")))) {
    - g = purple_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
    - purple_blist_add_group(g, NULL);
    - }
    -
    - purple_debug_info("oscar",
    - "ssi: adding buddy %s to group %s to local list\n", name, gname_utf8 ? gname_utf8 : _("Orphans"));
    - purple_blist_add_buddy(b, NULL, g, NULL);
    -
    - /* Mobile users should always be online */
    - if (name[0] == '+') {
    - purple_prpl_got_user_status(account,
    - name, OSCAR_STATUS_ID_AVAILABLE, NULL);
    - purple_prpl_got_user_status(account,
    - name, OSCAR_STATUS_ID_MOBILE, NULL);
    - }
    -
    - }
    -
    - ssi_item = aim_ssi_itemlist_finditem(od->ssi.local,
    - gname, name, AIM_SSI_TYPE_BUDDY);
    - if (ssi_item == NULL)
    - {
    - purple_debug_error("oscar", "purple_ssi_parseaddmod: "
    - "Could not find ssi item for oncoming buddy %s, "
    - "group %s\n", name, gname);
    - }
    -
    - g_free(gname_utf8);
    - g_free(alias_utf8);
    -
    - return 1;
    -}
    -
    -static int purple_ssi_authgiven(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - char *bn;
    - gchar *dialog_msg, *nombre;
    - struct name_data *data;
    - PurpleBuddy *buddy;
    -
    - va_start(ap, fr);
    - bn = va_arg(ap, char *);
    - va_arg(ap, char *); /* msg */
    - va_end(ap);
    -
    - purple_debug_info("oscar",
    - "ssi: %s has given you permission to add him to your buddy list\n", bn);
    -
    - buddy = purple_find_buddy(purple_connection_get_account(gc), bn);
    - if (buddy && (purple_buddy_get_alias_only(buddy)))
    - nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy));
    - else
    - nombre = g_strdup(bn);
    -
    - dialog_msg = g_strdup_printf(_("The user %s has given you permission to add him or her to your buddy list. Do you want to add this user?"), nombre);
    - g_free(nombre);
    -
    - data = g_new(struct name_data, 1);
    - data->gc = gc;
    - data->name = g_strdup(bn);
    - data->nick = (buddy ? g_strdup(purple_buddy_get_alias_only(buddy)) : NULL);
    -
    - purple_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg,
    - PURPLE_DEFAULT_ACTION_NONE,
    - purple_connection_get_account(gc), bn, NULL,
    - data,
    - G_CALLBACK(purple_icq_buddyadd),
    - G_CALLBACK(oscar_free_name_data));
    - g_free(dialog_msg);
    -
    - return 1;
    -}
    -
    -static int purple_ssi_authrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - va_list ap;
    - const char *bn;
    - char *msg;
    -
    - va_start(ap, fr);
    - bn = va_arg(ap, const char *);
    - msg = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_info("oscar",
    - "ssi: received authorization request from %s\n", bn);
    -
    - if (!msg) {
    - purple_debug_warning("oscar", "Received auth request from %s with "
    - "empty message\n", bn);
    - } else if (!g_utf8_validate(msg, -1, NULL)) {
    - purple_debug_warning("oscar", "Received auth request from %s with "
    - "invalid UTF-8 message\n", bn);
    - msg = NULL;
    - }
    -
    - aim_icq_getalias(od, bn, TRUE, msg);
    - return 1;
    -}
    -
    -static int purple_ssi_authreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - char *bn, *msg;
    - gchar *dialog_msg, *nombre;
    - guint8 reply;
    - PurpleBuddy *buddy;
    -
    - va_start(ap, fr);
    - bn = va_arg(ap, char *);
    - reply = (guint8)va_arg(ap, int);
    - msg = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_info("oscar",
    - "ssi: received authorization reply from %s. Reply is 0x%04hhx\n", bn, reply);
    -
    - buddy = purple_find_buddy(purple_connection_get_account(gc), bn);
    - if (buddy && (purple_buddy_get_alias_only(buddy)))
    - nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy));
    - else
    - nombre = g_strdup(bn);
    -
    - if (reply) {
    - /* Granted */
    - dialog_msg = g_strdup_printf(_("The user %s has granted your request to add them to your buddy list."), nombre);
    - purple_notify_info(gc, NULL, _("Authorization Granted"), dialog_msg);
    - } else {
    - /* Denied */
    - dialog_msg = g_strdup_printf(_("The user %s has denied your request to add them to your buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given."));
    - purple_notify_info(gc, NULL, _("Authorization Denied"), dialog_msg);
    - }
    - g_free(dialog_msg);
    - g_free(nombre);
    -
    - return 1;
    -}
    -
    -static int purple_ssi_gotadded(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - va_list ap;
    - char *bn;
    - PurpleBuddy *buddy;
    -
    - va_start(ap, fr);
    - bn = va_arg(ap, char *);
    - va_end(ap);
    -
    - buddy = purple_find_buddy(account, bn);
    - purple_debug_info("oscar", "ssi: %s added you to their buddy list\n", bn);
    - purple_account_notify_added(account, bn, NULL,
    - (buddy ? purple_buddy_get_alias_only(buddy) : NULL), NULL);
    -
    - return 1;
    -}
    -
    -GList *oscar_chat_info(PurpleConnection *gc) {
    - GList *m = NULL;
    - struct proto_chat_entry *pce;
    -
    - pce = g_new0(struct proto_chat_entry, 1);
    - pce->label = _("_Room:");
    - pce->identifier = "room";
    - pce->required = TRUE;
    - m = g_list_append(m, pce);
    -
    - pce = g_new0(struct proto_chat_entry, 1);
    - pce->label = _("_Exchange:");
    - pce->identifier = "exchange";
    - pce->required = TRUE;
    - pce->is_int = TRUE;
    - pce->min = 4;
    - pce->max = 20;
    - m = g_list_append(m, pce);
    -
    - return m;
    -}
    -
    -GHashTable *oscar_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
    -{
    - GHashTable *defaults;
    -
    - defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
    -
    - if (chat_name != NULL)
    - g_hash_table_insert(defaults, "room", g_strdup(chat_name));
    - g_hash_table_insert(defaults, "exchange", g_strdup("4"));
    -
    - return defaults;
    -}
    -
    -char *
    -oscar_get_chat_name(GHashTable *data)
    -{
    - return g_strdup(g_hash_table_lookup(data, "room"));
    -}
    -
    -void
    -oscar_join_chat(PurpleConnection *gc, GHashTable *data)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - FlapConnection *conn;
    - char *name, *exchange;
    - int exchange_int;
    -
    - name = g_hash_table_lookup(data, "room");
    - exchange = g_hash_table_lookup(data, "exchange");
    -
    - g_return_if_fail(name != NULL && *name != '\0');
    - g_return_if_fail(exchange != NULL);
    -
    - errno = 0;
    - exchange_int = strtol(exchange, NULL, 10);
    - g_return_if_fail(errno == 0);
    -
    - purple_debug_info("oscar", "Attempting to join chat room %s.\n", name);
    -
    - if ((conn = flap_connection_getbytype(od, SNAC_FAMILY_CHATNAV)))
    - {
    - purple_debug_info("oscar", "chatnav exists, creating room\n");
    - aim_chatnav_createroom(od, conn, name, exchange_int);
    - } else {
    - /* this gets tricky */
    - struct create_room *cr = g_new0(struct create_room, 1);
    - purple_debug_info("oscar", "chatnav does not exist, opening chatnav\n");
    - cr->exchange = exchange_int;
    - cr->name = g_strdup(name);
    - od->create_rooms = g_slist_prepend(od->create_rooms, cr);
    - aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV);
    - }
    -}
    -
    -void
    -oscar_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - struct chat_connection *ccon = find_oscar_chat(gc, id);
    -
    - if (ccon == NULL)
    - return;
    -
    - aim_im_sendch2_chatinvite(od, name, message ? message : "",
    - ccon->exchange, ccon->name, 0x0);
    -}
    -
    -void
    -oscar_chat_leave(PurpleConnection *gc, int id)
    -{
    - PurpleConversation *conv;
    - struct chat_connection *cc;
    -
    - conv = purple_find_chat(gc, id);
    -
    - g_return_if_fail(conv != NULL);
    -
    - purple_debug_info("oscar", "Leaving chat room %s\n",
    - purple_conversation_get_name(conv));
    -
    - cc = find_oscar_chat(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
    - flap_connection_schedule_destroy(cc->conn, OSCAR_DISCONNECT_DONE, NULL);
    - oscar_chat_kill(gc, cc);
    -}
    -
    -int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - PurpleConversation *conv = NULL;
    - struct chat_connection *c = NULL;
    - char *buf, *buf2, *buf3;
    - guint16 charset;
    - char *charsetstr;
    - gsize len;
    -
    - if (!(conv = purple_find_chat(gc, id)))
    - return -EINVAL;
    -
    - if (!(c = find_oscar_chat_by_conv(gc, conv)))
    - return -EINVAL;
    -
    - buf = purple_strdup_withhtml(message);
    -
    - if (strstr(buf, "<IMG "))
    - purple_conversation_write(conv, "",
    - _("Your IM Image was not sent. "
    - "You cannot send IM Images in AIM chats."),
    - PURPLE_MESSAGE_ERROR, time(NULL));
    -
    - buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr);
    - /*
    - * Evan S. suggested that maxvis really does mean "number of
    - * visible characters" and not "number of bytes"
    - */
    - if ((len > c->maxlen) || (len > c->maxvis)) {
    - /* If the length was too long, try stripping the HTML and then running it back through
    - * purple_strdup_withhtml() and the encoding process. The result may be shorter. */
    - g_free(buf2);
    -
    - buf3 = purple_markup_strip_html(buf);
    - g_free(buf);
    -
    - buf = purple_strdup_withhtml(buf3);
    - g_free(buf3);
    -
    - buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr);
    -
    - if ((len > c->maxlen) || (len > c->maxvis)) {
    - purple_debug_warning("oscar",
    - "Could not send %s because (%" G_GSIZE_FORMAT " > maxlen %i) or (%" G_GSIZE_FORMAT " > maxvis %i)\n",
    - buf2, len, c->maxlen, len, c->maxvis);
    - g_free(buf);
    - g_free(buf2);
    - return -E2BIG;
    - }
    -
    - purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
    - message, buf2);
    - }
    -
    - aim_chat_send_im(od, c->conn, 0, buf2, len, charsetstr, "en");
    - g_free(buf2);
    - g_free(buf);
    -
    - return 0;
    -}
    -
    -PurpleMood* oscar_get_purple_moods(PurpleAccount *account)
    -{
    - return icq_get_purple_moods(account);
    -}
    -
    -const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b)
    -{
    - const char *name = b ? purple_buddy_get_name(b) : NULL;
    - if (name && !oscar_util_valid_name_sms(name) && oscar_util_valid_name_icq(name))
    - return "icq";
    -
    - return "icq";
    -}
    -
    -const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b)
    -{
    - const char *name = b ? purple_buddy_get_name(b) : NULL;
    - if (name && !oscar_util_valid_name_sms(name) && oscar_util_valid_name_icq(name))
    - return "icq";
    -
    - return "aim";
    -}
    -
    -const char *oscar_list_emblem(PurpleBuddy *b)
    -{
    - PurpleConnection *gc = NULL;
    - OscarData *od = NULL;
    - PurpleAccount *account = NULL;
    - PurplePresence *presence;
    - aim_userinfo_t *userinfo = NULL;
    - const char *name;
    -
    - account = purple_buddy_get_account(b);
    - name = purple_buddy_get_name(b);
    - if (account != NULL)
    - gc = purple_account_get_connection(account);
    - if (gc != NULL)
    - od = purple_connection_get_protocol_data(gc);
    - if (od != NULL)
    - userinfo = aim_locate_finduserinfo(od, name);
    -
    - presence = purple_buddy_get_presence(b);
    -
    - if (purple_presence_is_online(presence) == FALSE) {
    - char *gname;
    - if ((name) && (od) && (od->ssi.received_data) &&
    - (gname = aim_ssi_itemlist_findparentname(od->ssi.local, name)) &&
    - (aim_ssi_waitingforauth(od->ssi.local, gname, name))) {
    - return "not-authorized";
    - }
    - }
    -
    - if (userinfo != NULL ) {
    - if (userinfo->flags & AIM_FLAG_ADMINISTRATOR)
    - return "admin";
    - if (userinfo->flags & AIM_FLAG_ACTIVEBUDDY)
    - return "bot";
    - if (userinfo->capabilities & OSCAR_CAPABILITY_SECUREIM)
    - return "secure";
    - if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY)
    - return "birthday";
    -
    - /* Make the mood icon override anything below this. */
    - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD))
    - return NULL;
    -
    - if (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP)
    - return "hiptop";
    - }
    - return NULL;
    -}
    -
    -void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - OscarData *od;
    - aim_userinfo_t *userinfo;
    -
    - if (!PURPLE_BUDDY_IS_ONLINE(b))
    - return;
    -
    - account = purple_buddy_get_account(b);
    - gc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(gc);
    - userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
    -
    - oscar_user_info_append_status(gc, user_info, b, userinfo, /* use_html_status */ FALSE);
    -
    - if (full)
    - oscar_user_info_append_extra_info(gc, user_info, b, userinfo);
    -}
    -
    -char *oscar_status_text(PurpleBuddy *b)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - OscarData *od;
    - const PurplePresence *presence;
    - const PurpleStatus *status;
    - const char *message;
    - gchar *ret = NULL;
    -
    - gc = purple_account_get_connection(purple_buddy_get_account(b));
    - account = purple_connection_get_account(gc);
    - od = purple_connection_get_protocol_data(gc);
    - presence = purple_buddy_get_presence(b);
    - status = purple_presence_get_active_status(presence);
    -
    - if ((od != NULL) && !purple_presence_is_online(presence))
    - {
    - const char *name = purple_buddy_get_name(b);
    - char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
    - if (aim_ssi_waitingforauth(od->ssi.local, gname, name))
    - ret = g_strdup(_("Not Authorized"));
    - else
    - ret = g_strdup(_("Offline"));
    - }
    - else
    - {
    - message = purple_status_get_attr_string(status, "message");
    - if (message != NULL)
    - {
    - gchar *tmp = oscar_util_format_string(message, purple_account_get_username(account));
    - ret = purple_markup_escape_text(tmp, -1);
    - g_free(tmp);
    - }
    - else if (purple_status_is_available(status))
    - {
    - /* Don't show "Available" as status message in case buddy doesn't have a status message */
    - }
    - else
    - {
    - ret = g_strdup(purple_status_get_name(status));
    - }
    - }
    -
    - return ret;
    -}
    -
    -void oscar_set_aim_permdeny(PurpleConnection *gc) {
    - PurpleAccount *account = purple_connection_get_account(gc);
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - /*
    - * Conveniently there is a one-to-one mapping between the
    - * values of libpurple's PurplePrivacyType and the values used
    - * by the oscar protocol.
    - */
    - aim_ssi_setpermdeny(od, account->perm_deny);
    -}
    -
    -void oscar_add_permit(PurpleConnection *gc, const char *who) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - purple_debug_info("oscar", "ssi: About to add a permit\n");
    - aim_ssi_add_to_private_list(od, who, AIM_SSI_TYPE_PERMIT);
    -}
    -
    -void oscar_add_deny(PurpleConnection *gc, const char *who) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - purple_debug_info("oscar", "ssi: About to add a deny\n");
    - aim_ssi_add_to_private_list(od, who, aim_ssi_getdenyentrytype(od));
    -}
    -
    -void oscar_rem_permit(PurpleConnection *gc, const char *who) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - purple_debug_info("oscar", "ssi: About to delete a permit\n");
    - aim_ssi_del_from_private_list(od, who, AIM_SSI_TYPE_PERMIT);
    -}
    -
    -void oscar_rem_deny(PurpleConnection *gc, const char *who) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - purple_debug_info("oscar", "ssi: About to delete a deny\n");
    - aim_ssi_del_from_private_list(od, who, aim_ssi_getdenyentrytype(od));
    -}
    -
    -GList *
    -oscar_status_types(PurpleAccount *account)
    -{
    - gboolean is_icq;
    - GList *status_types = NULL;
    - PurpleStatusType *type;
    -
    - g_return_val_if_fail(account != NULL, NULL);
    -
    - /* Used to flag some statuses as "user settable" or not */
    - is_icq = oscar_util_valid_name_icq(purple_account_get_username(account));
    -
    - /* Common status types */
    - /* Really the available message should only be settable for AIM accounts */
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_AVAILABLE,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING),
    - "itmsurl", _("iTunes Music Store Link"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_FREE4CHAT,
    - _("Free For Chat"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    -
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_EVIL,
    - _("Evil"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_DEPRESSION,
    - _("Depression"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_ATHOME,
    - _("At home"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_ATWORK,
    - _("At work"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    -
    - status_types = g_list_prepend(status_types, type);
    -
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_LUNCH,
    - _("Lunch"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    -
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
    - OSCAR_STATUS_ID_AWAY,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE,
    - OSCAR_STATUS_ID_INVISIBLE,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    -
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, OSCAR_STATUS_ID_MOBILE, NULL, FALSE, FALSE, TRUE);
    - status_types = g_list_prepend(status_types, type);
    -
    - /* ICQ-specific status types */
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
    - OSCAR_STATUS_ID_OCCUPIED,
    - _("Occupied"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
    - OSCAR_STATUS_ID_DND,
    - _("Do Not Disturb"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY,
    - OSCAR_STATUS_ID_NA,
    - _("Not Available"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
    - OSCAR_STATUS_ID_OFFLINE,
    - NULL, TRUE, TRUE, FALSE);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD,
    - "mood", NULL, TRUE, is_icq, TRUE,
    - PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(PURPLE_TYPE_STRING),
    - PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(PURPLE_TYPE_STRING),
    - NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - return g_list_reverse(status_types);
    -}
    -
    -static void oscar_ssi_editcomment(struct name_data *data, const char *text) {
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - OscarData *od;
    - PurpleBuddy *b;
    - PurpleGroup *g;
    -
    - gc = data->gc;
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - b = purple_find_buddy(account, data->name);
    - if (b == NULL) {
    - oscar_free_name_data(data);
    - return;
    - }
    -
    - g = purple_buddy_get_group(b);
    - if (g == NULL) {
    - oscar_free_name_data(data);
    - return;
    - }
    -
    - aim_ssi_editcomment(od, purple_group_get_name(g), data->name, text);
    - oscar_free_name_data(data);
    -}
    -
    -static void oscar_buddycb_edit_comment(PurpleBlistNode *node, gpointer ignore) {
    -
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    - OscarData *od;
    - struct name_data *data;
    - PurpleGroup *g;
    - char *comment;
    - gchar *comment_utf8;
    - gchar *title;
    - PurpleAccount *account;
    - const char *name;
    -
    - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *) node;
    - name = purple_buddy_get_name(buddy);
    - account = purple_buddy_get_account(buddy);
    - gc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(gc);
    -
    - if (!(g = purple_buddy_get_group(buddy)))
    - return;
    -
    - data = g_new(struct name_data, 1);
    -
    - comment = aim_ssi_getcomment(od->ssi.local, purple_group_get_name(g), name);
    - comment_utf8 = comment ? oscar_utf8_try_convert(account, od, comment) : NULL;
    -
    - data->gc = gc;
    - data->name = g_strdup(name);
    - data->nick = g_strdup(purple_buddy_get_alias_only(buddy));
    -
    - title = g_strdup_printf(_("Buddy Comment for %s"), data->name);
    - purple_request_input(gc, title, _("Buddy Comment:"), NULL,
    - comment_utf8, TRUE, FALSE, NULL,
    - _("_OK"), G_CALLBACK(oscar_ssi_editcomment),
    - _("_Cancel"), G_CALLBACK(oscar_free_name_data),
    - account, data->name, NULL,
    - data);
    - g_free(title);
    -
    - g_free(comment);
    - g_free(comment_utf8);
    -}
    -
    -static void
    -oscar_ask_directim_yes_cb(struct oscar_ask_directim_data *data)
    -{
    - peer_connection_propose(data->od, OSCAR_CAPABILITY_DIRECTIM, data->who);
    - g_free(data->who);
    - g_free(data);
    -}
    -
    -static void
    -oscar_ask_directim_no_cb(struct oscar_ask_directim_data *data)
    -{
    - g_free(data->who);
    - g_free(data);
    -}
    -
    -/* This is called from right-click menu on a buddy node. */
    -static void
    -oscar_ask_directim(gpointer object, gpointer ignored)
    -{
    - PurpleBlistNode *node;
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    - gchar *buf;
    - struct oscar_ask_directim_data *data;
    - PurpleAccount *account;
    -
    - node = object;
    -
    - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *)node;
    - account = purple_buddy_get_account(buddy);
    - gc = purple_account_get_connection(account);
    -
    - data = g_new0(struct oscar_ask_directim_data, 1);
    - data->who = g_strdup(purple_buddy_get_name(buddy));
    - data->od = purple_connection_get_protocol_data(gc);
    - buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."),
    - data->who);
    -
    - purple_request_action(gc, NULL, buf,
    - _("Because this reveals your IP address, it "
    - "may be considered a security risk. Do you "
    - "wish to continue?"),
    - 0, /* Default action is "connect" */
    - account, data->who, NULL,
    - data, 2,
    - _("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb),
    - _("_Cancel"), G_CALLBACK(oscar_ask_directim_no_cb));
    - g_free(buf);
    -}
    -
    -static void
    -oscar_close_directim(gpointer object, gpointer ignored)
    -{
    - PurpleBlistNode *node;
    - PurpleBuddy *buddy;
    - PurpleAccount *account;
    - PurpleConnection *gc;
    - PurpleConversation *conv;
    - OscarData *od;
    - PeerConnection *conn;
    - const char *name;
    -
    - node = object;
    -
    - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy*)node;
    - name = purple_buddy_get_name(buddy);
    - account = purple_buddy_get_account(buddy);
    - gc = purple_account_get_connection(account);
    - od = gc->proto_data;
    - conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
    -
    - if (conn != NULL)
    - {
    - if (!conn->ready)
    - aim_im_sendch2_cancel(conn);
    -
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    -
    - /* OSCAR_DISCONNECT_LOCAL_CLOSED doesn't write anything to the convo
    - * window. Let the user know that we cancelled the Direct IM. */
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
    - purple_conversation_write(conv, NULL, _("You closed the connection."),
    - PURPLE_MESSAGE_SYSTEM, time(NULL));
    - }
    -}
    -
    -static void oscar_get_icqxstatusmsg(PurpleBlistNode *node, gpointer ignore)
    -{
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    - OscarData *od;
    - PurpleAccount *account;
    - const char *bname;
    -
    - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *)node;
    - bname = purple_buddy_get_name(buddy);
    -
    - account = purple_buddy_get_account(buddy);
    - gc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(gc);
    -
    - purple_debug_info("oscar", "Manual X-Status Get From %s to %s:\n", bname, purple_account_get_username(account));
    -
    - icq_im_xstatus_request(od, bname);
    -}
    -
    -static void
    -oscar_get_aim_info_cb(PurpleBlistNode *node, gpointer ignore)
    -{
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    -
    - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *)node;
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    -
    - aim_locate_getinfoshort(purple_connection_get_protocol_data(gc),
    - purple_buddy_get_name(buddy), 0x00000003);
    -}
    -
    -static GList *
    -oscar_buddy_menu(PurpleBuddy *buddy) {
    - PurpleConnection *gc;
    - OscarData *od;
    - GList *menu;
    - PurpleMenuAction *act;
    - aim_userinfo_t *userinfo;
    - PurpleAccount *account;
    - const char *bname = purple_buddy_get_name(buddy);
    -
    - account = purple_buddy_get_account(buddy);
    - gc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(gc);
    - userinfo = aim_locate_finduserinfo(od, bname);
    - menu = NULL;
    -
    - if (od->icq && oscar_util_valid_name_icq(bname))
    - {
    - act = purple_menu_action_new(_("Get AIM Info"),
    - PURPLE_CALLBACK(oscar_get_aim_info_cb),
    - NULL, NULL);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - if (purple_buddy_get_group(buddy) != NULL)
    - {
    - /* We only do this if the user is in our buddy list */
    - act = purple_menu_action_new(_("Edit Buddy Comment"),
    - PURPLE_CALLBACK(oscar_buddycb_edit_comment),
    - NULL, NULL);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - if (od->icq)
    - {
    - act = purple_menu_action_new(_("Get X-Status Msg"),
    - PURPLE_CALLBACK(oscar_get_icqxstatusmsg),
    - NULL, NULL);
    - menu = g_list_prepend(menu, act);
    - menu = g_list_prepend(menu, create_visibility_menu_item(od, bname));
    - }
    -
    - if (userinfo &&
    - oscar_util_name_compare(purple_account_get_username(account), bname) &&
    - PURPLE_BUDDY_IS_ONLINE(buddy))
    - {
    - PeerConnection *conn;
    - conn = peer_connection_find_by_type(od, bname, OSCAR_CAPABILITY_DIRECTIM);
    -
    - if (userinfo->capabilities & OSCAR_CAPABILITY_DIRECTIM)
    - {
    - if (conn)
    - {
    - act = purple_menu_action_new(_("End Direct IM Session"),
    - PURPLE_CALLBACK(oscar_close_directim),
    - NULL, NULL);
    - }
    - else
    - {
    - act = purple_menu_action_new(_("Direct IM"),
    - PURPLE_CALLBACK(oscar_ask_directim),
    - NULL, NULL);
    - }
    - menu = g_list_prepend(menu, act);
    - }
    - }
    -
    - if (od->ssi.received_data && purple_buddy_get_group(buddy) != NULL)
    - {
    - /*
    - * We only do this if the user is in our buddy list and we're
    - * waiting for authorization.
    - */
    - char *gname;
    - gname = aim_ssi_itemlist_findparentname(od->ssi.local, bname);
    - if (gname && aim_ssi_waitingforauth(od->ssi.local, gname, bname))
    - {
    - act = purple_menu_action_new(_("Re-request Authorization"),
    - PURPLE_CALLBACK(oscar_auth_sendrequest_menu),
    - NULL, NULL);
    - menu = g_list_prepend(menu, act);
    - }
    - }
    -
    - menu = g_list_reverse(menu);
    -
    - return menu;
    -}
    -
    -
    -GList *oscar_blist_node_menu(PurpleBlistNode *node) {
    - if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
    - return oscar_buddy_menu((PurpleBuddy *) node);
    - } else {
    - return NULL;
    - }
    -}
    -
    -static void
    -oscar_icq_privacy_opts(PurpleConnection *gc, PurpleRequestFields *fields)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleRequestField *f;
    - gboolean auth, web_aware;
    -
    - f = purple_request_fields_get_field(fields, "authorization");
    - auth = purple_request_field_bool_get_value(f);
    -
    - f = purple_request_fields_get_field(fields, "web_aware");
    - web_aware = purple_request_field_bool_get_value(f);
    -
    - purple_account_set_bool(account, "authorization", auth);
    - purple_account_set_bool(account, "web_aware", web_aware);
    -
    - oscar_set_extended_status(gc);
    - aim_icq_setsecurity(od, auth, web_aware);
    -}
    -
    -static void
    -oscar_show_icq_privacy_opts(PurplePluginAction *action)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleRequestFields *fields;
    - PurpleRequestFieldGroup *g;
    - PurpleRequestField *f;
    - gboolean auth, web_aware;
    -
    - auth = purple_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION);
    - web_aware = purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE);
    -
    - fields = purple_request_fields_new();
    -
    - g = purple_request_field_group_new(NULL);
    -
    - f = purple_request_field_bool_new("authorization", _("Require authorization"), auth);
    - purple_request_field_group_add_field(g, f);
    -
    - f = purple_request_field_bool_new("web_aware", _("Web aware (enabling this will cause you to receive SPAM!)"), web_aware);
    - purple_request_field_group_add_field(g, f);
    -
    - purple_request_fields_add_group(fields, g);
    -
    - purple_request_fields(gc, _("ICQ Privacy Options"), _("ICQ Privacy Options"),
    - NULL, fields,
    - _("OK"), G_CALLBACK(oscar_icq_privacy_opts),
    - _("Cancel"), NULL,
    - purple_connection_get_account(gc), NULL, NULL,
    - gc);
    -}
    -
    -static void oscar_confirm_account(PurplePluginAction *action)
    -{
    - PurpleConnection *gc;
    - OscarData *od;
    - FlapConnection *conn;
    -
    - gc = (PurpleConnection *)action->context;
    - od = purple_connection_get_protocol_data(gc);
    -
    - conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
    - if (conn != NULL) {
    - aim_admin_reqconfirm(od, conn);
    - } else {
    - od->conf = TRUE;
    - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
    - }
    -}
    -
    -static void oscar_show_email(PurplePluginAction *action)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
    -
    - if (conn) {
    - aim_admin_getinfo(od, conn, 0x11);
    - } else {
    - od->reqemail = TRUE;
    - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
    - }
    -}
    -
    -static void oscar_change_email(PurpleConnection *gc, const char *email)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
    -
    - if (conn) {
    - aim_admin_setemail(od, conn, email);
    - } else {
    - od->setemail = TRUE;
    - od->email = g_strdup(email);
    - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
    - }
    -}
    -
    -static void oscar_show_change_email(PurplePluginAction *action)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - purple_request_input(gc, NULL, _("Change Address To:"), NULL, NULL,
    - FALSE, FALSE, NULL,
    - _("_OK"), G_CALLBACK(oscar_change_email),
    - _("_Cancel"), NULL,
    - purple_connection_get_account(gc), NULL, NULL,
    - gc);
    -}
    -
    -static void oscar_show_awaitingauth(PurplePluginAction *action)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - GSList *buddies, *filtered_buddies, *cur;
    - gchar *text;
    -
    - buddies = purple_find_buddies(account, NULL);
    - filtered_buddies = NULL;
    - for (cur = buddies; cur != NULL; cur = cur->next) {
    - PurpleBuddy *buddy;
    - const gchar *bname, *gname;
    -
    - buddy = cur->data;
    - bname = purple_buddy_get_name(buddy);
    - gname = purple_group_get_name(purple_buddy_get_group(buddy));
    - if (aim_ssi_waitingforauth(od->ssi.local, gname, bname)) {
    - filtered_buddies = g_slist_prepend(filtered_buddies, buddy);
    - }
    - }
    -
    - g_slist_free(buddies);
    -
    - filtered_buddies = g_slist_reverse(filtered_buddies);
    - text = oscar_format_buddies(filtered_buddies, _("you are not waiting for authorization"));
    - g_slist_free(filtered_buddies);
    -
    - purple_notify_formatted(gc, NULL, _("You are awaiting authorization from "
    - "the following buddies"), _("You can re-request "
    - "authorization from these buddies by "
    - "right-clicking on them and selecting "
    - "\"Re-request Authorization.\""), text, NULL, NULL);
    - g_free(text);
    -}
    -
    -static void search_by_email_cb(PurpleConnection *gc, const char *email)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - aim_search_address(od, email);
    -}
    -
    -static void oscar_show_find_email(PurplePluginAction *action)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - purple_request_input(gc, _("Find Buddy by Email"),
    - _("Search for a buddy by email address"),
    - _("Type the email address of the buddy you are "
    - "searching for."),
    - NULL, FALSE, FALSE, NULL,
    - _("_Search"), G_CALLBACK(search_by_email_cb),
    - _("_Cancel"), NULL,
    - purple_connection_get_account(gc), NULL, NULL,
    - gc);
    -}
    -
    -static void oscar_show_set_info(PurplePluginAction *action)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - purple_account_request_change_user_info(purple_connection_get_account(gc));
    -}
    -
    -static void oscar_show_set_info_icqurl(PurplePluginAction *action)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - purple_notify_uri(gc, "http://www.icq.com/whitepages/user_details.php");
    -}
    -
    -static void oscar_change_pass(PurplePluginAction *action)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - purple_account_request_change_password(purple_connection_get_account(gc));
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static void oscar_show_chpassurl(PurplePluginAction *action)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - gchar *substituted = purple_strreplace(od->authinfo->chpassurl, "%s", purple_account_get_username(purple_connection_get_account(gc)));
    - purple_notify_uri(gc, substituted);
    - g_free(substituted);
    -}
    -
    -static void oscar_show_imforwardingurl(PurplePluginAction *action)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - purple_notify_uri(gc, "http://mymobile.aol.com/dbreg/register?action=imf&clientID=1");
    -}
    -
    -void oscar_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (img == NULL) {
    - aim_ssi_delicon(od);
    - } else {
    - PurpleCipherContext *context;
    - guchar md5[16];
    - gconstpointer data = purple_imgstore_get_data(img);
    - size_t len = purple_imgstore_get_size(img);
    -
    - context = purple_cipher_context_new_by_name("md5", NULL);
    - purple_cipher_context_append(context, data, len);
    - purple_cipher_context_digest(context, 16, md5, NULL);
    - purple_cipher_context_destroy(context);
    -
    - aim_ssi_seticon(od, md5, 16);
    - }
    -}
    -
    -/**
    - * Called by the Purple core to determine whether or not we're
    - * allowed to send a file to this user.
    - */
    -gboolean
    -oscar_can_receive_file(PurpleConnection *gc, const char *who)
    -{
    - OscarData *od;
    - PurpleAccount *account;
    -
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - if (od != NULL)
    - {
    - aim_userinfo_t *userinfo;
    - userinfo = aim_locate_finduserinfo(od, who);
    -
    - /*
    - * Don't allowing sending a file to a user that does not support
    - * file transfer, and don't allow sending to ourselves.
    - */
    - if (((userinfo == NULL) ||
    - (userinfo->capabilities & OSCAR_CAPABILITY_SENDFILE)) &&
    - oscar_util_name_compare(who, purple_account_get_username(account)))
    - {
    - return TRUE;
    - }
    - }
    -
    - return FALSE;
    -}
    -
    -PurpleXfer *
    -oscar_new_xfer(PurpleConnection *gc, const char *who)
    -{
    - PurpleXfer *xfer;
    - OscarData *od;
    - PurpleAccount *account;
    - PeerConnection *conn;
    -
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - xfer = purple_xfer_new(account, PURPLE_XFER_SEND, who);
    - if (xfer)
    - {
    - purple_xfer_ref(xfer);
    - purple_xfer_set_init_fnc(xfer, peer_oft_sendcb_init);
    - purple_xfer_set_cancel_send_fnc(xfer, peer_oft_cb_generic_cancel);
    - purple_xfer_set_request_denied_fnc(xfer, peer_oft_cb_generic_cancel);
    - purple_xfer_set_ack_fnc(xfer, peer_oft_sendcb_ack);
    -
    - conn = peer_connection_new(od, OSCAR_CAPABILITY_SENDFILE, who);
    - conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    - aim_icbm_makecookie(conn->cookie);
    - conn->xfer = xfer;
    - xfer->data = conn;
    - }
    -
    - return xfer;
    -}
    -
    -/*
    - * Called by the Purple core when the user indicates that a
    - * file is to be sent to a special someone.
    - */
    -void
    -oscar_send_file(PurpleConnection *gc, const char *who, const char *file)
    -{
    - PurpleXfer *xfer;
    -
    - xfer = oscar_new_xfer(gc, who);
    -
    - if (file != NULL)
    - purple_xfer_request_accepted(xfer, file);
    - else
    - purple_xfer_request(xfer);
    -}
    -
    -GList *
    -oscar_actions(PurplePlugin *plugin, gpointer context)
    -{
    - PurpleConnection *gc = (PurpleConnection *) context;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - GList *menu = NULL;
    - PurplePluginAction *act;
    -
    - act = purple_plugin_action_new(_("Set User Info..."),
    - oscar_show_set_info);
    - menu = g_list_prepend(menu, act);
    -
    - if (od->icq)
    - {
    - act = purple_plugin_action_new(_("Set User Info (web)..."),
    - oscar_show_set_info_icqurl);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - act = purple_plugin_action_new(_("Change Password..."),
    - oscar_change_pass);
    - menu = g_list_prepend(menu, act);
    -
    - if (od->authinfo != NULL && od->authinfo->chpassurl != NULL)
    - {
    - /* This only happens when connecting with the old-style BUCP login */
    - act = purple_plugin_action_new(_("Change Password (web)"),
    - oscar_show_chpassurl);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - if (!od->icq)
    - {
    - act = purple_plugin_action_new(_("Configure IM Forwarding (web)"),
    - oscar_show_imforwardingurl);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - menu = g_list_prepend(menu, NULL);
    -
    - if (od->icq)
    - {
    - /* ICQ actions */
    - act = purple_plugin_action_new(_("Set Privacy Options..."),
    - oscar_show_icq_privacy_opts);
    - menu = g_list_prepend(menu, act);
    -
    - act = purple_plugin_action_new(_("Show Visible List"), oscar_show_visible_list);
    - menu = g_list_prepend(menu, act);
    -
    - act = purple_plugin_action_new(_("Show Invisible List"), oscar_show_invisible_list);
    - menu = g_list_prepend(menu, act);
    - }
    - else
    - {
    - /* AIM actions */
    - act = purple_plugin_action_new(_("Confirm Account"),
    - oscar_confirm_account);
    - menu = g_list_prepend(menu, act);
    -
    - act = purple_plugin_action_new(_("Display Currently Registered Email Address"),
    - oscar_show_email);
    - menu = g_list_prepend(menu, act);
    -
    - act = purple_plugin_action_new(_("Change Currently Registered Email Address..."),
    - oscar_show_change_email);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - menu = g_list_prepend(menu, NULL);
    -
    - act = purple_plugin_action_new(_("Show Buddies Awaiting Authorization"),
    - oscar_show_awaitingauth);
    - menu = g_list_prepend(menu, act);
    -
    - menu = g_list_prepend(menu, NULL);
    -
    - act = purple_plugin_action_new(_("Search for Buddy by Email Address..."),
    - oscar_show_find_email);
    - menu = g_list_prepend(menu, act);
    -
    - menu = g_list_reverse(menu);
    -
    - return menu;
    -}
    -
    -void oscar_change_passwd(PurpleConnection *gc, const char *old, const char *new)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->icq) {
    - aim_icq_changepasswd(od, new);
    - } else {
    - FlapConnection *conn;
    - conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
    - if (conn) {
    - aim_admin_changepasswd(od, conn, new, old);
    - } else {
    - od->chpass = TRUE;
    - od->oldp = g_strdup(old);
    - od->newp = g_strdup(new);
    - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
    - }
    - }
    -}
    -
    -void
    -oscar_convo_closed(PurpleConnection *gc, const char *who)
    -{
    - OscarData *od;
    - PeerConnection *conn;
    -
    - od = purple_connection_get_protocol_data(gc);
    - conn = peer_connection_find_by_type(od, who, OSCAR_CAPABILITY_DIRECTIM);
    -
    - if (conn != NULL)
    - {
    - if (!conn->ready)
    - aim_im_sendch2_cancel(conn);
    -
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    - }
    -}
    -
    -const char *
    -oscar_normalize(const PurpleAccount *account, const char *str)
    -{
    - static char buf[BUF_LEN];
    - char *tmp1, *tmp2;
    - int i, j;
    -
    - g_return_val_if_fail(str != NULL, NULL);
    -
    - /* copy str to buf and skip all blanks */
    - i = 0;
    - for (j = 0; str[j]; j++) {
    - if (str[j] != ' ') {
    - buf[i++] = str[j];
    - if (i >= BUF_LEN - 1)
    - break;
    - }
    - }
    - buf[i] = '\0';
    -
    - tmp1 = g_utf8_strdown(buf, -1);
    - tmp2 = g_utf8_normalize(tmp1, -1, G_NORMALIZE_DEFAULT);
    - if (strlen(tmp2) > sizeof(buf) - 1) {
    - purple_debug_error("oscar", "normalized string exceeds buffer length!\n");
    - }
    - g_strlcpy(buf, tmp2, sizeof(buf));
    - g_free(tmp2);
    - g_free(tmp1);
    -
    - return buf;
    -}
    -
    -gboolean
    -oscar_offline_message(const PurpleBuddy *buddy)
    -{
    - return TRUE;
    -}
    -
    -/* TODO: Find somewhere to put this instead of including it in a bunch of places.
    - * Maybe just change purple_accounts_find() to return anything for the prpl if there is no acct_id.
    - */
    -static PurpleAccount *find_acct(const char *prpl, const char *acct_id)
    -{
    - PurpleAccount *acct = NULL;
    -
    - /* If we have a specific acct, use it */
    - if (acct_id) {
    - acct = purple_accounts_find(acct_id, prpl);
    - 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 (purple_strequal(prpl, purple_account_get_protocol_id(l->data))
    - && purple_account_is_connected(l->data)) {
    - acct = l->data;
    - break;
    - }
    - l = l->next;
    - }
    - }
    -
    - return acct;
    -}
    -
    -
    -static gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params)
    -{
    - char *acct_id = g_hash_table_lookup(params, "account");
    - char prpl[11];
    - PurpleAccount *acct;
    -
    - if (g_ascii_strcasecmp(proto, "aim") && g_ascii_strcasecmp(proto, "icq"))
    - return FALSE;
    -
    - g_snprintf(prpl, sizeof(prpl), "prpl-%s", proto);
    -
    - acct = find_acct(prpl, acct_id);
    -
    - if (!acct)
    - return FALSE;
    -
    - /* aim:GoIM?screenname=SCREENNAME&message=MESSAGE */
    - if (!g_ascii_strcasecmp(cmd, "GoIM")) {
    - char *bname = g_hash_table_lookup(params, "screenname");
    - if (bname) {
    - char *message = g_hash_table_lookup(params, "message");
    -
    - PurpleConversation *conv = purple_find_conversation_with_account(
    - PURPLE_CONV_TYPE_IM, bname, acct);
    - if (conv == NULL)
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, bname);
    - purple_conversation_present(conv);
    -
    - if (message) {
    - /* Spaces are encoded as '+' */
    - g_strdelimit(message, "+", ' ');
    - purple_conv_send_confirm(conv, 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;
    - }
    - /* aim:GoChat?roomname=CHATROOMNAME&exchange=4 */
    - else if (!g_ascii_strcasecmp(cmd, "GoChat")) {
    - char *rname = g_hash_table_lookup(params, "roomname");
    - if (rname) {
    - /* This is somewhat hacky, but the params aren't useful after this command */
    - g_hash_table_insert(params, g_strdup("exchange"), g_strdup("4"));
    - g_hash_table_insert(params, g_strdup("room"), g_strdup(rname));
    - 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;
    - }
    - /* aim:AddBuddy?screenname=SCREENNAME&groupname=GROUPNAME*/
    - else if (!g_ascii_strcasecmp(cmd, "AddBuddy")) {
    - char *bname = g_hash_table_lookup(params, "screenname");
    - char *gname = g_hash_table_lookup(params, "groupname");
    - purple_blist_request_add_buddy(acct, bname, gname, NULL);
    - return TRUE;
    - }
    -
    - return FALSE;
    -}
    -
    -void oscar_init(PurplePlugin *plugin, gboolean is_icq)
    -{
    - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
    - PurpleAccountOption *option;
    - static gboolean init = FALSE;
    - static const gchar *encryption_keys[] = {
    - N_("Use encryption if available"),
    - N_("Require encryption"),
    - N_("Don't use encryption"),
    - NULL
    - };
    - static const gchar *encryption_values[] = {
    - OSCAR_OPPORTUNISTIC_ENCRYPTION,
    - OSCAR_REQUIRE_ENCRYPTION,
    - OSCAR_NO_ENCRYPTION,
    - NULL
    - };
    - static const gchar *aim_login_keys[] = {
    - N_("clientLogin"),
    - N_("Kerberos"),
    - N_("MD5-based"),
    - NULL
    - };
    - static const gchar *aim_login_values[] = {
    - OSCAR_CLIENT_LOGIN,
    - OSCAR_KERBEROS_LOGIN,
    - OSCAR_MD5_LOGIN,
    - NULL
    - };
    - static const gchar *icq_login_keys[] = {
    - N_("clientLogin"),
    - N_("MD5-based"),
    - NULL
    - };
    - static const gchar *icq_login_values[] = {
    - OSCAR_CLIENT_LOGIN,
    - OSCAR_MD5_LOGIN,
    - NULL
    - };
    - const gchar **login_keys;
    - const gchar **login_values;
    - GList *encryption_options = NULL;
    - GList *login_options = NULL;
    - int i;
    -
    - option = purple_account_option_string_new(_("Server"), "server", get_login_server(is_icq, TRUE));
    - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
    -
    - option = purple_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT);
    - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
    -
    - for (i = 0; encryption_keys[i]; i++) {
    - PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1);
    - kvp->key = g_strdup(_(encryption_keys[i]));
    - kvp->value = g_strdup(encryption_values[i]);
    - encryption_options = g_list_append(encryption_options, kvp);
    - }
    - option = purple_account_option_list_new(_("Connection security"), "encryption", encryption_options);
    - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
    -
    - if (is_icq) {
    - login_keys = icq_login_keys;
    - login_values = icq_login_values;
    - } else {
    - login_keys = aim_login_keys;
    - login_values = aim_login_values;
    - }
    - for (i = 0; login_keys[i]; i++) {
    - PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1);
    - kvp->key = g_strdup(_(login_keys[i]));
    - kvp->value = g_strdup(login_values[i]);
    - login_options = g_list_append(login_options, kvp);
    - }
    - option = purple_account_option_list_new(_("Authentication method"), "login_type", login_options);
    - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
    -
    - option = purple_account_option_bool_new(
    - _("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy",
    - OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY);
    - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
    -
    - if (purple_strequal(purple_plugin_get_id(plugin), "prpl-aim")) {
    - option = purple_account_option_bool_new(_("Allow multiple simultaneous logins"), "allow_multiple_logins",
    - OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS);
    - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
    - }
    -
    - if (init)
    - return;
    - init = TRUE;
    -
    - /* Preferences */
    - purple_prefs_add_none("/plugins/prpl/oscar");
    - purple_prefs_add_bool("/plugins/prpl/oscar/recent_buddies", FALSE);
    -
    - purple_prefs_remove("/plugins/prpl/oscar/show_idle");
    - purple_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy");
    -
    - /* protocol handler */
    - /* TODO: figure out a good instance to use here */
    - purple_signal_connect(purple_get_core(), "uri-handler", &init,
    - PURPLE_CALLBACK(oscar_uri_handler), NULL);
    -}
    -
    --- a/libpurple/protocols/oscar/oscar.h Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1355 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Main libfaim header. Must be included in client for prototypes/macros.
    - *
    - * "come on, i turned a chick lesbian; i think this is the hackish equivalent"
    - * -- Josh Myer
    - *
    - */
    -
    -#ifndef _OSCAR_H_
    -#define _OSCAR_H_
    -
    -#include "internal.h"
    -#include "circbuffer.h"
    -#include "debug.h"
    -#include "eventloop.h"
    -#include "proxy.h"
    -#include "sslconn.h"
    -
    -#include <stdio.h>
    -#include <string.h>
    -#include <fcntl.h>
    -#include <sys/types.h>
    -#include <stdlib.h>
    -#include <stdarg.h>
    -#include <errno.h>
    -#include <time.h>
    -
    -#ifndef _WIN32
    -#include <sys/time.h>
    -#include <unistd.h>
    -#include <netdb.h>
    -#include <netinet/in.h>
    -#include <sys/socket.h>
    -#else
    -#include "libc_interface.h"
    -#endif
    -
    -typedef struct _ByteStream ByteStream;
    -typedef struct _ClientInfo ClientInfo;
    -typedef struct _FlapConnection FlapConnection;
    -typedef struct _FlapFrame FlapFrame;
    -typedef struct _IcbmArgsCh2 IcbmArgsCh2;
    -typedef struct _IcbmCookie IcbmCookie;
    -typedef struct _OscarData OscarData;
    -typedef struct _QueuedSnac QueuedSnac;
    -
    -typedef guint32 aim_snacid_t;
    -
    -#include "snactypes.h"
    -
    -#ifdef __cplusplus
    -extern "C" {
    -#endif
    -
    -#define FAIM_SNAC_HASH_SIZE 16
    -
    -/*
    - * Current Maximum Length for usernames (not including NULL)
    - *
    - * Currently only names up to 16 characters can be registered
    - * however it is apparently legal for them to be larger.
    - */
    -#define MAXSNLEN 97
    -
    -/*
    - * Current Maximum Length for Instant Messages
    - *
    - * This was found basically by experiment, but not wholly
    - * accurate experiment. It should not be regarded
    - * as completely correct. But its a decent approximation.
    - *
    - * Note that although we can send this much, its impossible
    - * for WinAIM clients (up through the latest (4.0.1957)) to
    - * send any more than 1kb. Amaze all your windows friends
    - * with utterly oversized instant messages!
    - */
    -#define MAXMSGLEN 2544
    -
    -/*
    - * Maximum size of a Buddy Icon.
    - */
    -#define MAXICONLEN 7168
    -#define AIM_ICONIDENT "AVT1picture.id"
    -
    -/*
    - * Found by trial and error.
    - */
    -#define MAXAVAILMSGLEN 251
    -
    -/**
    - * Maximum length for the password of an ICQ account
    - */
    -#define MAXICQPASSLEN 16
    -
    -#define AIM_MD5_STRING "AOL Instant Messenger (SM)"
    -
    -/*
    - * Client info. Filled in by the client and passed in to
    - * aim_send_login(). The information ends up getting passed to OSCAR
    - * through the initial login command.
    - *
    - */
    -struct _ClientInfo
    -{
    - const char *clientstring;
    - guint16 clientid;
    - guint16 major;
    - guint16 minor;
    - guint16 point;
    - guint16 build;
    - guint32 distrib;
    - const char *country; /* two-letter abbrev */
    - const char *lang; /* two-letter abbrev */
    -};
    -
    -/*
    - * We need to use the major-minor-micro versions from the official
    - * AIM and ICQ programs here or AOL won't let us use certain features.
    - *
    - * 0x00000611 is the distid given to us by AOL for use as the default
    - * libpurple distid.
    - */
    -#define CLIENTINFO_PURPLE_AIM { \
    - NULL, \
    - 0x0109, \
    - 0x0005, 0x0001, \
    - 0x0000, 0x0bdc, \
    - 0x00000611, \
    - "us", "en", \
    -}
    -
    -#define CLIENTINFO_PURPLE_ICQ { \
    - NULL, \
    - 0x010a, \
    - 0x0014, 0x0034, \
    - 0x0000, 0x0c18, \
    - 0x00000611, \
    - "us", "en", \
    -}
    -
    -typedef enum
    -{
    - OSCAR_DISCONNECT_DONE, /* not considered an error */
    - OSCAR_DISCONNECT_LOCAL_CLOSED, /* peer connections only, not considered an error */
    - OSCAR_DISCONNECT_REMOTE_CLOSED,
    - OSCAR_DISCONNECT_REMOTE_REFUSED, /* peer connections only */
    - OSCAR_DISCONNECT_LOST_CONNECTION,
    - OSCAR_DISCONNECT_INVALID_DATA,
    - OSCAR_DISCONNECT_COULD_NOT_CONNECT,
    - OSCAR_DISCONNECT_RETRYING /* peer connections only */
    -} OscarDisconnectReason;
    -
    -#define OSCAR_CAPABILITY_BUDDYICON 0x0000000000000001LL
    -#define OSCAR_CAPABILITY_TALK 0x0000000000000002LL
    -#define OSCAR_CAPABILITY_DIRECTIM 0x0000000000000004LL
    -#define OSCAR_CAPABILITY_CHAT 0x0000000000000008LL
    -#define OSCAR_CAPABILITY_GETFILE 0x0000000000000010LL
    -#define OSCAR_CAPABILITY_SENDFILE 0x0000000000000020LL
    -#define OSCAR_CAPABILITY_GAMES 0x0000000000000040LL
    -#define OSCAR_CAPABILITY_ADDINS 0x0000000000000080LL
    -#define OSCAR_CAPABILITY_SENDBUDDYLIST 0x0000000000000100LL
    -#define OSCAR_CAPABILITY_GAMES2 0x0000000000000200LL
    -#define OSCAR_CAPABILITY_ICQ_DIRECT 0x0000000000000400LL
    -#define OSCAR_CAPABILITY_APINFO 0x0000000000000800LL
    -#define OSCAR_CAPABILITY_ICQRTF 0x0000000000001000LL
    -#define OSCAR_CAPABILITY_EMPTY 0x0000000000002000LL
    -#define OSCAR_CAPABILITY_ICQSERVERRELAY 0x0000000000004000LL
    -#define OSCAR_CAPABILITY_UNICODEOLD 0x0000000000008000LL
    -#define OSCAR_CAPABILITY_TRILLIANCRYPT 0x0000000000010000LL
    -#define OSCAR_CAPABILITY_UNICODE 0x0000000000020000LL
    -#define OSCAR_CAPABILITY_INTEROPERATE 0x0000000000040000LL
    -#define OSCAR_CAPABILITY_SHORTCAPS 0x0000000000080000LL
    -#define OSCAR_CAPABILITY_HIPTOP 0x0000000000100000LL
    -#define OSCAR_CAPABILITY_SECUREIM 0x0000000000200000LL
    -#define OSCAR_CAPABILITY_SMS 0x0000000000400000LL
    -#define OSCAR_CAPABILITY_VIDEO 0x0000000000800000LL
    -#define OSCAR_CAPABILITY_ICHATAV 0x0000000001000000LL
    -#define OSCAR_CAPABILITY_LIVEVIDEO 0x0000000002000000LL
    -#define OSCAR_CAPABILITY_CAMERA 0x0000000004000000LL
    -#define OSCAR_CAPABILITY_ICHAT_SCREENSHARE 0x0000000008000000LL
    -#define OSCAR_CAPABILITY_TYPING 0x0000000010000000LL
    -#define OSCAR_CAPABILITY_NEWCAPS 0x0000000020000000LL
    -#define OSCAR_CAPABILITY_XTRAZ 0x0000000040000000LL
    -#define OSCAR_CAPABILITY_GENERICUNKNOWN 0x0000000080000000LL
    -#define OSCAR_CAPABILITY_HTML_MSGS 0x0000000100000000LL
    -#define OSCAR_CAPABILITY_LAST 0x0000000200000000LL
    -
    -#define OSCAR_STATUS_ID_INVISIBLE "invisible"
    -#define OSCAR_STATUS_ID_OFFLINE "offline"
    -#define OSCAR_STATUS_ID_AVAILABLE "available"
    -#define OSCAR_STATUS_ID_AWAY "away"
    -#define OSCAR_STATUS_ID_DND "dnd"
    -#define OSCAR_STATUS_ID_NA "na"
    -#define OSCAR_STATUS_ID_OCCUPIED "occupied"
    -#define OSCAR_STATUS_ID_FREE4CHAT "free4chat"
    -#define OSCAR_STATUS_ID_CUSTOM "custom"
    -#define OSCAR_STATUS_ID_MOBILE "mobile"
    -#define OSCAR_STATUS_ID_EVIL "evil"
    -#define OSCAR_STATUS_ID_DEPRESSION "depression"
    -#define OSCAR_STATUS_ID_ATHOME "athome"
    -#define OSCAR_STATUS_ID_ATWORK "atwork"
    -#define OSCAR_STATUS_ID_LUNCH "lunch"
    -
    -/*
    - * Byte Stream type. Sort of.
    - *
    - * Use of this type serves a couple purposes:
    - * - Buffer/buflen pairs are passed all around everywhere. This turns
    - * that into one value, as well as abstracting it slightly.
    - * - Through the abstraction, it is possible to enable bounds checking
    - * for robustness at the cost of performance. But a clean failure on
    - * weird packets is much better than a segfault.
    - * - I like having variables named "bs".
    - *
    - * Don't touch the insides of this struct. Or I'll have to kill you.
    - *
    - */
    -struct _ByteStream
    -{
    - guint8 *data;
    - size_t len;
    - size_t offset;
    -};
    -
    -struct _QueuedSnac
    -{
    - guint16 family;
    - guint16 subtype;
    - FlapFrame *frame;
    -};
    -
    -struct _FlapFrame
    -{
    - guint8 channel;
    - guint16 seqnum;
    - ByteStream data; /* payload stream */
    -};
    -
    -struct _FlapConnection
    -{
    - OscarData *od; /**< Pointer to parent session. */
    - gboolean connected;
    - time_t lastactivity; /**< Time of last transmit. */
    - guint destroy_timeout;
    - OscarDisconnectReason disconnect_reason;
    - gchar *error_message;
    - guint16 disconnect_code;
    -
    - /* A few variables that are only used when connecting */
    - PurpleProxyConnectData *connect_data;
    - guint16 cookielen;
    - guint8 *cookie;
    - gpointer new_conn_data;
    -
    - int fd;
    - PurpleSslConnection *gsc;
    - guint8 header[6];
    - gssize header_received;
    - FlapFrame buffer_incoming;
    - PurpleCircBuffer *buffer_outgoing;
    - guint watcher_incoming;
    - guint watcher_outgoing;
    -
    - guint16 type;
    - guint16 subtype;
    - guint16 seqnum_out; /**< The sequence number of most recently sent packet. */
    - guint16 seqnum_in; /**< The sequence number of most recently received packet. */
    - GSList *groups;
    - GSList *rateclasses; /* Contains nodes of struct rateclass. */
    - struct rateclass *default_rateclass;
    - GHashTable *rateclass_members; /* Key is family and subtype, value is pointer to the rateclass struct to use. */
    -
    - GQueue *queued_snacs; /**< Contains QueuedSnacs. */
    - GQueue *queued_lowpriority_snacs; /**< Contains QueuedSnacs to send only once queued_snacs is empty */
    - guint queued_timeout;
    -
    - void *internal; /* internal conn-specific libfaim data */
    -};
    -
    -struct _IcbmCookie
    -{
    - guchar cookie[8];
    - int type;
    - void *data;
    - time_t addtime;
    - struct _IcbmCookie *next;
    -};
    -
    -#include "peer.h"
    -
    -/*
    - * AIM Session: The main client-data interface.
    - *
    - */
    -struct _OscarData
    -{
    - /** Only used when connecting with clientLogin */
    - PurpleUtilFetchUrlData *url_data;
    -
    - gboolean iconconnecting;
    - gboolean set_icon;
    -
    - GSList *create_rooms;
    -
    - gboolean conf;
    - gboolean reqemail;
    - gboolean setemail;
    - char *email;
    - gboolean setnick;
    - char *newformatting;
    - gboolean chpass;
    - char *oldp;
    - char *newp;
    -
    - GSList *oscar_chats;
    - GHashTable *buddyinfo;
    - GSList *requesticon;
    -
    - gboolean use_ssl;
    - gboolean icq;
    - guint getblisttimer;
    -
    - struct {
    - guint maxwatchers; /* max users who can watch you */
    - guint maxbuddies; /* max users you can watch */
    - guint maxgroups; /* max groups in server list */
    - guint maxpermits; /* max users on permit list */
    - guint maxdenies; /* max users on deny list */
    - guint maxsiglen; /* max size (bytes) of profile */
    - guint maxawaymsglen; /* max size (bytes) of posted away message */
    - } rights;
    -
    - PurpleConnection *gc;
    -
    - void *modlistv;
    -
    - /*
    - * Outstanding snac handling
    - *
    - * TODO: Should these be per-connection? -mid
    - */
    - void *snac_hash[FAIM_SNAC_HASH_SIZE];
    - aim_snacid_t snacid_next;
    -
    - /*
    - * TODO: Data specific to a certain family should go into a
    - * hashtable and the core parts of libfaim shouldn't
    - * need to know about them.
    - */
    -
    - IcbmCookie *msgcookies;
    - GSList *icq_info;
    -
    - /** Only used when connecting with the old-style BUCP login. */
    - struct aim_authresp_info *authinfo;
    - struct aim_emailinfo *emailinfo;
    -
    - struct {
    - struct aim_userinfo_s *userinfo;
    - } locate;
    -
    - struct {
    - gboolean have_rights;
    - } bos;
    -
    - /* Server-stored information (ssi) */
    - struct {
    - gboolean received_data;
    - guint16 numitems;
    - struct aim_ssi_item *official;
    - struct aim_ssi_item *local;
    - struct aim_ssi_tmp *pending;
    - time_t timestamp;
    - gboolean waiting_for_ack;
    - gboolean in_transaction;
    - } ssi;
    -
    - /** Contains pointers to handler functions for each family/subtype. */
    - GHashTable *handlerlist;
    -
    - /** A linked list containing FlapConnections. */
    - GSList *oscar_connections;
    - guint16 default_port;
    -
    - /** A linked list containing PeerConnections. */
    - GSList *peer_connections;
    -};
    -
    -/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */
    -#define AIM_ICQ_STATE_NORMAL 0x00000000
    -#define AIM_ICQ_STATE_AWAY 0x00000001
    -#define AIM_ICQ_STATE_DND 0x00000002
    -#define AIM_ICQ_STATE_OUT 0x00000004
    -#define AIM_ICQ_STATE_BUSY 0x00000010
    -#define AIM_ICQ_STATE_CHAT 0x00000020
    -#define AIM_ICQ_STATE_INVISIBLE 0x00000100
    -#define AIM_ICQ_STATE_EVIL 0x00003000
    -#define AIM_ICQ_STATE_DEPRESSION 0x00004000
    -#define AIM_ICQ_STATE_ATHOME 0x00005000
    -#define AIM_ICQ_STATE_ATWORK 0x00006000
    -#define AIM_ICQ_STATE_LUNCH 0x00002001
    -#define AIM_ICQ_STATE_EVIL 0x00003000
    -#define AIM_ICQ_STATE_WEBAWARE 0x00010000
    -#define AIM_ICQ_STATE_HIDEIP 0x00020000
    -#define AIM_ICQ_STATE_BIRTHDAY 0x00080000
    -#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000
    -#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -struct aim_clientrelease
    -{
    - char *name;
    - guint32 build;
    - char *url;
    - char *info;
    -};
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -struct aim_authresp_info
    -{
    - char *bn;
    - guint16 errorcode;
    - char *errorurl;
    - guint16 regstatus;
    - char *email;
    - char *bosip;
    - guint16 cookielen;
    - guint8 *cookie;
    - char *chpassurl;
    - struct aim_clientrelease latestrelease;
    - struct aim_clientrelease latestbeta;
    -};
    -
    -/* Callback data for redirect. */
    -struct aim_redirect_data
    -{
    - guint16 group;
    - const char *ip;
    - guint16 cookielen;
    - const guint8 *cookie;
    - const char *ssl_cert_cn;
    - guint8 use_ssl;
    - struct { /* group == SNAC_FAMILY_CHAT */
    - guint16 exchange;
    - const char *room;
    - guint16 instance;
    - } chat;
    -};
    -
    -int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen, const char *tls_certname);
    -
    -/* family_auth.c */
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -int aim_request_login(OscarData *od, FlapConnection *conn, const char *bn);
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -int aim_send_login(OscarData *od, FlapConnection *conn, const char *bn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins);
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -/* 0x000b */ int aim_auth_securid_send(OscarData *od, const char *securid);
    -
    -/**
    - * Only used when connecting with clientLogin.
    - */
    -void send_client_login(OscarData *od, const char *username);
    -
    -/**
    - * Only used when connecting with kerberos login.
    - */
    -void send_kerberos_login(OscarData *od, const char *username);
    -
    -/* flap_connection.c */
    -FlapConnection *flap_connection_new(OscarData *, int type);
    -void flap_connection_close(OscarData *od, FlapConnection *conn);
    -void flap_connection_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message);
    -void flap_connection_schedule_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message);
    -FlapConnection *flap_connection_findbygroup(OscarData *od, guint16 group);
    -FlapConnection *flap_connection_getbytype(OscarData *, int type);
    -FlapConnection *flap_connection_getbytype_all(OscarData *, int type);
    -void flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond);
    -void flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
    -
    -void flap_connection_send(FlapConnection *conn, FlapFrame *frame);
    -void flap_connection_send_version(OscarData *od, FlapConnection *conn);
    -void flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy);
    -void flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci, gboolean allow_multiple_login);
    -void flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data);
    -void flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority);
    -void flap_connection_send_keepalive(OscarData *od, FlapConnection *conn);
    -FlapFrame *flap_frame_new(OscarData *od, guint16 channel, int datalen);
    -
    -/* oscar_data.c */
    -typedef int (*aim_rxcallback_t)(OscarData *od, FlapConnection *conn, FlapFrame *frame, ...);
    -
    -OscarData *oscar_data_new(void);
    -void oscar_data_destroy(OscarData *);
    -void oscar_data_addhandler(OscarData *od, guint16 family, guint16 subtype, aim_rxcallback_t newhandler, guint16 flags);
    -aim_rxcallback_t aim_callhandler(OscarData *od, guint16 family, guint16 subtype);
    -
    -/* 0x0001 - family_oservice.c */
    -/* 0x0002 */ void aim_srv_clientready(OscarData *od, FlapConnection *conn);
    -/* 0x0004 */ void aim_srv_requestnew(OscarData *od, guint16 serviceid);
    -/* 0x0006 */ void aim_srv_reqrates(OscarData *od, FlapConnection *conn);
    -/* 0x0008 */ void aim_srv_rates_addparam(OscarData *od, FlapConnection *conn);
    -/* 0x000e */ void aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn);
    -/* 0x0011 */ void aim_srv_setidle(OscarData *od, guint32 idletime);
    -/* 0x0017 */ void aim_srv_setversions(OscarData *od, FlapConnection *conn);
    -/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setstatusmsg, const char *statusmsg, const char *itmsurl);
    -void aim_srv_set_dc_info(OscarData *od);
    -
    -
    -void aim_bos_reqrights(OscarData *od, FlapConnection *conn);
    -
    -#define AIM_RATE_CODE_LIMIT 0x0003
    -
    -/* family_icbm.c */
    -#define AIM_OFT_SUBTYPE_SEND_DIR 0x0002
    -
    -#define AIM_TRANSFER_DENY_DECLINE 0x0001
    -
    -#define AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED 0x00000001
    -#define AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED 0x00000002
    -#define AIM_IMPARAM_FLAG_EVENTS_ALLOWED 0x00000008
    -#define AIM_IMPARAM_FLAG_SMS_SUPPORTED 0x00000010
    -#define AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED 0x00000100
    -
    -/**
    - * This flag tells the server that we always send HTML in messages
    - * sent from an ICQ account to an ICQ account. (If this flag is
    - * not sent then plaintext is sent ICQ<-->ICQ (HTML is sent in all
    - * other cases)).
    - *
    - * If we send an HTML message to an old client that doesn't support
    - * HTML messages, then the oscar servers will merrily strip the HTML
    - * for us.
    - *
    - * All incoming IMs are treated as HTML.
    - */
    -#define AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ 0x00000400
    -
    -struct aim_icbmparameters
    -{
    - guint16 maxchan;
    - guint32 flags; /* AIM_IMPARAM_FLAG_ */
    - guint16 maxmsglen; /* message size that you will accept */
    - guint16 maxsenderwarn; /* this and below are *10 (999=99.9%) */
    - guint16 maxrecverwarn;
    - guint32 minmsginterval; /* in milliseconds? */
    -};
    -
    -/*
    - * TODO: Should probably combine this with struct chat_connection.
    - */
    -struct aim_chat_roominfo
    -{
    - guint16 exchange;
    - char *name;
    - guint8 namelen;
    - guint16 instance;
    -};
    -
    -struct chat_connection
    -{
    - char *name;
    - char *show; /* AOL did something funny to us */
    - guint16 exchange;
    - guint16 instance;
    - FlapConnection *conn;
    - int id;
    - PurpleConnection *gc;
    - PurpleConversation *conv;
    - guint16 maxlen;
    - guint16 maxvis;
    -};
    -
    -/*
    - * All this chat struct stuff should be in family_chat.c
    - */
    -void oscar_chat_destroy(struct chat_connection *cc);
    -
    -#define AIM_IMFLAGS_AWAY 0x0001 /* mark as an autoreply */
    -#define AIM_IMFLAGS_ACK 0x0002 /* request a receipt notice */
    -#define AIM_IMFLAGS_BUDDYREQ 0x0010 /* buddy icon requested */
    -#define AIM_IMFLAGS_HASICON 0x0020 /* already has icon */
    -#define AIM_IMFLAGS_SUBENC_MACINTOSH 0x0040 /* damn that Steve Jobs! */
    -#define AIM_IMFLAGS_CUSTOMFEATURES 0x0080 /* features field present */
    -#define AIM_IMFLAGS_OFFLINE 0x0800 /* send to offline user */
    -#define AIM_IMFLAGS_TYPINGNOT 0x1000 /* typing notification */
    -
    -#define AIM_CHARSET_ASCII 0x0000 /* ISO 646 */
    -#define AIM_CHARSET_UNICODE 0x0002 /* ISO 10646 (UTF-16/UCS-2BE) */
    -#define AIM_CHARSET_LATIN_1 0x0003 /* ISO 8859-1 */
    -
    -/*
    - * Arguments to aim_send_im_ext().
    - *
    - * This is really complicated. But immensely versatile.
    - *
    - */
    -struct aim_sendimext_args
    -{
    - /* These are _required_ */
    - const char *destbn;
    - guint32 flags; /* often 0 */
    -
    - const char *msg;
    - gsize msglen;
    -
    - /* Only used if AIM_IMFLAGS_HASICON is set */
    - guint32 iconlen;
    - time_t iconstamp;
    - guint32 iconsum;
    -
    - guint16 featureslen;
    - guint8 *features;
    -
    - guint16 charset;
    -};
    -
    -/*
    - * This information is provided in the Incoming ICBM callback for
    - * Channel 1 ICBM's.
    - */
    -struct aim_incomingim_ch1_args
    -{
    - guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */
    - time_t timestamp; /* Only set for offline messages */
    -
    - gchar *msg;
    -
    - /* Only provided if AIM_IMFLAGS_HASICON is set */
    - time_t iconstamp;
    - guint32 iconlen;
    - guint16 iconsum;
    -};
    -
    -/* Valid values for channel 2 args->status */
    -#define AIM_RENDEZVOUS_PROPOSE 0x0000
    -#define AIM_RENDEZVOUS_CANCEL 0x0001
    -#define AIM_RENDEZVOUS_CONNECTED 0x0002
    -
    -struct _IcbmArgsCh2
    -{
    - guint16 status;
    - guchar cookie[8];
    - guint64 type; /* One of the OSCAR_CAPABILITY_ constants */
    - const char *proxyip;
    - const char *clientip;
    - const char *verifiedip;
    - guint16 port;
    - gboolean use_proxy;
    - guint16 errorcode;
    - const char *msg; /* invite message or file description */
    - guint16 msglen;
    - const char *encoding;
    - const char *language;
    - guint16 requestnumber;
    - union {
    - struct {
    - guint32 checksum;
    - guint32 length;
    - time_t timestamp;
    - guint8 *icon;
    - } icon;
    - struct {
    - struct aim_chat_roominfo roominfo;
    - } chat;
    - struct {
    - guint8 msgtype;
    - const char *msg;
    - } rtfmsg;
    - struct {
    - guint16 subtype;
    - guint16 totfiles;
    - guint32 totsize;
    - char *filename;
    - } sendfile;
    - } info;
    - void *destructor; /* used internally only */
    -};
    -
    -struct aim_incomingim_ch4_args
    -{
    - guint32 uin; /* Of the sender of the ICBM */
    - guint8 type;
    - guint8 flags;
    - gchar *msg; /* Reason for auth request, deny, or accept */
    - int msglen;
    -};
    -
    -/* SNAC sending functions */
    -/* 0x0002 */ int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params);
    -/* 0x0004 */ int aim_im_reqparams(OscarData *od);
    -/* 0x0006 */ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args);
    -/* 0x0006 */ int aim_im_sendch1(OscarData *, const char *destbn, guint16 flags, const char *msg);
    -/* 0x0006 */ int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance);
    -/* 0x0006 */ int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum);
    -
    -/* 0x0006 */ void aim_im_sendch2_cancel(PeerConnection *peer_conn);
    -/* 0x0006 */ void aim_im_sendch2_connected(PeerConnection *peer_conn);
    -/* 0x0006 */ void aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber);
    -/* 0x0006 */ void aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber);
    -/* 0x0006 */ void aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles);
    -/* 0x0006 */ void aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles);
    -
    -/* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code);
    -/* 0x0010 */ int aim_im_reqofflinemsgs(OscarData *od);
    -/* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *bn, guint16 type2);
    -/* 0x000b */ int icq_relay_xstatus (OscarData *od, const char *sn, const guchar* cookie);
    -void aim_icbm_makecookie(guchar* cookie);
    -void aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie);
    -
    -/* 0x0002 - family_locate.c */
    -/*
    - * AIM User Info, Standard Form.
    - */
    -#define AIM_FLAG_ADMINISTRATOR 0x0002
    -#define AIM_FLAG_AOL 0x0004
    -#define AIM_FLAG_AWAY 0x0020
    -#define AIM_FLAG_WIRELESS 0x0080
    -#define AIM_FLAG_ICQ 0x0040
    -#define AIM_FLAG_ACTIVEBUDDY 0x0400
    -
    -#define AIM_USERINFO_PRESENT_FLAGS 0x00000001
    -#define AIM_USERINFO_PRESENT_MEMBERSINCE 0x00000002
    -#define AIM_USERINFO_PRESENT_ONLINESINCE 0x00000004
    -#define AIM_USERINFO_PRESENT_IDLE 0x00000008
    -#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010
    -#define AIM_USERINFO_PRESENT_ICQIPADDR 0x00000020
    -#define AIM_USERINFO_PRESENT_ICQDATA 0x00000040
    -#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080
    -#define AIM_USERINFO_PRESENT_SESSIONLEN 0x00000100
    -#define AIM_USERINFO_PRESENT_CREATETIME 0x00000200
    -
    -struct userinfo_node
    -{
    - char *bn;
    - struct userinfo_node *next;
    -};
    -
    -typedef struct aim_userinfo_s
    -{
    - char *bn;
    - guint16 warnlevel; /* evil percent * 10 (999 = 99.9%) */
    - guint16 idletime; /* in seconds */
    - guint16 flags;
    - guint32 createtime; /* time_t */
    - guint32 membersince; /* time_t */
    - guint32 onlinesince; /* time_t */
    - guint32 sessionlen; /* in seconds */
    - guint64 capabilities;
    - struct {
    - guint32 status;
    - guint32 ipaddr;
    - guint8 crap[0x25]; /* until we figure it out... */
    - } icqinfo;
    - guint32 present;
    -
    - guint8 iconcsumtype;
    - guint16 iconcsumlen;
    - guint8 *iconcsum;
    -
    - char *info;
    - char *info_encoding;
    - guint16 info_len;
    -
    - char *status;
    - char *status_encoding;
    - guint16 status_len;
    -
    - char *itmsurl;
    - char *itmsurl_encoding;
    - guint16 itmsurl_len;
    -
    - char *away;
    - char *away_encoding;
    - guint16 away_len;
    -
    - struct aim_userinfo_s *next;
    -} aim_userinfo_t;
    -
    -#define AIM_SENDMEMBLOCK_FLAG_ISREQUEST 0
    -#define AIM_SENDMEMBLOCK_FLAG_ISHASH 1
    -
    -int aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag);
    -
    -struct aim_invite_priv
    -{
    - char *bn;
    - char *roomname;
    - guint16 exchange;
    - guint16 instance;
    -};
    -
    -#define AIM_COOKIETYPE_CHAT 0x01
    -#define AIM_COOKIETYPE_INVITE 0x02
    -
    -aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn);
    -void aim_locate_dorequest(OscarData *od);
    -
    -/* 0x0002 */ int aim_locate_reqrights(OscarData *od);
    -/* 0x0004 */ int aim_locate_setcaps(OscarData *od, guint64 caps);
    -/* 0x0004 */ int aim_locate_setprofile(OscarData *od, const char *profile_encoding, const gchar *profile, const int profile_len, const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len);
    -/* 0x0015 */ int aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags);
    -
    -guint64 aim_locate_getcaps(OscarData *od, ByteStream *bs, int len);
    -guint64 aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len);
    -void aim_info_free(aim_userinfo_t *);
    -int aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *);
    -int aim_putuserinfo(ByteStream *bs, aim_userinfo_t *info);
    -PurpleMood* icq_get_purple_moods(PurpleAccount *account);
    -const char* icq_get_custom_icon_description(const char *mood);
    -guint8* icq_get_custom_icon_data(const char *mood);
    -int icq_im_xstatus_request(OscarData *od, const char *sn);
    -
    -/* 0x0003 - family_buddy.c */
    -/* 0x0002 */ void aim_buddylist_reqrights(OscarData *, FlapConnection *);
    -
    -
    -/* 0x000a - family_userlookup.c */
    -int aim_search_address(OscarData *, const char *);
    -
    -struct aim_chat_exchangeinfo
    -{
    - guint16 number;
    - guint16 flags;
    - char *name;
    - char *charset1;
    - char *lang1;
    - char *charset2;
    - char *lang2;
    -};
    -
    -#define AIM_CHATFLAGS_NOREFLECT 0x0001
    -#define AIM_CHATFLAGS_AWAY 0x0002
    -int aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language);
    -int aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance);
    -
    -void aim_chatnav_reqrights(OscarData *od, FlapConnection *conn);
    -
    -int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name, guint16 exchange);
    -
    -
    -/* 0x0010 - family_bart.c */
    -int aim_bart_upload(OscarData *od, const guint8 *icon, guint16 iconlen);
    -int aim_bart_request(OscarData *od, const char *bn, guint8 iconcsumtype, const guint8 *iconstr, guint16 iconstrlen);
    -
    -
    -
    -/* 0x0013 - family_feedbag.c */
    -#define AIM_SSI_TYPE_BUDDY 0x0000
    -#define AIM_SSI_TYPE_GROUP 0x0001
    -#define AIM_SSI_TYPE_PERMIT 0x0002
    -#define AIM_SSI_TYPE_DENY 0x0003
    -#define AIM_SSI_TYPE_PDINFO 0x0004
    -#define AIM_SSI_TYPE_PRESENCEPREFS 0x0005
    -#define AIM_SSI_TYPE_ICQDENY 0x000e
    -#define AIM_SSI_TYPE_ICONINFO 0x0014
    -
    -/* These flags are set in the 0x00c9 TLV of SSI type 0x0005 */
    -#define AIM_SSI_PRESENCE_FLAG_SHOWIDLE 0x00000400
    -#define AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES 0x00020000
    -
    -struct aim_ssi_item
    -{
    - char *name;
    - guint16 gid;
    - guint16 bid;
    - guint16 type;
    - GSList *data;
    - struct aim_ssi_item *next;
    -};
    -
    -struct aim_ssi_tmp
    -{
    - guint16 action;
    - guint16 ack;
    - char *name;
    - struct aim_ssi_item *item;
    - struct aim_ssi_tmp *next;
    -};
    -
    -/* These build the actual SNACs and queue them to be sent */
    -/* 0x0002 */ int aim_ssi_reqrights(OscarData *od);
    -/* 0x0004 */ int aim_ssi_reqdata(OscarData *od);
    -/* 0x0007 */ int aim_ssi_enable(OscarData *od);
    -/* 0x0011 */ int aim_ssi_modbegin(OscarData *od);
    -/* 0x0012 */ int aim_ssi_modend(OscarData *od);
    -/* 0x0018 */ int aim_ssi_sendauthrequest(OscarData *od, const char *bn, const char *msg);
    -/* 0x001a */ int aim_ssi_sendauthreply(OscarData *od, const char *bn, guint8 reply, const char *msg);
    -
    -/* Client functions for retrieving SSI data */
    -struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid);
    -struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *bn, guint16 type);
    -struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *bn);
    -char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *bn);
    -int aim_ssi_getpermdeny(struct aim_ssi_item *list);
    -guint32 aim_ssi_getpresence(struct aim_ssi_item *list);
    -char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *bn);
    -char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *bn);
    -gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *bn);
    -
    -/* Client functions for changing SSI data */
    -int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *tlvlist, const char *alias, const char *comment, const char *smsnum, gboolean needauth);
    -int aim_ssi_delbuddy(OscarData *od, const char *name, const char *group);
    -int aim_ssi_delgroup(OscarData *od, const char *group);
    -int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *bn);
    -int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias);
    -int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *alias);
    -int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn);
    -int aim_ssi_cleanlist(OscarData *od);
    -int aim_ssi_deletelist(OscarData *od);
    -int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny);
    -int aim_ssi_setpresence(OscarData *od, guint32 presence);
    -int aim_ssi_seticon(OscarData *od, const guint8 *iconsum, guint8 iconsumlen);
    -int aim_ssi_delicon(OscarData *od);
    -int aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type);
    -int aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type);
    -
    -guint16 aim_ssi_getdenyentrytype(OscarData* od);
    -
    -struct aim_icq_info
    -{
    - guint16 reqid;
    -
    - /* simple */
    - guint32 uin;
    -
    - /* general and "home" information (0x00c8) */
    - char *nick;
    - char *first;
    - char *last;
    - char *email;
    - char *homecity;
    - char *homestate;
    - char *homephone;
    - char *homefax;
    - char *homeaddr;
    - char *mobile;
    - char *homezip;
    - guint16 homecountry;
    -/* guint8 timezone;
    - guint8 hideemail; */
    -
    - /* personal (0x00dc) */
    - guint8 age;
    - guint8 unknown;
    - guint8 gender;
    - char *personalwebpage;
    - guint16 birthyear;
    - guint8 birthmonth;
    - guint8 birthday;
    - guint8 language1;
    - guint8 language2;
    - guint8 language3;
    -
    - /* work (0x00d2) */
    - char *workcity;
    - char *workstate;
    - char *workphone;
    - char *workfax;
    - char *workaddr;
    - char *workzip;
    - guint16 workcountry;
    - char *workcompany;
    - char *workdivision;
    - char *workposition;
    - char *workwebpage;
    -
    - /* additional personal information (0x00e6) */
    - char *info;
    -
    - /* email (0x00eb) */
    - guint16 numaddresses;
    - char **email2;
    -
    - /* status note info */
    - guint8 icbm_cookie[8];
    - char *status_note_title;
    -
    - gboolean for_auth_request;
    - char *auth_request_reason;
    -};
    -
    -int aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware);
    -int aim_icq_changepasswd(OscarData *od, const char *passwd);
    -int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason);
    -int aim_icq_getallinfo(OscarData *od, const char *uin);
    -int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias);
    -
    -
    -/* 0x0017 - family_auth.c */
    -void aim_sendcookie(OscarData *, FlapConnection *, const guint16 length, const guint8 *);
    -void aim_admin_changepasswd(OscarData *, FlapConnection *, const char *newpw, const char *curpw);
    -void aim_admin_reqconfirm(OscarData *od, FlapConnection *conn);
    -void aim_admin_getinfo(OscarData *od, FlapConnection *conn, guint16 info);
    -void aim_admin_setemail(OscarData *od, FlapConnection *conn, const char *newemail);
    -void aim_admin_setnick(OscarData *od, FlapConnection *conn, const char *newnick);
    -
    -
    -
    -/* 0x0018 - family_alert.c */
    -struct aim_emailinfo
    -{
    - guint8 *cookie16;
    - guint8 *cookie8;
    - char *url;
    - guint16 nummsgs;
    - guint8 unread;
    - char *domain;
    - guint16 flag;
    - struct aim_emailinfo *next;
    -};
    -
    -int aim_email_sendcookies(OscarData *od);
    -int aim_email_activate(OscarData *od);
    -
    -
    -
    -/* tlv.c - TLV handling */
    -
    -/* TLV structure */
    -typedef struct aim_tlv_s
    -{
    - guint16 type;
    - guint16 length;
    - guint8 *value;
    -} aim_tlv_t;
    -
    -/* TLV handling functions */
    -char *aim_tlv_getvalue_as_string(aim_tlv_t *tlv);
    -
    -aim_tlv_t *aim_tlv_gettlv(GSList *list, const guint16 type, const int nth);
    -int aim_tlv_getlength(GSList *list, const guint16 type, const int nth);
    -char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth);
    -guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth);
    -guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth);
    -guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth);
    -
    -/* TLV list handling functions */
    -GSList *aim_tlvlist_read(ByteStream *bs);
    -GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num);
    -GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len);
    -GSList *aim_tlvlist_copy(GSList *orig);
    -
    -int aim_tlvlist_count(GSList *list);
    -int aim_tlvlist_size(GSList *list);
    -int aim_tlvlist_cmp(GSList *one, GSList *two);
    -int aim_tlvlist_write(ByteStream *bs, GSList **list);
    -void aim_tlvlist_free(GSList *list);
    -
    -int aim_tlvlist_add_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value);
    -int aim_tlvlist_add_noval(GSList **list, const guint16 type);
    -int aim_tlvlist_add_8(GSList **list, const guint16 type, const guint8 value);
    -int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value);
    -int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value);
    -int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value);
    -int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint64 caps, const char *mood);
    -int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo);
    -int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance);
    -int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tl);
    -
    -int aim_tlvlist_replace_raw(GSList **list, const guint16 type, const guint16 lenth, const guint8 *value);
    -int aim_tlvlist_replace_str(GSList **list, const guint16 type, const char *str);
    -int aim_tlvlist_replace_noval(GSList **list, const guint16 type);
    -int aim_tlvlist_replace_8(GSList **list, const guint16 type, const guint8 value);
    -int aim_tlvlist_replace_16(GSList **list, const guint16 type, const guint16 value);
    -int aim_tlvlist_replace_32(GSList **list, const guint16 type, const guint32 value);
    -
    -void aim_tlvlist_remove(GSList **list, const guint16 type);
    -
    -
    -
    -/* util.c */
    -/* These are really ugly. You'd think this was LISP. I wish it was. */
    -#define aimutil_put8(buf, data) ((*(buf) = (guint8)(data)&0xff),1)
    -#define aimutil_get8(buf) ((*(buf))&0xff)
    -#define aimutil_put16(buf, data) ( \
    - (*(buf) = (guint8)((data)>>8)&0xff), \
    - (*((buf)+1) = (guint8)(data)&0xff), \
    - 2)
    -#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
    -#define aimutil_put32(buf, data) ( \
    - (*((buf)) = (guint8)((data)>>24)&0xff), \
    - (*((buf)+1) = (guint8)((data)>>16)&0xff), \
    - (*((buf)+2) = (guint8)((data)>>8)&0xff), \
    - (*((buf)+3) = (guint8)(data)&0xff), \
    - 4)
    -#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \
    - (((*((buf)+1))<<16)&0x00ff0000) + \
    - (((*((buf)+2))<< 8)&0x0000ff00) + \
    - (((*((buf)+3) )&0x000000ff)))
    -
    -/* Little-endian versions (damn ICQ) */
    -#define aimutil_putle8(buf, data) ( \
    - (*(buf) = (guint8)(data) & 0xff), \
    - 1)
    -#define aimutil_getle8(buf) ( \
    - (*(buf)) & 0xff \
    - )
    -#define aimutil_putle16(buf, data) ( \
    - (*((buf)+0) = (guint8)((data) >> 0) & 0xff), \
    - (*((buf)+1) = (guint8)((data) >> 8) & 0xff), \
    - 2)
    -#define aimutil_getle16(buf) ( \
    - (((*((buf)+0)) << 0) & 0x00ff) + \
    - (((*((buf)+1)) << 8) & 0xff00) \
    - )
    -#define aimutil_putle32(buf, data) ( \
    - (*((buf)+0) = (guint8)((data) >> 0) & 0xff), \
    - (*((buf)+1) = (guint8)((data) >> 8) & 0xff), \
    - (*((buf)+2) = (guint8)((data) >> 16) & 0xff), \
    - (*((buf)+3) = (guint8)((data) >> 24) & 0xff), \
    - 4)
    -#define aimutil_getle32(buf) ( \
    - (((*((buf)+0)) << 0) & 0x000000ff) + \
    - (((*((buf)+1)) << 8) & 0x0000ff00) + \
    - (((*((buf)+2)) << 16) & 0x00ff0000) + \
    - (((*((buf)+3)) << 24) & 0xff000000))
    -
    -const char *oscar_get_msgerr_reason(size_t reason);
    -int oscar_get_ui_info_int(const char *str, int default_value);
    -const char *oscar_get_ui_info_string(const char *str, const char *default_value);
    -gchar *oscar_get_clientstring(void);
    -
    -guint16 aimutil_iconsum(const guint8 *buf, int buflen);
    -
    -gboolean oscar_util_valid_name(const char *bn);
    -gboolean oscar_util_valid_name_icq(const char *bn);
    -gboolean oscar_util_valid_name_sms(const char *bn);
    -int oscar_util_name_compare(const char *bn1, const char *bn2);
    -gchar *oscar_util_format_string(const char *str, const char *name);
    -gchar *oscar_format_buddies(GSList *buddies, const gchar *no_buddies_message);
    -
    -typedef struct {
    - guint16 family;
    - guint16 subtype;
    - guint16 flags;
    - guint32 id;
    -} aim_modsnac_t;
    -
    -#define AIM_MODULENAME_MAXLEN 16
    -#define AIM_MODFLAG_MULTIFAMILY 0x0001
    -typedef struct aim_module_s
    -{
    - guint16 family;
    - guint16 version;
    - guint16 toolid;
    - guint16 toolversion;
    - guint16 flags;
    - char name[AIM_MODULENAME_MAXLEN+1];
    - int (*snachandler)(OscarData *od, FlapConnection *conn, struct aim_module_s *mod, FlapFrame *rx, aim_modsnac_t *snac, ByteStream *bs);
    - void (*shutdown)(OscarData *od, struct aim_module_s *mod);
    - void *priv;
    - struct aim_module_s *next;
    -} aim_module_t;
    -
    -int aim__registermodule(OscarData *od, int (*modfirst)(OscarData *, aim_module_t *));
    -void aim__shutdownmodules(OscarData *od);
    -aim_module_t *aim__findmodulebygroup(OscarData *od, guint16 group);
    -aim_module_t *aim__findmodule(OscarData *od, const char *name);
    -
    -int admin_modfirst(OscarData *od, aim_module_t *mod);
    -int buddylist_modfirst(OscarData *od, aim_module_t *mod);
    -int bos_modfirst(OscarData *od, aim_module_t *mod);
    -int search_modfirst(OscarData *od, aim_module_t *mod);
    -int stats_modfirst(OscarData *od, aim_module_t *mod);
    -int auth_modfirst(OscarData *od, aim_module_t *mod);
    -int msg_modfirst(OscarData *od, aim_module_t *mod);
    -int misc_modfirst(OscarData *od, aim_module_t *mod);
    -int chatnav_modfirst(OscarData *od, aim_module_t *mod);
    -int chat_modfirst(OscarData *od, aim_module_t *mod);
    -int locate_modfirst(OscarData *od, aim_module_t *mod);
    -int service_modfirst(OscarData *od, aim_module_t *mod);
    -int popups_modfirst(OscarData *od, aim_module_t *mod);
    -int bart_modfirst(OscarData *od, aim_module_t *mod);
    -int ssi_modfirst(OscarData *od, aim_module_t *mod);
    -int icq_modfirst(OscarData *od, aim_module_t *mod);
    -int email_modfirst(OscarData *od, aim_module_t *mod);
    -
    -void aim_genericreq_n(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype);
    -void aim_genericreq_n_snacid(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype);
    -void aim_genericreq_l(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint32 *);
    -
    -/* bstream.c */
    -int byte_stream_new(ByteStream *bs, size_t len);
    -int byte_stream_init(ByteStream *bs, guint8 *data, size_t len);
    -void byte_stream_destroy(ByteStream *bs);
    -size_t byte_stream_bytes_left(ByteStream *bs);
    -int byte_stream_curpos(ByteStream *bs);
    -int byte_stream_setpos(ByteStream *bs, size_t off);
    -void byte_stream_rewind(ByteStream *bs);
    -int byte_stream_advance(ByteStream *bs, int n);
    -guint8 byte_stream_get8(ByteStream *bs);
    -guint16 byte_stream_get16(ByteStream *bs);
    -guint32 byte_stream_get32(ByteStream *bs);
    -guint8 byte_stream_getle8(ByteStream *bs);
    -guint16 byte_stream_getle16(ByteStream *bs);
    -guint32 byte_stream_getle32(ByteStream *bs);
    -int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, size_t len);
    -guint8 *byte_stream_getraw(ByteStream *bs, size_t len);
    -char *byte_stream_getstr(ByteStream *bs, size_t len);
    -int byte_stream_put8(ByteStream *bs, guint8 v);
    -int byte_stream_put16(ByteStream *bs, guint16 v);
    -int byte_stream_put32(ByteStream *bs, guint32 v);
    -int byte_stream_putle8(ByteStream *bs, guint8 v);
    -int byte_stream_putle16(ByteStream *bs, guint16 v);
    -int byte_stream_putle32(ByteStream *bs, guint32 v);
    -int byte_stream_putraw(ByteStream *bs, const guint8 *v, size_t len);
    -int byte_stream_putstr(ByteStream *bs, const char *str);
    -int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, size_t len);
    -int byte_stream_putuid(ByteStream *bs, OscarData *od);
    -int byte_stream_putcaps(ByteStream *bs, guint64 caps);
    -
    -/**
    - * Inserts a BART asset block into the given byte stream. The flags
    - * and length are set appropriately based on the value of data.
    - */
    -void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data);
    -
    -/**
    - * A helper function that calls byte_stream_put_bart_asset with the
    - * appropriate data ByteStream given the datastr.
    - */
    -void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr);
    -
    -/*
    - * Generic SNAC structure. Rarely if ever used.
    - */
    -typedef struct aim_snac_s {
    - aim_snacid_t id;
    - guint16 family;
    - guint16 type;
    - guint16 flags;
    - void *data;
    - time_t issuetime;
    - struct aim_snac_s *next;
    -} aim_snac_t;
    -
    -/* snac.c */
    -void aim_initsnachash(OscarData *od);
    -aim_snacid_t aim_newsnac(OscarData *, aim_snac_t *newsnac);
    -aim_snacid_t aim_cachesnac(OscarData *od, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen);
    -aim_snac_t *aim_remsnac(OscarData *, aim_snacid_t id);
    -void aim_cleansnacs(OscarData *, int maxage);
    -int aim_putsnac(ByteStream *, guint16 family, guint16 type, aim_snacid_t id);
    -
    -struct chatsnacinfo {
    - guint16 exchange;
    - char name[128];
    - guint16 instance;
    -};
    -
    -struct rateclass {
    - guint16 classid;
    - guint32 windowsize;
    - guint32 clear;
    - guint32 alert;
    - guint32 limit;
    - guint32 disconnect;
    - guint32 current;
    - guint32 max;
    - guint8 dropping_snacs;
    -
    - struct timeval last; /**< The time when we last sent a SNAC of this rate class. */
    -};
    -
    -int aim_cachecookie(OscarData *od, IcbmCookie *cookie);
    -IcbmCookie *aim_uncachecookie(OscarData *od, guint8 *cookie, int type);
    -IcbmCookie *aim_mkcookie(guint8 *, int, void *);
    -IcbmCookie *aim_checkcookie(OscarData *, const unsigned char *, const int);
    -int aim_freecookie(OscarData *od, IcbmCookie *cookie);
    -int aim_cookie_free(OscarData *od, IcbmCookie *cookie);
    -
    -int aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo);
    -
    -void flap_connection_destroy_chat(OscarData *od, FlapConnection *conn);
    -
    -/* userinfo.c - displaying user information */
    -
    -void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean use_html_status);
    -void oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo);
    -void oscar_user_info_display_error(OscarData *od, guint16 error_reason, char *buddy);
    -void oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info);
    -void oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo);
    -
    -/* authorization.c - OSCAR authorization requests */
    -void oscar_auth_sendrequest(PurpleConnection *gc, const char *name, const char *msg);
    -void oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored);
    -void oscar_auth_recvrequest(PurpleConnection *gc, gchar *name, gchar *nick, gchar *reason);
    -
    -void oscar_set_aim_permdeny(PurpleConnection *gc);
    -
    -struct buddyinfo
    -{
    - gboolean typingnot;
    - guint32 ipaddr;
    -
    - unsigned long ico_me_len;
    - unsigned long ico_me_csum;
    - time_t ico_me_time;
    - gboolean ico_informed;
    -
    - unsigned long ico_len;
    - unsigned long ico_csum;
    - time_t ico_time;
    - gboolean ico_need;
    - gboolean ico_sent;
    -};
    -
    -struct name_data
    -{
    - PurpleConnection *gc;
    - gchar *name;
    - gchar *nick;
    -};
    -
    -void oscar_free_name_data(struct name_data *data);
    -
    -#ifdef __cplusplus
    -}
    -#endif
    -
    -#endif /* _OSCAR_H_ */
    --- a/libpurple/protocols/oscar/oscar_data.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,157 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "oscar.h"
    -
    -typedef struct _SnacHandler SnacHandler;
    -
    -struct _SnacHandler
    -{
    - guint16 family;
    - guint16 subtype;
    - aim_rxcallback_t handler;
    - guint16 flags;
    -};
    -
    -/**
    - * Allocates a new OscarData and initializes it with default values.
    - */
    -OscarData *
    -oscar_data_new(void)
    -{
    - OscarData *od;
    - aim_module_t *cur;
    - GString *msg;
    -
    - od = g_new0(OscarData, 1);
    -
    - aim_initsnachash(od);
    - od->snacid_next = 0x00000001;
    - od->buddyinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    - od->handlerlist = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
    -
    - /*
    - * Register all the modules for this session...
    - */
    - aim__registermodule(od, misc_modfirst); /* load the catch-all first */
    - aim__registermodule(od, service_modfirst);
    - aim__registermodule(od, locate_modfirst);
    - aim__registermodule(od, buddylist_modfirst);
    - aim__registermodule(od, msg_modfirst);
    - aim__registermodule(od, admin_modfirst);
    - aim__registermodule(od, popups_modfirst);
    - aim__registermodule(od, bos_modfirst);
    - aim__registermodule(od, search_modfirst);
    - aim__registermodule(od, stats_modfirst);
    - aim__registermodule(od, chatnav_modfirst);
    - aim__registermodule(od, chat_modfirst);
    - aim__registermodule(od, bart_modfirst);
    - /* missing 0x11 - 0x12 */
    - aim__registermodule(od, ssi_modfirst);
    - /* missing 0x14 */
    - aim__registermodule(od, icq_modfirst);
    - /* missing 0x16 */
    - /* auth_modfirst is only needed if we're connecting with the old-style BUCP login */
    - aim__registermodule(od, auth_modfirst);
    - aim__registermodule(od, email_modfirst);
    -
    - msg = g_string_new("Registered modules: ");
    - for (cur = od->modlistv; cur; cur = cur->next) {
    - g_string_append_printf(
    - msg,
    - "%s (family=0x%04x, version=0x%04x, toolid=0x%04x, toolversion=0x%04x), ",
    - cur->name,
    - cur->family,
    - cur->version,
    - cur->toolid,
    - cur->toolversion);
    - }
    - purple_debug_misc("oscar", "%s\n", msg->str);
    - g_string_free(msg, TRUE);
    -
    - return od;
    -}
    -
    -/**
    - * Logoff and deallocate a session.
    - *
    - * @param od Session to kill
    - */
    -void
    -oscar_data_destroy(OscarData *od)
    -{
    - aim_cleansnacs(od, -1);
    -
    - /* Only used when connecting with clientLogin */
    - if (od->url_data != NULL)
    - purple_util_fetch_url_cancel(od->url_data);
    -
    - while (od->requesticon)
    - {
    - g_free(od->requesticon->data);
    - od->requesticon = g_slist_delete_link(od->requesticon, od->requesticon);
    - }
    - g_free(od->email);
    - g_free(od->newp);
    - g_free(od->oldp);
    - if (od->getblisttimer > 0)
    - purple_timeout_remove(od->getblisttimer);
    - while (od->oscar_connections != NULL)
    - flap_connection_destroy(od->oscar_connections->data,
    - OSCAR_DISCONNECT_DONE, NULL);
    -
    - while (od->peer_connections != NULL)
    - peer_connection_destroy(od->peer_connections->data,
    - OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    -
    - aim__shutdownmodules(od);
    -
    - g_hash_table_destroy(od->buddyinfo);
    - g_hash_table_destroy(od->handlerlist);
    -
    - g_free(od);
    -}
    -
    -void
    -oscar_data_addhandler(OscarData *od, guint16 family, guint16 subtype, aim_rxcallback_t newhandler, guint16 flags)
    -{
    - SnacHandler *snac_handler;
    -
    - snac_handler = g_new0(SnacHandler, 1);
    -
    - snac_handler->family = family;
    - snac_handler->subtype = subtype;
    - snac_handler->flags = flags;
    - snac_handler->handler = newhandler;
    -
    - g_hash_table_insert(od->handlerlist,
    - GUINT_TO_POINTER((family << 16) + subtype),
    - snac_handler);
    -}
    -
    -aim_rxcallback_t
    -aim_callhandler(OscarData *od, guint16 family, guint16 subtype)
    -{
    - SnacHandler *snac_handler;
    -
    - snac_handler = g_hash_table_lookup(od->handlerlist, GUINT_TO_POINTER((family << 16) + subtype));
    -
    - return snac_handler ? snac_handler->handler : NULL;
    -}
    --- a/libpurple/protocols/oscar/oscarcommon.h Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,124 +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
    - *
    - */
    -
    -/* oscarcommon.h contains prototypes for the prpl functions used by libaim.c
    - * and libicq.c
    - */
    -
    -#include "internal.h"
    -
    -#include "accountopt.h"
    -#include "prpl.h"
    -#include "version.h"
    -#include "notify.h"
    -#include "status.h"
    -
    -#define AIM_DEFAULT_LOGIN_SERVER "login.oscar.aol.com"
    -#define AIM_ALT_LOGIN_SERVER "login.messaging.aol.com"
    -#define AIM_DEFAULT_SSL_LOGIN_SERVER "slogin.oscar.aol.com"
    -#define ICQ_DEFAULT_LOGIN_SERVER "login.icq.com"
    -#define ICQ_DEFAULT_SSL_LOGIN_SERVER "slogin.icq.com"
    -
    -#define AIM_DEFAULT_KDC_SERVER "kdc.uas.aol.com"
    -#define AIM_DEFAULT_KDC_PORT 443
    -
    -/*
    - * Using clientLogin requires a developer ID. This key is for libpurple.
    - * It is the default key for all libpurple-based clients. AOL encourages
    - * UIs (especially ones with lots of users) to override this with their
    - * own key.
    - */
    -#define ICQ_DEFAULT_DIST_ID 1553
    -#define ICQ_DEFAULT_CLIENT_KEY "ma15d7JTxbmVG-RP"
    -#define AIM_DEFAULT_DIST_ID 1717
    -#define AIM_DEFAULT_CLIENT_KEY "ma19CwYN9i9Mw5nY"
    -
    -#define OSCAR_DEFAULT_LOGIN_PORT 5190
    -
    -#define OSCAR_OPPORTUNISTIC_ENCRYPTION "opportunistic_encryption"
    -#define OSCAR_REQUIRE_ENCRYPTION "require_encryption"
    -#define OSCAR_NO_ENCRYPTION "no_encryption"
    -
    -#define OSCAR_MD5_LOGIN "md5_login"
    -#define OSCAR_CLIENT_LOGIN "client_login"
    -#define OSCAR_KERBEROS_LOGIN "kerberos_login"
    -
    -#ifndef _WIN32
    -#define OSCAR_DEFAULT_CUSTOM_ENCODING "ISO-8859-1"
    -#else
    -#define OSCAR_DEFAULT_CUSTOM_ENCODING oscar_get_locale_charset()
    -#endif
    -#define OSCAR_DEFAULT_AUTHORIZATION TRUE
    -#define OSCAR_DEFAULT_HIDE_IP TRUE
    -#define OSCAR_DEFAULT_WEB_AWARE FALSE
    -#define OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY FALSE
    -#define OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS TRUE
    -#define OSCAR_DEFAULT_LOGIN OSCAR_CLIENT_LOGIN
    -#define OSCAR_DEFAULT_ENCRYPTION OSCAR_OPPORTUNISTIC_ENCRYPTION
    -
    -#ifdef _WIN32
    -const char *oscar_get_locale_charset(void);
    -#endif
    -PurpleMood* oscar_get_purple_moods(PurpleAccount *account);
    -const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b);
    -const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b);
    -const char* oscar_list_emblem(PurpleBuddy *b);
    -char *oscar_status_text(PurpleBuddy *b);
    -void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full);
    -GList *oscar_status_types(PurpleAccount *account);
    -GList *oscar_blist_node_menu(PurpleBlistNode *node);
    -GList *oscar_chat_info(PurpleConnection *gc);
    -GHashTable *oscar_chat_info_defaults(PurpleConnection *gc, const char *chat_name);
    -void oscar_login(PurpleAccount *account);
    -void oscar_close(PurpleConnection *gc);
    -int oscar_send_im(PurpleConnection *gc, const char *name, const char *message, PurpleMessageFlags imflags);
    -void oscar_set_info(PurpleConnection *gc, const char *rawinfo);
    -unsigned int oscar_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state);
    -void oscar_get_info(PurpleConnection *gc, const char *name);
    -void oscar_set_status(PurpleAccount *account, PurpleStatus *status);
    -void oscar_set_idle(PurpleConnection *gc, int time);
    -void oscar_change_passwd(PurpleConnection *gc, const char *old, const char *new);
    -void oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *msg);
    -void oscar_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
    -void oscar_add_permit(PurpleConnection *gc, const char *who);
    -void oscar_add_deny(PurpleConnection *gc, const char *who);
    -void oscar_rem_permit(PurpleConnection *gc, const char *who);
    -void oscar_rem_deny(PurpleConnection *gc, const char *who);
    -void oscar_join_chat(PurpleConnection *gc, GHashTable *data);
    -char *oscar_get_chat_name(GHashTable *data);
    -void oscar_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name);
    -void oscar_chat_leave(PurpleConnection *gc, int id);
    -int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags);
    -void oscar_keepalive(PurpleConnection *gc);
    -void oscar_alias_buddy(PurpleConnection *gc, const char *name, const char *alias);
    -void oscar_move_buddy(PurpleConnection *gc, const char *name, const char *old_group, const char *new_group);
    -void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies);
    -void oscar_convo_closed(PurpleConnection *gc, const char *who);
    -const char *oscar_normalize(const PurpleAccount *account, const char *str);
    -void oscar_set_icon(PurpleConnection *gc, PurpleStoredImage *img);
    -void oscar_remove_group(PurpleConnection *gc, PurpleGroup *group);
    -gboolean oscar_can_receive_file(PurpleConnection *gc, const char *who);
    -void oscar_send_file(PurpleConnection *gc, const char *who, const char *file);
    -PurpleXfer *oscar_new_xfer(PurpleConnection *gc, const char *who);
    -gboolean oscar_offline_message(const PurpleBuddy *buddy);
    -GList *oscar_actions(PurplePlugin *plugin, gpointer context);
    -void oscar_init(PurplePlugin *plugin, gboolean is_icq);
    --- a/libpurple/protocols/oscar/peer.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1127 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Functions dealing with peer connections. This includes the code
    - * used to establish a peer connection for both Oscar File transfer
    - * (OFT) and Oscar Direct Connect (ODC). (ODC is also referred to
    - * as DirectIM and IM Image.)
    - */
    -
    -#ifdef HAVE_CONFIG_H
    -#include <config.h>
    -#endif
    -
    -/* From the oscar PRPL */
    -#include "oscar.h"
    -#include "peer.h"
    -
    -/* From Purple */
    -#include "conversation.h"
    -#include "ft.h"
    -#include "network.h"
    -#include "notify.h"
    -#include "request.h"
    -#include "util.h"
    -
    -#ifndef _WIN32
    -#include <stdio.h>
    -#include <netdb.h>
    -#include <sys/socket.h>
    -#include <netinet/in.h>
    -#include <arpa/inet.h> /* for inet_ntoa */
    -#include <limits.h> /* for UINT_MAX */
    -#endif
    -
    -#ifdef _WIN32
    -#include "win32dep.h"
    -#endif
    -
    -/*
    - * I really want to switch all our networking code to using IPv6 only,
    - * but that really isn't a good idea at all. Evan S. of Adium says
    - * OS X sets all connections as "AF_INET6/PF_INET6," even if there is
    - * nothing inherently IPv6 about them. And I feel like Linux kernel
    - * 2.6.5 is doing the same thing. So we REALLY should accept
    - * connections if they're showing up as IPv6. Old OSes (Solaris?)
    - * that might not have full IPv6 support yet will fail if we try
    - * to use PF_INET6 but it isn't defined. --Mark Doliner
    - */
    -#ifndef PF_INET6
    -#define PF_INET6 PF_INET
    -#endif
    -
    -PeerConnection *
    -peer_connection_find_by_type(OscarData *od, const char *bn, guint64 type)
    -{
    - GSList *cur;
    - PeerConnection *conn;
    -
    - for (cur = od->peer_connections; cur != NULL; cur = cur->next)
    - {
    - conn = cur->data;
    - if ((conn->type == type) && !oscar_util_name_compare(conn->bn, bn))
    - return conn;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * @param cookie This must be exactly 8 characters.
    - */
    -PeerConnection *
    -peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie)
    -{
    - GSList *cur;
    - PeerConnection *conn;
    -
    - for (cur = od->peer_connections; cur != NULL; cur = cur->next)
    - {
    - conn = cur->data;
    - if (!memcmp(conn->cookie, cookie, 8) && !oscar_util_name_compare(conn->bn, bn))
    - return conn;
    - }
    -
    - return NULL;
    -}
    -
    -PeerConnection *
    -peer_connection_new(OscarData *od, guint64 type, const char *bn)
    -{
    - PeerConnection *conn;
    - PurpleAccount *account;
    -
    - account = purple_connection_get_account(od->gc);
    -
    - conn = g_new0(PeerConnection, 1);
    - conn->od = od;
    - conn->type = type;
    - conn->bn = g_strdup(bn);
    - conn->buffer_outgoing = purple_circ_buffer_new(0);
    - conn->listenerfd = -1;
    - conn->fd = -1;
    - conn->lastactivity = time(NULL);
    - conn->use_proxy |= purple_account_get_bool(account, "always_use_rv_proxy", FALSE);
    -
    - if (type == OSCAR_CAPABILITY_DIRECTIM)
    - memcpy(conn->magic, "ODC2", 4);
    - else if (type == OSCAR_CAPABILITY_SENDFILE)
    - memcpy(conn->magic, "OFT2", 4);
    -
    - od->peer_connections = g_slist_prepend(od->peer_connections, conn);
    -
    - return conn;
    -}
    -
    -static void
    -peer_connection_close(PeerConnection *conn)
    -{
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - peer_odc_close(conn);
    - else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
    - peer_oft_close(conn);
    -
    - if (conn->verified_connect_data != NULL)
    - {
    - purple_proxy_connect_cancel(conn->verified_connect_data);
    - conn->verified_connect_data = NULL;
    - }
    -
    - if (conn->client_connect_data != NULL)
    - {
    - purple_proxy_connect_cancel(conn->client_connect_data);
    - conn->client_connect_data = NULL;
    - }
    -
    - if (conn->listen_data != NULL)
    - {
    - purple_network_listen_cancel(conn->listen_data);
    - conn->listen_data = NULL;
    - }
    -
    - if (conn->connect_timeout_timer != 0)
    - {
    - purple_timeout_remove(conn->connect_timeout_timer);
    - conn->connect_timeout_timer = 0;
    - }
    -
    - if (conn->watcher_incoming != 0)
    - {
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    - }
    - if (conn->watcher_outgoing != 0)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - }
    - if (conn->listenerfd >= 0)
    - {
    - close(conn->listenerfd);
    - conn->listenerfd = -1;
    - }
    - if (conn->fd >= 0)
    - {
    - close(conn->fd);
    - conn->fd = -1;
    - }
    -
    - g_free(conn->buffer_incoming.data);
    - conn->buffer_incoming.data = NULL;
    - conn->buffer_incoming.len = 0;
    - conn->buffer_incoming.offset = 0;
    -
    - purple_circ_buffer_destroy(conn->buffer_outgoing);
    - conn->buffer_outgoing = purple_circ_buffer_new(0);
    -
    - conn->flags &= ~PEER_CONNECTION_FLAG_IS_INCOMING;
    -}
    -
    -static gboolean
    -peer_connection_destroy_cb(gpointer data)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - purple_request_close_with_handle(conn);
    -
    - peer_connection_close(conn);
    -
    - if (conn->checksum_data != NULL)
    - peer_oft_checksum_destroy(conn->checksum_data);
    -
    - if (conn->xfer != NULL)
    - {
    - PurpleXferStatusType status;
    - conn->xfer->data = NULL;
    - status = purple_xfer_get_status(conn->xfer);
    - if ((status != PURPLE_XFER_STATUS_DONE) &&
    - (status != PURPLE_XFER_STATUS_CANCEL_LOCAL) &&
    - (status != PURPLE_XFER_STATUS_CANCEL_REMOTE))
    - {
    - if ((conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) ||
    - (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED))
    - purple_xfer_cancel_remote(conn->xfer);
    - else
    - purple_xfer_cancel_local(conn->xfer);
    - }
    - purple_xfer_unref(conn->xfer);
    - conn->xfer = NULL;
    - }
    -
    - g_free(conn->bn);
    - g_free(conn->error_message);
    - g_free(conn->proxyip);
    - g_free(conn->clientip);
    - g_free(conn->verifiedip);
    - g_free(conn->xferdata.name);
    - purple_circ_buffer_destroy(conn->buffer_outgoing);
    -
    - conn->od->peer_connections = g_slist_remove(conn->od->peer_connections, conn);
    -
    - g_free(conn);
    -
    - return FALSE;
    -}
    -
    -void
    -peer_connection_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
    -{
    - if (conn->destroy_timeout != 0)
    - purple_timeout_remove(conn->destroy_timeout);
    - conn->disconnect_reason = reason;
    - g_free(conn->error_message);
    - conn->error_message = g_strdup(error_message);
    - peer_connection_destroy_cb(conn);
    -}
    -
    -void
    -peer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
    -{
    - if (conn->destroy_timeout != 0)
    - /* Already taken care of */
    - return;
    -
    - purple_debug_info("oscar", "Scheduling destruction of peer connection\n");
    - conn->disconnect_reason = reason;
    - g_free(conn->error_message);
    - conn->error_message = g_strdup(error_message);
    - conn->destroy_timeout = purple_timeout_add(0, peer_connection_destroy_cb, conn);
    -}
    -
    -/*******************************************************************/
    -/* Begin code for receiving data on a peer connection */
    -/*******************************************************************/
    -
    -/**
    - * This should be used to read ODC and OFT framing info. It should
    - * NOT be used to read the payload sent across the connection (IMs,
    - * file data, etc), and it should NOT be used to read proxy negotiation
    - * headers.
    - *
    - * Unlike flap_connection_recv_cb(), this only reads one frame at a
    - * time. This is done so that the watcher can be changed during the
    - * handling of the frame. If the watcher is changed then this
    - * function will not read in any more data. This happens when
    - * reading the payload of a direct IM frame, or when we're
    - * receiving a file from the remote user. Once the data has been
    - * read, the watcher will be switched back to this function to
    - * continue reading the next frame.
    - */
    -void
    -peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PeerConnection *conn;
    - gssize read;
    -
    - conn = data;
    -
    - /* Start reading a new ODC/OFT frame */
    - if (conn->buffer_incoming.data == NULL)
    - {
    - /* Read the first 6 bytes (magic string and frame length) */
    - read = recv(conn->fd, conn->header + conn->header_received,
    - 6 - conn->header_received, 0);
    -
    - /* Check if the remote user closed the connection */
    - if (read == 0)
    - {
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - return;
    - }
    -
    - /* If there was an error then close the connection */
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - peer_connection_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - return;
    - }
    -
    - conn->lastactivity = time(NULL);
    -
    - /* If we don't even have the first 6 bytes then do nothing */
    - conn->header_received += read;
    - if (conn->header_received < 6)
    - return;
    -
    - /* All ODC/OFT frames must start with a magic string */
    - if (memcmp(conn->magic, conn->header, 4))
    - {
    - purple_debug_warning("oscar", "Expecting magic string to "
    - "be %c%c%c%c but received magic string %c%c%c%c. "
    - "Closing connection.\n",
    - conn->magic[0], conn->magic[1], conn->magic[2],
    - conn->magic[3], conn->header[0], conn->header[1],
    - conn->header[2], conn->header[3]);
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - return;
    - }
    -
    - /* Initialize a new temporary ByteStream for incoming data */
    - conn->buffer_incoming.len = aimutil_get16(&conn->header[4]) - 6;
    - conn->buffer_incoming.data = g_new(guint8, conn->buffer_incoming.len);
    - conn->buffer_incoming.offset = 0;
    - }
    -
    - /* Read data into the temporary buffer until it is complete */
    - read = recv(conn->fd,
    - &conn->buffer_incoming.data[conn->buffer_incoming.offset],
    - conn->buffer_incoming.len - conn->buffer_incoming.offset,
    - 0);
    -
    - /* Check if the remote user closed the connection */
    - if (read == 0)
    - {
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - return;
    - }
    -
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - peer_connection_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - return;
    - }
    -
    - conn->lastactivity = time(NULL);
    - conn->buffer_incoming.offset += read;
    - if (conn->buffer_incoming.offset < conn->buffer_incoming.len)
    - /* Waiting for more data to arrive */
    - return;
    -
    - /* We have a complete ODC/OFT frame! Handle it and continue reading */
    - byte_stream_rewind(&conn->buffer_incoming);
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - peer_odc_recv_frame(conn, &conn->buffer_incoming);
    - }
    - else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - peer_oft_recv_frame(conn, &conn->buffer_incoming);
    - }
    -
    - g_free(conn->buffer_incoming.data);
    - conn->buffer_incoming.data = NULL;
    -
    - conn->header_received = 0;
    -}
    -
    -/*******************************************************************/
    -/* End code for receiving data on a peer connection */
    -/*******************************************************************/
    -
    -/*******************************************************************/
    -/* Begin code for sending data on a peer connection */
    -/*******************************************************************/
    -
    -static void
    -send_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PeerConnection *conn;
    - gsize writelen;
    - gssize wrotelen;
    -
    - conn = data;
    - writelen = purple_circ_buffer_get_max_read(conn->buffer_outgoing);
    -
    - if (writelen == 0)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - /*
    - * The buffer is currently empty, so reset the current input
    - * and output positions to the start of the buffer. We do
    - * this so that the next chunk of data that we put into the
    - * buffer can be read back out of the buffer in one fell swoop.
    - * Otherwise it gets fragmented and we have to read from the
    - * second half of the buffer than go back and read the rest of
    - * the chunk from the first half.
    - *
    - * We're using TCP, which is a stream based protocol, so this
    - * isn't supposed to matter. However, experience has shown
    - * that at least the proxy file transfer code in AIM 6.1.41.2
    - * requires that the entire OFT frame arrive all at once. If
    - * the frame is fragmented then AIM freaks out and aborts the
    - * file transfer. Somebody should teach those guys how to
    - * write good TCP code.
    - */
    - conn->buffer_outgoing->inptr = conn->buffer_outgoing->buffer;
    - conn->buffer_outgoing->outptr = conn->buffer_outgoing->buffer;
    - return;
    - }
    -
    - wrotelen = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
    - if (wrotelen <= 0)
    - {
    - if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
    - /* No worries */
    - return;
    -
    - if (conn->ready)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - close(conn->fd);
    - conn->fd = -1;
    - peer_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, NULL);
    - }
    - else
    - {
    - /*
    - * This could happen when unable to send a negotiation
    - * frame to a peer proxy server.
    - */
    - peer_connection_trynext(conn);
    - }
    - return;
    - }
    -
    - purple_circ_buffer_mark_read(conn->buffer_outgoing, wrotelen);
    - conn->lastactivity = time(NULL);
    -}
    -
    -/**
    - * This should be called by OFT/ODC code to send a standard OFT or ODC
    - * frame across the peer connection along with some payload data. Or
    - * maybe a file. Anything, really.
    - */
    -void
    -peer_connection_send(PeerConnection *conn, ByteStream *bs)
    -{
    - /* Add everything to our outgoing buffer */
    - purple_circ_buffer_append(conn->buffer_outgoing, bs->data, bs->len);
    -
    - /* If we haven't already started writing stuff, then start the cycle */
    - if ((conn->watcher_outgoing == 0) && (conn->fd >= 0))
    - {
    - conn->watcher_outgoing = purple_input_add(conn->fd,
    - PURPLE_INPUT_WRITE, send_cb, conn);
    - send_cb(conn, conn->fd, 0);
    - }
    -}
    -
    -/*******************************************************************/
    -/* End code for sending data on a peer connection */
    -/*******************************************************************/
    -
    -/*******************************************************************/
    -/* Begin code for establishing a peer connection */
    -/*******************************************************************/
    -
    -void
    -peer_connection_finalize_connection(PeerConnection *conn)
    -{
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
    -
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - /*
    - * If we are connecting to them then send our cookie so they
    - * can verify who we are. Note: This doesn't seem to be
    - * necessary, but it also doesn't seem to hurt.
    - */
    - if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING))
    - peer_odc_send_cookie(conn);
    - }
    - else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - if (purple_xfer_get_type(conn->xfer) == PURPLE_XFER_SEND)
    - {
    - peer_oft_send_prompt(conn);
    - }
    - }
    -
    - /*
    - * Tell the remote user that we're connected (which may also imply
    - * that we've accepted their request).
    - */
    - if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING))
    - aim_im_sendch2_connected(conn);
    -}
    -
    -/**
    - * We tried to make an outgoing connection to a remote user. It
    - * either connected or failed to connect.
    - */
    -static void
    -peer_connection_common_established_cb(gpointer data, gint source, const gchar *error_message, gboolean verified)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - if (verified)
    - conn->verified_connect_data = NULL;
    - else
    - conn->client_connect_data = NULL;
    -
    - if (source < 0)
    - {
    - if ((conn->verified_connect_data == NULL) &&
    - (conn->client_connect_data == NULL))
    - {
    - /* Our parallel connection attemps have both failed. */
    - peer_connection_trynext(conn);
    - }
    - return;
    - }
    -
    - purple_timeout_remove(conn->connect_timeout_timer);
    - conn->connect_timeout_timer = 0;
    -
    - if (conn->client_connect_data != NULL)
    - {
    - purple_proxy_connect_cancel(conn->client_connect_data);
    - conn->client_connect_data = NULL;
    - }
    -
    - if (conn->verified_connect_data != NULL)
    - {
    - purple_proxy_connect_cancel(conn->verified_connect_data);
    - conn->verified_connect_data = NULL;
    - }
    -
    - conn->fd = source;
    -
    - peer_connection_finalize_connection(conn);
    -}
    -
    -static void
    -peer_connection_verified_established_cb(gpointer data, gint source, const gchar *error_message)
    -{
    - peer_connection_common_established_cb(data, source, error_message, TRUE);
    -}
    -
    -static void
    -peer_connection_client_established_cb(gpointer data, gint source, const gchar *error_message)
    -{
    - peer_connection_common_established_cb(data, source, error_message, FALSE);
    -}
    -
    -/**
    - * This is the watcher callback for any listening socket that is
    - * waiting for a peer to connect. When a peer connects we set the
    - * input watcher to start reading data from the peer.
    - *
    - * To make sure that the connection is with the intended person and
    - * not with a malicious middle man, we don't send anything until we've
    - * received a peer frame from the remote user and have verified that
    - * the cookie in the peer frame matches the cookie that was exchanged
    - * in the channel 2 ICBM.
    - */
    -void
    -peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PeerConnection *conn;
    - struct sockaddr addr;
    - socklen_t addrlen = sizeof(addr);
    -
    - conn = data;
    -
    - purple_debug_info("oscar", "Accepting connection on listener socket.\n");
    -
    - conn->fd = accept(conn->listenerfd, &addr, &addrlen);
    - if (conn->fd < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No connection yet--no worries */
    - /* TODO: Hmm, but they SHOULD be connected if we're here, right? */
    - return;
    -
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6))
    - {
    - /* Invalid connection type?! Continue waiting. */
    - close(conn->fd);
    - return;
    - }
    -
    - _purple_network_set_common_socket_flags(conn->fd);
    -
    - purple_input_remove(conn->watcher_incoming);
    -
    - peer_connection_finalize_connection(conn);
    -}
    -
    -/**
    - * We've just opened a listener socket, so we send the remote
    - * user an ICBM and ask them to connect to us.
    - */
    -static void
    -peer_connection_establish_listener_cb(int listenerfd, gpointer data)
    -{
    - PeerConnection *conn;
    - OscarData *od;
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PurpleConversation *conv;
    - char *tmp;
    - FlapConnection *bos_conn;
    - const char *listener_ip;
    - const guchar *ip_atoi;
    - unsigned short listener_port;
    -
    - conn = data;
    - conn->listen_data = NULL;
    -
    - if (listenerfd < 0)
    - {
    - /* Could not open listener socket */
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - od = conn->od;
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    - conn->listenerfd = listenerfd;
    -
    - /* Watch for new connections on our listener socket */
    - conn->watcher_incoming = purple_input_add(conn->listenerfd,
    - PURPLE_INPUT_READ, peer_connection_listen_cb, conn);
    -
    - /* Send the "please connect to me!" ICBM */
    - bos_conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (bos_conn == NULL)
    - {
    - /* Not good */
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - if (bos_conn->gsc)
    - listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd);
    - else
    - listener_ip = purple_network_get_my_ip(bos_conn->fd);
    -
    - ip_atoi = purple_network_ip_atoi(listener_ip);
    - if (ip_atoi == NULL) {
    - /* Could not convert IP to 4 byte array--weird, but this does
    - happen for some users (#4829, Adium #15839). Maybe they're
    - connecting with IPv6...? Maybe through a proxy? */
    - purple_debug_error("oscar", "Can't ask peer to connect to us "
    - "because purple_network_ip_atoi(%s) returned NULL. "
    - "fd=%d. is_ssl=%d\n",
    - listener_ip ? listener_ip : "(null)",
    - bos_conn->gsc ? bos_conn->gsc->fd : bos_conn->fd,
    - bos_conn->gsc ? 1 : 0);
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - listener_port = purple_network_get_port_from_fd(conn->listenerfd);
    -
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - aim_im_sendch2_odc_requestdirect(od,
    - conn->cookie, conn->bn, ip_atoi,
    - listener_port, ++conn->lastrequestnumber);
    -
    - /* Print a message to a local conversation window */
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
    - tmp = g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
    - "Direct IM."), conn->bn, listener_ip, listener_port);
    - purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
    - g_free(tmp);
    - }
    - else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - aim_im_sendch2_sendfile_requestdirect(od,
    - conn->cookie, conn->bn,
    - ip_atoi,
    - listener_port, ++conn->lastrequestnumber,
    - (const gchar *)conn->xferdata.name,
    - conn->xferdata.size, conn->xferdata.totfiles);
    - }
    -}
    -
    -/**
    - * This is a callback function used when we're connecting to a peer
    - * using either the client IP or the verified IP and the connection
    - * took longer than 5 seconds to complete. We do this because
    - * waiting for the OS to time out the connection attempt is not
    - * practical--the default timeout on many OSes can be 3 minutes or
    - * more, and users are impatient.
    - *
    - * Worst case scenario: the user is connected to the Internet using
    - * a modem with severe lag. The peer connections fail and Purple falls
    - * back to using a proxied connection. The lower bandwidth
    - * limitations imposed by the proxied connection won't matter because
    - * the user is using a modem.
    - *
    - * I suppose this line of thinking is discriminatory against people
    - * with very high lag but decent throughput who are transferring
    - * large files. But we don't care about those people.
    - *
    - * I (Sean) changed the timeout from 15 to 5 seconds, as 60 seconds is
    - * too long for a user to wait to send a file. I'm also parallelizing
    - * requests when possible. The longest we should have to wait now is 10
    - * seconds. We shouldn't make it shorter than this.
    - */
    -static gboolean
    -peer_connection_tooktoolong(gpointer data)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - purple_debug_info("oscar", "Peer connection timed out after 5 seconds. "
    - "Trying next method...\n");
    -
    - peer_connection_trynext(conn);
    -
    - /* Cancel this timer. It'll be added again, if needed. */
    - return FALSE;
    -}
    -
    -/**
    - * Try to establish the given PeerConnection using a defined
    - * sequence of steps.
    - */
    -void
    -peer_connection_trynext(PeerConnection *conn)
    -{
    - PurpleAccount *account;
    -
    - account = purple_connection_get_account(conn->od->gc);
    -
    - /*
    - * Close any remnants of a previous failed connection attempt.
    - */
    - peer_connection_close(conn);
    -
    - /*
    - * 1. Attempt to connect to the remote user using their verifiedip and clientip.
    - * We try these at the same time and use whichever succeeds first, so we don't
    - * have to wait for a timeout.
    - */
    - if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_DIRECT) &&
    - (conn->verifiedip != NULL) && (conn->port != 0) && (!conn->use_proxy))
    - {
    - conn->flags |= PEER_CONNECTION_FLAG_TRIED_DIRECT;
    -
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - gchar *tmp;
    - PurpleConversation *conv;
    - tmp = g_strdup_printf(_("Attempting to connect to %s:%hu."),
    - conn->verifiedip, conn->port);
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
    - purple_conversation_write(conv, NULL, tmp,
    - PURPLE_MESSAGE_SYSTEM, time(NULL));
    - g_free(tmp);
    - }
    -
    - conn->verified_connect_data = purple_proxy_connect(NULL, account,
    - conn->verifiedip, conn->port,
    - peer_connection_verified_established_cb, conn);
    -
    - if ((conn->verifiedip == NULL) ||
    - !purple_strequal(conn->verifiedip, conn->clientip))
    - {
    - conn->client_connect_data = purple_proxy_connect(NULL, account,
    - conn->clientip, conn->port,
    - peer_connection_client_established_cb, conn);
    - }
    -
    - if ((conn->verified_connect_data != NULL) ||
    - (conn->client_connect_data != NULL))
    - {
    - /* Connecting... */
    - conn->connect_timeout_timer = purple_timeout_add_seconds(5,
    - peer_connection_tooktoolong, conn);
    - return;
    - }
    - }
    -
    - /*
    - * 2. Attempt to have the remote user connect to us (using both
    - * our verifiedip and our clientip).
    - */
    - if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_INCOMING) &&
    - (!conn->use_proxy))
    - {
    - conn->flags |= PEER_CONNECTION_FLAG_TRIED_INCOMING;
    -
    - /*
    - * Remote user is connecting to us, so we'll need to verify
    - * that the user who connected is our friend.
    - */
    - conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
    -
    - conn->listen_data = purple_network_listen_range(5190, 5290, SOCK_STREAM,
    - peer_connection_establish_listener_cb, conn);
    - if (conn->listen_data != NULL)
    - {
    - /* Opening listener socket... */
    - return;
    - }
    - }
    -
    - /*
    - * 3. Attempt to have both users connect to an intermediate proxy
    - * server.
    - */
    - if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_PROXY))
    - {
    - conn->flags |= PEER_CONNECTION_FLAG_TRIED_PROXY;
    -
    - /*
    - * If we initiate the proxy connection, then the remote user
    - * could be anyone, so we need to verify that the user who
    - * connected is our friend.
    - */
    - if (!conn->use_proxy)
    - conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
    -
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - gchar *tmp;
    - PurpleConversation *conv;
    - tmp = g_strdup(_("Attempting to connect via proxy server."));
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
    - purple_conversation_write(conv, NULL, tmp,
    - PURPLE_MESSAGE_SYSTEM, time(NULL));
    - g_free(tmp);
    - }
    -
    - conn->verified_connect_data = purple_proxy_connect(NULL, account,
    - (conn->proxyip != NULL)
    - ? conn->proxyip
    - : (conn->od->icq ? ICQ_PEER_PROXY_SERVER : AIM_PEER_PROXY_SERVER),
    - PEER_PROXY_PORT,
    - peer_proxy_connection_established_cb, conn);
    - if (conn->verified_connect_data != NULL)
    - {
    - /* Connecting... */
    - return;
    - }
    - }
    -
    - /* Give up! */
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_COULD_NOT_CONNECT, NULL);
    -}
    -
    -/**
    - * Initiate a peer connection with someone.
    - */
    -void
    -peer_connection_propose(OscarData *od, guint64 type, const char *bn)
    -{
    - PeerConnection *conn;
    -
    - if (type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - conn = peer_connection_find_by_type(od, bn, type);
    - if (conn != NULL)
    - {
    - if (conn->ready)
    - {
    - PurpleAccount *account;
    - PurpleConversation *conv;
    -
    - purple_debug_info("oscar", "Already have a direct IM "
    - "session with %s.\n", bn);
    - account = purple_connection_get_account(od->gc);
    - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
    - bn, account);
    - if (conv != NULL)
    - purple_conversation_present(conv);
    - return;
    - }
    -
    - /* Cancel the old connection and try again */
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_RETRYING, NULL);
    - }
    - }
    -
    - conn = peer_connection_new(od, type, bn);
    - conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    - aim_icbm_makecookie(conn->cookie);
    -
    - peer_connection_trynext(conn);
    -}
    -
    -/**
    - * Someone else wants to establish a peer connection with us,
    - * and we said yes.
    - */
    -static void
    -peer_connection_got_proposition_yes_cb(gpointer data, gint id)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    - peer_connection_trynext(conn);
    -}
    -
    -/**
    - * Someone else wants to establish a peer connection with us,
    - * and we said no.
    - *
    - * "Well, one time my friend asked me if I wanted to play the
    - * piccolo. But I said no."
    - */
    -static void
    -peer_connection_got_proposition_no_cb(gpointer data, gint id)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - aim_im_denytransfer(conn->od, conn->bn, conn->cookie,
    - AIM_TRANSFER_DENY_DECLINE);
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    -}
    -
    -/**
    - * Someone else wants to establish a peer connection with us.
    - */
    -void
    -peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PeerConnection *conn;
    - gchar *buf;
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - /*
    - * If we have a connection with this same cookie then they are
    - * probably just telling us they weren't able to connect to us
    - * and we should try connecting to them, instead. Or they want
    - * to go through a proxy.
    - */
    - conn = peer_connection_find_by_cookie(od, bn, args->cookie);
    - if ((conn != NULL) && (conn->type == args->type))
    - {
    - purple_debug_info("oscar", "Remote user wants to try a "
    - "different connection method\n");
    - g_free(conn->proxyip);
    - g_free(conn->clientip);
    - g_free(conn->verifiedip);
    - if (args->use_proxy)
    - conn->proxyip = g_strdup(args->proxyip);
    - else
    - conn->proxyip = NULL;
    - conn->verifiedip = g_strdup(args->verifiedip);
    - conn->clientip = g_strdup(args->clientip);
    - conn->port = args->port;
    - conn->use_proxy |= args->use_proxy;
    - conn->lastrequestnumber++;
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - /* If this is a direct IM, then close any existing session */
    - if (args->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - conn = peer_connection_find_by_type(od, bn, args->type);
    - if (conn != NULL)
    - {
    - /* Close the old direct IM and start a new one */
    - purple_debug_info("oscar", "Received new direct IM request "
    - "from %s. Destroying old connection.\n", bn);
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - }
    - }
    -
    - /* Check for proper arguments */
    - if (args->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - if ((args->info.sendfile.filename == NULL) ||
    - (args->info.sendfile.totsize == 0) ||
    - (args->info.sendfile.totfiles == 0))
    - {
    - purple_debug_warning("oscar",
    - "%s tried to send you a file with incomplete "
    - "information.\n", bn);
    - return;
    - }
    - }
    -
    - conn = peer_connection_new(od, args->type, bn);
    - memcpy(conn->cookie, args->cookie, 8);
    - if (args->use_proxy)
    - conn->proxyip = g_strdup(args->proxyip);
    - conn->clientip = g_strdup(args->clientip);
    - conn->verifiedip = g_strdup(args->verifiedip);
    - conn->port = args->port;
    - conn->use_proxy |= args->use_proxy;
    - conn->lastrequestnumber++;
    -
    - if (args->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - buf = g_strdup_printf(_("%s has just asked to directly connect to %s"),
    - bn, purple_account_get_username(account));
    -
    - purple_request_action(conn, NULL, buf,
    - _("This requires a direct connection between "
    - "the two computers and is necessary for IM "
    - "Images. Because your IP address will be "
    - "revealed, this may be considered a privacy "
    - "risk."),
    - PURPLE_DEFAULT_ACTION_NONE,
    - account, bn, NULL,
    - conn, 2,
    - _("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
    - _("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb));
    - }
    - else if (args->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - gchar *filename;
    -
    - conn->xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, bn);
    - if (conn->xfer)
    - {
    - conn->xfer->data = conn;
    - purple_xfer_ref(conn->xfer);
    - purple_xfer_set_size(conn->xfer, args->info.sendfile.totsize);
    -
    - /* Set the file name */
    - if (g_utf8_validate(args->info.sendfile.filename, -1, NULL))
    - filename = g_strdup(args->info.sendfile.filename);
    - else
    - filename = purple_utf8_salvage(args->info.sendfile.filename);
    -
    - if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR)
    - {
    - /*
    - * If they are sending us a directory then the last character
    - * of the file name will be an asterisk. We don't want to
    - * save stuff to a directory named "*" so we remove the
    - * asterisk from the file name.
    - */
    - char *tmp = strrchr(filename, '\\');
    - if ((tmp != NULL) && (tmp[1] == '*'))
    - tmp[0] = '\0';
    - }
    - purple_xfer_set_filename(conn->xfer, filename);
    - g_free(filename);
    -
    - /*
    - * Set the message, unless this is the dummy message from an
    - * ICQ client or an empty message from an AIM client.
    - * TODO: Maybe we should strip HTML and then see if strlen>0?
    - */
    - if ((message != NULL) &&
    - (g_ascii_strncasecmp(message, "<ICQ_COOL_FT>", 13) != 0) &&
    - (g_ascii_strcasecmp(message, "<HTML>") != 0))
    - {
    - purple_xfer_set_message(conn->xfer, message);
    - }
    -
    - /* Setup our I/O op functions */
    - purple_xfer_set_init_fnc(conn->xfer, peer_oft_recvcb_init);
    - purple_xfer_set_end_fnc(conn->xfer, peer_oft_recvcb_end);
    - purple_xfer_set_request_denied_fnc(conn->xfer, peer_oft_cb_generic_cancel);
    - purple_xfer_set_cancel_recv_fnc(conn->xfer, peer_oft_cb_generic_cancel);
    - purple_xfer_set_ack_fnc(conn->xfer, peer_oft_recvcb_ack_recv);
    -
    - /* Now perform the request */
    - purple_xfer_request(conn->xfer);
    - }
    - }
    -}
    -
    -/*******************************************************************/
    -/* End code for establishing a peer connection */
    -/*******************************************************************/
    --- a/libpurple/protocols/oscar/peer.h Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,282 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * OFT and ODC Services
    - */
    -
    -#ifndef _PEER_H_
    -#define _PEER_H_
    -
    -#include "ft.h"
    -#include "network.h"
    -#include "proxy.h"
    -
    -typedef struct _ChecksumData ChecksumData;
    -typedef struct _OdcFrame OdcFrame;
    -typedef struct _OftFrame OftFrame;
    -typedef struct _ProxyFrame ProxyFrame;
    -typedef struct _PeerConnection PeerConnection;
    -
    -#define PEER_CONNECTION_FLAG_INITIATED_BY_ME 0x0001
    -#define PEER_CONNECTION_FLAG_APPROVED 0x0002
    -#define PEER_CONNECTION_FLAG_TRIED_DIRECT 0x0004
    -#define PEER_CONNECTION_FLAG_TRIED_INCOMING 0x0008
    -#define PEER_CONNECTION_FLAG_TRIED_PROXY 0x0010
    -#define PEER_CONNECTION_FLAG_IS_INCOMING 0x0020
    -
    -#define PEER_TYPE_PROMPT 0x0101 /* "I am going to send you this file, is that ok?" */
    -#define PEER_TYPE_RESUMEACCEPT 0x0106 /* We are accepting the resume */
    -#define PEER_TYPE_ACK 0x0202 /* "Yes, it is ok for you to send me that file" */
    -#define PEER_TYPE_DONE 0x0204 /* "I received that file with no problems" or "I already have that file, great!" */
    -#define PEER_TYPE_RESUME 0x0205 /* Resume transferring, sent by whoever receives */
    -#define PEER_TYPE_RESUMEACK 0x0207 /* Our resume accept was ACKed */
    -
    -#define PEER_TYPE_GETFILE_REQUESTLISTING 0x1108 /* "I have a listing.txt file, do you want it?" */
    -#define PEER_TYPE_GETFILE_RECEIVELISTING 0x1209 /* "Yes, please send me your listing.txt file" */
    -#define PEER_TYPE_GETFILE_RECEIVEDLISTING 0x120a /* received corrupt listing.txt file? I'm just guessing about this one... */
    -#define PEER_TYPE_GETFILE_ACKLISTING 0x120b /* "I received the listing.txt file successfully" */
    -#define PEER_TYPE_GETFILE_REQUESTFILE 0x120c /* "Please send me this file" */
    -
    -/*
    - * For peer proxying
    - */
    -#define AIM_PEER_PROXY_SERVER "ars.oscar.aol.com"
    -#define ICQ_PEER_PROXY_SERVER "ars.icq.com"
    -#define PEER_PROXY_PORT 5190 /* The port we should always connect to */
    -#define PEER_PROXY_PACKET_VERSION 0x044a
    -
    -/* Thanks to Keith Lea and the Joust project for documenting these */
    -#define PEER_PROXY_TYPE_ERROR 0x0001
    -#define PEER_PROXY_TYPE_CREATE 0x0002
    -#define PEER_PROXY_TYPE_CREATED 0x0003
    -#define PEER_PROXY_TYPE_JOIN 0x0004
    -#define PEER_PROXY_TYPE_READY 0x0005
    -
    -struct _OdcFrame
    -{
    - /* guchar magic[4]; */ /* 0 */
    - /* guint16 length; */ /* 4 */
    - guint16 type; /* 6 */
    - guint16 subtype; /* 8 */
    - /* Unknown */ /* 10 */
    - guchar cookie[8]; /* 12 */
    - /* Unknown */
    - /* guint32 payloadlength; */ /* 28 */
    - guint16 encoding; /* 32 */
    - /* Unknown */
    - guint16 flags; /* 38 */
    - /* Unknown */
    - guchar bn[32]; /* 44 */
    - /* Unknown */
    - ByteStream payload; /* 76 */
    -};
    -
    -struct _OftFrame
    -{
    - /* guchar magic[4]; */ /* 0 */
    - /* guint16 length; */ /* 4 */
    - guint16 type; /* 6 */
    - guchar cookie[8]; /* 8 */
    - guint16 encrypt; /* 16 */
    - guint16 compress; /* 18 */
    - guint16 totfiles; /* 20 */
    - guint16 filesleft; /* 22 */
    - guint16 totparts; /* 24 */
    - guint16 partsleft; /* 26 */
    - guint32 totsize; /* 28 */
    - guint32 size; /* 32 */
    - guint32 modtime; /* 36 */
    - guint32 checksum; /* 40 */
    - guint32 rfrcsum; /* 44 */
    - guint32 rfsize; /* 48 */
    - guint32 cretime; /* 52 */
    - guint32 rfcsum; /* 56 */
    - guint32 nrecvd; /* 60 */
    - guint32 recvcsum; /* 64 */
    - guchar idstring[32]; /* 68 */
    - guint8 flags; /* 100 */
    - guint8 lnameoffset; /* 101 */
    - guint8 lsizeoffset; /* 102 */
    - guchar dummy[69]; /* 103 */
    - guchar macfileinfo[16]; /* 172 */
    - guint16 nencode; /* 188 */
    - guint16 nlanguage; /* 190 */
    - guchar *name; /* 192 */
    - size_t name_length;
    - /* Payload? */ /* 256 */
    -};
    -
    -struct _ProxyFrame
    -{
    - /* guint16 length; */ /* 0 */
    - guint16 version; /* 2 */
    - guint16 type; /* 4 */
    - guint32 unknown; /* 6 */
    - guint16 flags; /* 10 */
    - ByteStream payload; /* 12 */
    -};
    -
    -struct _PeerConnection
    -{
    - OscarData *od;
    - guint64 type;
    - char *bn;
    - guchar magic[4];
    - guchar cookie[8];
    - guint16 lastrequestnumber;
    -
    - gboolean ready;
    - int flags; /**< Bitmask of PEER_CONNECTION_FLAG_ */
    - time_t lastactivity; /**< Time of last transmit. */
    - guint destroy_timeout;
    - OscarDisconnectReason disconnect_reason;
    - char *error_message;
    -
    - /**
    - * A pointer to either an OdcFrame or an OftFrame.
    - */
    - gpointer frame;
    -
    - /**
    - * This is only used when the peer connection is being established.
    - */
    - PurpleProxyConnectData *client_connect_data;
    - PurpleProxyConnectData *verified_connect_data;
    -
    - /**
    - * This is only used when the peer connection is being established.
    - */
    - PurpleNetworkListenData *listen_data;
    -
    -
    - /**
    - * This is only used when the peer connection is being established.
    - */
    - guint connect_timeout_timer;
    -
    - /**
    - * This is only used while the remote user is attempting to
    - * connect to us.
    - */
    - int listenerfd;
    -
    - int fd;
    - guint8 header[6];
    - gssize header_received;
    - guint8 proxy_header[12];
    - gssize proxy_header_received;
    - ByteStream buffer_incoming;
    - PurpleCircBuffer *buffer_outgoing;
    - guint watcher_incoming;
    - guint watcher_outgoing;
    -
    - /**
    - * IP address of the proxy server, if applicable.
    - */
    - gchar *proxyip;
    -
    - /**
    - * IP address of the remote user from THEIR point of view.
    - */
    - gchar *clientip;
    -
    - /**
    - * IP address of the remote user from the oscar server's
    - * point of view.
    - */
    - gchar *verifiedip;
    -
    - guint16 port;
    - gboolean use_proxy;
    -
    - /**
    - * Checksumming
    - */
    - ChecksumData *checksum_data;
    -
    - /* TODOFT */
    - PurpleXfer *xfer;
    - OftFrame xferdata;
    - guint sending_data_timer;
    -};
    -
    -/*
    - * For all peer connections
    - */
    -
    -/**
    - * Create a new PeerConnection structure and initialize it with some
    - * sane defaults.
    - *
    - * @param type The type of the peer connection. One of
    - * OSCAR_CAPABILITY_DIRECTIM or OSCAR_CAPABILITY_SENDFILE.
    - */
    -PeerConnection *peer_connection_new(OscarData *od, guint64 type, const char *bn);
    -
    -void peer_connection_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message);
    -void peer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message);
    -PeerConnection *peer_connection_find_by_type(OscarData *od, const char *bn, guint64 type);
    -PeerConnection *peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie);
    -
    -void peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond);
    -void peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond);
    -void peer_connection_send(PeerConnection *conn, ByteStream *bs);
    -
    -void peer_connection_trynext(PeerConnection *conn);
    -void peer_connection_finalize_connection(PeerConnection *conn);
    -void peer_connection_propose(OscarData *od, guint64 type, const char *bn);
    -void peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args);
    -
    -/*
    - * For ODC
    - */
    -void peer_odc_close(PeerConnection *conn);
    -void peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs);
    -void peer_odc_send_cookie(PeerConnection *conn);
    -void peer_odc_send_typing(PeerConnection *conn, PurpleTypingState typing);
    -void peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply);
    -
    -/*
    - * For OFT
    - */
    -void peer_oft_close(PeerConnection *conn);
    -void peer_oft_recv_frame(PeerConnection *conn, ByteStream *bs);
    -void peer_oft_send_prompt(PeerConnection *conn);
    -void peer_oft_checksum_destroy(ChecksumData *checksum_data);
    -
    -/* Xfer callbacks for receiving a file */
    -void peer_oft_recvcb_init(PurpleXfer *xfer);
    -void peer_oft_recvcb_end(PurpleXfer *xfer);
    -void peer_oft_recvcb_ack_recv(PurpleXfer *xfer, const guchar *buffer, size_t size);
    -
    -/* Xfer callbacks for sending a file */
    -void peer_oft_sendcb_init(PurpleXfer *xfer);
    -void peer_oft_sendcb_ack(PurpleXfer *xfer, const guchar *buffer, size_t size);
    -
    -/* Xfer callbacks for both sending and receiving */
    -void peer_oft_cb_generic_cancel(PurpleXfer *xfer);
    -
    -/*
    - * For peer proxying
    - */
    -void peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message);
    -
    -#endif /* _PEER_H_ */
    --- a/libpurple/protocols/oscar/peer_proxy.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,355 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#ifdef HAVE_CONFIG_H
    -#include <config.h>
    -#endif
    -
    -#include "oscar.h"
    -#include "peer.h"
    -
    -static void
    -peer_proxy_send(PeerConnection *conn, ProxyFrame *frame)
    -{
    - size_t length;
    - ByteStream bs;
    -
    - purple_debug_info("oscar", "Outgoing peer proxy frame with "
    - "type=0x%04hx, unknown=0x%08x, flags=0x%04hx, and "
    - "payload length=%" G_GSIZE_FORMAT "\n",
    - frame->type, frame->unknown,
    - frame->flags, frame->payload.len);
    -
    - length = 12 + frame->payload.len;
    - byte_stream_new(&bs, length);
    - byte_stream_put16(&bs, length - 2);
    - byte_stream_put16(&bs, PEER_PROXY_PACKET_VERSION);
    - byte_stream_put16(&bs, frame->type);
    - byte_stream_put32(&bs, frame->unknown);
    - byte_stream_put16(&bs, frame->flags);
    - byte_stream_putraw(&bs, frame->payload.data, frame->payload.len);
    -
    - peer_connection_send(conn, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Create a rendezvous "init send" packet and send it on its merry way.
    - * This is the first packet sent to the proxy server by the first client
    - * to indicate that this will be a proxied connection
    - *
    - * @param conn The peer connection.
    - */
    -static void
    -peer_proxy_send_create_new_conn(PeerConnection *conn)
    -{
    - ProxyFrame frame;
    - PurpleAccount *account;
    - const gchar *bn;
    - guint8 bn_length;
    -
    - memset(&frame, 0, sizeof(ProxyFrame));
    - frame.type = PEER_PROXY_TYPE_CREATE;
    - frame.flags = 0x0000;
    -
    - account = purple_connection_get_account(conn->od->gc);
    - bn = purple_account_get_username(account);
    - bn_length = strlen(bn);
    - byte_stream_new(&frame.payload, 1 + bn_length + 8 + 20);
    - byte_stream_put8(&frame.payload, bn_length);
    - byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length);
    - byte_stream_putraw(&frame.payload, conn->cookie, 8);
    -
    - byte_stream_put16(&frame.payload, 0x0001); /* Type */
    - byte_stream_put16(&frame.payload, 16); /* Length */
    - byte_stream_putcaps(&frame.payload, conn->type); /* Value */
    -
    - peer_proxy_send(conn, &frame);
    -
    - byte_stream_destroy(&frame.payload);
    -}
    -
    -/**
    - * Create a rendezvous "init recv" packet and send it on its merry way.
    - * This is the first packet sent to the proxy server by the second client
    - * involved in this rendezvous proxy session.
    - *
    - * @param conn The peer connection.
    - * @param pin The 2 byte PIN sent to us by the other user. This acts
    - * as our passcode when establishing the proxy session.
    - */
    -static void
    -peer_proxy_send_join_existing_conn(PeerConnection *conn, guint16 pin)
    -{
    - ProxyFrame frame;
    - PurpleAccount *account;
    - const gchar *bn;
    - guint8 bn_length;
    -
    - memset(&frame, 0, sizeof(ProxyFrame));
    - frame.type = PEER_PROXY_TYPE_JOIN;
    - frame.flags = 0x0000;
    -
    - account = purple_connection_get_account(conn->od->gc);
    - bn = purple_account_get_username(account);
    - bn_length = strlen(bn);
    - byte_stream_new(&frame.payload, 1 + bn_length + 2 + 8 + 20);
    - byte_stream_put8(&frame.payload, bn_length);
    - byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length);
    - byte_stream_put16(&frame.payload, pin);
    - byte_stream_putraw(&frame.payload, conn->cookie, 8);
    -
    - byte_stream_put16(&frame.payload, 0x0001); /* Type */
    - byte_stream_put16(&frame.payload, 16); /* Length */
    - byte_stream_putcaps(&frame.payload, conn->type); /* Value */
    -
    - peer_proxy_send(conn, &frame);
    -
    - byte_stream_destroy(&frame.payload);
    -}
    -
    -/**
    - * Handle an incoming peer proxy negotiation frame.
    - */
    -static void
    -peer_proxy_recv_frame(PeerConnection *conn, ProxyFrame *frame)
    -{
    - purple_debug_info("oscar", "Incoming peer proxy frame with "
    - "type=0x%04hx, unknown=0x%08x, flags=0x%04hx, and "
    - "payload length=%" G_GSIZE_FORMAT "\n", frame->type,
    - frame->unknown, frame->flags, frame->payload.len);
    -
    - if (frame->type == PEER_PROXY_TYPE_CREATED)
    - {
    - /*
    - * Read in 2 byte port then 4 byte IP and tell the
    - * remote user to connect to it by sending an ICBM.
    - */
    - guint16 pin;
    - int i;
    - guint8 ip[4];
    -
    - pin = byte_stream_get16(&frame->payload);
    - for (i = 0; i < 4; i++)
    - ip[i] = byte_stream_get8(&frame->payload);
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - aim_im_sendch2_odc_requestproxy(conn->od,
    - conn->cookie,
    - conn->bn, ip, pin, ++conn->lastrequestnumber);
    - else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - aim_im_sendch2_sendfile_requestproxy(conn->od,
    - conn->cookie, conn->bn,
    - ip, pin, ++conn->lastrequestnumber,
    - (const gchar *)conn->xferdata.name,
    - conn->xferdata.size, conn->xferdata.totfiles);
    - }
    - }
    - else if (frame->type == PEER_PROXY_TYPE_READY)
    - {
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    -
    - peer_connection_finalize_connection(conn);
    - }
    - else if (frame->type == PEER_PROXY_TYPE_ERROR)
    - {
    - if (byte_stream_bytes_left(&frame->payload) >= 2)
    - {
    - guint16 error;
    - const char *msg;
    - error = byte_stream_get16(&frame->payload);
    - if (error == 0x000d)
    - msg = "bad request";
    - else if (error == 0x0010)
    - msg = "initial request timed out";
    - else if (error == 0x001a)
    - msg ="accept period timed out";
    - else
    - msg = "unknown reason";
    - purple_debug_info("oscar", "Proxy negotiation failed with "
    - "error 0x%04hx: %s\n", error, msg);
    - }
    - else
    - {
    - purple_debug_warning("oscar", "Proxy negotiation failed with "
    - "an unknown error\n");
    - }
    - peer_connection_trynext(conn);
    - }
    - else
    - {
    - purple_debug_warning("oscar", "Unknown peer proxy frame type 0x%04hx.\n",
    - frame->type);
    - }
    -}
    -
    -static void
    -peer_proxy_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PeerConnection *conn;
    - gssize read;
    - ProxyFrame *frame;
    -
    - conn = data;
    - frame = conn->frame;
    -
    - /* Start reading a new proxy frame */
    - if (frame == NULL)
    - {
    - /* Read the first 12 bytes (frame length and header) */
    - read = recv(conn->fd, conn->proxy_header + conn->proxy_header_received,
    - 12 - conn->proxy_header_received, 0);
    -
    - /* Check if the proxy server closed the connection */
    - if (read == 0)
    - {
    - purple_debug_info("oscar", "Peer proxy server closed connection\n");
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - /* If there was an error then close the connection */
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - purple_debug_info("oscar", "Lost connection with peer proxy server\n");
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - conn->lastactivity = time(NULL);
    -
    - /* If we don't even have the first 12 bytes then do nothing */
    - conn->proxy_header_received += read;
    - if (conn->proxy_header_received < 12)
    - return;
    -
    - /* We only support a specific version of the proxy protocol */
    - if (aimutil_get16(&conn->proxy_header[2]) != PEER_PROXY_PACKET_VERSION)
    - {
    - purple_debug_warning("oscar", "Expected peer proxy protocol "
    - "version %u but received version %u. Closing "
    - "connection.\n", PEER_PROXY_PACKET_VERSION,
    - aimutil_get16(&conn->proxy_header[2]));
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - /* Initialize a new temporary ProxyFrame for incoming data */
    - frame = g_new0(ProxyFrame, 1);
    - frame->payload.len = aimutil_get16(&conn->proxy_header[0]) - 10;
    - frame->version = aimutil_get16(&conn->proxy_header[2]);
    - frame->type = aimutil_get16(&conn->proxy_header[4]);
    - frame->unknown = aimutil_get16(&conn->proxy_header[6]);
    - frame->flags = aimutil_get16(&conn->proxy_header[10]);
    - if (frame->payload.len > 0)
    - frame->payload.data = g_new(guint8, frame->payload.len);
    - conn->frame = frame;
    - }
    -
    - /* If this frame has a payload then attempt to read it */
    - if (frame->payload.len - frame->payload.offset > 0)
    - {
    - /* Read data into the temporary buffer until it is complete */
    - read = recv(conn->fd,
    - &frame->payload.data[frame->payload.offset],
    - frame->payload.len - frame->payload.offset,
    - 0);
    -
    - /* Check if the proxy server closed the connection */
    - if (read == 0)
    - {
    - purple_debug_info("oscar", "Peer proxy server closed connection\n");
    - g_free(frame->payload.data);
    - g_free(frame);
    - conn->frame = NULL;
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - /* If there was an error then close the connection */
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - purple_debug_info("oscar", "Lost connection with peer proxy server\n");
    - g_free(frame->payload.data);
    - g_free(frame);
    - conn->frame = NULL;
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - frame->payload.offset += read;
    - }
    -
    - conn->lastactivity = time(NULL);
    - if (frame->payload.offset < frame->payload.len)
    - /* Waiting for more data to arrive */
    - return;
    -
    - /* We have a complete proxy frame! Handle it and continue reading */
    - conn->frame = NULL;
    - byte_stream_rewind(&frame->payload);
    - peer_proxy_recv_frame(conn, frame);
    -
    - g_free(frame->payload.data);
    - g_free(frame);
    -
    - conn->proxy_header_received = 0;
    -}
    -
    -/**
    - * We tried to make an outgoing connection to a proxy server. It
    - * either connected or failed to connect.
    - */
    -void
    -peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - conn->verified_connect_data = NULL;
    -
    - if (source < 0)
    - {
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - conn->fd = source;
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, peer_proxy_connection_recv_cb, conn);
    -
    - if (conn->proxyip != NULL)
    - /* Connect to the session created by the remote user */
    - peer_proxy_send_join_existing_conn(conn, conn->port);
    - else
    - /* Create a new session */
    - peer_proxy_send_create_new_conn(conn);
    -}
    --- a/libpurple/protocols/oscar/rxhandlers.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,95 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "oscar.h"
    -#include "peer.h"
    -
    -aim_module_t *aim__findmodulebygroup(OscarData *od, guint16 group)
    -{
    - aim_module_t *cur;
    -
    - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
    - if (cur->family == group)
    - return cur;
    - }
    -
    - return NULL;
    -}
    -
    -aim_module_t *aim__findmodule(OscarData *od, const char *name)
    -{
    - aim_module_t *cur;
    -
    - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
    - if (purple_strequal(name, cur->name))
    - return cur;
    - }
    -
    - return NULL;
    -}
    -
    -int aim__registermodule(OscarData *od, int (*modfirst)(OscarData *, aim_module_t *))
    -{
    - aim_module_t *mod;
    -
    - if (!od || !modfirst)
    - return -1;
    -
    - mod = g_new0(aim_module_t, 1);
    -
    - if (modfirst(od, mod) == -1) {
    - g_free(mod);
    - return -1;
    - }
    -
    - if (aim__findmodule(od, mod->name)) {
    - if (mod->shutdown)
    - mod->shutdown(od, mod);
    - g_free(mod);
    - return -1;
    - }
    -
    - mod->next = (aim_module_t *)od->modlistv;
    - od->modlistv = mod;
    -
    - return 0;
    -}
    -
    -void aim__shutdownmodules(OscarData *od)
    -{
    - aim_module_t *cur;
    -
    - for (cur = (aim_module_t *)od->modlistv; cur; ) {
    - aim_module_t *tmp;
    -
    - tmp = cur->next;
    -
    - if (cur->shutdown)
    - cur->shutdown(od, cur);
    -
    - g_free(cur);
    -
    - cur = tmp;
    - }
    -
    - od->modlistv = NULL;
    -
    - return;
    -}
    --- a/libpurple/protocols/oscar/snac.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,165 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - *
    - * Various SNAC-related dodads...
    - *
    - * outstanding_snacs is a list of aim_snac_t structs. A SNAC should be added
    - * whenever a new SNAC is sent and it should remain in the list until the
    - * response for it has been received.
    - *
    - * cleansnacs() should be called periodically by the client in order
    - * to facilitate the aging out of unreplied-to SNACs. This can and does
    - * happen, so it should be handled.
    - *
    - */
    -
    -#include "oscar.h"
    -
    -#include "glibcompat.h"
    -
    -/*
    - * Called from oscar_session_new() to initialize the hash.
    - */
    -void aim_initsnachash(OscarData *od)
    -{
    - int i;
    -
    - for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++)
    - od->snac_hash[i] = NULL;
    -
    - return;
    -}
    -
    -aim_snacid_t aim_cachesnac(OscarData *od, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen)
    -{
    - aim_snac_t snac;
    -
    - snac.id = od->snacid_next++;
    - snac.family = family;
    - snac.type = type;
    - snac.flags = flags;
    -
    - if (datalen)
    - snac.data = g_memdup2(data, datalen);
    - else
    - snac.data = NULL;
    -
    - return aim_newsnac(od, &snac);
    -}
    -
    -/*
    - * Clones the passed snac structure and caches it in the
    - * list/hash.
    - */
    -aim_snacid_t aim_newsnac(OscarData *od, aim_snac_t *newsnac)
    -{
    - aim_snac_t *snac;
    - int index;
    -
    - if (!newsnac)
    - return 0;
    -
    - snac = g_memdup2(newsnac, sizeof(aim_snac_t));
    - snac->issuetime = time(NULL);
    -
    - index = snac->id % FAIM_SNAC_HASH_SIZE;
    -
    - snac->next = (aim_snac_t *)od->snac_hash[index];
    - od->snac_hash[index] = (void *)snac;
    -
    - return snac->id;
    -}
    -
    -/*
    - * Finds a snac structure with the passed SNAC ID,
    - * removes it from the list/hash, and returns a pointer to it.
    - *
    - * The returned structure must be freed by the caller.
    - *
    - */
    -aim_snac_t *aim_remsnac(OscarData *od, aim_snacid_t id)
    -{
    - aim_snac_t *cur, **prev;
    - int index;
    -
    - index = id % FAIM_SNAC_HASH_SIZE;
    -
    - for (prev = (aim_snac_t **)&od->snac_hash[index]; (cur = *prev); ) {
    - if (cur->id == id) {
    - *prev = cur->next;
    - if (cur->flags & AIM_SNACFLAGS_DESTRUCTOR) {
    - g_free(cur->data);
    - cur->data = NULL;
    - }
    - return cur;
    - } else
    - prev = &cur->next;
    - }
    -
    - return cur;
    -}
    -
    -/*
    - * This is for cleaning up old SNACs that either don't get replies or
    - * a reply was never received for. Garbage collection. Plain and simple.
    - *
    - * maxage is the _minimum_ age in seconds to keep SNACs.
    - *
    - */
    -void aim_cleansnacs(OscarData *od, int maxage)
    -{
    - int i;
    -
    - for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
    - aim_snac_t *cur, **prev;
    - time_t curtime;
    -
    - if (!od->snac_hash[i])
    - continue;
    -
    - curtime = time(NULL); /* done here in case we waited for the lock */
    -
    - for (prev = (aim_snac_t **)&od->snac_hash[i]; (cur = *prev); ) {
    - if ((curtime - cur->issuetime) > maxage) {
    -
    - *prev = cur->next;
    -
    - g_free(cur->data);
    - g_free(cur);
    - } else
    - prev = &cur->next;
    - }
    - }
    -
    - return;
    -}
    -
    -int aim_putsnac(ByteStream *bs, guint16 family, guint16 subtype, aim_snacid_t snacid)
    -{
    -
    - byte_stream_put16(bs, family);
    - byte_stream_put16(bs, subtype);
    - byte_stream_put16(bs, 0x0000);
    - byte_stream_put32(bs, snacid);
    -
    - return 10;
    -}
    --- a/libpurple/protocols/oscar/snactypes.h Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,287 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * AIM Callback Types
    - *
    - */
    -#ifndef _SNACTYPES_H_
    -#define _SNACTYPES_H_
    -
    -/*
    - * SNAC Families.
    - */
    -#define SNAC_FAMILY_OSERVICE 0x0001
    -#define SNAC_FAMILY_LOCATE 0x0002
    -#define SNAC_FAMILY_BUDDY 0x0003
    -#define SNAC_FAMILY_ICBM 0x0004
    -#define SNAC_FAMILY_ADVERT 0x0005
    -#define SNAC_FAMILY_INVITE 0x0006
    -#define SNAC_FAMILY_ADMIN 0x0007
    -#define SNAC_FAMILY_POPUP 0x0008
    -#define SNAC_FAMILY_BOS 0x0009
    -#define SNAC_FAMILY_USERLOOKUP 0x000a
    -#define SNAC_FAMILY_STATS 0x000b
    -#define SNAC_FAMILY_TRANSLATE 0x000c
    -#define SNAC_FAMILY_CHATNAV 0x000d
    -#define SNAC_FAMILY_CHAT 0x000e
    -#define SNAC_FAMILY_ODIR 0x000f
    -#define SNAC_FAMILY_BART 0x0010
    -#define SNAC_FAMILY_FEEDBAG 0x0013
    -#define SNAC_FAMILY_ICQ 0x0015
    -#define SNAC_FAMILY_AUTH 0x0017
    -#define SNAC_FAMILY_ALERT 0x0018
    -
    -#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */
    -
    -/*
    - * SNAC Family: Ack.
    - *
    - * Not really a family, but treating it as one really
    - * helps it fit into the libfaim callback structure better.
    - *
    - */
    -#define AIM_CB_ACK_ACK 0x0001
    -
    -/*
    - * SNAC Family: General.
    - */
    -#define SNAC_SUBTYPE_OSERVICE_ERROR 0x0001
    -#define SNAC_SUBTYPE_OSERVICE_CLIENTREADY 0x0002
    -#define SNAC_SUBTYPE_OSERVICE_SERVERREADY 0x0003
    -#define SNAC_SUBTYPE_OSERVICE_SERVICEREQ 0x0004
    -#define SNAC_SUBTYPE_OSERVICE_REDIRECT 0x0005
    -#define SNAC_SUBTYPE_OSERVICE_RATEINFOREQ 0x0006
    -#define SNAC_SUBTYPE_OSERVICE_RATEINFO 0x0007
    -#define SNAC_SUBTYPE_OSERVICE_RATEINFOACK 0x0008
    -#define SNAC_SUBTYPE_OSERVICE_RATECHANGE 0x000a
    -#define SNAC_SUBTYPE_OSERVICE_SERVERPAUSE 0x000b
    -#define SNAC_SUBTYPE_OSERVICE_SERVERRESUME 0x000d
    -#define SNAC_SUBTYPE_OSERVICE_REQSELFINFO 0x000e
    -#define SNAC_SUBTYPE_OSERVICE_SELFINFO 0x000f
    -#define SNAC_SUBTYPE_OSERVICE_EVIL 0x0010
    -#define SNAC_SUBTYPE_OSERVICE_SETIDLE 0x0011
    -#define SNAC_SUBTYPE_OSERVICE_MIGRATIONREQ 0x0012
    -#define SNAC_SUBTYPE_OSERVICE_MOTD 0x0013
    -#define SNAC_SUBTYPE_OSERVICE_SETPRIVFLAGS 0x0014
    -#define SNAC_SUBTYPE_OSERVICE_WELLKNOWNURL 0x0015
    -#define SNAC_SUBTYPE_OSERVICE_NOP 0x0016
    -#define SNAC_SUBTYPE_OSERVICE_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Location Services.
    - */
    -#define SNAC_SUBTYPE_LOCATE_ERROR 0x0001
    -#define SNAC_SUBTYPE_LOCATE_REQRIGHTS 0x0002
    -#define SNAC_SUBTYPE_LOCATE_RIGHTSINFO 0x0003
    -#define SNAC_SUBTYPE_LOCATE_SETUSERINFO 0x0004
    -#define SNAC_SUBTYPE_LOCATE_REQUSERINFO 0x0005
    -#define SNAC_SUBTYPE_LOCATE_USERINFO 0x0006
    -#define SNAC_SUBTYPE_LOCATE_WATCHERSUBREQ 0x0007
    -#define SNAC_SUBTYPE_LOCATE_WATCHERNOT 0x0008
    -#define SNAC_SUBTYPE_LOCATE_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Buddy List Management Services.
    - */
    -#define SNAC_SUBTYPE_BUDDY_ERROR 0x0001
    -#define SNAC_SUBTYPE_BUDDY_REQRIGHTS 0x0002
    -#define SNAC_SUBTYPE_BUDDY_RIGHTSINFO 0x0003
    -#define SNAC_SUBTYPE_BUDDY_ADDBUDDY 0x0004
    -#define SNAC_SUBTYPE_BUDDY_REMBUDDY 0x0005
    -#define SNAC_SUBTYPE_BUDDY_REJECT 0x000a
    -#define SNAC_SUBTYPE_BUDDY_ONCOMING 0x000b
    -#define SNAC_SUBTYPE_BUDDY_OFFGOING 0x000c
    -#define SNAC_SUBTYPE_BUDDY_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Messaging Services.
    - */
    -#define SNAC_SUBTYPE_ICBM_ERROR 0x0001
    -#define SNAC_SUBTYPE_ICBM_PARAMINFO 0x0005
    -#define SNAC_SUBTYPE_ICBM_INCOMING 0x0007
    -#define SNAC_SUBTYPE_ICBM_EVIL 0x0009
    -#define SNAC_SUBTYPE_ICBM_MISSEDCALL 0x000a
    -#define SNAC_SUBTYPE_ICBM_CLIENTAUTORESP 0x000b
    -#define SNAC_SUBTYPE_ICBM_ACK 0x000c
    -#define SNAC_SUBTYPE_ICBM_MTN 0x0014
    -#define SNAC_SUBTYPE_ICBM_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Advertisement Services
    - */
    -#define SNAC_SUBTYPE_ADVERT_ERROR 0x0001
    -#define SNAC_SUBTYPE_ADVERT_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Invitation Services.
    - */
    -#define SNAC_SUBTYPE_INVITE_ERROR 0x0001
    -#define SNAC_SUBTYPE_INVITE_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Administrative Services.
    - */
    -#define SNAC_SUBTYPE_ADMIN_ERROR 0x0001
    -#define SNAC_SUBTYPE_ADMIN_INFOCHANGE_REPLY 0x0005
    -#define SNAC_SUBTYPE_ADMIN_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Popup Messages
    - */
    -#define SNAC_SUBTYPE_POPUP_ERROR 0x0001
    -#define SNAC_SUBTYPE_POPUP_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Misc BOS Services.
    - */
    -#define SNAC_SUBTYPE_BOS_ERROR 0x0001
    -#define SNAC_SUBTYPE_BOS_RIGHTSQUERY 0x0002
    -#define SNAC_SUBTYPE_BOS_RIGHTS 0x0003
    -#define SNAC_SUBTYPE_BOS_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: User Lookup Services
    - */
    -#define SNAC_SUBTYPE_USERLOOKUP_ERROR 0x0001
    -#define SNAC_SUBTYPE_USERLOOKUP_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: User Status Services
    - */
    -#define SNAC_SUBTYPE_STATS_ERROR 0x0001
    -#define SNAC_SUBTYPE_STATS_SETREPORTINTERVAL 0x0002
    -#define SNAC_SUBTYPE_STATS_REPORTACK 0x0004
    -#define SNAC_SUBTYPE_STATS_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Translation Services
    - */
    -#define SNAC_SUBTYPE_TRANSLATE_ERROR 0x0001
    -#define SNAC_SUBTYPE_TRANSLATE_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Chat Navigation Services
    - */
    -#define SNAC_SUBTYPE_CHATNAV_ERROR 0x0001
    -#define SNAC_SUBTYPE_CHATNAV_CREATE 0x0008
    -#define SNAC_SUBTYPE_CHATNAV_INFO 0x0009
    -#define SNAC_SUBTYPE_CHATNAV_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Chat Services
    - */
    -#define SNAC_SUBTYPE_CHAT_ERROR 0x0001
    -#define SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE 0x0002
    -#define SNAC_SUBTYPE_CHAT_USERJOIN 0x0003
    -#define SNAC_SUBTYPE_CHAT_USERLEAVE 0x0004
    -#define SNAC_SUBTYPE_CHAT_OUTGOINGMSG 0x0005
    -#define SNAC_SUBTYPE_CHAT_INCOMINGMSG 0x0006
    -#define SNAC_SUBTYPE_CHAT_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: "New" Search
    - */
    -#define SNAC_SUBTYPE_ODIR_ERROR 0x0001
    -#define SNAC_SUBTYPE_ODIR_SEARCH 0x0002
    -#define SNAC_SUBTYPE_ODIR_RESULTS 0x0003
    -
    -/*
    - * SNAC Family: Buddy icons
    - */
    -#define SNAC_SUBTYPE_BART_ERROR 0x0001
    -#define SNAC_SUBTYPE_BART_REQUEST 0x0004
    -#define SNAC_SUBTYPE_BART_RESPONSE 0x0005
    -
    -/*
    - * SNAC Family: Server-Stored Buddy Lists
    - */
    -#define SNAC_SUBTYPE_FEEDBAG_ERROR 0x0001
    -#define SNAC_SUBTYPE_FEEDBAG_REQRIGHTS 0x0002
    -#define SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO 0x0003
    -#define SNAC_SUBTYPE_FEEDBAG_REQDATA 0x0004
    -#define SNAC_SUBTYPE_FEEDBAG_REQIFCHANGED 0x0005
    -#define SNAC_SUBTYPE_FEEDBAG_LIST 0x0006
    -#define SNAC_SUBTYPE_FEEDBAG_ACTIVATE 0x0007
    -#define SNAC_SUBTYPE_FEEDBAG_ADD 0x0008
    -#define SNAC_SUBTYPE_FEEDBAG_MOD 0x0009
    -#define SNAC_SUBTYPE_FEEDBAG_DEL 0x000A
    -#define SNAC_SUBTYPE_FEEDBAG_SRVACK 0x000E
    -#define SNAC_SUBTYPE_FEEDBAG_NOLIST 0x000F
    -#define SNAC_SUBTYPE_FEEDBAG_EDITSTART 0x0011
    -#define SNAC_SUBTYPE_FEEDBAG_EDITSTOP 0x0012
    -#define SNAC_SUBTYPE_FEEDBAG_SENDAUTH 0x0014
    -#define SNAC_SUBTYPE_FEEDBAG_RECVAUTH 0x0015
    -#define SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ 0x0018
    -#define SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ 0x0019
    -#define SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP 0x001a
    -#define SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP 0x001b
    -#define SNAC_SUBTYPE_FEEDBAG_ADDED 0x001c
    -
    -/*
    - * SNAC Family: ICQ
    - *
    - * Most of these are actually special.
    - */
    -#define SNAC_SUBTYPE_ICQ_ERROR 0x0001
    -#define SNAC_SUBTYPE_ICQ_OFFLINEMSG 0x00f0
    -#define SNAC_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE 0x00f1
    -#define SNAC_SUBTYPE_ICQ_INFO 0x00f2
    -#define SNAC_SUBTYPE_ICQ_ALIAS 0x00f3
    -#define SNAC_SUBTYPE_ICQ_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Authorizer
    - *
    - * Used only in protocol versions three and above.
    - */
    -#define SNAC_SUBTYPE_AUTH_ERROR 0x0001
    -#define SNAC_SUBTYPE_AUTH_LOGINREQEST 0x0002
    -#define SNAC_SUBTYPE_AUTH_LOGINRESPONSE 0x0003
    -#define SNAC_SUBTYPE_AUTH_AUTHREQ 0x0006
    -#define SNAC_SUBTYPE_AUTH_AUTHRESPONSE 0x0007
    -#define SNAC_SUBTYPE_AUTH_SECURID_REQUEST 0x000a
    -#define SNAC_SUBTYPE_AUTH_SECURID_RESPONSE 0x000b
    -
    -/*
    - * SNAC Family: Email
    - *
    - * Used for getting information on the email address
    - * associated with your username.
    - */
    -#define SNAC_SUBTYPE_ALERT_ERROR 0x0001
    -#define SNAC_SUBTYPE_ALERT_SENDCOOKIES 0x0006
    -#define SNAC_SUBTYPE_ALERT_MAILSTATUS 0x0007
    -#define SNAC_SUBTYPE_ALERT_INIT 0x0016
    -
    -/*
    - * SNAC Family: Internal Messages
    - *
    - * This isn't truly a SNAC family either, but using
    - * these, we can integrated non-SNAC services into
    - * the SNAC-centered libfaim callback structure.
    - */
    -#define AIM_CB_SPECIAL_CONNERR 0x0003
    -#define AIM_CB_SPECIAL_CONNINITDONE 0x0006
    -
    -/* SNAC flags */
    -#define AIM_SNACFLAGS_DESTRUCTOR 0x0001
    -
    -#endif /* _SNACTYPES_H_ */
    --- a/libpurple/protocols/oscar/tlv.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,830 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "oscar.h"
    -
    -#include "glibcompat.h"
    -
    -static aim_tlv_t *
    -createtlv(guint16 type, guint16 length, guint8 *value)
    -{
    - aim_tlv_t *ret;
    -
    - ret = g_new(aim_tlv_t, 1);
    - ret->type = type;
    - ret->length = length;
    - ret->value = value;
    -
    - return ret;
    -}
    -
    -static void
    -freetlv(aim_tlv_t *oldtlv)
    -{
    - g_free(oldtlv->value);
    - g_free(oldtlv);
    -}
    -
    -static GSList *
    -aim_tlv_read(GSList *list, ByteStream *bs)
    -{
    - guint16 type, length;
    - aim_tlv_t *tlv;
    -
    - type = byte_stream_get16(bs);
    - length = byte_stream_get16(bs);
    -
    - if (length > byte_stream_bytes_left(bs)) {
    - aim_tlvlist_free(list);
    - return NULL;
    - }
    -
    - tlv = createtlv(type, length, NULL);
    - if (tlv->length > 0) {
    - tlv->value = byte_stream_getraw(bs, length);
    - if (!tlv->value) {
    - freetlv(tlv);
    - aim_tlvlist_free(list);
    - return NULL;
    - }
    - }
    -
    - return g_slist_prepend(list, tlv);
    -}
    -
    -/**
    - * Read a TLV chain from a buffer.
    - *
    - * Reads and parses a series of TLV patterns from a data buffer; the
    - * returned structure is manipulatable with the rest of the TLV
    - * routines. When done with a TLV chain, aim_tlvlist_free() should
    - * be called to free the dynamic substructures.
    - *
    - * TODO: There should be a flag setable here to have the tlvlist contain
    - * bstream references, so that at least the ->value portion of each
    - * element doesn't need to be malloc/memcpy'd. This could prove to be
    - * just as efficient as the in-place TLV parsing used in a couple places
    - * in libfaim.
    - *
    - * @param bs Input bstream
    - * @return Return the TLV chain read
    - */
    -GSList *aim_tlvlist_read(ByteStream *bs)
    -{
    - GSList *list = NULL;
    -
    - while (byte_stream_bytes_left(bs) > 0) {
    - list = aim_tlv_read(list, bs);
    - if (list == NULL)
    - return NULL;
    - }
    -
    - return g_slist_reverse(list);
    -}
    -
    -/**
    - * Read a TLV chain from a buffer.
    - *
    - * Reads and parses a series of TLV patterns from a data buffer; the
    - * returned structure is manipulatable with the rest of the TLV
    - * routines. When done with a TLV chain, aim_tlvlist_free() should
    - * be called to free the dynamic substructures.
    - *
    - * TODO: There should be a flag setable here to have the tlvlist contain
    - * bstream references, so that at least the ->value portion of each
    - * element doesn't need to be malloc/memcpy'd. This could prove to be
    - * just as efficient as the in-place TLV parsing used in a couple places
    - * in libfaim.
    - *
    - * @param bs Input bstream
    - * @param num The max number of TLVs that will be read, or -1 if unlimited.
    - * There are a number of places where you want to read in a tlvchain,
    - * but the chain is not at the end of the SNAC, and the chain is
    - * preceded by the number of TLVs. So you can limit that with this.
    - * @return Return the TLV chain read
    - */
    -GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num)
    -{
    - GSList *list = NULL;
    -
    - while ((byte_stream_bytes_left(bs) > 0) && (num != 0)) {
    - list = aim_tlv_read(list, bs);
    - if (list == NULL)
    - return NULL;
    - num--;
    - }
    -
    - return g_slist_reverse(list);
    -}
    -
    -/**
    - * Read a TLV chain from a buffer.
    - *
    - * Reads and parses a series of TLV patterns from a data buffer; the
    - * returned structure is manipulatable with the rest of the TLV
    - * routines. When done with a TLV chain, aim_tlvlist_free() should
    - * be called to free the dynamic substructures.
    - *
    - * TODO: There should be a flag setable here to have the tlvlist contain
    - * bstream references, so that at least the ->value portion of each
    - * element doesn't need to be malloc/memcpy'd. This could prove to be
    - * just as efficient as the in-place TLV parsing used in a couple places
    - * in libfaim.
    - *
    - * @param bs Input bstream
    - * @param len The max length in bytes that will be read.
    - * There are a number of places where you want to read in a tlvchain,
    - * but the chain is not at the end of the SNAC, and the chain is
    - * preceded by the length of the TLVs. So you can limit that with this.
    - * @return Return the TLV chain read
    - */
    -GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len)
    -{
    - GSList *list = NULL;
    -
    - while ((byte_stream_bytes_left(bs) > 0) && (len > 0)) {
    - list = aim_tlv_read(list, bs);
    - if (list == NULL)
    - return NULL;
    -
    - len -= 2 + 2 + ((aim_tlv_t *)list->data)->length;
    - }
    -
    - return g_slist_reverse(list);
    -}
    -
    -/**
    - * Duplicate a TLV chain.
    - * This is pretty self explanatory.
    - *
    - * @param orig The TLV chain you want to make a copy of.
    - * @return A newly allocated TLV chain.
    - */
    -GSList *aim_tlvlist_copy(GSList *orig)
    -{
    - GSList *new = NULL;
    - aim_tlv_t *tlv;
    -
    - while (orig != NULL) {
    - tlv = orig->data;
    - aim_tlvlist_add_raw(&new, tlv->type, tlv->length, tlv->value);
    - orig = orig->next;
    - }
    -
    - return new;
    -}
    -
    -/*
    - * Compare two TLV lists for equality. This probably is not the most
    - * efficient way to do this.
    - *
    - * @param one One of the TLV chains to compare.
    - * @param two The other TLV chain to compare.
    - * @return Return 0 if the lists are the same, return 1 if they are different.
    - */
    -int aim_tlvlist_cmp(GSList *one, GSList *two)
    -{
    - ByteStream bs1, bs2;
    -
    - if (aim_tlvlist_size(one) != aim_tlvlist_size(two))
    - return 1;
    -
    - byte_stream_new(&bs1, aim_tlvlist_size(one));
    - byte_stream_new(&bs2, aim_tlvlist_size(two));
    -
    - aim_tlvlist_write(&bs1, &one);
    - aim_tlvlist_write(&bs2, &two);
    -
    - if (memcmp(bs1.data, bs2.data, bs1.len)) {
    - byte_stream_destroy(&bs1);
    - byte_stream_destroy(&bs2);
    - return 1;
    - }
    -
    - byte_stream_destroy(&bs1);
    - byte_stream_destroy(&bs2);
    -
    - return 0;
    -}
    -
    -/**
    - * Free a TLV chain structure
    - *
    - * Walks the list of TLVs in the passed TLV chain and
    - * frees each one. Note that any references to this data
    - * should be removed before calling this.
    - *
    - * @param list Chain to be freed
    - */
    -void aim_tlvlist_free(GSList *list)
    -{
    - while (list != NULL)
    - {
    - freetlv(list->data);
    - list = g_slist_delete_link(list, list);
    - }
    -}
    -
    -/**
    - * Count the number of TLVs in a chain.
    - *
    - * @param list Chain to be counted.
    - * @return The number of TLVs stored in the passed chain.
    - */
    -int aim_tlvlist_count(GSList *list)
    -{
    - GSList *cur;
    - int count;
    -
    - if (list == NULL)
    - return 0;
    -
    - for (cur = list, count = 0; cur; cur = cur->next)
    - count++;
    -
    - return count;
    -}
    -
    -/**
    - * Count the number of bytes in a TLV chain.
    - *
    - * @param list Chain to be sized
    - * @return The number of bytes that would be needed to
    - * write the passed TLV chain to a data buffer.
    - */
    -int aim_tlvlist_size(GSList *list)
    -{
    - GSList *cur;
    - int size;
    -
    - if (list == NULL)
    - return 0;
    -
    - for (cur = list, size = 0; cur; cur = cur->next)
    - size += (4 + ((aim_tlv_t *)cur->data)->length);
    -
    - return size;
    -}
    -
    -/**
    - * Adds the passed string as a TLV element of the passed type
    - * to the TLV chain.
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @param length Length of string to add (not including %NULL).
    - * @param value String to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value)
    -{
    - aim_tlv_t *tlv;
    -
    - if (list == NULL)
    - return 0;
    -
    - tlv = createtlv(type, length, NULL);
    - if (tlv->length > 0)
    - tlv->value = g_memdup2(value, length);
    -
    - *list = g_slist_append(*list, tlv);
    -
    - return tlv->length;
    -}
    -
    -/**
    - * Add a one byte integer to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param value Value to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_8(GSList **list, const guint16 type, const guint8 value)
    -{
    - guint8 v8[1];
    -
    - (void)aimutil_put8(v8, value);
    -
    - return aim_tlvlist_add_raw(list, type, 1, v8);
    -}
    -
    -/**
    - * Add a two byte integer to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param value Value to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value)
    -{
    - guint8 v16[2];
    -
    - (void)aimutil_put16(v16, value);
    -
    - return aim_tlvlist_add_raw(list, type, 2, v16);
    -}
    -
    -/**
    - * Add a four byte integer to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param value Value to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value)
    -{
    - guint8 v32[4];
    -
    - (void)aimutil_put32(v32, value);
    -
    - return aim_tlvlist_add_raw(list, type, 4, v32);
    -}
    -
    -/**
    - * Add a string to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param value Value to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value)
    -{
    - return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value);
    -}
    -
    -static int
    -count_caps(guint64 caps)
    -{
    - int set_bits = 0;
    - while (caps) {
    - set_bits += caps & 1;
    - caps >>= 1;
    - }
    - return set_bits;
    -}
    -
    -/**
    - * Adds a block of capability blocks to a TLV chain. The bitfield
    - * passed in should be a bitwise %OR of any of the %AIM_CAPS constants:
    - *
    - * %OSCAR_CAPABILITY_BUDDYICON Supports Buddy Icons
    - * %OSCAR_CAPABILITY_TALK Supports Voice Chat
    - * %OSCAR_CAPABILITY_IMIMAGE Supports DirectIM/IMImage
    - * %OSCAR_CAPABILITY_CHAT Supports Chat
    - * %OSCAR_CAPABILITY_GETFILE Supports Get File functions
    - * %OSCAR_CAPABILITY_SENDFILE Supports Send File functions
    - *
    - * @param list Destination chain
    - * @param type TLV type to add
    - * @param caps Bitfield of capability flags to send
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint64 caps, const char *mood)
    -{
    - int len;
    - ByteStream bs;
    - guint32 bs_size;
    - guint8 *data;
    -
    - if (caps == 0)
    - return 0; /* nothing there anyway */
    -
    - data = icq_get_custom_icon_data(mood);
    - bs_size = 16*(count_caps(caps) + (data != NULL ? 1 : 0));
    -
    - byte_stream_new(&bs, bs_size);
    - byte_stream_putcaps(&bs, caps);
    -
    - /* adding of custom icon GUID */
    - if (data != NULL)
    - byte_stream_putraw(&bs, data, 16);
    -
    - len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
    -
    - byte_stream_destroy(&bs);
    -
    - return len;
    -}
    -
    -/**
    - * Adds the given chatroom info to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param roomname The name of the chat.
    - * @param instance The instance.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance)
    -{
    - int len;
    - ByteStream bs;
    -
    - byte_stream_new(&bs, 2 + 1 + strlen(roomname) + 2);
    -
    - byte_stream_put16(&bs, exchange);
    - byte_stream_put8(&bs, strlen(roomname));
    - byte_stream_putstr(&bs, roomname);
    - byte_stream_put16(&bs, instance);
    -
    - len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
    -
    - byte_stream_destroy(&bs);
    -
    - return len;
    -}
    -
    -/**
    - * Adds a TLV with a zero length to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_noval(GSList **list, const guint16 type)
    -{
    - return aim_tlvlist_add_raw(list, type, 0, NULL);
    -}
    -
    -/*
    - * Note that the inner TLV chain will not be modifiable as a tlvchain once
    - * it is written using this. Or rather, it can be, but updates won't be
    - * made to this.
    - *
    - * TODO: Should probably support sublists for real.
    - *
    - * This is so neat.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param t1 The TLV chain you want to write.
    - * @return The number of bytes written to the destination TLV chain.
    - * 0 is returned if there was an error or if the destination
    - * TLV chain has length 0.
    - */
    -int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tlvlist)
    -{
    - int buflen;
    - ByteStream bs;
    -
    - buflen = aim_tlvlist_size(*tlvlist);
    -
    - if (buflen <= 0)
    - return 0;
    -
    - byte_stream_new(&bs, buflen);
    -
    - aim_tlvlist_write(&bs, tlvlist);
    -
    - aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
    -
    - byte_stream_destroy(&bs);
    -
    - return buflen;
    -}
    -
    -/**
    - * Substitute a TLV of a given type with a new TLV of the same type. If
    - * you attempt to replace a TLV that does not exist, this function will
    - * just add a new TLV as if you called aim_tlvlist_add_raw().
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @param length Length of string to add (not including %NULL).
    - * @param value String to add.
    - * @return The length of the TLV.
    - */
    -int aim_tlvlist_replace_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value)
    -{
    - GSList *cur;
    - aim_tlv_t *tlv;
    -
    - if (list == NULL)
    - return 0;
    -
    - for (cur = *list; cur != NULL; cur = cur->next)
    - {
    - tlv = cur->data;
    - if (tlv->type == type)
    - break;
    - }
    -
    - if (cur == NULL)
    - /* TLV does not exist, so add a new one */
    - return aim_tlvlist_add_raw(list, type, length, value);
    -
    - g_free(tlv->value);
    - tlv->length = length;
    - if (tlv->length > 0) {
    - tlv->value = g_memdup2(value, length);
    - } else
    - tlv->value = NULL;
    -
    - return tlv->length;
    -}
    -
    -/**
    - * Substitute a TLV of a given type with a new TLV of the same type. If
    - * you attempt to replace a TLV that does not exist, this function will
    - * just add a new TLV as if you called aim_tlvlist_add_str().
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @param str String to add.
    - * @return The length of the TLV.
    - */
    -int aim_tlvlist_replace_str(GSList **list, const guint16 type, const char *str)
    -{
    - return aim_tlvlist_replace_raw(list, type, strlen(str), (const guchar *)str);
    -}
    -
    -/**
    - * Substitute a TLV of a given type with a new TLV of the same type. If
    - * you attempt to replace a TLV that does not exist, this function will
    - * just add a new TLV as if you called aim_tlvlist_add_raw().
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @return The length of the TLV.
    - */
    -int aim_tlvlist_replace_noval(GSList **list, const guint16 type)
    -{
    - return aim_tlvlist_replace_raw(list, type, 0, NULL);
    -}
    -
    -/**
    - * Substitute a TLV of a given type with a new TLV of the same type. If
    - * you attempt to replace a TLV that does not exist, this function will
    - * just add a new TLV as if you called aim_tlvlist_add_raw().
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @param value 8 bit value to add.
    - * @return The length of the TLV.
    - */
    -int aim_tlvlist_replace_8(GSList **list, const guint16 type, const guint8 value)
    -{
    - guint8 v8[1];
    -
    - (void)aimutil_put8(v8, value);
    -
    - return aim_tlvlist_replace_raw(list, type, 1, v8);
    -}
    -
    -/**
    - * Substitute a TLV of a given type with a new TLV of the same type. If
    - * you attempt to replace a TLV that does not exist, this function will
    - * just add a new TLV as if you called aim_tlvlist_add_raw().
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @param value 32 bit value to add.
    - * @return The length of the TLV.
    - */
    -int aim_tlvlist_replace_32(GSList **list, const guint16 type, const guint32 value)
    -{
    - guint8 v32[4];
    -
    - (void)aimutil_put32(v32, value);
    -
    - return aim_tlvlist_replace_raw(list, type, 4, v32);
    -}
    -
    -/**
    - * Remove all TLVs of a given type. If you attempt to remove a TLV
    - * that does not exist, nothing happens.
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - */
    -void aim_tlvlist_remove(GSList **list, const guint16 type)
    -{
    - GSList *cur, *next;
    - aim_tlv_t *tlv;
    -
    - if (list == NULL || *list == NULL)
    - return;
    -
    - cur = *list;
    - while (cur != NULL)
    - {
    - tlv = cur->data;
    - next = cur->next;
    -
    - if (tlv->type == type)
    - {
    - /* Delete this TLV */
    - *list = g_slist_delete_link(*list, cur);
    - g_free(tlv->value);
    - g_free(tlv);
    - }
    -
    - cur = next;
    - }
    -}
    -
    -/**
    - * Write a TLV chain into a data buffer.
    - *
    - * Copies a TLV chain into a raw data buffer, writing only the number
    - * of bytes specified. This operation does not free the chain;
    - * aim_tlvlist_free() must still be called to free up the memory used
    - * by the chain structures.
    - *
    - * TODO: Clean this up, make better use of bstreams
    - *
    - * @param bs Input bstream
    - * @param list Source TLV chain
    - * @return Return 0 if the destination bstream is too small.
    - */
    -int aim_tlvlist_write(ByteStream *bs, GSList **list)
    -{
    - size_t goodbuflen;
    - GSList *cur;
    - aim_tlv_t *tlv;
    -
    - /* do an initial run to test total length */
    - goodbuflen = aim_tlvlist_size(*list);
    -
    - if (goodbuflen > byte_stream_bytes_left(bs))
    - return 0; /* not enough buffer */
    -
    - /* do the real write-out */
    - for (cur = *list; cur; cur = cur->next) {
    - tlv = cur->data;
    - byte_stream_put16(bs, tlv->type);
    - byte_stream_put16(bs, tlv->length);
    - if (tlv->length > 0)
    - byte_stream_putraw(bs, tlv->value, tlv->length);
    - }
    -
    - return 1; /* TODO: This is a nonsensical return */
    -}
    -
    -
    -/**
    - * Grab the Nth TLV of type type in the TLV list list.
    - *
    - * Returns a pointer to an aim_tlv_t of the specified type;
    - * %NULL on error. The @nth parameter is specified starting at %1.
    - * In most cases, there will be no more than one TLV of any type
    - * in a chain.
    - *
    - * @param list Source chain.
    - * @param type Requested TLV type.
    - * @param nth Index of TLV of type to get.
    - * @return The TLV you were looking for, or NULL if one could not be found.
    - */
    -aim_tlv_t *aim_tlv_gettlv(GSList *list, const guint16 type, const int nth)
    -{
    - GSList *cur;
    - aim_tlv_t *tlv;
    - int i;
    -
    - for (cur = list, i = 0; cur != NULL; cur = cur->next) {
    - tlv = cur->data;
    - if (tlv->type == type)
    - i++;
    - if (i >= nth)
    - return tlv;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * Get the length of the data of the nth TLV in the given TLV chain.
    - *
    - * @param list Source chain.
    - * @param type Requested TLV type.
    - * @param nth Index of TLV of type to get.
    - * @return The length of the data in this TLV, or -1 if the TLV could not be
    - * found. Unless -1 is returned, this value will be 2 bytes.
    - */
    -int aim_tlv_getlength(GSList *list, const guint16 type, const int nth)
    -{
    - aim_tlv_t *tlv;
    -
    - tlv = aim_tlv_gettlv(list, type, nth);
    - if (tlv == NULL)
    - return -1;
    -
    - return tlv->length;
    -}
    -
    -char *
    -aim_tlv_getvalue_as_string(aim_tlv_t *tlv)
    -{
    - char *ret;
    -
    - ret = g_malloc(tlv->length + 1);
    - memcpy(ret, tlv->value, tlv->length);
    - ret[tlv->length] = '\0';
    -
    - return ret;
    -}
    -
    -/**
    - * Retrieve the data from the nth TLV in the given TLV chain as a string.
    - *
    - * @param list Source TLV chain.
    - * @param type TLV type to search for.
    - * @param nth Index of TLV to return.
    - * @return The value of the TLV you were looking for, or NULL if one could
    - * not be found. This is a dynamic buffer and must be freed by the
    - * caller.
    - */
    -char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth)
    -{
    - aim_tlv_t *tlv;
    -
    - tlv = aim_tlv_gettlv(list, type, nth);
    - if (tlv == NULL)
    - return NULL;
    -
    - return aim_tlv_getvalue_as_string(tlv);
    -}
    -
    -/**
    - * Retrieve the data from the nth TLV in the given TLV chain as an 8bit
    - * integer.
    - *
    - * @param list Source TLV chain.
    - * @param type TLV type to search for.
    - * @param nth Index of TLV to return.
    - * @return The value the TLV you were looking for, or 0 if one could
    - * not be found.
    - */
    -guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth)
    -{
    - aim_tlv_t *tlv;
    -
    - tlv = aim_tlv_gettlv(list, type, nth);
    - if (tlv == NULL)
    - return 0; /* erm */
    -
    - return aimutil_get8(tlv->value);
    -}
    -
    -/**
    - * Retrieve the data from the nth TLV in the given TLV chain as a 16bit
    - * integer.
    - *
    - * @param list Source TLV chain.
    - * @param type TLV type to search for.
    - * @param nth Index of TLV to return.
    - * @return The value the TLV you were looking for, or 0 if one could
    - * not be found.
    - */
    -guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth)
    -{
    - aim_tlv_t *tlv;
    -
    - tlv = aim_tlv_gettlv(list, type, nth);
    - if (tlv == NULL)
    - return 0; /* erm */
    -
    - return aimutil_get16(tlv->value);
    -}
    -
    -/**
    - * Retrieve the data from the nth TLV in the given TLV chain as a 32bit
    - * integer.
    - *
    - * @param list Source TLV chain.
    - * @param type TLV type to search for.
    - * @param nth Index of TLV to return.
    - * @return The value the TLV you were looking for, or 0 if one could
    - * not be found.
    - */
    -guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth)
    -{
    - aim_tlv_t *tlv;
    -
    - tlv = aim_tlv_gettlv(list, type, nth);
    - if (tlv == NULL)
    - return 0; /* erm */
    -
    - return aimutil_get32(tlv->value);
    -}
    --- a/libpurple/protocols/oscar/userinfo.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,542 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Displaying various information about buddies.
    - */
    -
    -#include "encoding.h"
    -#include "oscar.h"
    -
    -static gchar *
    -oscar_caps_to_string(guint64 caps)
    -{
    - GString *str;
    - const gchar *tmp;
    - guint64 bit = 1;
    -
    - str = g_string_new("");
    -
    - if (!caps) {
    - return NULL;
    - } else while (bit <= OSCAR_CAPABILITY_LAST) {
    - if (bit & caps) {
    - switch (bit) {
    - case OSCAR_CAPABILITY_BUDDYICON:
    - tmp = _("Buddy Icon");
    - break;
    - case OSCAR_CAPABILITY_TALK:
    - tmp = _("Voice");
    - break;
    - case OSCAR_CAPABILITY_DIRECTIM:
    - tmp = _("AIM Direct IM");
    - break;
    - case OSCAR_CAPABILITY_CHAT:
    - tmp = _("Chat");
    - break;
    - case OSCAR_CAPABILITY_GETFILE:
    - tmp = _("Get File");
    - break;
    - case OSCAR_CAPABILITY_SENDFILE:
    - tmp = _("Send File");
    - break;
    - case OSCAR_CAPABILITY_GAMES:
    - case OSCAR_CAPABILITY_GAMES2:
    - tmp = _("Games");
    - break;
    - case OSCAR_CAPABILITY_XTRAZ:
    - case OSCAR_CAPABILITY_NEWCAPS:
    - tmp = _("ICQ Xtraz");
    - break;
    - case OSCAR_CAPABILITY_ADDINS:
    - tmp = _("Add-Ins");
    - break;
    - case OSCAR_CAPABILITY_SENDBUDDYLIST:
    - tmp = _("Send Buddy List");
    - break;
    - case OSCAR_CAPABILITY_ICQ_DIRECT:
    - tmp = _("ICQ Direct Connect");
    - break;
    - case OSCAR_CAPABILITY_APINFO:
    - tmp = _("AP User");
    - break;
    - case OSCAR_CAPABILITY_ICQRTF:
    - tmp = _("ICQ RTF");
    - break;
    - case OSCAR_CAPABILITY_EMPTY:
    - tmp = _("Nihilist");
    - break;
    - case OSCAR_CAPABILITY_ICQSERVERRELAY:
    - tmp = _("ICQ Server Relay");
    - break;
    - case OSCAR_CAPABILITY_UNICODEOLD:
    - tmp = _("Old ICQ UTF8");
    - break;
    - case OSCAR_CAPABILITY_TRILLIANCRYPT:
    - tmp = _("Trillian Encryption");
    - break;
    - case OSCAR_CAPABILITY_UNICODE:
    - tmp = _("ICQ UTF8");
    - break;
    - case OSCAR_CAPABILITY_HIPTOP:
    - tmp = _("Hiptop");
    - break;
    - case OSCAR_CAPABILITY_SECUREIM:
    - tmp = _("Security Enabled");
    - break;
    - case OSCAR_CAPABILITY_VIDEO:
    - tmp = _("Video Chat");
    - break;
    - /* Not actually sure about this one... WinAIM doesn't show anything */
    - case OSCAR_CAPABILITY_ICHATAV:
    - tmp = _("iChat AV");
    - break;
    - case OSCAR_CAPABILITY_LIVEVIDEO:
    - tmp = _("Live Video");
    - break;
    - case OSCAR_CAPABILITY_CAMERA:
    - tmp = _("Camera");
    - break;
    - case OSCAR_CAPABILITY_ICHAT_SCREENSHARE:
    - tmp = _("Screen Sharing");
    - break;
    - default:
    - tmp = NULL;
    - break;
    - }
    - if (tmp)
    - g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp);
    - }
    - bit <<= 1;
    - }
    -
    - return g_string_free(str, FALSE);
    -}
    -
    -static void
    -oscar_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *name, const char *value)
    -{
    - if (value && value[0]) {
    - purple_notify_user_info_add_pair(user_info, name, value);
    - }
    -}
    -
    -static void
    -oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
    - const char *name, const char *value)
    -{
    - gchar *utf8;
    -
    - if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
    - purple_notify_user_info_add_pair(user_info, name, utf8);
    - g_free(utf8);
    - }
    -}
    -
    -static void
    -oscar_user_info_convert_and_add_hyperlink(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
    - const char *name, const char *value, const char *url_prefix)
    -{
    - gchar *utf8;
    -
    - if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
    - gchar *tmp = g_strdup_printf("<a href=\"%s%s\">%s</a>", url_prefix, utf8, utf8);
    - purple_notify_user_info_add_pair(user_info, name, tmp);
    - g_free(utf8);
    - g_free(tmp);
    - }
    -}
    -
    -/**
    - * @brief Append the status information to a user_info struct
    - *
    - * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML.
    - *
    - * @param gc The PurpleConnection
    - * @param user_info A PurpleNotifyUserInfo object to which status information will be added
    - * @param b The PurpleBuddy whose status is desired. This or the aim_userinfo_t (or both) must be passed to oscar_user_info_append_status().
    - * @param userinfo The aim_userinfo_t of the buddy whose status is desired. This or the PurpleBuddy (or both) must be passed to oscar_user_info_append_status().
    - * @param use_html_status If TRUE, prefer HTML-formatted away message over plaintext available message.
    - */
    -void
    -oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean use_html_status)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - OscarData *od;
    - PurplePresence *presence = NULL;
    - PurpleStatus *status = NULL;
    - gchar *message = NULL, *itmsurl = NULL, *tmp;
    - gboolean escaping_needed = TRUE;
    -
    - od = purple_connection_get_protocol_data(gc);
    -
    - if (b == NULL && userinfo == NULL)
    - return;
    -
    - if (b == NULL)
    - b = purple_find_buddy(purple_connection_get_account(gc), userinfo->bn);
    - else
    - userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
    -
    - if (b) {
    - presence = purple_buddy_get_presence(b);
    - status = purple_presence_get_active_status(presence);
    - }
    -
    - /* If we have both b and userinfo we favor userinfo, because if we're
    - viewing someone's profile then we want the HTML away message, and
    - the "message" attribute of the status contains only the plaintext
    - message. */
    - if (userinfo) {
    - if ((userinfo->flags & AIM_FLAG_AWAY) && use_html_status && userinfo->away_len > 0 && userinfo->away != NULL && userinfo->away_encoding != NULL) {
    - /* Away message */
    - message = oscar_encoding_to_utf8(userinfo->away_encoding, userinfo->away, userinfo->away_len);
    - escaping_needed = FALSE;
    - } else {
    - /*
    - * Available message or non-HTML away message (because that's
    - * all we have right now.
    - */
    - if ((userinfo->status != NULL) && userinfo->status[0] != '\0') {
    - message = oscar_encoding_to_utf8(userinfo->status_encoding, userinfo->status, userinfo->status_len);
    - }
    -#if defined (_WIN32) || defined (__APPLE__)
    - if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) {
    - itmsurl = oscar_encoding_to_utf8(userinfo->itmsurl_encoding, userinfo->itmsurl, userinfo->itmsurl_len);
    - }
    -#endif
    - }
    - } else {
    - message = g_strdup(purple_status_get_attr_string(status, "message"));
    - itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
    - }
    -
    - if (message) {
    - tmp = oscar_util_format_string(message, purple_account_get_username(account));
    - g_free(message);
    - message = tmp;
    - if (escaping_needed) {
    - tmp = purple_markup_escape_text(message, -1);
    - g_free(message);
    - message = tmp;
    - }
    - }
    -
    - if (use_html_status && itmsurl) {
    - tmp = g_strdup_printf("<a href=\"%s\">%s</a>", itmsurl, message);
    - g_free(message);
    - message = tmp;
    - }
    -
    - if (b) {
    - if (purple_presence_is_online(presence)) {
    - gboolean is_away = ((status && !purple_status_is_available(status)) || (userinfo && (userinfo->flags & AIM_FLAG_AWAY)));
    - if (oscar_util_valid_name_icq(purple_buddy_get_name(b)) || is_away || !message || !(*message)) {
    - /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
    - * If the status name and the message are the same, only show one. */
    - const char *status_name = purple_status_get_name(status);
    - if (status_name && message && purple_strequal(status_name, message))
    - status_name = NULL;
    -
    - tmp = g_strdup_printf("%s%s%s",
    - status_name ? status_name : "",
    - ((status_name && message) && *message) ? ": " : "",
    - (message && *message) ? message : "");
    - g_free(message);
    - message = tmp;
    - }
    -
    - } else if (aim_ssi_waitingforauth(od->ssi.local,
    - aim_ssi_itemlist_findparentname(od->ssi.local, purple_buddy_get_name(b)),
    - purple_buddy_get_name(b)))
    - {
    - /* Note if an offline buddy is not authorized */
    - tmp = g_strdup_printf("%s%s%s",
    - _("Not Authorized"),
    - (message && *message) ? ": " : "",
    - (message && *message) ? message : "");
    - g_free(message);
    - message = tmp;
    - } else {
    - g_free(message);
    - message = g_strdup(_("Offline"));
    - }
    - }
    -
    - if (presence) {
    - const char *mood;
    - const char *comment;
    - char *description;
    - status = purple_presence_get_status(presence, "mood");
    - mood = icq_get_custom_icon_description(purple_status_get_attr_string(status, PURPLE_MOOD_NAME));
    - if (mood) {
    - comment = purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT);
    - if (comment) {
    - char *escaped_comment = purple_markup_escape_text(comment, -1);
    - description = g_strdup_printf("%s (%s)", _(mood), escaped_comment);
    - g_free(escaped_comment);
    - } else {
    - description = g_strdup(_(mood));
    - }
    - purple_notify_user_info_add_pair(user_info, _("Mood"), description);
    - g_free(description);
    - }
    - }
    -
    - purple_notify_user_info_add_pair(user_info, _("Status"), message);
    - g_free(message);
    -}
    -
    -void
    -oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
    -{
    - OscarData *od;
    - PurpleAccount *account;
    - PurpleGroup *g = NULL;
    - struct buddyinfo *bi = NULL;
    - char *tmp;
    - const char *bname = NULL, *gname = NULL;
    -
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
    - return;
    -
    - if (userinfo == NULL)
    - userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
    -
    - if (b == NULL)
    - b = purple_find_buddy(account, userinfo->bn);
    -
    - if (b != NULL) {
    - bname = purple_buddy_get_name(b);
    - g = purple_buddy_get_group(b);
    - gname = purple_group_get_name(g);
    - }
    -
    - if (userinfo != NULL)
    - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
    -
    - if ((bi != NULL) && (bi->ipaddr != 0)) {
    - tmp = g_strdup_printf("%u.%u.%u.%u",
    - 0xFF & ((bi->ipaddr & 0xff000000) >> 24),
    - 0xFF & ((bi->ipaddr & 0x00ff0000) >> 16),
    - 0xFF & ((bi->ipaddr & 0x0000ff00) >> 8),
    - 0xFF & (bi->ipaddr & 0x000000ff));
    - oscar_user_info_add_pair(user_info, _("IP Address"), tmp);
    - g_free(tmp);
    - }
    -
    - if ((userinfo != NULL) && (userinfo->warnlevel != 0)) {
    - tmp = g_strdup_printf("%d", (int)(userinfo->warnlevel/10.0 + .5));
    - oscar_user_info_add_pair(user_info, _("Warning Level"), tmp);
    - g_free(tmp);
    - }
    -
    - if ((b != NULL) && (bname != NULL) && (g != NULL) && (gname != NULL)) {
    - tmp = aim_ssi_getcomment(od->ssi.local, gname, bname);
    - if (tmp != NULL) {
    - char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
    - g_free(tmp);
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Buddy Comment"), tmp2);
    - g_free(tmp2);
    - }
    - }
    -}
    -
    -void
    -oscar_user_info_display_error(OscarData *od, guint16 error_reason, gchar *buddy)
    -{
    - PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
    - gchar *buf = g_strdup_printf(_("User information not available: %s"), oscar_get_msgerr_reason(error_reason));
    - purple_notify_user_info_add_pair(user_info, NULL, buf);
    - purple_notify_userinfo(od->gc, buddy, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    - if (!purple_conv_present_error(buddy, purple_connection_get_account(od->gc), buf))
    - purple_notify_error(od->gc, NULL, buf, NULL);
    - g_free(buf);
    -}
    -
    -void
    -oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleBuddy *buddy;
    - struct buddyinfo *bi;
    - gchar who[16];
    - PurpleNotifyUserInfo *user_info;
    -
    - if (!info->uin)
    - return;
    -
    - user_info = purple_notify_user_info_new();
    -
    - g_snprintf(who, sizeof(who), "%u", info->uin);
    - buddy = purple_find_buddy(account, who);
    - if (buddy != NULL)
    - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, purple_buddy_get_name(buddy)));
    - else
    - bi = NULL;
    -
    - purple_notify_user_info_add_pair(user_info, _("UIN"), who);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick);
    - if ((bi != NULL) && (bi->ipaddr != 0)) {
    - char *tstr = g_strdup_printf("%u.%u.%u.%u",
    - 0xFF & ((bi->ipaddr & 0xff000000) >> 24),
    - 0xFF & ((bi->ipaddr & 0x00ff0000) >> 16),
    - 0xFF & ((bi->ipaddr & 0x0000ff00) >> 8),
    - 0xFF & (bi->ipaddr & 0x000000ff));
    - purple_notify_user_info_add_pair(user_info, _("IP Address"), tstr);
    - g_free(tstr);
    - }
    - oscar_user_info_convert_and_add(account, od, user_info, _("First Name"), info->first);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Last Name"), info->last);
    - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Email Address"), info->email, "mailto:");
    - if (info->numaddresses && info->email2) {
    - int i;
    - for (i = 0; i < info->numaddresses; i++) {
    - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Email Address"), info->email2[i], "mailto:");
    - }
    - }
    - oscar_user_info_convert_and_add(account, od, user_info, _("Mobile Phone"), info->mobile);
    -
    - if (info->gender != 0)
    - purple_notify_user_info_add_pair(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male")));
    -
    - if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) {
    - /* Initialize the struct properly or strftime() will crash
    - * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */
    - time_t t = time(NULL);
    - struct tm *tm = localtime(&t);
    -
    - tm->tm_mday = (int)info->birthday;
    - tm->tm_mon = (int)info->birthmonth - 1;
    - tm->tm_year = (int)info->birthyear - 1900;
    -
    - /* Ignore dst setting of today to avoid timezone shift between
    - * dates in summer and winter time. */
    - tm->tm_isdst = -1;
    -
    - /* To be 100% sure that the fields are re-normalized.
    - * If you're sure strftime() ALWAYS does this EVERYWHERE,
    - * feel free to remove it. --rlaager */
    - mktime(tm);
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm));
    - }
    - if ((info->age > 0) && (info->age < 255)) {
    - char age[5];
    - snprintf(age, sizeof(age), "%hhd", info->age);
    - purple_notify_user_info_add_pair(user_info, _("Age"), age);
    - }
    - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Personal Web Page"), info->email, "");
    - if (buddy != NULL)
    - oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* use_html_status */ TRUE);
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info);
    - purple_notify_user_info_add_section_break(user_info);
    -
    - if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
    - purple_notify_user_info_add_section_header(user_info, _("Home Address"));
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->homeaddr);
    - oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->homecity);
    - oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->homestate);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->homezip);
    - }
    - if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
    - purple_notify_user_info_add_section_header(user_info, _("Work Address"));
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->workaddr);
    - oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->workcity);
    - oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->workstate);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->workzip);
    - }
    - if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
    - purple_notify_user_info_add_section_header(user_info, _("Work Information"));
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Company"), info->workcompany);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Division"), info->workdivision);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Position"), info->workposition);
    - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Web Page"), info->email, "");
    - }
    -
    - purple_notify_userinfo(gc, who, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    -}
    -
    -void
    -oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
    - gchar *tmp = NULL, *info_utf8 = NULL, *base_profile_url = NULL;
    -
    - oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* use_html_status */ TRUE);
    -
    - if ((userinfo->present & AIM_USERINFO_PRESENT_IDLE) && userinfo->idletime != 0) {
    - tmp = purple_str_seconds_to_string(userinfo->idletime*60);
    - oscar_user_info_add_pair(user_info, _("Idle"), tmp);
    - g_free(tmp);
    - }
    -
    - oscar_user_info_append_extra_info(gc, user_info, NULL, userinfo);
    -
    - if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !oscar_util_valid_name_sms(userinfo->bn)) {
    - /* An SMS contact is always online; its Online Since value is not useful */
    - time_t t = userinfo->onlinesince;
    - oscar_user_info_add_pair(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
    - }
    -
    - if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
    - time_t t = userinfo->membersince;
    - oscar_user_info_add_pair(user_info, _("Member Since"), purple_date_format_full(localtime(&t)));
    - }
    -
    - if (userinfo->capabilities != 0) {
    - tmp = oscar_caps_to_string(userinfo->capabilities);
    - oscar_user_info_add_pair(user_info, _("Capabilities"), tmp);
    - g_free(tmp);
    - }
    -
    - /* Info */
    - if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
    - info_utf8 = oscar_encoding_to_utf8(userinfo->info_encoding, userinfo->info, userinfo->info_len);
    - tmp = oscar_util_format_string(info_utf8, purple_account_get_username(account));
    - purple_notify_user_info_add_section_break(user_info);
    - oscar_user_info_add_pair(user_info, _("Profile"), tmp);
    - g_free(tmp);
    - g_free(info_utf8);
    - }
    -
    - purple_notify_user_info_add_section_break(user_info);
    - base_profile_url = oscar_util_valid_name_icq(userinfo->bn) ? "http://www.icq.com/people" : "http://profiles.aim.com";
    - tmp = g_strdup_printf("<a href=\"%s/%s\">%s</a>",
    - base_profile_url, purple_normalize(account, userinfo->bn), _("View web profile"));
    - purple_notify_user_info_add_pair(user_info, NULL, tmp);
    - g_free(tmp);
    -
    - purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    -}
    --- a/libpurple/protocols/oscar/util.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,326 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * A little bit of this
    - * A little bit of that
    - * It started with a kiss
    - * Now we're up to bat
    - */
    -
    -#include "oscar.h"
    -
    -#include "core.h"
    -
    -#include <ctype.h>
    -
    -#ifdef _WIN32
    -#include "win32dep.h"
    -#endif
    -
    -static const char * const msgerrreason[] = {
    - N_("Invalid error"),
    - N_("Invalid SNAC"),
    - N_("Server rate limit exceeded"),
    - N_("Client rate limit exceeded"),
    - N_("Not logged in"),
    - N_("Service unavailable"),
    - N_("Service not defined"),
    - N_("Obsolete SNAC"),
    - N_("Not supported by host"),
    - N_("Not supported by client"),
    - N_("Refused by client"),
    - N_("Reply too big"),
    - N_("Responses lost"),
    - N_("Request denied"),
    - N_("Busted SNAC payload"),
    - N_("Insufficient rights"),
    - N_("In local permit/deny"),
    - N_("Warning level too high (sender)"),
    - N_("Warning level too high (receiver)"),
    - N_("User temporarily unavailable"),
    - N_("No match"),
    - N_("List overflow"),
    - N_("Request ambiguous"),
    - N_("Queue full"),
    - N_("Not while on AOL")
    -};
    -static const gsize msgerrreasonlen = G_N_ELEMENTS(msgerrreason);
    -
    -const char *oscar_get_msgerr_reason(size_t reason)
    -{
    - return (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("Unknown reason");
    -}
    -
    -int oscar_get_ui_info_int(const char *str, int default_value)
    -{
    - GHashTable *ui_info;
    -
    - ui_info = purple_core_get_ui_info();
    - if (ui_info != NULL) {
    - gpointer value;
    - if (g_hash_table_lookup_extended(ui_info, str, NULL, &value))
    - return GPOINTER_TO_INT(value);
    - }
    -
    - return default_value;
    -}
    -
    -const char *oscar_get_ui_info_string(const char *str, const char *default_value)
    -{
    - GHashTable *ui_info;
    - const char *value = NULL;
    -
    - ui_info = purple_core_get_ui_info();
    - if (ui_info != NULL)
    - value = g_hash_table_lookup(ui_info, str);
    - if (value == NULL)
    - value = default_value;
    -
    - return value;
    -}
    -
    -gchar *oscar_get_clientstring(void)
    -{
    - const char *name, *version;
    -
    - name = oscar_get_ui_info_string("name", "Purple");
    - version = oscar_get_ui_info_string("version", VERSION);
    -
    - return g_strdup_printf("%s/%s", name, version);;
    -}
    -
    -/**
    - * Calculate the checksum of a given icon.
    - */
    -guint16
    -aimutil_iconsum(const guint8 *buf, int buflen)
    -{
    - guint32 sum;
    - int i;
    -
    - for (i=0, sum=0; i+1<buflen; i+=2)
    - sum += (buf[i+1] << 8) + buf[i];
    - if (i < buflen)
    - sum += buf[i];
    - sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);
    -
    - return sum;
    -}
    -
    -/**
    - * Check if the given name is a valid AIM username.
    - * Example: BobDole
    - * Example: Henry_Ford@mac.com
    - * Example: 1KrazyKat@example.com
    - *
    - * @return TRUE if the name is valid, FALSE if not.
    - */
    -static gboolean
    -oscar_util_valid_name_aim(const char *name)
    -{
    - int i;
    -
    - if (purple_email_is_valid(name))
    - return TRUE;
    -
    - /* Normal AIM usernames can't start with a number, period or underscore */
    - if (isalnum(name[0]) == 0)
    - return FALSE;
    -
    - for (i = 0; name[i] != '\0'; i++) {
    - if (!isalnum(name[i]) && name[i] != ' ' && name[i] != '.' && name[i] != '_')
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/**
    - * Check if the given name is a valid ICQ username.
    - * Example: 1234567
    - *
    - * @return TRUE if the name is valid, FALSE if not.
    - */
    -gboolean
    -oscar_util_valid_name_icq(const char *name)
    -{
    - int i;
    -
    - for (i = 0; name[i] != '\0'; i++) {
    - if (!isdigit(name[i]))
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/**
    - * Check if the given name is a valid SMS username.
    - * Example: +19195551234
    - *
    - * @return TRUE if the name is valid, FALSE if not.
    - */
    -gboolean
    -oscar_util_valid_name_sms(const char *name)
    -{
    - int i;
    -
    - if (name[0] != '+')
    - return FALSE;
    -
    - for (i = 1; name[i] != '\0'; i++) {
    - if (!isdigit(name[i]))
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/**
    - * Check if the given name is a valid oscar username.
    - *
    - * @return TRUE if the name is valid, FALSE if not.
    - */
    -gboolean
    -oscar_util_valid_name(const char *name)
    -{
    - if ((name == NULL) || (*name == '\0'))
    - return FALSE;
    -
    - return oscar_util_valid_name_icq(name)
    - || oscar_util_valid_name_sms(name)
    - || oscar_util_valid_name_aim(name);
    -}
    -
    -/**
    - * This takes two names and compares them using the rules
    - * on usernames for AIM/AOL. Mainly, this means case and space
    - * insensitivity (all case differences and spacing differences are
    - * ignored, with the exception that usernames can not start with
    - * a space).
    - *
    - * @return 0 if equal, non-0 if different
    - */
    -/* TODO: Do something different for email addresses. */
    -int
    -oscar_util_name_compare(const char *name1, const char *name2)
    -{
    -
    - if ((name1 == NULL) || (name2 == NULL))
    - return -1;
    -
    - do {
    - while (*name2 == ' ')
    - name2++;
    - while (*name1 == ' ')
    - name1++;
    - if (toupper(*name1) != toupper(*name2))
    - return 1;
    - } while ((*name1 != '\0') && name1++ && name2++);
    -
    - return 0;
    -}
    -
    -/**
    - * Looks for %n, %d, or %t in a string, and replaces them with the
    - * specified name, date, and time, respectively.
    - *
    - * @param str The string that may contain the special variables.
    - * @param name The sender name.
    - *
    - * @return A newly allocated string where the special variables are
    - * expanded. This should be g_free'd by the caller.
    - */
    -gchar *
    -oscar_util_format_string(const char *str, const char *name)
    -{
    - char *c;
    - GString *cpy;
    - time_t t;
    - struct tm *tme;
    -
    - g_return_val_if_fail(str != NULL, NULL);
    - g_return_val_if_fail(name != NULL, NULL);
    -
    - /* Create an empty GString that is hopefully big enough for most messages */
    - cpy = g_string_sized_new(1024);
    -
    - t = time(NULL);
    - tme = localtime(&t);
    -
    - c = (char *)str;
    - while (*c) {
    - switch (*c) {
    - case '%':
    - if (*(c + 1)) {
    - switch (*(c + 1)) {
    - case 'n':
    - /* append name */
    - g_string_append(cpy, name);
    - c++;
    - break;
    - case 'd':
    - /* append date */
    - g_string_append(cpy, purple_date_format_short(tme));
    - c++;
    - break;
    - case 't':
    - /* append time */
    - g_string_append(cpy, purple_time_format(tme));
    - c++;
    - break;
    - default:
    - g_string_append_c(cpy, *c);
    - }
    - } else {
    - g_string_append_c(cpy, *c);
    - }
    - break;
    - default:
    - g_string_append_c(cpy, *c);
    - }
    - c++;
    - }
    -
    - return g_string_free(cpy, FALSE);
    -}
    -
    -gchar *
    -oscar_format_buddies(GSList *buddies, const gchar *no_buddies_message)
    -{
    - GSList *cur;
    - GString *result;
    - if (!buddies) {
    - return g_strdup_printf("<i>%s</i>", no_buddies_message);
    - }
    - result = g_string_new("");
    - for (cur = buddies; cur != NULL; cur = cur->next) {
    - PurpleBuddy *buddy = cur->data;
    - const gchar *bname = purple_buddy_get_name(buddy);
    - const gchar *alias = purple_buddy_get_alias_only(buddy);
    - g_string_append(result, bname);
    - if (alias) {
    - g_string_append_printf(result, " (%s)", alias);
    - }
    - g_string_append(result, "<br>");
    - }
    - return g_string_free(result, FALSE);
    -}
    --- a/libpurple/protocols/oscar/visibility.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,140 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "visibility.h"
    -
    -/* Translators: This string is a menu option that, if selected, will cause
    - you to appear online to the chosen user even when your status is set to
    - Invisible. */
    -#define APPEAR_ONLINE N_("Appear Online")
    -
    -/* Translators: This string is a menu option that, if selected, will cause
    - you to appear offline to the chosen user when your status is set to
    - Invisible (this is the default). */
    -#define DONT_APPEAR_ONLINE N_("Don't Appear Online")
    -
    -/* Translators: This string is a menu option that, if selected, will cause
    - you to always appear offline to the chosen user (even when your status
    - isn't Invisible). */
    -#define APPEAR_OFFLINE N_("Appear Offline")
    -
    -/* Translators: This string is a menu option that, if selected, will cause
    - you to appear offline to the chosen user if you are invisible, and
    - appear online to the chosen user if you are not invisible (this is the
    - default). */
    -#define DONT_APPEAR_OFFLINE N_("Don't Appear Offline")
    -
    -static guint16
    -get_buddy_list_type(OscarData *od)
    -{
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - return purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE) ? AIM_SSI_TYPE_PERMIT : AIM_SSI_TYPE_DENY;
    -}
    -
    -static gboolean
    -is_buddy_on_list(OscarData *od, const char *bname)
    -{
    - return aim_ssi_itemlist_finditem(od->ssi.local, NULL, bname, get_buddy_list_type(od)) != NULL;
    -}
    -
    -static void
    -visibility_cb(PurpleBlistNode *node, gpointer whatever)
    -{
    - PurpleBuddy *buddy = PURPLE_BUDDY(node);
    - const char* bname = purple_buddy_get_name(buddy);
    - OscarData *od = purple_connection_get_protocol_data(purple_account_get_connection(purple_buddy_get_account(buddy)));
    - guint16 list_type = get_buddy_list_type(od);
    -
    - if (!is_buddy_on_list(od, bname)) {
    - aim_ssi_add_to_private_list(od, bname, list_type);
    - } else {
    - aim_ssi_del_from_private_list(od, bname, list_type);
    - }
    -}
    -
    -PurpleMenuAction *
    -create_visibility_menu_item(OscarData *od, const char *bname)
    -{
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - gboolean invisible = purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE);
    - gboolean on_list = is_buddy_on_list(od, bname);
    - const gchar *label;
    -
    - if (invisible) {
    - label = on_list ? _(DONT_APPEAR_ONLINE) : _(APPEAR_ONLINE);
    - } else {
    - label = on_list ? _(DONT_APPEAR_OFFLINE) : _(APPEAR_OFFLINE);
    - }
    - return purple_menu_action_new(label, PURPLE_CALLBACK(visibility_cb), NULL, NULL);
    -}
    -
    -static void
    -show_private_list(PurplePluginAction *action, guint16 list_type, const gchar *title, const gchar *list_description, const gchar *menu_action_name)
    -{
    - PurpleConnection *gc = (PurpleConnection *) action->context;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - GSList *buddies, *filtered_buddies, *cur;
    - gchar *text, *secondary;
    -
    - buddies = purple_find_buddies(account, NULL);
    - filtered_buddies = NULL;
    - for (cur = buddies; cur != NULL; cur = cur->next) {
    - PurpleBuddy *buddy;
    - const gchar *bname;
    -
    - buddy = cur->data;
    - bname = purple_buddy_get_name(buddy);
    - if (aim_ssi_itemlist_finditem(od->ssi.local, NULL, bname, list_type)) {
    - filtered_buddies = g_slist_prepend(filtered_buddies, buddy);
    - }
    - }
    -
    - g_slist_free(buddies);
    -
    - filtered_buddies = g_slist_reverse(filtered_buddies);
    - text = oscar_format_buddies(filtered_buddies, _("you have no buddies on this list"));
    - g_slist_free(filtered_buddies);
    -
    - secondary = g_strdup_printf(_("You can add a buddy to this list "
    - "by right-clicking on them and "
    - "selecting \"%s\""), menu_action_name);
    - purple_notify_formatted(gc, title, list_description, secondary, text, NULL, NULL);
    - g_free(secondary);
    - g_free(text);
    -}
    -
    -void
    -oscar_show_visible_list(PurplePluginAction *action)
    -{
    - show_private_list(action, AIM_SSI_TYPE_PERMIT, _("Visible List"),
    - _("These buddies will see "
    - "your status when you switch "
    - "to \"Invisible\""),
    - _(APPEAR_ONLINE));
    -}
    -
    -void
    -oscar_show_invisible_list(PurplePluginAction *action)
    -{
    - show_private_list(action, AIM_SSI_TYPE_DENY, _("Invisible List"),
    - _("These buddies will always see you as offline"),
    - _(APPEAR_OFFLINE));
    -}
    --- a/libpurple/protocols/oscar/visibility.h Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,32 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library 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
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#ifndef _VISIBILITY_H_
    -#define _VISIBILITY_H_
    -
    -#include "oscar.h"
    -#include "plugin.h"
    -#include "util.h"
    -
    -PurpleMenuAction * create_visibility_menu_item(OscarData *od, const char *bname);
    -void oscar_show_visible_list(PurplePluginAction *action);
    -void oscar_show_invisible_list(PurplePluginAction *action);
    -
    -#endif
    \ No newline at end of file
    --- a/libpurple/purple-url-handler Mon Apr 05 20:41:42 2021 -0500
    +++ b/libpurple/purple-url-handler Thu Apr 08 22:30:53 2021 -0500
    @@ -123,32 +123,6 @@
    cpurple.PurpleBlistRequestAddBuddy(account, screenname, group, alias)
    -def aim(uri):
    - protocol = "prpl-aim"
    - match = re.match(r"^aim:([^?]*)(\?(.*))", uri)
    - if not match:
    - print("Invalid aim URI: %s" % uri)
    - return
    -
    - command = unquote_plus(match.group(1))
    - paramstring = match.group(3)
    - params = {}
    - if paramstring:
    - for param in paramstring.split("&"):
    - key, value = extendlist(param.split("=", 1), 2, "")
    - params[key] = unquote_plus(value)
    - accountname = params.get("account", "")
    - screenname = params.get("screenname", "")
    -
    - account = findaccount(protocol, accountname)
    -
    - if command.lower() == "goim":
    - goim(account, screenname, params.get("message"))
    - elif command.lower() == "gochat":
    - gochat(account, params)
    - elif command.lower() == "addbuddy":
    - addbuddy(account, screenname, params.get("group", ""))
    -
    def gg(uri):
    protocol = "prpl-gg"
    match = re.match(r"^gg:(.*)", uri)
    @@ -317,9 +291,7 @@
    type = uri.split(":")[0]
    try:
    - if type == "aim":
    - aim(uri)
    - elif type == "gg":
    + if type == "gg":
    gg(uri)
    elif type == "icq":
    icq(uri)
    --- a/libpurple/tests/Makefile.am Mon Apr 05 20:41:42 2021 -0500
    +++ b/libpurple/tests/Makefile.am Thu Apr 08 22:30:53 2021 -0500
    @@ -14,7 +14,6 @@
    test_jabber_digest_md5.c \
    test_jabber_jutil.c \
    test_jabber_scram.c \
    - test_oscar_util.c \
    test_util.c \
    test_xmlnode.c \
    $(top_builddir)/libpurple/util.h
    @@ -30,7 +29,6 @@
    check_libpurple_LDADD=\
    $(top_builddir)/libpurple/protocols/jabber/libjabber.la \
    - $(top_builddir)/libpurple/protocols/oscar/liboscar.la \
    $(top_builddir)/libpurple/libpurple.la \
    @CHECK_LIBS@ \
    $(GLIB_LIBS)
    --- a/libpurple/tests/test_oscar_util.c Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,47 +0,0 @@
    -#include <string.h>
    -
    -#include "tests.h"
    -#include "../protocols/oscar/oscar.h"
    -
    -START_TEST(test_oscar_util_name_compare)
    -{
    - size_t i;
    - const char *good[] = {
    - "test",
    - "TEST",
    - "Test",
    - "teSt",
    - " TesT",
    - "test ",
    - " T E s T "
    - };
    - const char *bad[] = {
    - "toast",
    - "test@example.com",
    - "test@aim.com"
    - };
    -
    - for (i = 0; i < G_N_ELEMENTS(good); i++) {
    - ck_assert_int_eq(0, oscar_util_name_compare("test", good[i]));
    - ck_assert_int_eq(0, oscar_util_name_compare(good[i], "test"));
    - }
    - for (i = 0; i < G_N_ELEMENTS(bad); i++) {
    - ck_assert_int_ne(0, oscar_util_name_compare("test", bad[i]));
    - ck_assert_int_ne(0, oscar_util_name_compare(bad[i], "test"));
    - }
    -}
    -END_TEST
    -
    -Suite *oscar_util_suite(void)
    -{
    - Suite *s;
    - TCase *tc;
    -
    - s = suite_create("OSCAR Utility Functions");
    -
    - tc = tcase_create("Convert IM from network format to HTML");
    - tcase_add_test(tc, test_oscar_util_name_compare);
    - suite_add_tcase(s, tc);
    -
    - return s;
    -}
    --- a/pidgin.apspec.in Mon Apr 05 20:41:42 2021 -0500
    +++ b/pidgin.apspec.in Thu Apr 08 22:30:53 2021 -0500
    @@ -14,7 +14,7 @@
    [Description]
    Pidgin allows you to talk to anyone using a variety of messaging protocols,
    -including AIM (Oscar and TOC), ICQ, IRC, XMPP,
    +including IRC, XMPP,
    Gadu-Gadu, and Zephyr. These protocols are implemented using a
    modular, easy to use design. To use a protocol, just add an account using the
    account editor.
    @@ -22,9 +22,6 @@
    Pidgin supports many common features of other clients, as well as many unique
    features, such as perl scripting, TCL scripting and C plugins.
    -Pidgin is NOT affiliated with or endorsed by America Online, Inc., Microsoft
    -Corporation, or ICQ Inc.
    -
    [BuildPrepare]
    APBUILD_STATIC="Xss startup-notification-1" prepareBuild --enable-nss --enable-gnutls --enable-binreloc --disable-perl --disable-tcl --disable-gtktest --disable-glibtest --disable-vv --disable-fortify
    #APBUILD_STATIC="Xss startup-notification-1" prepareBuild --enable-nss --enable-gnutls --enable-binreloc --disable-perl --disable-tcl --disable-vv
    --- a/pidgin.spec.in Mon Apr 05 20:41:42 2021 -0500
    +++ b/pidgin.spec.in Thu Apr 08 22:30:53 2021 -0500
    @@ -161,8 +161,8 @@
    %description
    Pidgin allows you to talk to anyone using a variety of messaging
    -protocols including AIM, XMPP, Bonjour, Gadu-Gadu,
    -ICQ, IRC, Novell Groupwise, QQ, Lotus Sametime, SILC, Simple and
    +protocols including XMPP, Bonjour, Gadu-Gadu,
    +IRC, Novell Groupwise, QQ, Lotus Sametime, SILC, Simple and
    Zephyr. These protocols are implemented using a modular, easy to
    use design. To use a protocol, just add an account using the
    account editor.
    @@ -170,9 +170,6 @@
    Pidgin supports many common features of other clients, as well as many
    unique features, such as perl scripting, TCL scripting and C plugins.
    -Pidgin is not affiliated with or endorsed by America Online, Inc.,
    -Microsoft Corporation, or ICQ Inc.
    -
    %description devel
    The pidgin-devel package contains the header files, developer
    documentation, and libraries required for development of Pidgin scripts
    @@ -182,8 +179,8 @@
    libpurple contains the core IM support for IM clients such as Pidgin
    and Finch.
    -libpurple supports a variety of messaging protocols including AIM,
    -XMPP, Bonjour, Gadu-Gadu, ICQ, IRC, Novell Groupwise, QQ,
    +libpurple supports a variety of messaging protocols including
    +XMPP, Bonjour, Gadu-Gadu, IRC, Novell Groupwise, QQ,
    Lotus Sametime, SILC, Simple and Zephyr.
    %description -n libpurple-devel
    --- a/pidgin/gtkstatusbox.c Mon Apr 05 20:41:42 2021 -0500
    +++ b/pidgin/gtkstatusbox.c Thu Apr 08 22:30:53 2021 -0500
    @@ -368,53 +368,6 @@
    gtk_drag_finish(dc, FALSE, FALSE, t);
    }
    -static void
    -statusbox_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data,
    - const gchar *themedata, size_t len, const gchar *error_message)
    -{
    - FILE *f;
    - gchar *path;
    - size_t wc;
    -
    - if ((error_message != NULL) || (len == 0))
    - return;
    -
    - f = purple_mkstemp(&path, TRUE);
    - wc = fwrite(themedata, len, 1, f);
    - if (wc != 1) {
    - purple_debug_warning("theme_got_url", "Unable to write theme data.\n");
    - fclose(f);
    - g_unlink(path);
    - g_free(path);
    - return;
    - }
    - fclose(f);
    -
    - icon_choose_cb(path, user_data);
    -
    - g_unlink(path);
    - g_free(path);
    -}
    -
    -
    -static gboolean
    -statusbox_uri_handler(const char *proto, const char *cmd, GHashTable *params, void *data)
    -{
    - const char *src;
    -
    - if (g_ascii_strcasecmp(proto, "aim"))
    - return FALSE;
    -
    - if (g_ascii_strcasecmp(cmd, "buddyicon"))
    - return FALSE;
    -
    - src = g_hash_table_lookup(params, "account");
    - if (src == NULL)
    - return FALSE;
    -
    - purple_util_fetch_url(src, TRUE, NULL, FALSE, statusbox_got_url, data);
    - return TRUE;
    -}
    static gboolean
    icon_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, PidginStatusBox *box)
    @@ -1919,9 +1872,6 @@
    spellcheck_prefs_cb, status_box);
    purple_prefs_connect_callback(status_box, PIDGIN_PREFS_ROOT "/accounts/buddyicon",
    update_buddyicon_cb, status_box);
    - purple_signal_connect(purple_get_core(), "uri-handler", status_box,
    - PURPLE_CALLBACK(statusbox_uri_handler), status_box);
    -
    }
    static void
    --- a/pidgin/gtkutils.c Mon Apr 05 20:41:42 2021 -0500
    +++ b/pidgin/gtkutils.c Thu Apr 08 22:30:53 2021 -0500
    @@ -1142,49 +1142,6 @@
    account = NULL;
    }
    - /* Special case for AIM and ICQ */
    - if (account == NULL && (purple_strequal(protocol, "aim") ||
    - purple_strequal(protocol, "icq")))
    - {
    - for (l = list; l != NULL; l = l->next)
    - {
    - PurpleConnection *gc;
    - PurplePluginProtocolInfo *prpl_info = NULL;
    - PurplePlugin *plugin;
    -
    - if (all_accounts)
    - {
    - account = (PurpleAccount *)l->data;
    -
    - plugin = purple_plugins_find_with_id(
    - purple_account_get_protocol_id(account));
    -
    - if (plugin == NULL)
    - {
    - account = NULL;
    -
    - continue;
    - }
    -
    - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
    - }
    - else
    - {
    - gc = (PurpleConnection *)l->data;
    - account = purple_connection_get_account(gc);
    -
    - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
    - }
    -
    - protoname = prpl_info->list_icon(account, NULL);
    -
    - if (purple_strequal(protoname, "aim") || purple_strequal(protoname, "icq"))
    - break;
    -
    - account = NULL;
    - }
    - }
    -
    *ret_account = account;
    }
    }
    --- a/pidgin/pixmaps/Makefile.am Mon Apr 05 20:41:42 2021 -0500
    +++ b/pidgin/pixmaps/Makefile.am Thu Apr 08 22:30:53 2021 -0500
    @@ -201,11 +201,9 @@
    emotes/small/16/scalable/pidgin-emotes.svg
    PROTOCOLS_16_SCALABLE = \
    - protocols/16/scalable/aim.svg \
    protocols/16/scalable/bonjour.svg \
    protocols/16/scalable/gadu-gadu.svg \
    protocols/16/scalable/novell.svg \
    - protocols/16/scalable/icq.svg \
    protocols/16/scalable/irc.svg \
    protocols/16/scalable/jabber.svg \
    protocols/16/scalable/meanwhile.svg \
    @@ -214,13 +212,11 @@
    protocols/16/scalable/zephyr.svg
    PROTOCOLS_16 = \
    - protocols/16/aim.png \
    protocols/16/bonjour.png \
    protocols/16/facebook.png \
    protocols/16/gadu-gadu.png \
    protocols/16/google-talk.png \
    protocols/16/novell.png \
    - protocols/16/icq.png \
    protocols/16/irc.png \
    protocols/16/jabber.png \
    protocols/16/meanwhile.png \
    @@ -250,11 +246,9 @@
    ICONS_SCALABLE = icons/hicolor/scalable/apps/pidgin.svg
    PROTOCOLS_22_SCALABLE = \
    - protocols/22/scalable/aim.svg \
    protocols/22/scalable/bonjour.svg \
    protocols/22/scalable/gadu-gadu.svg \
    protocols/22/scalable/novell.svg \
    - protocols/22/scalable/icq.svg \
    protocols/22/scalable/irc.svg \
    protocols/22/scalable/jabber.svg \
    protocols/22/scalable/meanwhile.svg \
    @@ -263,13 +257,11 @@
    protocols/22/scalable/zephyr.svg
    PROTOCOLS_22 = \
    - protocols/22/aim.png \
    protocols/22/bonjour.png \
    protocols/22/facebook.png \
    protocols/22/gadu-gadu.png \
    protocols/22/google-talk.png \
    protocols/22/novell.png \
    - protocols/22/icq.png \
    protocols/22/irc.png \
    protocols/22/jabber.png \
    protocols/22/meanwhile.png \
    @@ -278,12 +270,10 @@
    protocols/22/zephyr.png
    PROTOCOLS_48 = \
    - protocols/48/aim.png \
    protocols/48/bonjour.png \
    protocols/48/facebook.png \
    protocols/48/gadu-gadu.png \
    protocols/48/novell.png \
    - protocols/48/icq.png \
    protocols/48/irc.png \
    protocols/48/jabber.png \
    protocols/48/meanwhile.png \
    @@ -292,12 +282,10 @@
    protocols/48/zephyr.png
    PROTOCOLS_SCALABLE = \
    - protocols/scalable/aim.svg \
    protocols/scalable/bonjour.svg \
    protocols/scalable/gadu-gadu.svg \
    protocols/scalable/google-talk.svg \
    protocols/scalable/novell.svg \
    - protocols/scalable/icq.svg \
    protocols/scalable/irc.svg \
    protocols/scalable/jabber.svg \
    protocols/scalable/meanwhile.svg \
    Binary file pidgin/pixmaps/protocols/16/aim.png has changed
    Binary file pidgin/pixmaps/protocols/16/icq.png has changed
    --- a/pidgin/pixmaps/protocols/16/scalable/aim.svg Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,265 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="16px"
    - height="16px"
    - id="svg4248"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - sodipodi:docbase="/home/hbons/GUI/Tango/Gaim Refresh/protocols/16/scalable"
    - sodipodi:docname="aim.svg"
    - inkscape:export-filename="/home/hbons/Bureaublad/aim.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4250">
    - <inkscape:perspective
    - sodipodi:type="inkscape:persp3d"
    - inkscape:vp_x="0 : 8 : 1"
    - inkscape:vp_y="0 : 1000 : 0"
    - inkscape:vp_z="16 : 8 : 1"
    - inkscape:persp3d-origin="8 : 5.3333333 : 1"
    - id="perspective40" />
    - <linearGradient
    - id="linearGradient8711"
    - inkscape:collect="always">
    - <stop
    - id="stop8713"
    - offset="0"
    - style="stop-color:#bb7e07;stop-opacity:1" />
    - <stop
    - id="stop8715"
    - offset="1"
    - style="stop-color:#926205;stop-opacity:1" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8703">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8705" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8707" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8686">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8688" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8690" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8784">
    - <stop
    - style="stop-color:#bb9007;stop-opacity:1;"
    - offset="0"
    - id="stop8786" />
    - <stop
    - style="stop-color:#927005;stop-opacity:1"
    - offset="1"
    - id="stop8788" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8776">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8778" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8780" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8653">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8655" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8657" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8637">
    - <stop
    - style="stop-color:#be7800;stop-opacity:1"
    - offset="0"
    - id="stop8639" />
    - <stop
    - style="stop-color:#906400;stop-opacity:1"
    - offset="1"
    - id="stop8641" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8637"
    - id="linearGradient8643"
    - x1="9.1367941"
    - y1="1.628371"
    - x2="16.636042"
    - y2="10.933484"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8653"
    - id="linearGradient8659"
    - x1="4.7796931"
    - y1="-4.2560463"
    - x2="14.475067"
    - y2="9.7333546"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8776"
    - id="linearGradient8782"
    - x1="9.4571943"
    - y1="4.418859"
    - x2="4.7050147"
    - y2="15.798837"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8711"
    - id="linearGradient8790"
    - x1="11.863748"
    - y1="8.6871567"
    - x2="1.8747424"
    - y2="13.60194"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="translate(1.2641541,-0.2145011)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8686"
    - id="linearGradient8692"
    - x1="8.3698959"
    - y1="-1.1186354"
    - x2="14.103671"
    - y2="8.6191578"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8784"
    - id="linearGradient8696"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="translate(1.2641541,-0.2145011)"
    - x1="5.2542644"
    - y1="8.9798889"
    - x2="8.4612246"
    - y2="14.318395" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8703"
    - id="linearGradient8709"
    - x1="4.9951673"
    - y1="8.807704"
    - x2="11.323474"
    - y2="13.381649"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="19.324309"
    - inkscape:cx="22.516951"
    - inkscape:cy="8.219724"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - inkscape:grid-bbox="true"
    - inkscape:document-units="px"
    - inkscape:window-width="1440"
    - inkscape:window-height="847"
    - inkscape:window-x="0"
    - inkscape:window-y="0"
    - inkscape:snap-bbox="true"
    - inkscape:snap-nodes="false"
    - objecttolerance="10"
    - gridtolerance="10">
    - <inkscape:grid
    - type="xygrid"
    - visible="true"
    - enabled="true"
    - id="grid7865" />
    - </sodipodi:namedview>
    - <metadata
    - id="metadata4253">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - id="layer1"
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer">
    - <path
    - style="opacity:1;fill:#f8c766;fill-opacity:1;stroke:url(#linearGradient8790);stroke-width:1.00000024000000010;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - d="M 8.9039189,4.5121855 C 2.6129138,6.1978553 8.2398799,12.598865 1.5258742,12.598865 L 1.5342001,15.603027 C 7.5993543,15.603027 7.4985792,12.514605 7.4985792,12.514605 C 9.596051,12.514605 10.513648,13.498816 10.513648,15.520135 L 14.530663,15.541737 C 14.530663,9.8333244 9.0743771,9.4942293 9.0743771,9.4942293 C 9.0743771,9.4942293 14.495884,9.4837515 14.495884,9.4837515 C 14.495884,9.4837515 14.522789,6.4996057 14.522789,6.4996057 C 12.764013,6.4996057 9.6229595,6.7951443 8.9039189,4.5121855 z"
    - id="path4275"
    - sodipodi:nodetypes="cccccccccc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-1.0049574"
    - inkscape:original="M 8.90625 4.5 C 2.6152446 6.1856697 8.2452557 12.59375 1.53125 12.59375 L 1.53125 15.59375 C 7.5964042 15.59375 7.5 12.5 7.5 12.5 C 9.597472 12.5 10.5 13.509931 10.5 15.53125 L 14.53125 15.53125 C 14.53125 9.8228378 9.0625 9.5 9.0625 9.5 C 9.0625 9.5 14.5 9.46875 14.5 9.46875 C 14.5 9.4687502 14.53125 6.5 14.53125 6.5 C 12.772474 6.5 9.6252906 6.7829588 8.90625 4.5 z "
    - style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient8709);stroke-width:1.00000024000000010;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path8698"
    - d="M 8.59375,5.71875 C 7.7134558,6.0620005 7.2084274,6.4771924 6.9375,6.96875 C 6.6038545,7.5741002 6.4857642,8.3919875 6.375,9.3125 C 6.2642358,10.233013 6.1551074,11.306258 5.40625,12.21875 C 4.8173687,12.93631 3.7928472,13.277265 2.53125,13.4375 L 2.53125,14.46875 C 4.5119249,14.298403 5.6447647,13.783904 6.0625,13.34375 C 6.5586949,12.820926 6.5,12.53125 6.5,12.53125 C 6.4929534,12.261059 6.5950282,11.999416 6.7831848,11.80538 C 6.9713414,11.611343 7.2297197,11.501268 7.5,11.5 C 8.7100314,11.5 9.7708702,11.807465 10.5,12.5625 C 11.005067,13.085512 11.24875,13.784669 11.375,14.53125 L 13.28125,14.53125 C 12.990694,12.836887 12.221366,11.796156 11.28125,11.25 C 10.140118,10.587065 9,10.5 9,10.5 C 8.4861533,10.434579 8.1061894,9.9893393 8.1223688,9.4715975 C 8.1385483,8.9538556 8.5455711,8.5332097 9.0625,8.5 C 9.0625,8.5 12.168012,8.4764051 13.5,8.46875 C 13.504703,8.0219179 13.526621,7.9084884 13.53125,7.46875 C 12.783297,7.4790943 12.026654,7.5202371 11.125,7.34375 C 10.175282,7.157855 9.2606835,6.5802813 8.59375,5.71875 z" />
    - <path
    - sodipodi:type="arc"
    - style="fill:#f8c766;fill-opacity:1;stroke:url(#linearGradient8643);stroke-width:2.02997636999999997;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path4302"
    - sodipodi:cx="10.555883"
    - sodipodi:cy="4.0385542"
    - sodipodi:rx="5.074944"
    - sodipodi:ry="5.074944"
    - d="M 15.630827,4.0385542 A 5.074944,5.074944 0 1 1 5.4809394,4.0385542 A 5.074944,5.074944 0 1 1 15.630827,4.0385542 z"
    - transform="matrix(0.4926163,0,0,0.4926163,3.7999997,1.0105423)" />
    - <path
    - sodipodi:type="arc"
    - style="fill:none;fill-opacity:1;stroke:url(#linearGradient8692);stroke-width:3.38329338999999996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path8682"
    - sodipodi:cx="10.555883"
    - sodipodi:cy="4.0385542"
    - sodipodi:rx="5.074944"
    - sodipodi:ry="5.074944"
    - d="M 15.630827,4.0385542 A 5.074944,5.074944 0 1 1 5.4809394,4.0385542 A 5.074944,5.074944 0 1 1 15.630827,4.0385542 z"
    - transform="matrix(0.2955698,0,0,0.2955698,5.8799995,1.8063252)" />
    - <rect
    - style="opacity:0.11574074;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="rect8649"
    - width="1"
    - height="4"
    - x="1"
    - y="12" />
    - </g>
    -</svg>
    --- a/pidgin/pixmaps/protocols/16/scalable/icq.svg Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,579 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="16px"
    - height="16px"
    - id="svg4248"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - sodipodi:docbase="/home/hbons/GUI/Tango/Gaim Refresh/protocols/16"
    - sodipodi:docname="icq.svg"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape"
    - inkscape:export-filename="/home/hbons/Bureaublad/icq.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90">
    - <defs
    - id="defs4250">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8780">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8782" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8784" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8772">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8774" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8776" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8764">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8766" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8768" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8756">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8758" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8760" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8748">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8750" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8752" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8740">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8742" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8744" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8732">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8734" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8736" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8724">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8726" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8728" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8716">
    - <stop
    - style="stop-color:#4e9a06;stop-opacity:1;"
    - offset="0"
    - id="stop8718" />
    - <stop
    - style="stop-color:#2a5402;stop-opacity:1"
    - offset="1"
    - id="stop8720" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8708">
    - <stop
    - style="stop-color:#4e9a06;stop-opacity:1;"
    - offset="0"
    - id="stop8710" />
    - <stop
    - style="stop-color:#346804;stop-opacity:1"
    - offset="1"
    - id="stop8712" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8700">
    - <stop
    - style="stop-color:#b90000;stop-opacity:1"
    - offset="0"
    - id="stop8702" />
    - <stop
    - style="stop-color:#6c0000;stop-opacity:1"
    - offset="1"
    - id="stop8704" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8692">
    - <stop
    - style="stop-color:#4e9a06;stop-opacity:1;"
    - offset="0"
    - id="stop8694" />
    - <stop
    - style="stop-color:#3c7604;stop-opacity:1"
    - offset="1"
    - id="stop8696" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8684">
    - <stop
    - style="stop-color:#4e9a06;stop-opacity:1;"
    - offset="0"
    - id="stop8686" />
    - <stop
    - style="stop-color:#356a04;stop-opacity:1"
    - offset="1"
    - id="stop8688" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8676">
    - <stop
    - style="stop-color:#4e9a06;stop-opacity:1;"
    - offset="0"
    - id="stop8678" />
    - <stop
    - style="stop-color:#346803;stop-opacity:1"
    - offset="1"
    - id="stop8680" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8668">
    - <stop
    - style="stop-color:#4e9a06;stop-opacity:1;"
    - offset="0"
    - id="stop8670" />
    - <stop
    - style="stop-color:#336603;stop-opacity:1"
    - offset="1"
    - id="stop8672" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8660">
    - <stop
    - style="stop-color:#4e9a06;stop-opacity:1;"
    - offset="0"
    - id="stop8662" />
    - <stop
    - style="stop-color:#3a7404;stop-opacity:1"
    - offset="1"
    - id="stop8664" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8652">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8654" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8656" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8644">
    - <stop
    - style="stop-color:#c4a000;stop-opacity:1;"
    - offset="0"
    - id="stop8646" />
    - <stop
    - style="stop-color:#907600;stop-opacity:1"
    - offset="1"
    - id="stop8648" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2321">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2323" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0"
    - offset="1"
    - id="stop2325" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2361">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2363" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2365" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2321"
    - id="linearGradient4381"
    - gradientUnits="userSpaceOnUse"
    - x1="11.787398"
    - y1="11.115861"
    - x2="12.075002"
    - y2="12.360133" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2361"
    - id="linearGradient4383"
    - gradientUnits="userSpaceOnUse"
    - x1="6.3009863"
    - y1="8.3052416"
    - x2="6.3009863"
    - y2="17.320574"
    - gradientTransform="matrix(0.7516425,0,0,0.6729501,-0.8826506,1.4152198)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8644"
    - id="linearGradient8650"
    - x1="6.5158038"
    - y1="6.3653116"
    - x2="9.9300232"
    - y2="10.764107"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8652"
    - id="linearGradient8658"
    - x1="10.444659"
    - y1="12.360133"
    - x2="13.705344"
    - y2="12.360133"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8660"
    - id="linearGradient8666"
    - x1="9.8111737"
    - y1="4.9999985"
    - x2="17.188837"
    - y2="4.9999985"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(0.666684,0,0,0.8570726,0.499765,-0.7853612)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8668"
    - id="linearGradient8674"
    - x1="15.004629"
    - y1="16.519621"
    - x2="16.441046"
    - y2="19.245642"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8676"
    - id="linearGradient8682"
    - x1="11.152212"
    - y1="15.892158"
    - x2="9.9561481"
    - y2="19.775995"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8684"
    - id="linearGradient8690"
    - x1="13.906936"
    - y1="12.503142"
    - x2="22.096945"
    - y2="12.503142"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8692"
    - id="linearGradient8698"
    - x1="17.85483"
    - y1="7.9999962"
    - x2="19.682709"
    - y2="9.6703825"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8700"
    - id="linearGradient8706"
    - x1="5.3079047"
    - y1="14.33169"
    - x2="6.2636456"
    - y2="16.783527"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8708"
    - id="linearGradient8714"
    - x1="4.4860797"
    - y1="2.7814157"
    - x2="6.3950372"
    - y2="4.8916488"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8716"
    - id="linearGradient8722"
    - x1="6.2442408"
    - y1="8.825284"
    - x2="7.2848325"
    - y2="12.785048"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8724"
    - id="linearGradient8730"
    - x1="13.914243"
    - y1="3.5516012"
    - x2="12.632175"
    - y2="7.0243564"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(0.507229,0,0,0.7963119,2.6510069,-0.4796384)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8732"
    - id="linearGradient8738"
    - x1="8.2830858"
    - y1="4.66011"
    - x2="10.320723"
    - y2="6.5236211"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(0.658982,0,0,0.733665,-0.42174,-4.3113681e-2)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8740"
    - id="linearGradient8746"
    - x1="4.4425683"
    - y1="8.8338633"
    - x2="8.630022"
    - y2="10.278849"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(0.8000894,0,0,0.761045,-1.308226,-0.22823)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8748"
    - id="linearGradient8754"
    - x1="19.372143"
    - y1="6.7050757"
    - x2="15.963639"
    - y2="9.5312366"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(0.7538717,0,0,0.6730629,-1.1855766,0.1251422)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8756"
    - id="linearGradient8762"
    - x1="19.567648"
    - y1="13.050652"
    - x2="15.45147"
    - y2="12.219797"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8764"
    - id="linearGradient8770"
    - x1="16.516554"
    - y1="18.227236"
    - x2="14.661788"
    - y2="15.899495"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(0.6456695,0,0,0.740569,0.8060648,-0.220494)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8772"
    - id="linearGradient8778"
    - x1="10.093229"
    - y1="18.792187"
    - x2="10.940947"
    - y2="15.521823"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(0.7610362,0,0,0.799758,-0.9891698,-1.10256)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8780"
    - id="linearGradient8786"
    - x1="4.1927366"
    - y1="16.487579"
    - x2="6.7825565"
    - y2="14.544259"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(0.7516425,0,0,0.6729501,-0.8826506,1.4152198)" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="33.645576"
    - inkscape:cx="17.956733"
    - inkscape:cy="7.8216704"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - inkscape:grid-bbox="true"
    - inkscape:document-units="px"
    - inkscape:window-width="1440"
    - inkscape:window-height="847"
    - inkscape:window-x="0"
    - inkscape:window-y="0"
    - objecttolerance="10"
    - gridtolerance="10"
    - inkscape:snap-bbox="true"
    - inkscape:snap-nodes="false">
    - <inkscape:grid
    - type="xygrid"
    - id="grid7872"
    - visible="true"
    - enabled="true" />
    - </sodipodi:namedview>
    - <metadata
    - id="metadata4253">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - id="layer1"
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer">
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:url(#linearGradient8674);stroke-width:1.21922076000000001;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 12.963148,17.247339 C 12.171926,14.859179 12.454176,14.470485 13.497752,13.880864 C 14.541326,13.291244 15.108816,13.335817 16.744216,15.111033 C 17.767103,16.844843 17.750309,18.730526 16.706739,19.320144 C 15.663162,19.909767 13.986034,18.981148 12.963148,17.247339 z "
    - id="path2226"
    - transform="matrix(0.801314,0,0,0.839525,-1.523412,-1.868634)" />
    - <path
    - style="opacity:0.75;fill:none;fill-opacity:1;stroke:url(#linearGradient8770);stroke-width:1.00000036;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 10.228804,10.517757 C 10.129288,10.479606 10.118014,10.490039 9.8252606,10.679756 C 9.6724455,10.778787 9.5775476,10.848926 9.5427802,10.888041 C 9.5080129,10.927156 9.5107318,10.913111 9.5024259,10.957469 C 9.4869472,11.040134 9.5429901,11.490751 9.7445519,12.230322 C 10.019277,12.740003 10.386743,13.148547 10.692879,13.341176 C 11.011903,13.541914 11.205257,13.517117 11.298194,13.45689 C 11.392617,13.395699 11.497569,13.253133 11.499966,12.855177 C 11.502252,12.475722 11.370376,11.941058 11.1166,11.420325 C 10.645066,10.845418 10.310619,10.549121 10.228804,10.517757 z"
    - id="path2242" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:url(#linearGradient8690);stroke-width:1.18385111999999992;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 16.771005,15.319703 C 14.33106,13.896777 14.27539,13.249081 14.702189,11.643547 C 15.128987,10.03802 15.633686,9.5050301 18.317374,9.5025628 C 20.461323,10.081613 21.854951,11.854605 21.428153,13.460133 C 21.001355,15.065667 18.914955,15.898753 16.771005,15.319703 z "
    - id="path2216"
    - transform="matrix(0.856389,0,0,0.833173,-2.916658,-1.417277)" />
    - <path
    - style="opacity:0.75;fill:none;fill-opacity:1;stroke:url(#linearGradient8762);stroke-width:1.30062020000000000;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 18.15625,10.5 C 17.064518,10.522558 16.402045,10.67233 16.1875,10.8125 C 15.955817,10.963867 15.820346,11.171396 15.625,11.90625 C 15.423716,12.663442 15.422318,12.879125 15.53125,13.09375 C 15.632536,13.29331 16.159381,13.76998 17.15625,14.375 C 17.98874,14.571156 18.819892,14.51269 19.40625,14.28125 C 20.02513,14.036974 20.378057,13.677476 20.5,13.21875 C 20.621263,12.762585 20.482161,12.251369 20.0625,11.71875 C 19.662743,11.211393 18.978208,10.748423 18.15625,10.5 z "
    - id="path2238"
    - transform="matrix(0.786923,0,0,0.751219,-1.665053,-0.387804)" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:url(#linearGradient8698);stroke-width:1.22383844999999991;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 18.231066,10.043051 C 15.826638,10.834882 15.435297,10.552414 14.841659,9.5080341 C 14.248025,8.4636559 14.292902,7.895729 16.08021,6.2590696 C 17.825828,5.235395 19.724355,5.2521994 20.317989,6.2965763 C 20.911627,7.3409564 19.976685,9.0193762 18.231066,10.043051 z "
    - id="path2224"
    - transform="matrix(0.833845,0,0,0.800696,-2.590942,-0.905561)" />
    - <path
    - style="fill:#ef2929;fill-opacity:1;stroke:url(#linearGradient8706);stroke-width:1.22309434000000006;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 7.2295458,17.045044 C 4.8262841,17.837501 4.435133,17.554808 3.8417836,16.509603 C 3.2484366,15.464398 3.2932918,14.896022 5.0797326,13.258068 C 6.8245041,12.233585 8.7221095,12.250402 9.3154554,13.295606 C 9.9088085,14.34081 8.9743169,16.020559 7.2295458,17.045044 z "
    - id="path2228"
    - transform="matrix(0.835521,0,0,0.800063,-1.432383,-0.501236)" />
    - <path
    - style="opacity:0.75;fill:url(#linearGradient4383);fill-opacity:1;stroke:url(#linearGradient8786);stroke-width:1.00000036;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 4.8486235,10.500046 C 4.4564782,10.496935 3.9190242,10.637124 3.3923161,10.899611 C 2.8449198,11.364365 2.5603453,11.685808 2.5232295,11.782857 C 2.4833052,11.88725 2.475064,11.884191 2.6641625,12.182421 C 2.765801,12.342716 2.8393005,12.443491 2.8755619,12.476837 C 2.9118233,12.510183 2.8851423,12.490643 2.9225396,12.497866 C 2.9909999,12.51109 3.4626384,12.466271 4.1909363,12.26654 C 4.7259059,11.978527 5.1414644,11.620122 5.3418889,11.299174 C 5.5465286,10.971477 5.5126093,10.773335 5.4593331,10.689313 C 5.4086116,10.60932 5.2638969,10.503341 4.8486235,10.500046 z"
    - id="path2244" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:url(#linearGradient8682);stroke-width:1.18253027999999993;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 7.6891342,15.77365 C 9.1118334,13.334726 9.7586709,13.278647 11.361671,13.703963 C 12.964665,14.129276 13.496553,14.633183 13.497097,17.314646 C 12.917316,19.457228 11.145785,20.850948 9.5427895,20.425634 C 7.93979,20.000318 7.109353,17.916231 7.6891342,15.77365 z "
    - id="path2218"
    - transform="matrix(0.834348,0,0,0.857095,-1.761273,-2.072186)" />
    - <path
    - style="opacity:0.75;fill:none;fill-opacity:1;stroke:url(#linearGradient8778);stroke-width:1.00000036;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 6.5498451,10.543916 C 6.4093043,10.616602 6.0575323,11.02735 5.5985498,11.81853 C 5.4505732,12.478908 5.4715025,13.128474 5.6461146,13.592993 C 5.8305566,14.083663 6.1144425,14.372852 6.4547155,14.467729 C 6.7934078,14.562164 7.1943156,14.450544 7.5962698,14.117834 C 7.9788085,13.801196 8.3117436,13.270312 8.5000003,12.618288 C 8.4834797,11.748172 8.3825717,11.225309 8.2859589,11.068757 C 8.1814922,10.899479 8.0370643,10.799946 7.4773579,10.643886 C 6.8871683,10.479326 6.6912608,10.470777 6.5498451,10.543916 z"
    - id="path2240" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:url(#linearGradient8714);stroke-width:1.00000142999999997;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 7.1358685,3.3890634 C 7.7698834,5.3939828 7.5437147,5.7203005 6.707483,6.2153016 C 5.8712512,6.7103014 5.4165141,6.6728807 4.1060474,5.1825438 C 3.2863948,3.7269694 3.29985,2.1438938 4.1360794,1.6488936 C 4.9723098,1.1538927 6.3162154,1.933489 7.1358685,3.3890634 z"
    - id="path2222" />
    - <path
    - style="opacity:0.75;fill:none;fill-opacity:1;stroke:url(#linearGradient8738);stroke-width:0.99999988;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 4.9118956,2.5017868 C 4.8344656,2.4933075 4.7351802,2.5160688 4.6853705,2.5476409 C 4.5947769,2.6050638 4.5024975,2.7622504 4.5000318,3.1666707 C 4.4976449,3.5581659 4.6240111,4.1063439 4.8913024,4.6340007 C 5.3464159,5.169468 5.6802006,5.4473321 5.7768094,5.4823009 C 5.8806263,5.5198786 5.8903157,5.5109256 6.1886732,5.3218116 C 6.3467933,5.2215868 6.4419865,5.153277 6.4769778,5.1154684 C 6.5119691,5.0776597 6.4898306,5.0865385 6.497571,5.0466873 C 6.5117411,4.9737348 6.4672348,4.52308 6.2710459,3.8086276 C 5.8382221,3.0048392 5.2079215,2.5342044 4.9118956,2.5017868 z"
    - id="path2232" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:url(#linearGradient8722);stroke-width:1.18378782000000005;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 7.2320598,6.6863636 C 9.6715923,8.1091434 9.7272104,8.7568487 9.3003538,10.362442 C 8.8734993,11.96803 8.3688269,12.501065 5.6854848,12.503723 C 3.5418523,11.924813 2.1485268,10.151886 2.5753806,8.5462983 C 3.002236,6.9407045 5.0884273,6.1074547 7.2320598,6.6863636 z "
    - id="path2220"
    - transform="matrix(0.8565,0,0,0.833155,-1.639952,-0.917534)" />
    - <path
    - style="opacity:0.75;fill:none;fill-opacity:1;stroke:url(#linearGradient8746);stroke-width:1.00000083;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 4.1673858,5.5985208 C 3.5054714,5.4505063 2.8569334,5.4716193 2.3921875,5.6460861 C 1.9012783,5.8303746 1.6129084,6.1118678 1.5170897,6.4546964 C 1.4218347,6.7955083 1.5091367,7.1942976 1.842126,7.5962639 C 2.1592142,7.9790353 2.7135907,8.3119339 3.3672964,8.5000048 C 4.2422495,8.4831842 4.7779308,8.3858367 4.9424724,8.2859609 C 5.1201805,8.1780935 5.2118965,8.0341525 5.3675199,7.4773506 C 5.5280018,6.9031664 5.529332,6.7148477 5.4425283,6.549827 C 5.3619401,6.3966226 4.9589342,6.0564559 4.1673858,5.5985208 z"
    - id="path2234" />
    - <path
    - style="opacity:0.75;fill:none;fill-opacity:1;stroke:url(#linearGradient8754);stroke-width:1.00000203;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 12.855285,4.500051 C 12.462919,4.4969506 11.922943,4.6374156 11.394658,4.8996821 C 10.844605,5.3646776 10.559734,5.7082145 10.522993,5.8041104 C 10.483512,5.9071611 10.474936,5.8852008 10.664344,6.1827083 C 10.766342,6.3429165 10.840542,6.4443185 10.876371,6.4771733 C 10.9122,6.5100281 10.886952,6.4911647 10.923488,6.4982065 C 10.991576,6.5113294 11.462538,6.4555529 12.219205,6.2458079 C 12.737907,5.9609072 13.130324,5.6126604 13.326454,5.2993132 C 13.531108,4.9723484 13.51988,4.7711454 13.467804,4.68935 C 13.41783,4.6108533 13.270794,4.5033345 12.855285,4.500051 z"
    - id="path2236" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:url(#linearGradient8666);stroke-width:1.0000006;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 11.375232,4.5493545 C 10.422975,6.6416097 9.9911458,6.6905364 8.9215873,6.3281422 C 7.8520275,5.9657484 7.4975249,5.534567 7.5000157,3.2363329 C 7.8893038,1.3991926 9.0732966,0.20229738 10.142856,0.56469125 C 11.212416,0.92708537 11.764519,2.7122143 11.375232,4.5493545 z"
    - id="path2192" />
    - <path
    - style="opacity:0.75;fill:none;fill-opacity:1;stroke:url(#linearGradient8730);stroke-width:1.00000024;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 9.8631692,1.5360262 C 9.6347437,1.4415107 9.3708203,1.5274542 9.1023257,1.8595278 C 8.8466524,2.1757441 8.6265865,2.7251574 8.4999913,3.3774976 C 8.5099761,4.2473344 8.5751845,4.7795065 8.6426495,4.9452367 C 8.7155134,5.124228 8.8111337,5.2149983 9.1815803,5.3682774 C 9.5648319,5.5268552 9.6889381,5.5298463 9.7997656,5.4429315 C 9.9026575,5.3622401 10.127302,4.9625969 10.433802,4.1738094 C 10.533317,3.5150226 10.518933,2.8709279 10.4021,2.4069923 C 10.27869,1.9169393 10.091661,1.6305687 9.8631692,1.5360262 z"
    - id="path2230" />
    - <path
    - style="fill:#edd400;fill-opacity:1;stroke:url(#linearGradient8650);stroke-width:0.99999994000000003;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 10.411141,8.6353601 C 10.047567,9.96976 8.6723022,10.768109 7.3413532,10.417389 C 6.0104022,10.066669 5.2252873,8.6990358 5.5888616,7.3646359 C 5.9524355,6.0302378 7.3277,5.231889 8.6586509,5.5826094 C 9.9895999,5.9333292 10.774715,7.300962 10.411141,8.6353601 z"
    - id="path1317" />
    - <path
    - transform="matrix(1.208973,0.318578,-0.279444,1.025629,-3.144373,-8.523741)"
    - style="opacity:0.69907406999999999;fill:url(#linearGradient4381);fill-opacity:1;stroke:url(#linearGradient8658);stroke-width:0.86744213000000003;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 13.271623,12.360133 C 13.271623,13.140766 12.735537,13.774323 12.075002,13.774323 C 11.414466,13.774323 10.87838,13.140766 10.87838,12.360133 C 10.87838,11.579501 11.414466,10.945944 12.075002,10.945944 C 12.735537,10.945944 13.271623,11.579501 13.271623,12.360133 z "
    - id="path2319" />
    - </g>
    -</svg>
    Binary file pidgin/pixmaps/protocols/22/aim.png has changed
    Binary file pidgin/pixmaps/protocols/22/icq.png has changed
    --- a/pidgin/pixmaps/protocols/22/scalable/aim.svg Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,187 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="24"
    - height="24"
    - id="svg2"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - version="1.0"
    - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols"
    - sodipodi:docname="aim.svg"
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/aim.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2263">
    - <stop
    - style="stop-color:#edc100;stop-opacity:1;"
    - offset="0"
    - id="stop2265" />
    - <stop
    - style="stop-color:#edc100;stop-opacity:0;"
    - offset="1"
    - id="stop2267" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2255">
    - <stop
    - style="stop-color:#edc100;stop-opacity:1;"
    - offset="0"
    - id="stop2257" />
    - <stop
    - style="stop-color:#edc100;stop-opacity:0;"
    - offset="1"
    - id="stop2259" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient3150">
    - <stop
    - style="stop-color:#2e3436;stop-opacity:1;"
    - offset="0"
    - id="stop3152" />
    - <stop
    - style="stop-color:#2e3436;stop-opacity:0;"
    - offset="1"
    - id="stop3154" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient3150"
    - id="radialGradient4330"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
    - cx="10.748654"
    - cy="10.457643"
    - fx="10.748654"
    - fy="10.457643"
    - r="6.6449099" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2255"
    - id="linearGradient2261"
    - x1="12.514956"
    - y1="18.246853"
    - x2="12.514956"
    - y2="-0.22752593"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2263"
    - id="linearGradient2269"
    - x1="11.725797"
    - y1="6.7099471"
    - x2="11.725797"
    - y2="-1.7656245"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="18.194454"
    - inkscape:cx="21.028138"
    - inkscape:cy="12.129637"
    - inkscape:document-units="px"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - fill="#edd400"
    - showguides="true"
    - inkscape:guide-bbox="true"
    - inkscape:window-width="1268"
    - inkscape:window-height="971"
    - inkscape:window-x="6"
    - inkscape:window-y="21" />
    - <metadata
    - id="metadata7">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer"
    - id="layer1">
    - <path
    - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - d="M 11.958337,8.6532903 C 10.214088,7.6488702 9.8526923,12.097445 8.6371785,14.197294 C 7.4474897,16.252529 3.5065645,17.589535 3.5065645,17.589535 L 5.9517457,22.489783 L 11.922453,16.916072 C 11.922453,16.916072 15.933817,19.534872 17.160901,22.390612 L 20.390534,19.246933 C 20.390534,19.246933 18.693358,15.872721 14.921211,13.538266 L 20.491001,12.72627 L 19.715221,8.8524949 C 16.110632,9.7210318 13.926705,9.7867691 11.958337,8.6532903 z "
    - id="path1322"
    - sodipodi:nodetypes="czccccccccs" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.45242631;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path1324"
    - sodipodi:cx="10.555883"
    - sodipodi:cy="4.0385542"
    - sodipodi:rx="5.074944"
    - sodipodi:ry="5.074944"
    - d="M 15.630827 4.0385542 A 5.074944 5.074944 0 1 1 5.4809394,4.0385542 A 5.074944 5.074944 0 1 1 15.630827 4.0385542 z"
    - transform="matrix(0.688186,0,0,0.688819,6.743084,1.213885)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.25;fill:url(#radialGradient4330);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3140"
    - sodipodi:cx="10.748654"
    - sodipodi:cy="10.457643"
    - sodipodi:rx="6.6449099"
    - sodipodi:ry="2.3675451"
    - d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
    - transform="matrix(1.805894,0,0,1.300982,-7.410933,6.474929)" />
    - <path
    - style="opacity:1;fill:url(#linearGradient2261);fill-opacity:1.0;stroke:#c4a000;stroke-width:0.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - d="M 11.958337,8.6532903 C 10.214088,7.6488702 9.8526923,12.097445 8.6371785,14.197294 C 7.4474897,16.252529 3.5065645,17.589535 3.5065645,17.589535 L 5.9517457,22.489783 L 11.922453,16.916072 C 11.922453,16.916072 15.933817,19.534872 17.160901,22.390612 L 20.390534,19.246933 C 20.390534,19.246933 18.693358,15.872721 14.921211,13.538266 L 20.491001,12.72627 L 19.715221,8.8524949 C 16.110632,9.7210318 13.926705,9.7867691 11.958337,8.6532903 z "
    - id="path4275"
    - sodipodi:nodetypes="czccccccccs" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.97533494"
    - inkscape:original="M 11.96875 8.65625 C 10.224501 7.6518295 9.8405138 12.087651 8.625 14.1875 C 7.4353113 16.242735 3.5 17.59375 3.5 17.59375 L 5.9375 22.5 L 11.9375 16.90625 C 11.9375 16.906249 15.929166 19.51926 17.15625 22.375 L 20.375 19.25 C 20.375001 19.25 18.678397 15.865705 14.90625 13.53125 L 20.5 12.71875 L 19.71875 8.84375 C 16.114161 9.7122866 13.937118 9.7897288 11.96875 8.65625 z "
    - xlink:href="#path4275"
    - style="opacity:0.5;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path4304"
    - inkscape:href="#path4275"
    - d="M 11.5625,9.53125 C 11.452518,9.6624786 11.20418,10.047821 11,10.59375 C 10.56791,11.749055 10.203376,13.418405 9.46875,14.6875 C 8.6867322,16.038467 7.2662927,16.899179 6.0625,17.53125 C 5.412514,17.872536 5.2627915,17.893887 4.84375,18.0625 L 6.21875,20.90625 L 11.28125,16.1875 C 11.609827,15.89012 12.09758,15.851614 12.46875,16.09375 C 12.46875,16.09375 13.514111,16.786729 14.71875,17.84375 C 15.622229,18.636516 16.558838,19.686944 17.3125,20.84375 L 19.125,19.125 C 18.707223,18.391513 17.391924,16.222734 14.40625,14.375 C 14.042988,14.164323 13.855332,13.744801 13.940413,13.333577 C 14.025494,12.922352 14.364213,12.611735 14.78125,12.5625 L 19.34375,11.90625 L 18.9375,9.96875 C 15.910346,10.738935 13.497199,10.615244 11.5625,9.53125 z " />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:url(#linearGradient2269);fill-opacity:1.0;stroke:#c4a000;stroke-width:1.45242631;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path4302"
    - sodipodi:cx="10.555883"
    - sodipodi:cy="4.0385542"
    - sodipodi:rx="5.074944"
    - sodipodi:ry="5.074944"
    - d="M 15.630827 4.0385542 A 5.074944 5.074944 0 1 1 5.4809394,4.0385542 A 5.074944 5.074944 0 1 1 15.630827 4.0385542 z"
    - transform="matrix(0.688186,0,0,0.688819,6.743084,1.213885)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.5;fill:#fce94f;fill-opacity:1;stroke:#ffffff;stroke-width:2.02834463;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path4273"
    - sodipodi:cx="10.555883"
    - sodipodi:cy="4.0385542"
    - sodipodi:rx="5.074944"
    - sodipodi:ry="5.074944"
    - d="M 15.630827 4.0385542 A 5.074944 5.074944 0 1 1 5.4809394,4.0385542 A 5.074944 5.074944 0 1 1 15.630827 4.0385542 z"
    - transform="matrix(0.492994,0,0,0.493032,8.79793,2.010973)" />
    - </g>
    -</svg>
    --- a/pidgin/pixmaps/protocols/22/scalable/icq.svg Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,291 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="24"
    - height="24"
    - id="svg2"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - version="1.0"
    - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols"
    - sodipodi:docname="icq.svg"
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/icq.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2361">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2363" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2365" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2321">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2323" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2325" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient3150">
    - <stop
    - style="stop-color:#2e3436;stop-opacity:1;"
    - offset="0"
    - id="stop3152" />
    - <stop
    - style="stop-color:#2e3436;stop-opacity:0;"
    - offset="1"
    - id="stop3154" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient3150"
    - id="radialGradient3156"
    - cx="10.748654"
    - cy="10.457643"
    - fx="10.748654"
    - fy="10.457643"
    - r="6.6449099"
    - gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2321"
    - id="linearGradient2327"
    - x1="11.787398"
    - y1="11.115861"
    - x2="12.405842"
    - y2="13.791453"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2361"
    - id="linearGradient2367"
    - x1="6.3009863"
    - y1="8.3052416"
    - x2="6.3009863"
    - y2="17.320574"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="10.44997"
    - inkscape:cx="19.566864"
    - inkscape:cy="19.726611"
    - inkscape:document-units="px"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - fill="#ef2929"
    - inkscape:window-width="1268"
    - inkscape:window-height="971"
    - inkscape:window-x="6"
    - inkscape:window-y="21" />
    - <metadata
    - id="metadata7">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer"
    - id="layer1">
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.5;fill:url(#radialGradient3156);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3140"
    - sodipodi:cx="10.748654"
    - sodipodi:cy="10.457643"
    - sodipodi:rx="6.6449099"
    - sodipodi:ry="2.3675451"
    - d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
    - transform="matrix(1.805894,0,0,1.478325,-7.410928,3.04021)" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.00000036;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 12.963148,17.247339 C 12.171926,14.859179 12.454176,14.470485 13.497752,13.880864 C 14.541326,13.291244 15.108816,13.335817 16.744216,15.111033 C 17.767103,16.844843 17.750309,18.730526 16.706739,19.320144 C 15.663162,19.909767 13.986034,18.981148 12.963148,17.247339 z "
    - id="path2226"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.95876986"
    - inkscape:original="M 14.90625 13.59375 C 14.429506 13.434407 14.021787 13.58019 13.5 13.875 C 12.456424 14.464621 12.177528 14.86184 12.96875 17.25 C 13.991636 18.983809 15.675173 19.902122 16.71875 19.3125 C 17.76232 18.722882 17.772886 16.85881 16.75 15.125 C 15.9323 14.237392 15.382994 13.753093 14.90625 13.59375 z "
    - xlink:href="#path2226"
    - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
    - id="path2242"
    - inkscape:href="#path2226"
    - d="M 14.6875,15.40625 C 14.513417,15.351665 14.472292,15.359975 14.03125,15.59375 C 13.796094,15.718394 13.674719,15.830029 13.625,15.875 C 13.575281,15.919971 13.600191,15.87903 13.59375,15.90625 C 13.581862,15.956485 13.642727,16.51095 13.9375,17.40625 C 14.348011,18.027075 14.877648,18.50949 15.34375,18.75 C 15.831028,19.001437 16.171295,18.981096 16.3125,18.90625 C 16.454211,18.831136 16.558976,18.656064 16.5625,18.1875 C 16.565896,17.735918 16.387216,17.080025 16,16.4375 C 15.331224,15.779693 14.857908,15.459683 14.6875,15.40625 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 16.771005,15.319703 C 14.33106,13.896777 14.27539,13.249081 14.702189,11.643547 C 15.128987,10.03802 15.633686,9.5050301 18.317374,9.5025628 C 20.461323,10.081613 21.854951,11.854605 21.428153,13.460133 C 21.001355,15.065667 18.914955,15.898753 16.771005,15.319703 z "
    - id="path2216"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.98264426"
    - inkscape:original="M 18.3125 9.5 C 15.628812 9.5024673 15.114298 10.050723 14.6875 11.65625 C 14.260701 13.261784 14.341304 13.889574 16.78125 15.3125 C 18.9252 15.89155 21.010703 15.074284 21.4375 13.46875 C 21.864298 11.863222 20.45645 10.07905 18.3125 9.5 z "
    - xlink:href="#path2216"
    - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
    - id="path2238"
    - inkscape:href="#path2216"
    - d="M 18.21875,11.46875 C 17.125733,11.491249 16.482945,11.618664 16.28125,11.75 C 16.063159,11.892013 15.916049,12.106986 15.71875,12.84375 C 15.515581,13.602428 15.521796,13.851934 15.625,14.0625 C 15.720814,14.257989 16.227533,14.71374 17.21875,15.3125 C 18.053555,15.507642 18.8825,15.450801 19.46875,15.21875 C 20.087515,14.973829 20.440615,14.611396 20.5625,14.15625 C 20.683546,13.704238 20.542643,13.182022 20.125,12.65625 C 19.7273,12.155585 19.044575,11.716527 18.21875,11.46875 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.00000048;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 18.231066,10.043051 C 15.826638,10.834882 15.435297,10.552414 14.841659,9.5080341 C 14.248025,8.4636559 14.292902,7.895729 16.08021,6.2590696 C 17.825828,5.235395 19.724355,5.2521994 20.317989,6.2965763 C 20.911627,7.3409564 19.976685,9.0193762 18.231066,10.043051 z "
    - id="path2224"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - style="fill:#ef2929;fill-opacity:1;stroke:#a40000;stroke-width:1.00000119;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 7.2295458,17.045044 C 4.8262841,17.837501 4.435133,17.554808 3.8417836,16.509603 C 3.2484366,15.464398 3.2932918,14.896022 5.0797326,13.258068 C 6.8245041,12.233585 8.7221095,12.250402 9.3154554,13.295606 C 9.9088085,14.34081 8.9743169,16.020559 7.2295458,17.045044 z "
    - id="path2228"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-1.0000435"
    - inkscape:original="M 7.625 12.5 C 6.858334 12.493206 5.9661358 12.737758 5.09375 13.25 C 3.3073092 14.887954 3.2504031 15.454794 3.84375 16.5 C 4.4370994 17.545205 4.8154885 17.823707 7.21875 17.03125 C 8.9635211 16.006765 9.9058531 14.326454 9.3125 13.28125 C 9.0158271 12.758648 8.391666 12.506794 7.625 12.5 z "
    - xlink:href="#path2228"
    - style="opacity:0.35;fill:url(#linearGradient2367);fill-opacity:1.0;stroke:#ffffff;stroke-width:1.0000006;stroke-miterlimit:4;stroke-opacity:1"
    - id="path2244"
    - inkscape:href="#path2228"
    - d="M 7.6875,14.46875 C 7.1850852,14.464327 6.4432058,14.680127 5.75,15.0625 C 5.0356604,15.736543 4.6689236,16.21317 4.625,16.34375 C 4.5776671,16.484465 4.5627519,16.46929 4.8125,16.90625 C 4.9456222,17.139159 5.046245,17.29527 5.09375,17.34375 C 5.141255,17.39223 5.1063252,17.364089 5.15625,17.375 C 5.2475037,17.394943 5.8597606,17.320365 6.8125,17.03125 C 7.5124448,16.612892 8.0523433,16.090381 8.3125,15.625 C 8.5782457,15.149621 8.5343603,14.864791 8.46875,14.75 C 8.3973728,14.625118 8.2193466,14.473432 7.6875,14.46875 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.0000006;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 7.6891342,15.77365 C 9.1118334,13.334726 9.7586709,13.278647 11.361671,13.703963 C 12.964665,14.129276 13.496553,14.633183 13.497097,17.314646 C 12.917316,19.457228 11.145785,20.850948 9.5427895,20.425634 C 7.93979,20.000318 7.109353,17.916231 7.6891342,15.77365 z "
    - id="path2218"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-1.0089091"
    - inkscape:original="M 9.46875 13.65625 C 8.9288282 13.921971 8.3988496 14.561788 7.6875 15.78125 C 7.1077188 17.923831 7.9282505 20.012184 9.53125 20.4375 C 11.134246 20.862814 12.920219 19.455082 13.5 17.3125 C 13.499456 14.631037 12.977994 14.144063 11.375 13.71875 C 10.5735 13.506092 10.008672 13.390529 9.46875 13.65625 z "
    - xlink:href="#path2218"
    - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
    - id="path2240"
    - inkscape:href="#path2218"
    - d="M 9.9375,15.46875 C 9.758584,15.557674 9.317928,16.067421 8.71875,17.0625 C 8.5249462,17.893222 8.580847,18.722852 8.8125,19.3125 C 9.0570015,19.934852 9.4058239,20.288908 9.84375,20.40625 C 10.284224,20.524275 10.791323,20.413461 11.3125,20 C 11.80879,19.606283 12.253545,18.915371 12.5,18.09375 C 12.478422,16.998381 12.350171,16.329407 12.21875,16.125 C 12.076813,15.904237 11.884049,15.788763 11.15625,15.59375 C 10.38798,15.387892 10.114118,15.380968 9.9375,15.46875 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.00000036;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 11.045283,4.7716972 C 11.837827,7.1821833 11.555107,7.5745105 10.509784,8.1696432 C 9.4644611,8.7647745 8.8960214,8.7197841 7.2578858,6.9279732 C 6.2332874,5.1779568 6.2501071,3.2746475 7.2954271,2.6795158 C 8.3407485,2.0843833 10.020684,3.0216808 11.045283,4.7716972 z "
    - id="path2222"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.96927398"
    - inkscape:original="M 8.1875 2.5 C 7.8669984 2.4684749 7.5425803 2.5387169 7.28125 2.6875 C 6.23593 3.2826317 6.2254015 5.1874835 7.25 6.9375 C 8.8881356 8.7293109 9.4546774 8.7513811 10.5 8.15625 C 11.545323 7.5611173 11.823794 7.1917363 11.03125 4.78125 C 10.262801 3.4687377 9.1490049 2.5945754 8.1875 2.5 z "
    - xlink:href="#path2222"
    - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
    - id="path2232"
    - inkscape:href="#path2222"
    - d="M 8.15625,4.46875 C 7.9850798,4.4518474 7.8807377,4.4788593 7.84375,4.5 C 7.7166113,4.5726675 7.5662606,4.8028687 7.5625,5.34375 C 7.5588632,5.8668265 7.7601774,6.5753594 8.15625,7.28125 C 8.8326599,7.9986853 9.3286876,8.3591367 9.46875,8.40625 C 9.6195275,8.4569676 9.6232062,8.4698326 10.0625,8.21875 C 10.296133,8.0852146 10.416881,7.988326 10.46875,7.9375 C 10.520619,7.886674 10.519109,7.8993051 10.53125,7.84375 C 10.553442,7.7422051 10.477378,7.1419568 10.1875,6.1875 C 9.551335,5.1240857 8.6060031,4.5131619 8.15625,4.46875 z "
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.00000048;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 7.2320598,6.6863636 C 9.6715923,8.1091434 9.7272104,8.7568487 9.3003538,10.362442 C 8.8734993,11.96803 8.3688269,12.501065 5.6854848,12.503723 C 3.5418523,11.924813 2.1485268,10.151886 2.5753806,8.5462983 C 3.002236,6.9407045 5.0884273,6.1074547 7.2320598,6.6863636 z "
    - id="path2220"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-1.0062078"
    - inkscape:original="M 7.21875 6.6875 C 5.0751175 6.1085911 2.9893554 6.9256565 2.5625 8.53125 C 2.1356462 10.136838 3.5438674 11.92109 5.6875 12.5 C 8.3708421 12.497342 8.8856453 11.980588 9.3125 10.375 C 9.7393566 8.7694067 9.6582823 8.1102797 7.21875 6.6875 z "
    - xlink:href="#path2220"
    - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
    - id="path2234"
    - inkscape:href="#path2220"
    - d="M 6.90625,8.625 C 6.0757552,8.4313739 5.2679182,8.4577406 4.6875,8.6875 C 4.0744067,8.9301939 3.7136786,9.3024203 3.59375,9.75 C 3.4731424,10.200114 3.5937551,10.700108 4,11.21875 C 4.386622,11.71234 5.0555378,12.160107 5.875,12.40625 C 6.9710054,12.383966 7.6389832,12.256351 7.84375,12.125 C 8.0649019,11.983138 8.1794028,11.792477 8.375,11.0625 C 8.5781494,10.304338 8.5991322,10.046076 8.5,9.84375 C 8.40803,9.6560422 7.8949355,9.2223583 6.90625,8.625 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-1.0066811"
    - inkscape:original="M 18.625 5.5 C 17.857962 5.4932111 16.966559 5.7381628 16.09375 6.25 C 14.306442 7.8866594 14.250116 8.4556221 14.84375 9.5 C 15.437388 10.54438 15.814322 10.823081 18.21875 10.03125 C 19.964369 9.0075752 20.906138 7.3256299 20.3125 6.28125 C 20.015683 5.7590616 19.392038 5.5067889 18.625 5.5 z "
    - xlink:href="#path2224"
    - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
    - id="path2236"
    - inkscape:href="#path2224"
    - d="M 18.65625,6.625 C 18.144193,6.6204563 17.412705,6.8326612 16.71875,7.21875 C 15.998485,7.9033831 15.63724,8.4003618 15.59375,8.53125 C 15.546933,8.6721515 15.527986,8.6470459 15.78125,9.09375 C 15.914256,9.3283439 16.014881,9.4828276 16.0625,9.53125 C 16.110119,9.5796724 16.073321,9.5513897 16.125,9.5625 C 16.221307,9.5832048 16.847896,9.4973699 17.84375,9.1875 C 18.526233,8.7666523 19.058938,8.2387438 19.3125,7.78125 C 19.577081,7.3038742 19.539587,7.0311908 19.46875,6.90625 C 19.408404,6.7998131 19.198718,6.6298135 18.65625,6.625 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.00000024;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 16.312776,6.2243452 C 14.884428,8.6655097 14.236701,8.7225955 12.632405,8.2997676 C 11.028107,7.8769402 10.496367,7.373854 10.500103,4.6923611 C 11.08402,2.548855 12.859963,1.1523628 14.464261,1.5751903 C 16.068559,1.9980181 16.896691,4.0808393 16.312776,6.2243452 z "
    - id="path2192"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.99950546"
    - inkscape:original="M 14.46875 1.5625 C 12.864452 1.1396725 11.083917 2.5439941 10.5 4.6875 C 10.496264 7.3689929 11.020702 7.8896727 12.625 8.3125 C 14.229296 8.7353279 14.884151 8.6599145 16.3125 6.21875 C 16.896415 4.0752441 16.073048 1.9853278 14.46875 1.5625 z "
    - xlink:href="#path2192"
    - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
    - id="path2230"
    - inkscape:href="#path2192"
    - d="M 14.21875,2.53125 C 13.768437,2.4124791 13.248017,2.520656 12.71875,2.9375 C 12.214757,3.3344386 11.780816,4.0240635 11.53125,4.84375 C 11.550949,5.9366779 11.679626,6.6046957 11.8125,6.8125 C 11.956007,7.0369325 12.144428,7.1510597 12.875,7.34375 C 13.630622,7.5430474 13.875456,7.5464752 14.09375,7.4375 C 14.296413,7.3363279 14.739612,6.8347534 15.34375,5.84375 C 15.539967,5.0157934 15.511564,4.2073905 15.28125,3.625 C 15.03797,3.0098233 14.669402,2.6501106 14.21875,2.53125 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#edd400;fill-opacity:1;stroke:#c4a000;stroke-width:0.43295163;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path1317"
    - sodipodi:cx="12.075002"
    - sodipodi:cy="12.360133"
    - sodipodi:rx="1.1966218"
    - sodipodi:ry="1.4141895"
    - d="M 13.271623 12.360133 A 1.1966218 1.4141895 0 1 1 10.87838,12.360133 A 1.1966218 1.4141895 0 1 1 13.271623 12.360133 z"
    - transform="matrix(2.422923,0.638108,-0.560042,2.054325,-10.83461,-21.59688)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.4;fill:url(#linearGradient2327);fill-opacity:1.0;stroke:#ffffff;stroke-width:0.64902174;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2319"
    - sodipodi:cx="12.075002"
    - sodipodi:cy="12.360133"
    - sodipodi:rx="1.1966218"
    - sodipodi:ry="1.4141895"
    - d="M 13.271623 12.360133 A 1.1966218 1.4141895 0 1 1 10.87838,12.360133 A 1.1966218 1.4141895 0 1 1 13.271623 12.360133 z"
    - transform="matrix(1.616292,0.425671,-0.373594,1.370406,-3.399047,-10.57838)" />
    - </g>
    -</svg>
    Binary file pidgin/pixmaps/protocols/48/aim.png has changed
    Binary file pidgin/pixmaps/protocols/48/icq.png has changed
    --- a/pidgin/pixmaps/protocols/scalable/aim.svg Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,188 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="48"
    - height="48"
    - id="svg2"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - version="1.0"
    - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols/48"
    - sodipodi:docname="aim.svg"
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/48/aim.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2263">
    - <stop
    - style="stop-color:#edc100;stop-opacity:1;"
    - offset="0"
    - id="stop2265" />
    - <stop
    - style="stop-color:#edc100;stop-opacity:0;"
    - offset="1"
    - id="stop2267" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2255">
    - <stop
    - style="stop-color:#edc100;stop-opacity:1;"
    - offset="0"
    - id="stop2257" />
    - <stop
    - style="stop-color:#edc100;stop-opacity:0;"
    - offset="1"
    - id="stop2259" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient3150">
    - <stop
    - style="stop-color:#2e3436;stop-opacity:1;"
    - offset="0"
    - id="stop3152" />
    - <stop
    - style="stop-color:#2e3436;stop-opacity:0;"
    - offset="1"
    - id="stop3154" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient3150"
    - id="radialGradient4330"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
    - cx="10.748654"
    - cy="10.457643"
    - fx="10.748654"
    - fy="10.457643"
    - r="6.6449099" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2255"
    - id="linearGradient2261"
    - x1="12.514956"
    - y1="18.690643"
    - x2="12.514956"
    - y2="3.9849093"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(1.943689,0,0,1.932352,0.678089,2.050925)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2263"
    - id="linearGradient2269"
    - x1="10.555883"
    - y1="8.0642843"
    - x2="10.555883"
    - y2="1.4995424"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="12.865422"
    - inkscape:cx="46.678288"
    - inkscape:cy="27.286544"
    - inkscape:document-units="px"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - fill="#edd400"
    - showguides="true"
    - inkscape:guide-bbox="true"
    - inkscape:window-width="1268"
    - inkscape:window-height="971"
    - inkscape:window-x="6"
    - inkscape:window-y="21" />
    - <metadata
    - id="metadata7">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer"
    - id="layer1">
    - <path
    - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.99999982;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - d="M 23.921379,18.772128 C 20.531101,16.831234 19.828661,25.427448 17.466079,29.485095 C 15.153694,33.456532 7.4937606,36.040098 7.4937606,36.040098 L 12.246433,45.509102 L 23.851631,34.738731 C 23.851631,34.738731 31.648476,39.799175 34.033545,45.317469 L 40.310948,39.242775 C 40.310948,39.242775 37.012166,32.72261 29.680284,28.21162 L 40.506226,26.642559 L 38.998349,19.157061 C 31.992149,20.83538 27.747274,20.962407 23.921379,18.772128 z "
    - id="path1324"
    - sodipodi:nodetypes="czccccccccs" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.72577804;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path1326"
    - sodipodi:cx="10.555883"
    - sodipodi:cy="4.0385542"
    - sodipodi:rx="5.074944"
    - sodipodi:ry="5.074944"
    - d="M 15.630827 4.0385542 A 5.074944 5.074944 0 1 1 5.4809394,4.0385542 A 5.074944 5.074944 0 1 1 15.630827 4.0385542 z"
    - transform="matrix(1.37826,0,0,1.377401,12.95457,3.935583)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.25;fill:url(#radialGradient4330);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3140"
    - sodipodi:cx="10.748654"
    - sodipodi:cy="10.457643"
    - sodipodi:rx="6.6449099"
    - sodipodi:ry="2.3675451"
    - d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
    - transform="matrix(3.310805,0,0,2.323084,-11.5867,18.20601)" />
    - <path
    - style="opacity:1;fill:url(#linearGradient2261);fill-opacity:1;stroke:#b49500;stroke-width:0.99999982;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - d="M 23.921379,18.772128 C 20.531101,16.831234 21.460943,23.017888 19.098361,27.075535 C 16.785976,31.046972 7.4937606,36.040098 7.4937606,36.040098 L 12.246433,45.509102 L 23.851631,34.738731 C 23.851631,34.738731 31.648476,39.799175 34.033545,45.317469 L 40.310948,39.242775 C 40.310948,39.242775 37.012166,32.72261 29.680284,28.21162 L 40.661681,26.564831 L 38.687438,18.690695 C 31.681238,20.369014 27.747274,20.962407 23.921379,18.772128 z "
    - id="path4275"
    - sodipodi:nodetypes="czccccccccs" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.97533494"
    - inkscape:original="M 22.84375 18.40625 C 20.718205 18.345643 21.161009 23.512059 19.09375 27.0625 C 16.781366 31.033937 7.5 36.03125 7.5 36.03125 L 12.25 45.5 L 23.84375 34.75 C 23.84375 34.75 31.646181 39.794206 34.03125 45.3125 L 40.3125 39.25 C 40.312501 39.25 37.019382 32.72974 29.6875 28.21875 L 40.65625 26.5625 L 38.6875 18.6875 C 31.681299 20.365819 27.732145 20.971529 23.90625 18.78125 C 23.482465 18.538638 23.147399 18.414908 22.84375 18.40625 z "
    - xlink:href="#path4275"
    - style="opacity:0.5;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path4304"
    - inkscape:href="#path4275"
    - d="M 22.53125,19.15625 C 22.35655,19.185124 22.015713,19.423413 21.625,20.09375 C 21.234287,20.764087 20.848022,21.75407 20.46875,22.875 C 19.710206,25.11686 19.002281,27.842341 17.84375,29.84375 C 16.485288,32.190546 13.833524,33.858876 11.53125,35.0625 C 9.7758989,35.980194 8.9024732,36.291093 8.25,36.53125 L 12,44.09375 L 22.8125,34 C 23.141077,33.70262 23.62883,33.664114 24,33.90625 C 24,33.90625 26.045443,35.241847 28.375,37.28125 C 30.391732,39.046791 32.509362,41.396107 33.9375,43.96875 L 38.875,39.1875 C 38.425182,38.341062 35.513684,33.014709 28.875,28.90625 C 28.511738,28.695573 28.324082,28.276051 28.409163,27.864827 C 28.494244,27.453602 28.832963,27.142985 29.25,27.09375 L 39.1875,25.65625 L 38.03125,19.96875 C 31.456326,21.456721 26.979371,21.554763 23.03125,19.28125 C 22.754401,19.121827 22.666044,19.133971 22.53125,19.15625 z " />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:url(#linearGradient2269);fill-opacity:1;stroke:#b49500;stroke-width:0.72577804;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path4302"
    - sodipodi:cx="10.555883"
    - sodipodi:cy="4.0385542"
    - sodipodi:rx="5.074944"
    - sodipodi:ry="5.074944"
    - d="M 15.630827 4.0385542 A 5.074944 5.074944 0 1 1 5.4809394,4.0385542 A 5.074944 5.074944 0 1 1 15.630827 4.0385542 z"
    - transform="matrix(1.37826,0,0,1.377401,13.96243,3.935583)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.5;fill:#fce94f;fill-opacity:1;stroke:#ffffff;stroke-width:0.84666103;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path4273"
    - sodipodi:cx="10.555883"
    - sodipodi:cy="4.0385542"
    - sodipodi:rx="5.074944"
    - sodipodi:ry="5.074944"
    - d="M 15.630827 4.0385542 A 5.074944 5.074944 0 1 1 5.4809394,4.0385542 A 5.074944 5.074944 0 1 1 15.630827 4.0385542 z"
    - transform="matrix(1.181111,0,0,1.181111,16.03826,4.728084)" />
    - </g>
    -</svg>
    --- a/pidgin/pixmaps/protocols/scalable/icq.svg Mon Apr 05 20:41:42 2021 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,440 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="48"
    - height="48"
    - id="svg2"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - version="1.0"
    - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols/48"
    - sodipodi:docname="icq.svg"
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/48/icq.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2256">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2258" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2260" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2248">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2250" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2252" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2240">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2242" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2244" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2232">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2234" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2236" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2224">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2226" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2228" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2216">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2218" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2220" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2208">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2210" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2212" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2361">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2363" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2365" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2321">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2323" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2325" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient3150">
    - <stop
    - style="stop-color:#2e3436;stop-opacity:1;"
    - offset="0"
    - id="stop3152" />
    - <stop
    - style="stop-color:#2e3436;stop-opacity:0;"
    - offset="1"
    - id="stop3154" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient3150"
    - id="radialGradient3156"
    - cx="10.748654"
    - cy="10.457643"
    - fx="10.748654"
    - fy="10.457643"
    - r="6.6449099"
    - gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2321"
    - id="linearGradient2327"
    - x1="11.787398"
    - y1="11.115861"
    - x2="12.185872"
    - y2="12.839791"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2361"
    - id="linearGradient2367"
    - x1="14.592834"
    - y1="24.232048"
    - x2="14.592834"
    - y2="31.007147"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2208"
    - id="linearGradient2214"
    - x1="28.089931"
    - y1="3.8865747"
    - x2="28.089931"
    - y2="15.058928"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2216"
    - id="linearGradient2222"
    - x1="36.696918"
    - y1="10.99979"
    - x2="36.696918"
    - y2="20.717306"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2224"
    - id="linearGradient2230"
    - x1="36.701996"
    - y1="20.379145"
    - x2="36.701996"
    - y2="29.063459"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2232"
    - id="linearGradient2238"
    - x1="17.892992"
    - y1="6.7056818"
    - x2="17.892992"
    - y2="16.46983"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2240"
    - id="linearGradient2246"
    - x1="12.732279"
    - y1="14.645196"
    - x2="12.732279"
    - y2="23.238768"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2248"
    - id="linearGradient2254"
    - x1="21.683891"
    - y1="24.981401"
    - x2="21.683891"
    - y2="36.415653"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2256"
    - id="linearGradient2262"
    - x1="30.265167"
    - y1="27.26486"
    - x2="30.265167"
    - y2="35.256603"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="14.778489"
    - inkscape:cx="38.03465"
    - inkscape:cy="24.512139"
    - inkscape:document-units="px"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - fill="#ef2929"
    - inkscape:window-width="1268"
    - inkscape:window-height="971"
    - inkscape:window-x="6"
    - inkscape:window-y="21" />
    - <metadata
    - id="metadata7">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer"
    - id="layer1">
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.5;fill:url(#radialGradient3156);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3140"
    - sodipodi:cx="10.748654"
    - sodipodi:cy="10.457643"
    - sodipodi:rx="6.6449099"
    - sodipodi:ry="2.3675451"
    - d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
    - transform="matrix(3.009823,0,0,2.74546,-8.351546,7.855242)" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.00000048;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 26.367384,35.737238 C 24.85713,31.115741 25.395878,30.363552 27.387814,29.222534 C 29.379747,28.081519 30.462949,28.167777 33.584537,31.603121 C 35.536982,34.958339 35.504926,38.607457 33.513001,39.748468 C 31.521063,40.889489 28.319827,39.092453 26.367384,35.737238 z "
    - id="path2226"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.95876986"
    - inkscape:original="M 28.75 28.625 C 28.319559 28.723691 27.872983 28.933496 27.375 29.21875 C 25.383064 30.359768 24.864746 31.128501 26.375 35.75 C 28.327443 39.105215 31.508063 40.891023 33.5 39.75 C 35.491925 38.608989 35.546194 34.948968 33.59375 31.59375 C 31.252559 29.017242 30.041322 28.328926 28.75 28.625 z "
    - xlink:href="#path2226"
    - style="opacity:0.4;fill:url(#linearGradient2262);fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2242"
    - inkscape:href="#path2226"
    - d="M 28.96875,26.5 C 28.686015,26.564825 28.303156,26.736844 27.84375,27 C 27.368858,27.272027 27.042376,27.491026 26.84375,27.6875 C 26.645124,27.883974 26.558141,28.035355 26.5,28.3125 C 26.386493,28.853563 26.554045,30.085975 27.25,32.25 C 28.145595,33.765674 29.317595,34.913452 30.40625,35.53125 C 31.510083,36.157661 32.416733,36.195758 33.03125,35.84375 C 33.64681,35.491146 34.046689,34.69972 34.0625,33.4375 C 34.077874,32.210233 33.659976,30.628765 32.8125,29.125 C 31.711537,27.921881 30.874722,27.139693 30.28125,26.78125 C 29.672583,26.41363 29.393014,26.402725 28.96875,26.5 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:0.99999982;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 33.709491,31.278338 C 29.169302,28.745112 29.065713,27.592023 29.859889,24.733701 C 30.654065,21.875391 31.593198,20.926512 36.586936,20.922121 C 40.576342,21.952999 43.16957,25.109445 42.375396,27.967758 C 41.581221,30.826078 37.6989,32.309217 33.709491,31.278338 z "
    - id="path2216"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.98264426"
    - inkscape:original="M 36.59375 20.9375 C 31.600012 20.941891 30.669177 21.860439 29.875 24.71875 C 29.080824 27.577072 29.17856 28.748025 33.71875 31.28125 C 37.708159 32.312129 41.580824 30.82707 42.375 27.96875 C 43.169174 25.110437 40.583155 21.968378 36.59375 20.9375 z "
    - xlink:href="#path2216"
    - style="opacity:0.4;fill:url(#linearGradient2230);fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2238"
    - inkscape:href="#path2216"
    - d="M 36.4375,18.8125 C 34.127551,18.828525 32.839067,19.094363 32.1875,19.5 C 31.517812,19.916918 31.190595,20.545455 30.8125,21.90625 C 30.427461,23.292046 30.362754,23.987827 30.6875,24.625 C 31.003678,25.245362 31.967431,26.095761 34.0625,27.28125 C 35.838582,27.720407 37.581667,27.600958 38.90625,27.09375 C 40.254639,26.577426 41.138951,25.730758 41.4375,24.65625 C 41.735829,23.582532 41.424738,22.405518 40.53125,21.3125 C 39.654391,20.239825 38.199379,19.288753 36.4375,18.8125 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.0000006;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 36.67848,22.648043 C 32.052133,24.231706 31.299156,23.66677 30.15694,21.57801 C 29.014732,19.489253 29.101081,18.3534 32.540028,15.08008 C 35.898764,13.032731 39.551708,13.066339 40.693916,15.155093 C 41.836132,17.243854 40.037215,20.600694 36.67848,22.648043 z "
    - id="path2224"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - style="fill:#ef2929;fill-opacity:1;stroke:#a40000;stroke-width:1.00000167;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 15.680976,33.623016 C 11.05302,35.149988 10.299782,34.605272 9.1571703,32.591284 C 8.0145636,30.577295 8.100941,29.482102 11.541085,26.325956 C 14.900987,24.351896 18.555202,24.3843 19.697807,26.398287 C 20.840426,28.412273 19.040877,31.648952 15.680976,33.623016 z "
    - id="path2228"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-1.0000435"
    - inkscape:original="M 16.4375 24.875 C 14.961133 24.861908 13.211201 25.32547 11.53125 26.3125 C 8.091106 29.468646 8.0136433 30.579759 9.15625 32.59375 C 10.298862 34.607738 11.059544 35.151972 15.6875 33.625 C 19.047401 31.650936 20.830119 28.420236 19.6875 26.40625 C 19.116197 25.399256 17.913867 24.888092 16.4375 24.875 z "
    - xlink:href="#path2228"
    - style="opacity:0.35;fill:url(#linearGradient2367);fill-opacity:1;stroke:#ffffff;stroke-width:1.0000006;stroke-miterlimit:4;stroke-opacity:1"
    - id="path2244"
    - inkscape:href="#path2228"
    - d="M 16.4375,25.5625 C 15.225911,25.551756 13.660678,25.986763 12.15625,26.84375 C 10.550146,28.330129 9.7557904,29.328548 9.5625,29.90625 C 9.3649665,30.496634 9.5025865,30.849414 10.03125,31.78125 C 10.302942,32.260138 10.523169,32.613015 10.71875,32.8125 C 10.914331,33.011985 11.040068,33.097389 11.3125,33.15625 C 11.836378,33.269438 13.116625,33.081343 15.21875,32.40625 C 16.739932,31.503646 17.889523,30.32189 18.5,29.21875 C 19.115832,28.105933 19.160409,27.206976 18.8125,26.59375 C 18.466678,25.984194 17.687878,25.573588 16.4375,25.5625 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - transform="translate(-3.261295e-4,1.945628e-4)" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.00000036;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 15.781196,31.980797 C 18.406839,27.241987 19.600601,27.133027 22.558995,27.959412 C 25.517377,28.785792 26.498997,29.764879 26.5,34.97494 C 25.429993,39.137961 22.160569,41.845948 19.202184,41.019566 C 16.24379,40.193181 14.711189,36.143816 15.781196,31.980797 z "
    - id="path2218"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-1.0089091"
    - inkscape:original="M 20.625 27.5625 C 18.918105 27.466646 17.750483 28.414643 15.78125 31.96875 C 14.711243 36.131769 16.229105 40.204865 19.1875 41.03125 C 22.145885 41.857632 25.429993 39.13177 26.5 34.96875 C 26.498997 29.758689 25.520883 28.79513 22.5625 27.96875 C 21.822902 27.762154 21.193965 27.594451 20.625 27.5625 z "
    - xlink:href="#path2218"
    - style="opacity:0.4;fill:url(#linearGradient2254);fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2240"
    - inkscape:href="#path2218"
    - d="M 20.5625,25.5 C 19.907811,25.463235 19.561004,25.566238 18.96875,26.09375 C 18.39939,26.600871 17.659523,27.663233 16.75,29.28125 C 16.300255,31.130934 16.385773,32.955321 16.90625,34.34375 C 17.438569,35.763767 18.360161,36.690332 19.46875,37 C 20.577929,37.309833 21.771436,36.972415 22.90625,36.03125 C 24.020697,35.106976 25.004678,33.595596 25.5,31.75 C 25.485662,29.33285 25.202556,27.963789 24.78125,27.28125 C 24.348724,26.580535 23.69033,26.268606 22.28125,25.875 C 21.548137,25.670215 20.975703,25.523204 20.5625,25.5 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.0000006;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 22.589636,12.085809 C 24.174724,16.906782 23.609284,17.691436 21.518638,18.881701 C 19.427991,20.071964 18.291112,19.981984 15.014841,16.398361 C 12.965643,12.898328 12.999283,9.0917088 15.089923,7.9014452 C 17.180566,6.7111801 20.540438,8.5857754 22.589636,12.085809 z "
    - id="path2222"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.96927398"
    - inkscape:original="M 16.84375 7.5625 C 16.202747 7.4994497 15.616411 7.6086837 15.09375 7.90625 C 13.00311 9.0965136 12.950802 12.906217 15 16.40625 C 18.276271 19.989873 19.440602 20.065263 21.53125 18.875 C 23.621896 17.684735 24.178838 16.914723 22.59375 12.09375 C 21.056852 9.4687248 18.76676 7.7516509 16.84375 7.5625 z "
    - xlink:href="#path2222"
    - style="opacity:0.4;fill:url(#linearGradient2238);fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2232"
    - inkscape:href="#path2222"
    - d="M 16.75,5.46875 C 16.27484,5.4220124 15.902933,5.4936816 15.5625,5.6875 C 14.90056,6.0643619 14.484412,6.9299953 14.46875,8.25 C 14.453381,9.5453187 14.903286,11.163341 15.8125,12.75 C 17.344427,14.407623 18.369904,15.229192 19,15.4375 C 19.647022,15.651404 20.092056,15.52125 21.0625,14.96875 C 21.562192,14.684261 21.911138,14.459507 22.125,14.25 C 22.338862,14.040493 22.436992,13.887548 22.5,13.59375 C 22.621375,13.027792 22.42463,11.70151 21.71875,9.5 C 20.300551,7.1027674 18.170695,5.6084922 16.75,5.46875 z "
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - transform="translate(0,6.766591e-2)" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.00000072;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 15.110245,15.90519 C 19.82436,18.526878 19.931835,19.720374 19.106984,22.678924 C 18.282136,25.637465 17.306916,26.619664 12.121668,26.624562 C 7.9793465,25.557832 5.2869063,22.290944 6.1117518,19.332403 C 6.9366004,16.373851 10.967924,14.838461 15.110245,15.90519 z "
    - id="path2220"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-1.0062078"
    - inkscape:original="M 12.0625 15.5625 C 9.1424837 15.682984 6.7436363 17.124836 6.125 19.34375 C 5.3001545 22.302291 7.9826786 25.558271 12.125 26.625 C 17.310248 26.620102 18.268903 25.64604 19.09375 22.6875 C 19.918601 19.72895 19.839115 18.527938 15.125 15.90625 C 14.08942 15.639568 13.035839 15.522339 12.0625 15.5625 z "
    - xlink:href="#path2220"
    - style="opacity:0.4;fill:url(#linearGradient2246);fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2234"
    - inkscape:href="#path2220"
    - d="M 12.09375,13.5 C 9.4814409,13.607788 7.5619569,14.883144 7.09375,16.5625 C 6.7827532,17.677978 7.1032888,18.873814 8.03125,20 C 8.9425567,21.105974 10.44513,22.068869 12.28125,22.5625 C 14.685442,22.546282 16.040043,22.264255 16.71875,21.84375 C 17.415725,21.411927 17.731987,20.753401 18.125,19.34375 C 18.525721,17.906458 18.614647,17.219888 18.28125,16.5625 C 17.956369,15.921903 16.929755,15.04136 14.75,13.8125 C 13.845755,13.591694 12.918322,13.465977 12.09375,13.5 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-1.0066811"
    - inkscape:original="M 37.4375 13.5625 C 35.961646 13.548922 34.210618 14.070075 32.53125 15.09375 C 29.092303 18.36707 29.014042 19.473742 30.15625 21.5625 C 31.298466 23.65126 32.061155 24.239913 36.6875 22.65625 C 40.046235 20.608901 41.829716 17.245011 40.6875 15.15625 C 40.116396 14.111873 38.913354 13.576078 37.4375 13.5625 z "
    - xlink:href="#path2224"
    - style="opacity:0.4;fill:url(#linearGradient2222);fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2236"
    - inkscape:href="#path2224"
    - d="M 37.4375,11.5 C 36.209007,11.488698 34.640843,11.934525 33.125,12.84375 C 31.572994,14.343465 30.790495,15.387459 30.59375,16 C 30.38941,16.636186 30.501711,17.062882 31.03125,18.03125 C 31.303727,18.529526 31.551384,18.882242 31.75,19.09375 C 31.948616,19.305258 32.078397,19.376294 32.34375,19.4375 C 32.849897,19.554247 34.121629,19.37131 36.1875,18.6875 C 37.705568,17.753005 38.884734,16.530737 39.5,15.375 C 40.120629,14.209188 40.168638,13.213767 39.8125,12.5625 C 39.457388,11.913107 38.674773,11.511383 37.4375,11.5 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - style="fill:#73d216;fill-opacity:1;stroke:#4e9a06;stroke-width:1.00000048;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 33.125387,15.014401 C 30.26869,19.896731 28.973236,20.010902 25.764644,19.165247 C 22.556047,18.319592 21.492567,17.31342 21.500039,11.950433 C 22.667874,7.6634206 26.219761,4.8704358 29.428357,5.7160909 C 32.636953,6.5617466 34.293217,10.72739 33.125387,15.014401 z "
    - id="path2192"
    - sodipodi:nodetypes="cscsc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.99950546"
    - inkscape:original="M 27 5.71875 C 24.592663 6.34847 22.375877 8.7222409 21.5 11.9375 C 21.492528 17.300487 22.541402 18.310595 25.75 19.15625 C 28.958592 20.001905 30.268305 19.88233 33.125 15 C 34.29283 10.712989 32.646097 6.5644059 29.4375 5.71875 C 28.635351 5.5073362 27.802446 5.5088433 27 5.71875 z "
    - xlink:href="#path2192"
    - style="opacity:0.4;fill:url(#linearGradient2214);fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2230"
    - inkscape:href="#path2192"
    - d="M 27.25,3.625 C 25.314633,4.1312603 23.314958,6.1607857 22.5,9.03125 C 22.510826,11.522864 22.789343,12.909624 23.25,13.625 C 23.722547,14.358839 24.464582,14.720327 26,15.125 C 27.560661,15.536327 28.366368,15.654754 29.125,15.28125 C 29.867191,14.91584 30.838854,13.842149 32.1875,11.5625 C 32.678344,9.6587072 32.56307,7.7926235 32,6.375 C 31.424078,4.9250185 30.443053,3.9559128 29.1875,3.625 C 28.551361,3.4573398 27.889759,3.4576495 27.25,3.625 z "
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/path2232.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#edd400;fill-opacity:1;stroke:#c4a000;stroke-width:0.21428883;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path1317"
    - sodipodi:cx="12.075002"
    - sodipodi:cy="12.360133"
    - sodipodi:rx="1.1966218"
    - sodipodi:ry="1.4141895"
    - d="M 13.271623 12.360133 A 1.1966218 1.4141895 0 1 1 10.87838,12.360133 A 1.1966218 1.4141895 0 1 1 13.271623 12.360133 z"
    - transform="matrix(4.891864,1.290148,-1.13072,4.153499,-20.5947,-43.34178)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.4;fill:url(#linearGradient2327);fill-opacity:1;stroke:#ffffff;stroke-width:0.25782824;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2319"
    - sodipodi:cx="12.075002"
    - sodipodi:cy="12.360133"
    - sodipodi:rx="1.1966218"
    - sodipodi:ry="1.4141895"
    - d="M 13.271623 12.360133 A 1.1966218 1.4141895 0 1 1 10.87838,12.360133 A 1.1966218 1.4141895 0 1 1 13.271623 12.360133 z"
    - transform="matrix(4.068634,1.071527,-0.940435,3.449674,-13.02699,-32.06442)" />
    - </g>
    -</svg>
    --- a/pidgin/win32/nsis/pidgin-installer.nsi Mon Apr 05 20:41:42 2021 -0500
    +++ b/pidgin/win32/nsis/pidgin-installer.nsi Thu Apr 08 22:30:53 2021 -0500
    @@ -387,7 +387,6 @@
    SectionEnd
    !macroend
    SectionGroup /e $(URIHANDLERSSECTIONTITLE) SecURIHandlers
    - !insertmacro URI_SECTION "aim"
    !insertmacro URI_SECTION "xmpp"
    SectionGroupEnd