pidgin/pidgin

Remove the oscar protocol
release-2.x.y
8 months ago, 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;
    -}